Skip to content

Commit

Permalink
render: Make InvalidateSelection take a span of viewport rects
Browse files Browse the repository at this point in the history
The renderer will consume *buffer-relative selection spans* and prepare
them into *viewport-relative rects*.

PaintSelection will still filter rects by whether they are in the dirty
area.
  • Loading branch information
DHowett committed Aug 12, 2024
1 parent bfcef9c commit a738100
Show file tree
Hide file tree
Showing 11 changed files with 78 additions and 98 deletions.
35 changes: 18 additions & 17 deletions src/renderer/atlas/AtlasEngine.api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,27 +83,13 @@ constexpr HRESULT vec2_narrow(U x, U y, vec2<T>& out) noexcept
return Invalidate(&rect);
}

[[nodiscard]] HRESULT AtlasEngine::InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept
{
for (const auto& rect : rectangles)
{
// BeginPaint() protects against invalid out of bounds numbers.
// TODO: rect can contain invalid out of bounds coordinates when the selection is being
// dragged outside of the viewport (and the window begins scrolling automatically).
_api.invalidatedRows.start = gsl::narrow_cast<u16>(std::min<int>(_api.invalidatedRows.start, std::max<int>(0, rect.top)));
_api.invalidatedRows.end = gsl::narrow_cast<u16>(std::max<int>(_api.invalidatedRows.end, std::max<int>(0, rect.bottom)));
}
return S_OK;
}

