Move dirty interface to N rectangles, not just one (#4854)

## Summary of the Pull Request
- Changes the `IRenderEngine` interface to return a vector of values instead of just a single one. Engines that want to report one still can. Engines that want to report multiple smaller ones will be able to do so going forward.

## PR Checklist
* [x] In support of differential rendering #778
* [x] I work here.
* [x] Manually tested it still works.
* [x] Am core contributor.

## Detailed Description of the Pull Request / Additional comments
- Some of my ideas for the `DxEngine` require the ability to specify multiple smaller rectangles instead of one giant one, specifically to mitigate the case where someone refreshes just one cell in two opposite corners of the display (which currently coalesces into refreshing the entire display.)
- This is pulled out into an individual PR to make it easier to review that concept changing.

## Validation Steps Performed
- Ran the Terminal
This commit is contained in:
Michael Niksa 2020-03-10 13:31:46 -07:00 committed by GitHub
parent d954ad68f2
commit 2b6e96a745
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 93 additions and 73 deletions

View file

@ -232,7 +232,7 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid
return S_OK;
}
SMALL_RECT BgfxEngine::GetDirtyRectInChars()
std::vector<SMALL_RECT> BgfxEngine::GetDirtyArea()
{
SMALL_RECT r;
r.Bottom = _displayHeight > 0 ? (SHORT)(_displayHeight - 1) : 0;
@ -240,7 +240,7 @@ SMALL_RECT BgfxEngine::GetDirtyRectInChars()
r.Left = 0;
r.Right = _displayWidth > 0 ? (SHORT)(_displayWidth - 1) : 0;
return r;
return { r };
}
[[nodiscard]] HRESULT BgfxEngine::GetFontSize(_Out_ COORD* const pFontSize) noexcept

View file

@ -69,7 +69,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
SMALL_RECT GetDirtyRectInChars() override;
std::vector<SMALL_RECT> GetDirtyArea() override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;

View file