[[nodiscard]] HRESULT AtlasEngine::InvalidateHighlight(std::span<const til::point_span> highlights, const TextBuffer& buffer) noexcept
void AtlasEngine::_invalidateSpans(std::span<const til::point_span> spans, const TextBuffer& buffer) noexcept
{
const auto viewportOrigin = til::point{ _api.viewportOffset.x, _api.viewportOffset.y };
const auto viewport = til::rect{ 0, 0, _api.s->viewportCellCount.x, _api.s->viewportCellCount.y };
const auto cellCountX = static_cast<til::CoordType>(_api.s->viewportCellCount.x);
for (const auto& hi : highlights)
for (auto&& sp : spans)
{
hi.iterate_rows(cellCountX, [&](til::CoordType row, til::CoordType beg, til::CoordType end) {
sp.iterate_rows(til::CoordTypeMax, [&](til::CoordType row, til::CoordType beg, til::CoordType end) {
const auto shift = buffer.GetLineRendition(row) != LineRendition::SingleWidth ? 1 : 0;
beg <<= shift;
end <<= shift;
Expand All @@ -114,7 +100,22 @@ constexpr HRESULT vec2_narrow(U x, U y, vec2<T>& out) noexcept
_api.invalidatedRows.end = gsl::narrow_cast<u16>(std::max<int>(_api.invalidatedRows.end, std::max<int>(0, rect.bottom)));
});
}
}

[[nodiscard]] HRESULT AtlasEngine::InvalidateSelection(std::span<const til::rect> selections) noexcept
{
if (!selections.empty())
{
// INVARIANT: This assumes that `selections` is sorted by increasing Y
_api.invalidatedRows.start = gsl::narrow_cast<u16>(std::min<int>(_api.invalidatedRows.start, std::max<int>(0, selections.front().top)));
_api.invalidatedRows.end = gsl::narrow_cast<u16>(std::max<int>(_api.invalidatedRows.end, std::max<int>(0, selections.back().bottom)));
}
return S_OK;
}

[[nodiscard]] HRESULT AtlasEngine::InvalidateHighlight(std::span<const til::point_span> highlights, const TextBuffer& buffer) noexcept
{
_invalidateSpans(highlights, buffer);
return S_OK;
}

Expand Down
3 changes: 2 additions & 1 deletion src/renderer/atlas/AtlasEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace Microsoft::Console::Render::Atlas
[[nodiscard]] HRESULT Invalidate(const til::rect* psrRegion) noexcept override;
[[nodiscard]] HRESULT InvalidateCursor(const til::rect* psrRegion) noexcept override;
[[nodiscard]] HRESULT InvalidateSystem(const til::rect* prcDirtyClient) noexcept override;
[[nodiscard]] HRESULT InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept override;
[[nodiscard]] HRESULT InvalidateSelection(std::span<const til::rect> selections) noexcept override;
[[nodiscard]] HRESULT InvalidateHighlight(std::span<const til::point_span> highlights, const TextBuffer& buffer) noexcept override;
[[nodiscard]] HRESULT InvalidateScroll(const til::point* pcoordDelta) noexcept override;
[[nodiscard]] HRESULT InvalidateAll() noexcept override;
Expand Down Expand Up @@ -98,6 +98,7 @@ namespace Microsoft::Console::Render::Atlas
[[nodiscard]] HRESULT _updateFont(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, const std::unordered_map<std::wstring_view, float>& features, const std::unordered_map<std::wstring_view, float>& axes) noexcept;
void _resolveFontMetrics(const FontInfoDesired& fontInfoDesired, FontInfo& fontInfo, FontSettings* fontMetrics = nullptr);
[[nodiscard]] bool _updateWithNearbyFontCollection() noexcept;
void _invalidateSpans(std::span<const til::point_span> spans, const TextBuffer& buffer) noexcept;

// AtlasEngine.r.cpp
ATLAS_ATTR_COLD void _recreateAdapter();
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/base/RenderEngineBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using namespace Microsoft::Console;
using namespace Microsoft::Console::Render;

[[nodiscard]] HRESULT RenderEngineBase::InvalidateSelection(const std::vector<til::rect>& /*rectangles*/) noexcept
[[nodiscard]] HRESULT RenderEngineBase::InvalidateSelection(std::span<const til::rect> /*selections*/) noexcept
{
return S_OK;
}
Expand Down
78 changes: 31 additions & 47 deletions src/renderer/base/renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,32 +325,46 @@ void Renderer::TriggerTeardown() noexcept
// Return Value:
// - <none>
void Renderer::TriggerSelection()
try
{
try
const auto spans = _pData->GetSelectionSpans();
if (spans.size() != _lastSelectionPaintSize || (spans.size() && _lastSelectionPaintSpan != til::point_span{ spans.front().start, spans.back().end }))
{
// Get selection rectangles
auto rects = _GetSelectionRects();

// Make a viewport representing the coordinates that are currently presentable.
const til::rect viewport{ _pData->GetViewport().Dimensions() };
std::vector<til::rect> newSelectionViewportRects;

// Restrict all previous selection rectangles to inside the current viewport bounds
for (auto& sr : _previousSelection)
_lastSelectionPaintSize = spans.size();
if (_lastSelectionPaintSize)
{
sr &= viewport;
_lastSelectionPaintSpan = til::point_span{ spans.front().start, spans.back().end };

const auto& buffer = _pData->GetTextBuffer();
auto bufferWidth = buffer.GetSize().Width();
const til::rect vp{ _viewport.ToExclusive() };
for (auto&& sp : spans)
{
sp.iterate_rows(bufferWidth, [&](til::CoordType row, til::CoordType min, til::CoordType max) {
const auto shift = buffer.GetLineRendition(row) != LineRendition::SingleWidth ? 1 : 0;
max += 1; // Selection spans are inclusive (still)
min <<= shift;
max <<= shift;
til::rect r{ min, row, max, row + 1 };
newSelectionViewportRects.emplace_back(r.to_origin(vp));
});
}
}

FOREACH_ENGINE(pEngine)
{
LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSelection));
LOG_IF_FAILED(pEngine->InvalidateSelection(rects));
LOG_IF_FAILED(pEngine->InvalidateSelection(_lastSelectionRectsByViewport));
LOG_IF_FAILED(pEngine->InvalidateSelection(newSelectionViewportRects));
}

_previousSelection = std::move(rects);
std::exchange(_lastSelectionRectsByViewport, newSelectionViewportRects);

NotifyPaintFrame();
}
CATCH_LOG();
}
CATCH_LOG()

// Routine Description:
// - Called when the search highlight areas in the console have changed.
Expand Down Expand Up @@ -1278,15 +1292,11 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine)
std::span<const til::rect> dirtyAreas;
LOG_IF_FAILED(pEngine->GetDirtyArea(dirtyAreas));

// Get selection rectangles
const auto rectangles = _GetSelectionRects();

std::vector<til::rect> dirtySearchRectangles;
for (auto& dirtyRect : dirtyAreas)
for (auto&& dirtyRect : dirtyAreas)
{
for (const auto& rect : rectangles)
for (const auto& rect : _lastSelectionRectsByViewport)
{
if (const auto rectCopy = rect & dirtyRect)
if (const auto rectCopy{ rect & dirtyRect })
{
LOG_IF_FAILED(pEngine->PaintSelection(rectCopy));
}
Expand Down Expand Up @@ -1331,32 +1341,6 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine)
return pEngine->ScrollFrame();
}

// Routine Description:
// - Helper to determine the selected region of the buffer.
// Return Value:
// - A vector of rectangles representing the regions to select, line by line.
std::vector<til::rect> Renderer::_GetSelectionRects() const
{
const auto& buffer = _pData->GetTextBuffer();
auto rects = _pData->GetSelectionRects();
// Adjust rectangles to viewport
auto view = _pData->GetViewport();

std::vector<til::rect> result;
result.reserve(rects.size());

for (auto rect : rects)
{
// Convert buffer offsets to the equivalent range of screen cells
// expected by callers, taking line rendition into account.
const auto lineRendition = buffer.GetLineRendition(rect.Top());
rect = Viewport::FromInclusive(BufferToScreenLine(rect.ToInclusive(), lineRendition));
result.emplace_back(view.ConvertToOrigin(rect).ToExclusive());
}

return result;
}

// Method Description:
// - Offsets all of the selection rectangles we might be holding onto
// as the previously selected area. If the whole viewport scrolls,
Expand All @@ -1370,7 +1354,7 @@ void Renderer::_ScrollPreviousSelection(const til::point delta)
{
if (delta != til::point{ 0, 0 })
{
for (auto& rc : _previousSelection)
for (auto& rc : _lastSelectionRectsByViewport)
{
rc += delta;
}
Expand Down
6 changes: 4 additions & 2 deletions src/renderer/base/renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ namespace Microsoft::Console::Render
void _PaintCursor(_In_ IRenderEngine* const pEngine);
[[nodiscard]] HRESULT _UpdateDrawingBrushes(_In_ IRenderEngine* const pEngine, const TextAttribute attr, const bool usingSoftFont, const bool isSettingDefaultBrushes);
[[nodiscard]] HRESULT _PerformScrolling(_In_ IRenderEngine* const pEngine);
std::vector<til::rect> _GetSelectionRects() const;
void _ScrollPreviousSelection(const til::point delta);
[[nodiscard]] HRESULT _PaintTitle(IRenderEngine* const pEngine);
bool _isInHoveredInterval(til::point coordTarget) const noexcept;
Expand All @@ -131,11 +130,14 @@ namespace Microsoft::Console::Render
CursorOptions _currentCursorOptions;
std::optional<CompositionCache> _compositionCache;
std::vector<Cluster> _clusterBuffer;
std::vector<til::rect> _previousSelection;
std::function<void()> _pfnBackgroundColorChanged;
std::function<void()> _pfnFrameColorChanged;
std::function<void()> _pfnRendererEnteredErrorState;
bool _destructing = false;
bool _forceUpdateViewport = false;

til::point_span _lastSelectionPaintSpan{};
size_t _lastSelectionPaintSize{};
std::vector<til::rect> _lastSelectionRectsByViewport{};
};
}
2 changes: 1 addition & 1 deletion src/renderer/gdi/gdirenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace Microsoft::Console::Render

[[nodiscard]] HRESULT SetHwnd(const HWND hwnd) noexcept;

[[nodiscard]] HRESULT InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept override;
[[nodiscard]] HRESULT InvalidateSelection(std::span<const til::rect> selections) noexcept override;
[[nodiscard]] HRESULT InvalidateScroll(const til::point* const pcoordDelta) noexcept override;
[[nodiscard]] HRESULT InvalidateSystem(const til::rect* const prcDirtyClient) noexcept override;
[[nodiscard]] HRESULT Invalidate(const til::rect* const psrRegion) noexcept override;
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/gdi/invalidate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include "gdirenderer.hpp"
#include "../../types/inc/Viewport.hpp"
#include "../buffer/out/textBuffer.hpp"

#pragma hdrstop

Expand Down Expand Up @@ -46,13 +47,12 @@ HRESULT GdiEngine::InvalidateScroll(const til::point* const pcoordDelta) noexcep
// - rectangles - Vector of rectangles to draw, line by line
// Return Value:
// - HRESULT S_OK or GDI-based error code
HRESULT GdiEngine::InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept
HRESULT GdiEngine::InvalidateSelection(std::span<const til::rect> selections) noexcept
{
for (const auto& rect : rectangles)
for (auto&& rect : selections)
{
RETURN_IF_FAILED(Invalidate(&rect));
}

return S_OK;
}