@ -563,49 +563,54 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine)
// This is effectively the number of cells on the visible screen that need to be redrawn.
// The origin is always 0, 0 because it represents the screen itself, not the underlying buffer.
auto dirty = Viewport::FromInclusive(pEngine->GetDirtyRectInChars());
const auto dirtyAreas = pEngine->GetDirtyArea();
// Shift the origin of the dirty region to match the underlying buffer so we can
// compare the two regions directly for intersection.
dirty = Viewport::Offset(dirty, view.Origin());
// The intersection between what is dirty on the screen (in need of repaint)
// and what is supposed to be visible on the screen (the viewport) is what
// we need to walk through line-by-line and repaint onto the screen.
const auto redraw = Viewport::Intersect(dirty, view);
// Shortcut: don't bother redrawing if the width is 0.
if (redraw.Width() > 0)
for (const auto dirtyRect : dirtyAreas)
{
// Retrieve the text buffer so we can read information out of it.
const auto& buffer = _pData->GetTextBuffer();
auto dirty = Viewport::FromInclusive(dirtyRect);
// Now walk through each row of text that we need to redraw.
for (auto row = redraw.Top(); row < redraw.BottomExclusive(); row++)
// Shift the origin of the dirty region to match the underlying buffer so we can
// compare the two regions directly for intersection.
dirty = Viewport::Offset(dirty, view.Origin());
// The intersection between what is dirty on the screen (in need of repaint)
// and what is supposed to be visible on the screen (the viewport) is what
// we need to walk through line-by-line and repaint onto the screen.
const auto redraw = Viewport::Intersect(dirty, view);
// Shortcut: don't bother redrawing if the width is 0.
if (redraw.Width() > 0)
{
// Calculate the boundaries of a single line. This is from the left to right edge of the dirty
// area in width and exactly 1 tall.
const auto bufferLine = Viewport::FromDimensions({ redraw.Left(), row }, { redraw.Width(), 1 });
// Retrieve the text buffer so we can read information out of it.
const auto& buffer = _pData->GetTextBuffer();
// Find where on the screen we should place this line information. This requires us to re-map
// the buffer-based origin of the line back onto the screen-based origin of the line
// For example, the screen might say we need to paint 1,1 because it is dirty but the viewport is actually looking
// at 13,26 relative to the buffer.
// This means that we need 14,27 out of the backing buffer to fill in the 1,1 cell of the screen.
const auto screenLine = Viewport::Offset(bufferLine, -view.Origin());
// Now walk through each row of text that we need to redraw.
for (auto row = redraw.Top(); row < redraw.BottomExclusive(); row++)
{
// Calculate the boundaries of a single line. This is from the left to right edge of the dirty
// area in width and exactly 1 tall.
const auto bufferLine = Viewport::FromDimensions({ redraw.Left(), row }, { redraw.Width(), 1 });
// Retrieve the cell information iterator limited to just this line we want to redraw.
auto it = buffer.GetCellDataAt(bufferLine.Origin(), bufferLine);
// Find where on the screen we should place this line information. This requires us to re-map
// the buffer-based origin of the line back onto the screen-based origin of the line
// For example, the screen might say we need to paint 1,1 because it is dirty but the viewport is actually looking
// at 13,26 relative to the buffer.
// This means that we need 14,27 out of the backing buffer to fill in the 1,1 cell of the screen.
const auto screenLine = Viewport::Offset(bufferLine, -view.Origin());
// Calculate if two things are true:
// 1. this row wrapped
// 2. We're painting the last col of the row.
// In that case, set lineWrapped=true for the _PaintBufferOutputHelper call.
const auto lineWrapped = (buffer.GetRowByOffset(bufferLine.Origin().Y).GetCharRow().WasWrapForced()) &&
(bufferLine.RightExclusive() == buffer.GetSize().Width());
// Retrieve the cell information iterator limited to just this line we want to redraw.
auto it = buffer.GetCellDataAt(bufferLine.Origin(), bufferLine);
// Ask the helper to paint through this specific line.
_PaintBufferOutputHelper(pEngine, it, screenLine.Origin(), lineWrapped);
// Calculate if two things are true:
// 1. this row wrapped
// 2. We're painting the last col of the row.
// In that case, set lineWrapped=true for the _PaintBufferOutputHelper call.
const auto lineWrapped = (buffer.GetRowByOffset(bufferLine.Origin().Y).GetCharRow().WasWrapForced()) &&
(bufferLine.RightExclusive() == buffer.GetSize().Width());
// Ask the helper to paint through this specific line.
_PaintBufferOutputHelper(pEngine, it, screenLine.Origin(), lineWrapped);
}
}
}
}
@ -834,24 +839,25 @@ void Renderer::_PaintOverlay(IRenderEngine& engine,
// Set it up in a Viewport helper structure and trim it the IME viewport to be within the full console viewport.
Viewport viewConv = Viewport::FromInclusive(srCaView);
SMALL_RECT srDirty = engine.GetDirtyRectInChars();
// Dirty is an inclusive rectangle, but oddly enough the IME was an exclusive one, so correct it.
srDirty.Bottom++;
srDirty.Right++;
if (viewConv.TrimToViewport(&srDirty))
for (auto srDirty : engine.GetDirtyArea())
{
Viewport viewDirty = Viewport::FromInclusive(srDirty);
// Dirty is an inclusive rectangle, but oddly enough the IME was an exclusive one, so correct it.
srDirty.Bottom++;
srDirty.Right++;
for (SHORT iRow = viewDirty.Top(); iRow < viewDirty.BottomInclusive(); iRow++)
if (viewConv.TrimToViewport(&srDirty))
{
const COORD target{ viewDirty.Left(), iRow };
const auto source = target - overlay.origin;
Viewport viewDirty = Viewport::FromInclusive(srDirty);
auto it = overlay.buffer.GetCellLineDataAt(source);
for (SHORT iRow = viewDirty.Top(); iRow < viewDirty.BottomInclusive(); iRow++)
{
const COORD target{ viewDirty.Left(), iRow };
const auto source = target - overlay.origin;
_PaintBufferOutputHelper(&engine, it, target, false);
auto it = overlay.buffer.GetCellLineDataAt(source);
_PaintBufferOutputHelper(&engine, it, target, false);
}
}
}
}
@ -890,16 +896,19 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine)
{
try
{
SMALL_RECT srDirty = pEngine->GetDirtyRectInChars();
Viewport dirtyView = Viewport::FromInclusive(srDirty);
auto dirtyAreas = pEngine->GetDirtyArea();
// Get selection rectangles
const auto rectangles = _GetSelectionRects();
for (auto rect : rectangles)
{
if (dirtyView.TrimToViewport(&rect))
for (auto dirtyRect : dirtyAreas)
{
LOG_IF_FAILED(pEngine->PaintSelection(rect));
Viewport dirtyView = Viewport::FromInclusive(dirtyRect);
if (dirtyView.TrimToViewport(&rect))
{
LOG_IF_FAILED(pEngine->PaintSelection(rect));
}
}
}
}

View file

@ -1686,7 +1686,7 @@ float DxEngine::GetScaling() const noexcept
// - <none>
// Return Value:
// - Rectangle describing dirty area in characters.
[[nodiscard]] SMALL_RECT DxEngine::GetDirtyRectInChars() noexcept
[[nodiscard]] std::vector<SMALL_RECT> DxEngine::GetDirtyArea()
{
SMALL_RECT r;
r.Top = gsl::narrow<SHORT>(floor(_invalidRect.top / _glyphCell.cy));
@ -1698,7 +1698,7 @@ float DxEngine::GetScaling() const noexcept
r.Bottom--;
r.Right--;
return r;
return { r };
}
// Routine Description:

View file

@ -95,7 +95,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
[[nodiscard]] SMALL_RECT GetDirtyRectInChars() noexcept override;
[[nodiscard]] std::vector<SMALL_RECT> GetDirtyArea() override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;

View file

@ -68,7 +68,7 @@ namespace Microsoft::Console::Render
_Out_ FontInfo& Font,
const int iDpi) noexcept override;
SMALL_RECT GetDirtyRectInChars() override;
[[nodiscard]] std::vector<SMALL_RECT> GetDirtyArea() override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;