Expand Down
2 changes: 1 addition & 1 deletion src/renderer/inc/IRenderEngine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] virtual HRESULT Invalidate(const til::rect* psrRegion) noexcept = 0;
[[nodiscard]] virtual HRESULT InvalidateCursor(const til::rect* psrRegion) noexcept = 0;
[[nodiscard]] virtual HRESULT InvalidateSystem(const til::rect* prcDirtyClient) noexcept = 0;
[[nodiscard]] virtual HRESULT InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept = 0;
[[nodiscard]] virtual HRESULT InvalidateSelection(std::span<const til::rect> selections) noexcept = 0;
[[nodiscard]] virtual HRESULT InvalidateHighlight(std::span<const til::point_span> highlights, const TextBuffer& buffer) noexcept = 0;
[[nodiscard]] virtual HRESULT InvalidateScroll(const til::point* pcoordDelta) noexcept = 0;
[[nodiscard]] virtual HRESULT InvalidateAll() noexcept = 0;
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/inc/RenderEngineBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ namespace Microsoft::Console::Render
class RenderEngineBase : public IRenderEngine
{
public:
[[nodiscard]] HRESULT InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept override;
[[nodiscard]] HRESULT InvalidateSelection(std::span<const til::rect> selections) noexcept override;
[[nodiscard]] HRESULT InvalidateHighlight(std::span<const til::point_span> highlights, const TextBuffer& buffer) noexcept override;
[[nodiscard]] HRESULT InvalidateTitle(const std::wstring_view proposedTitle) noexcept override;

Expand Down
38 changes: 15 additions & 23 deletions src/renderer/uia/UiaRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,42 +110,34 @@ CATCH_RETURN();
// - rectangles - One or more rectangles describing character positions on the grid
// Return Value:
// - S_OK
[[nodiscard]] HRESULT UiaEngine::InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept
[[nodiscard]] HRESULT UiaEngine::InvalidateSelection(std::span<const til::rect> rectangles) noexcept
try
{
// early exit: different number of rows
if (_prevSelection.size() != rectangles.size())
{
try
{
_selectionChanged = true;
_prevSelection = rectangles;
}
CATCH_LOG_RETURN_HR(E_FAIL);
_selectionChanged = true;
_prevSelection.assign(rectangles.begin(), rectangles.end());
return S_OK;
}

for (size_t i = 0; i < rectangles.size(); i++)
_selectionChanged = false; // assume they're the same

auto i = rectangles.begin();
auto j = _prevSelection.begin();
// safe to iterate j until i is exhausted because we checked their sizes
for (; i != rectangles.end(); ++i, ++j)
{
try
if (*i != *j)
{
const auto prevRect = _prevSelection.at(i);
const auto newRect = rectangles.at(i);

// if any value is different, selection has changed
if (prevRect.top != newRect.top || prevRect.right != newRect.right || prevRect.left != newRect.left || prevRect.bottom != newRect.bottom)
{
_selectionChanged = true;
_prevSelection = rectangles;
return S_OK;
}
_selectionChanged = true;
_prevSelection.assign(rectangles.begin(), rectangles.end());
break;
}
CATCH_LOG_RETURN_HR(E_FAIL);
}

// assume selection has not changed
_selectionChanged = false;
return S_OK;
}
CATCH_LOG_RETURN_HR(E_FAIL);

// Routine Description:
// - Scrolls the existing dirty region (if it exists) and
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/uia/UiaRenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT Invalidate(const til::rect* const psrRegion) noexcept override;
[[nodiscard]] HRESULT InvalidateCursor(const til::rect* const psrRegion) noexcept override;
[[nodiscard]] HRESULT InvalidateSystem(const til::rect* const prcDirtyClient) noexcept override;
[[nodiscard]] HRESULT InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept override;
[[nodiscard]] HRESULT InvalidateSelection(std::span<const til::rect> rectangles) noexcept override;
[[nodiscard]] HRESULT InvalidateScroll(const til::point* const pcoordDelta) noexcept override;
[[nodiscard]] HRESULT InvalidateAll() noexcept override;
[[nodiscard]] HRESULT NotifyNewText(const std::wstring_view newText) noexcept override;
Expand Down

0 comments on commit a738100

Please sign in to comment.