View file

@ -16,14 +16,14 @@ using namespace Microsoft::Console::Render;
// Return Value:
// - The character dimensions of the current dirty area of the frame.
// This is an Inclusive rect.
SMALL_RECT GdiEngine::GetDirtyRectInChars()
std::vector<SMALL_RECT> GdiEngine::GetDirtyArea()
{
RECT rc = _psInvalidData.rcPaint;
SMALL_RECT sr = { 0 };
LOG_IF_FAILED(_ScaleByFont(&rc, &sr));
return sr;
return { sr };
}
// Routine Description:

View file

@ -117,7 +117,7 @@ namespace Microsoft::Console::Render
_Out_ FontInfo& FontInfo,
const int iDpi) noexcept = 0;
virtual SMALL_RECT GetDirtyRectInChars() = 0;
virtual std::vector<SMALL_RECT> GetDirtyArea() = 0;
[[nodiscard]] virtual HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept = 0;
[[nodiscard]] virtual HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept = 0;
[[nodiscard]] virtual HRESULT UpdateTitle(const std::wstring& newTitle) noexcept = 0;

View file

@ -403,9 +403,9 @@ UiaEngine::UiaEngine(IUiaEventDispatcher* dispatcher) :
// - <none>
// Return Value:
// - Rectangle describing dirty area in characters.
[[nodiscard]] SMALL_RECT UiaEngine::GetDirtyRectInChars() noexcept
[[nodiscard]] std::vector<SMALL_RECT> UiaEngine::GetDirtyArea()
{
return Viewport::Empty().ToInclusive();
return { Viewport::Empty().ToInclusive() };
}
// Routine Description:

View file

@ -71,7 +71,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
[[nodiscard]] SMALL_RECT GetDirtyRectInChars() noexcept override;
[[nodiscard]] std::vector<SMALL_RECT> GetDirtyArea() override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;

View file

@ -63,8 +63,19 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
}
else
{
const auto dirtyRect = GetDirtyRectInChars();
const auto dirtyView = Viewport::FromInclusive(dirtyRect);
const auto dirty = GetDirtyArea();
// If we have 0 or 1 dirty pieces in the area, set as appropriate.
Viewport dirtyView = dirty.empty() ? Viewport::Empty() : Viewport::FromInclusive(til::at(dirty, 0));
// If there's more than 1, union them all up with the 1 we already have.
for (size_t i = 1; i < dirty.size(); ++i)
{
dirtyView = Viewport::Union(dirtyView, Viewport::FromInclusive(til::at(dirty, i)));
}
// This is expecting the dirty view to be the union of all dirty regions as one big
// rectangle descrbing them all.
if (!_resized && dirtyView == _lastViewport)
{
// TODO: MSFT:21096414 - This is never actually hit. We set

View file

@ -17,14 +17,14 @@ using namespace Microsoft::Console::Types;
// Return Value:
// - The character dimensions of the current dirty area of the frame.
// This is an Inclusive rect.
SMALL_RECT VtEngine::GetDirtyRectInChars()
std::vector<SMALL_RECT> VtEngine::GetDirtyArea()
{
SMALL_RECT dirty = _invalidRect.ToInclusive();
if (dirty.Top < _virtualTop)
{
dirty.Top = _virtualTop;
}
return dirty;
return { dirty };
}
// Routine Description:

View file

@ -89,7 +89,7 @@ namespace Microsoft::Console::Render
_Out_ FontInfo& Font,
const int iDpi) noexcept override;
SMALL_RECT GetDirtyRectInChars() override;
std::vector<SMALL_RECT> GetDirtyArea() override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;

View file

@ -355,7 +355,7 @@ bool WddmConEngine::IsInitialized()
return S_OK;
}
SMALL_RECT WddmConEngine::GetDirtyRectInChars()
std::vector<SMALL_RECT> WddmConEngine::GetDirtyArea()
{
SMALL_RECT r;
r.Bottom = _displayHeight > 0 ? (SHORT)(_displayHeight - 1) : 0;
@ -363,7 +363,7 @@ SMALL_RECT WddmConEngine::GetDirtyRectInChars()
r.Left = 0;
r.Right = _displayWidth > 0 ? (SHORT)(_displayWidth - 1) : 0;
return r;
return { r };
}
RECT WddmConEngine::GetDisplaySize()

View file

@ -61,7 +61,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
SMALL_RECT GetDirtyRectInChars() override;
std::vector<SMALL_RECT> GetDirtyArea() override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;