Eliminate more transient allocations: Titles and invalid rectangles and bitmap runs and utf8 conversions (#8621)

## References
* See also #8617 

## PR Checklist
* [x] Supports #3075
* [x] I work here.
* [x] Manual test.

## Detailed Description of the Pull Request / Additional comments

### Window Title Generation
Every time the renderer checks the title, it's doing two bad things that
I've fixed:
1. It's assembling the prefix to the full title doing a concatenation.
   No one ever gets just the prefix ever after it is set besides the
   concat. So instead of storing prefix and the title, I store the
   assembled prefix + title and the bare title.
2. A copy must be made because it was returning `std::wstring` instead
   of `std::wstring&`. Now it returns the ref.

### Dirty Area Return
Every time the renderer checks the dirty area, which is sometimes
multiple times per pass (regular text printing, again for selection,
etc.), a vector is created off the heap to return the rectangles. The
consumers only ever iterate this data. Now we return a span over a
rectangle or rectangles that the engine must store itself.
1. For some renderers, it's always a constant 1 element. They update
   that 1 element when dirty is queried and return it in the span with a
   span size of 1.
2. For other renderers with more complex behavior, they're already
   holding a cached vector of rectangles. Now it's effectively giving
   out the ref to those in the span for iteration.

### Bitmap Runs
The `til::bitmap` used a `std::optional<std::vector<til::rectangle>>`
inside itself to cache its runs and would clear the optional when the
runs became invalidated. Unfortunately doing `.reset()` to clear the
optional will destroy the underlying vector and have it release its
memory. We know it's about to get reallocated again, so we're just going
to make it a `std::pmr::vector` and give it a memory pool. 

The alternative solution here was to use a `bool` and
`std::vector<til::rectangle>` and just flag when the vector was invalid,
but that was honestly more code changes and I love excuses to try out
PMR now.

Also, instead of returning the ref to the vector... I'm just returning a
span now. Everyone just iterates it anyway, may as well not share the
implementation detail.

### UTF-8 conversions
When testing with Terminal and looking at the `conhost.exe`'s PTY
renderer, it spends a TON of allocation time on converting all the
UTF-16 stuff inside to UTF-8 before it sends it out the PTY. This was
because `ConvertToA` was allocating a string inside itself and returning
it just to have it freed after printing and looping back around again...
as a PTY does.

The change here is to use `til::u16u8` that accepts a buffer out
parameter so the caller can just hold onto it.

## Validation Steps Performed
- [x] `big.txt` in conhost.exe (GDI renderer)
- [x] `big.txt` in Terminal (DX, PTY renderer)
- [x] Ensure WDDM and BGFX build under Razzle with this change.
This commit is contained in:
Michael Niksa 2021-02-16 12:52:33 -08:00 committed by GitHub
parent ca226d62e2
commit 525be22bd8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 187 additions and 135 deletions

View file

@ -182,7 +182,7 @@ public:
void SelectNewRegion(const COORD coordStart, const COORD coordEnd) override;
const COORD GetSelectionAnchor() const noexcept override;
const COORD GetSelectionEnd() const noexcept override;
const std::wstring GetConsoleTitle() const noexcept override;
const std::wstring_view GetConsoleTitle() const noexcept override;
void ColorSelection(const COORD coordSelectionStart, const COORD coordSelectionEnd, const TextAttribute) override;
#pragma endregion

View file

@ -215,7 +215,7 @@ void Terminal::SelectNewRegion(const COORD coordStart, const COORD coordEnd)
SetSelectionEnd(realCoordEnd, SelectionExpansionMode::Cell);
}
const std::wstring Terminal::GetConsoleTitle() const noexcept
const std::wstring_view Terminal::GetConsoleTitle() const noexcept
try
{
if (_title.has_value())

View file

@ -24,6 +24,8 @@ CONSOLE_INFORMATION::CONSOLE_INFORMATION() :
// ExeAliasList initialized below
_OriginalTitle(),
_Title(),
_Prefix(),
_TitleAndPrefix(),
_LinkTitle(),
Flags(0),
PopupCount(0),
@ -115,7 +117,12 @@ ULONG CONSOLE_INFORMATION::GetCSRecursionCount()
try
{
gci.SetTitle(title);
gci.SetOriginalTitle(std::wstring(TranslateConsoleTitle(gci.GetTitle().c_str(), TRUE, FALSE)));
// TranslateConsoleTitle must have a null terminated string.
// This should only happen once on startup so the copy shouldn't be costly
// but could be eliminated by rewriting TranslateConsoleTitle.
const std::wstring nullTerminatedTitle{ gci.GetTitle() };
gci.SetOriginalTitle(std::wstring(TranslateConsoleTitle(nullTerminatedTitle.c_str(), TRUE, FALSE)));
}
catch (...)
{
@ -269,6 +276,7 @@ std::pair<COLORREF, COLORREF> CONSOLE_INFORMATION::LookupAttributeColors(const T
void CONSOLE_INFORMATION::SetTitle(const std::wstring_view newTitle)
{
_Title = std::wstring{ newTitle.begin(), newTitle.end() };
_TitleAndPrefix = _Prefix + _Title;
auto* const pRender = ServiceLocator::LocateGlobals().pRender;
if (pRender)
@ -284,9 +292,10 @@ void CONSOLE_INFORMATION::SetTitle(const std::wstring_view newTitle)
// - newTitlePrefix: The new value to use for the title prefix
// Return Value:
// - <none>
void CONSOLE_INFORMATION::SetTitlePrefix(const std::wstring& newTitlePrefix)
void CONSOLE_INFORMATION::SetTitlePrefix(const std::wstring_view newTitlePrefix)
{
_TitlePrefix = newTitlePrefix;
_Prefix = newTitlePrefix;
_TitleAndPrefix = _Prefix + _Title;
auto* const pRender = ServiceLocator::LocateGlobals().pRender;
if (pRender)
@ -302,7 +311,7 @@ void CONSOLE_INFORMATION::SetTitlePrefix(const std::wstring& newTitlePrefix)
// - originalTitle: The new value to use for the console's original title
// Return Value:
// - <none>
void CONSOLE_INFORMATION::SetOriginalTitle(const std::wstring& originalTitle)
void CONSOLE_INFORMATION::SetOriginalTitle(const std::wstring_view originalTitle)
{
_OriginalTitle = originalTitle;
}
@ -314,7 +323,7 @@ void CONSOLE_INFORMATION::SetOriginalTitle(const std::wstring& originalTitle)
// - linkTitle: The new value to use for the console's link title
// Return Value:
// - <none>
void CONSOLE_INFORMATION::SetLinkTitle(const std::wstring& linkTitle)
void CONSOLE_INFORMATION::SetLinkTitle(const std::wstring_view linkTitle)
{
_LinkTitle = linkTitle;
}
@ -324,8 +333,8 @@ void CONSOLE_INFORMATION::SetLinkTitle(const std::wstring& linkTitle)
// Arguments:
// - <none>
// Return Value:
// - a reference to the console's title.
const std::wstring& CONSOLE_INFORMATION::GetTitle() const noexcept
// - the console's title.
const std::wstring_view CONSOLE_INFORMATION::GetTitle() const noexcept
{
return _Title;
}
@ -336,10 +345,10 @@ const std::wstring& CONSOLE_INFORMATION::GetTitle() const noexcept
// Arguments:
// - <none>
// Return Value:
// - a new wstring containing the combined prefix and title.
const std::wstring CONSOLE_INFORMATION::GetTitleAndPrefix() const
// - the combined prefix and title.
const std::wstring_view CONSOLE_INFORMATION::GetTitleAndPrefix() const
{
return _TitlePrefix + _Title;
return _TitleAndPrefix;
}
// Method Description:
@ -347,8 +356,8 @@ const std::wstring CONSOLE_INFORMATION::GetTitleAndPrefix() const
// Arguments:
// - <none>
// Return Value:
// - a reference to the console's original title.
const std::wstring& CONSOLE_INFORMATION::GetOriginalTitle() const noexcept
// - the console's original title.
const std::wstring_view CONSOLE_INFORMATION::GetOriginalTitle() const noexcept
{
return _OriginalTitle;
}
@ -358,8 +367,8 @@ const std::wstring& CONSOLE_INFORMATION::GetOriginalTitle() const noexcept
// Arguments:
// - <none>
// Return Value:
// - a reference to the console's link title.
const std::wstring& CONSOLE_INFORMATION::GetLinkTitle() const noexcept
// - the console's link title.
const std::wstring_view CONSOLE_INFORMATION::GetLinkTitle() const noexcept
{
return _LinkTitle;
}

View file

@ -1660,33 +1660,21 @@ void DoSrvPrivateRefreshWindow(_In_ const SCREEN_INFORMATION& screenInfo)
}
// Get the appropriate title and length depending on the mode.
const wchar_t* pwszTitle;
size_t cchTitleLength;
if (isOriginal)
{
pwszTitle = gci.GetOriginalTitle().c_str();
cchTitleLength = gci.GetOriginalTitle().length();
}
else
{
pwszTitle = gci.GetTitle().c_str();
cchTitleLength = gci.GetTitle().length();
}
const std::wstring_view storedTitle = isOriginal ? gci.GetOriginalTitle() : gci.GetTitle();
// Always report how much space we would need.
needed = cchTitleLength;
needed = storedTitle.size();
// If we have a pointer to receive the data, then copy it out.
if (title.has_value())
{
HRESULT const hr = StringCchCopyNW(title->data(), title->size(), pwszTitle, cchTitleLength);
HRESULT const hr = StringCchCopyNW(title->data(), title->size(), storedTitle.data(), storedTitle.size());
// Insufficient buffer is allowed. If we return a partial string, that's still OK by historical/compat standards.
// Just say how much we managed to return.
if (SUCCEEDED(hr) || STRSAFE_E_INSUFFICIENT_BUFFER == hr)
{
written = std::min(title->size(), cchTitleLength);
written = std::min(title->size(), storedTitle.size());
}
}
return S_OK;

View file

@ -318,7 +318,7 @@ const bool RenderData::IsGridLineDrawingAllowed() noexcept
// - Retrieves the title information to be displayed in the frame/edge of the window
// Return Value:
// - String with title information
const std::wstring RenderData::GetConsoleTitle() const noexcept
const std::wstring_view RenderData::GetConsoleTitle() const noexcept
{
const CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
return gci.GetTitleAndPrefix();

View file

@ -54,7 +54,7 @@ public:
const bool IsGridLineDrawingAllowed() noexcept override;
const std::wstring GetConsoleTitle() const noexcept override;
const std::wstring_view GetConsoleTitle() const noexcept override;
const std::wstring GetHyperlinkUri(uint16_t id) const noexcept override;
const std::wstring GetHyperlinkCustomId(uint16_t id) const noexcept override;

View file

@ -129,13 +129,13 @@ public:
std::pair<COLORREF, COLORREF> LookupAttributeColors(const TextAttribute& attr) const noexcept;
void SetTitle(const std::wstring_view newTitle);
void SetTitlePrefix(const std::wstring& newTitlePrefix);
void SetOriginalTitle(const std::wstring& originalTitle);
void SetLinkTitle(const std::wstring& linkTitle);
const std::wstring& GetTitle() const noexcept;
const std::wstring& GetOriginalTitle() const noexcept;
const std::wstring& GetLinkTitle() const noexcept;
const std::wstring GetTitleAndPrefix() const;
void SetTitlePrefix(const std::wstring_view newTitlePrefix);
void SetOriginalTitle(const std::wstring_view originalTitle);
void SetLinkTitle(const std::wstring_view linkTitle);
const std::wstring_view GetTitle() const noexcept;
const std::wstring_view GetOriginalTitle() const noexcept;
const std::wstring_view GetLinkTitle() const noexcept;
const std::wstring_view GetTitleAndPrefix() const;
[[nodiscard]] static NTSTATUS AllocateConsole(const std::wstring_view title);
// MSFT:16886775 : get rid of friends
@ -152,7 +152,8 @@ public:
private:
CRITICAL_SECTION _csConsoleLock; // serialize input and output using this
std::wstring _Title;
std::wstring _TitlePrefix; // Eg Select, Mark - things that we manually prepend to the title.
std::wstring _Prefix; // Eg Select, Mark - things that we manually prepend to the title.
std::wstring _TitleAndPrefix;
std::wstring _OriginalTitle;
std::wstring _LinkTitle; // Path to .lnk file
SCREEN_INFORMATION* pCurrentScreenBuffer;

View file

@ -207,10 +207,12 @@ class ApiRoutinesTests
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.SetTitle(L"Test window title.");
const auto title = gci.GetTitle();
int const iBytesNeeded = WideCharToMultiByte(gci.OutputCP,
0,
gci.GetTitle().c_str(),
-1,
title.data(),
gsl::narrow_cast<int>(title.size()),
nullptr,
0,
nullptr,
@ -221,8 +223,8 @@ class ApiRoutinesTests
VERIFY_WIN32_BOOL_SUCCEEDED(WideCharToMultiByte(gci.OutputCP,
0,
gci.GetTitle().c_str(),
-1,
title.data(),
gsl::narrow_cast<int>(title.size()),
pszExpected.get(),
iBytesNeeded,
nullptr,
@ -251,10 +253,13 @@ class ApiRoutinesTests
VERIFY_SUCCEEDED(_pApiRoutines->GetConsoleTitleWImpl(gsl::span<wchar_t>(pwszTitle, ARRAYSIZE(pwszTitle)), cchWritten, cchNeeded));
VERIFY_ARE_NOT_EQUAL(0u, cchWritten);
const auto title = gci.GetTitle();
// NOTE: W version of API returns string length. A version of API returns buffer length (string + null).
VERIFY_ARE_EQUAL(gci.GetTitle().length(), cchWritten);
VERIFY_ARE_EQUAL(gci.GetTitle().length(), cchNeeded);
VERIFY_ARE_EQUAL(WEX::Common::String(gci.GetTitle().c_str()), WEX::Common::String(pwszTitle));
VERIFY_ARE_EQUAL(title.length(), cchWritten);
VERIFY_ARE_EQUAL(title.length(), cchNeeded);
VERIFY_ARE_EQUAL(WEX::Common::String(title.data(), gsl::narrow_cast<int>(title.size())), WEX::Common::String(pwszTitle));
}
TEST_METHOD(ApiGetConsoleOriginalTitleA)
@ -262,10 +267,12 @@ class ApiRoutinesTests
CONSOLE_INFORMATION& gci = ServiceLocator::LocateGlobals().getConsoleInformation();
gci.SetOriginalTitle(L"Test original window title.");
const auto originalTitle = gci.GetOriginalTitle();
int const iBytesNeeded = WideCharToMultiByte(gci.OutputCP,
0,
gci.GetOriginalTitle().c_str(),
-1,
originalTitle.data(),
gsl::narrow_cast<int>(originalTitle.size()),
nullptr,
0,
nullptr,
@ -276,8 +283,8 @@ class ApiRoutinesTests
VERIFY_WIN32_BOOL_SUCCEEDED(WideCharToMultiByte(gci.OutputCP,
0,
gci.GetOriginalTitle().c_str(),
-1,
originalTitle.data(),
gsl::narrow_cast<int>(originalTitle.size()),
pszExpected.get(),
iBytesNeeded,
nullptr,
@ -306,10 +313,12 @@ class ApiRoutinesTests
VERIFY_SUCCEEDED(_pApiRoutines->GetConsoleOriginalTitleWImpl(gsl::span<wchar_t>(pwszTitle, ARRAYSIZE(pwszTitle)), cchWritten, cchNeeded));
VERIFY_ARE_NOT_EQUAL(0u, cchWritten);
const auto originalTitle = gci.GetOriginalTitle();
// NOTE: W version of API returns string length. A version of API returns buffer length (string + null).
VERIFY_ARE_EQUAL(gci.GetOriginalTitle().length(), cchWritten);
VERIFY_ARE_EQUAL(gci.GetOriginalTitle().length(), cchNeeded);
VERIFY_ARE_EQUAL(WEX::Common::String(gci.GetOriginalTitle().c_str()), WEX::Common::String(pwszTitle));
VERIFY_ARE_EQUAL(originalTitle.length(), cchWritten);
VERIFY_ARE_EQUAL(originalTitle.length(), cchNeeded);
VERIFY_ARE_EQUAL(WEX::Common::String(originalTitle.data(), gsl::narrow_cast<int>(originalTitle.size())), WEX::Common::String(pwszTitle));
}
static void s_AdjustOutputWait(const bool fShouldBlock)

View file

@ -355,9 +355,9 @@ public:
return false;
}
const std::wstring GetConsoleTitle() const noexcept override
const std::wstring_view GetConsoleTitle() const noexcept override
{
return std::wstring{};
return std::wstring_view{};
}
const bool IsSelectionActive() const override

View file

@ -263,7 +263,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
return const_iterator(_bits, _sz, _sz.area());
}
const std::vector<til::rectangle, run_allocator_type>& runs() const
const gsl::span<const til::rectangle> runs() const
{
// If we don't have cached runs, rebuild.
if (!_runs.has_value())
@ -271,7 +271,7 @@ namespace til // Terminal Implementation Library. Also: "Today I Learned"
_runs.emplace(begin(), end());
}
// Return a reference to the runs.
// Return the runs.
return _runs.value();
}

View file

@ -230,7 +230,7 @@ BgfxEngine::BgfxEngine(PVOID SharedViewBase, LONG DisplayHeight, LONG DisplayWid
return S_OK;
}
std::vector<til::rectangle> BgfxEngine::GetDirtyArea()
[[nodiscard]] HRESULT BgfxEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
{
SMALL_RECT r;
r.Bottom = _displayHeight > 0 ? (SHORT)(_displayHeight - 1) : 0;
@ -238,7 +238,12 @@ std::vector<til::rectangle> BgfxEngine::GetDirtyArea()
r.Left = 0;
r.Right = _displayWidth > 0 ? (SHORT)(_displayWidth - 1) : 0;
return { r };
_dirtyArea = r;
area = { &_dirtyArea,
1 };
return S_OK;
}
[[nodiscard]] HRESULT BgfxEngine::GetFontSize(_Out_ COORD* const pFontSize) noexcept
@ -260,7 +265,7 @@ std::vector<til::rectangle> BgfxEngine::GetDirtyArea()
// - newTitle: the new string to use for the title of the window
// Return Value:
// - S_OK
[[nodiscard]] HRESULT BgfxEngine::_DoUpdateTitle(_In_ const std::wstring& /*newTitle*/) noexcept
[[nodiscard]] HRESULT BgfxEngine::_DoUpdateTitle(_In_ const std::wstring_view /*newTitle*/) noexcept
{
return S_OK;
}

View file

@ -67,12 +67,12 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
std::vector<til::rectangle> GetDirtyArea() override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
protected:
[[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring& newTitle) noexcept override;
[[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring_view newTitle) noexcept override;
private:
ULONG_PTR _sharedViewBase;
@ -80,6 +80,7 @@ namespace Microsoft::Console::Render
LONG _displayHeight;
LONG _displayWidth;
til::rectangle _dirtyArea;
COORD _fontSize;

View file

@ -289,11 +289,17 @@ void Window::_UpdateSystemMetrics() const
}
}
// CreateWindowExW needs a null terminated string, so ensure
// title is null terminated in a std::wstring here.
// We don't mind the string copy here because making the window
// should be infrequent.
const std::wstring title{ gci.GetTitle() };
// Attempt to create window
HWND hWnd = CreateWindowExW(
CONSOLE_WINDOW_EX_FLAGS,
CONSOLE_WINDOW_CLASS,
gci.GetTitle().c_str(),
title.c_str(),
CONSOLE_WINDOW_FLAGS,
WI_IsFlagSet(gci.Flags, CONSOLE_AUTO_POSITION) ? CW_USEDEFAULT : rectProposed.left,
rectProposed.top, // field is ignored if CW_USEDEFAULT was chosen above

View file

@ -687,7 +687,10 @@ using namespace Microsoft::Console::Types;
case CM_UPDATE_TITLE:
{
SetWindowTextW(hWnd, gci.GetTitleAndPrefix().c_str());
// SetWindowTextW needs null terminated string so assign view to string.
const std::wstring titleAndPrefix{ gci.GetTitleAndPrefix() };
SetWindowTextW(hWnd, titleAndPrefix.c_str());
break;
}

View file

@ -13,7 +13,7 @@ RenderEngineBase::RenderEngineBase() :
{
}
HRESULT RenderEngineBase::InvalidateTitle(const std::wstring& proposedTitle) noexcept
HRESULT RenderEngineBase::InvalidateTitle(const std::wstring_view proposedTitle) noexcept
{
if (proposedTitle != _lastFrameTitle)
{
@ -23,7 +23,7 @@ HRESULT RenderEngineBase::InvalidateTitle(const std::wstring& proposedTitle) noe
return S_OK;
}
HRESULT RenderEngineBase::UpdateTitle(const std::wstring& newTitle) noexcept
HRESULT RenderEngineBase::UpdateTitle(const std::wstring_view newTitle) noexcept
{
HRESULT hr = S_FALSE;
if (newTitle != _lastFrameTitle)

View file

@ -467,7 +467,7 @@ void Renderer::TriggerCircling()
// - <none>
void Renderer::TriggerTitleChange()
{
const std::wstring newTitle = _pData->GetConsoleTitle();
const auto newTitle = _pData->GetConsoleTitle();
for (IRenderEngine* const pEngine : _rgpEngines)
{
LOG_IF_FAILED(pEngine->InvalidateTitle(newTitle));
@ -483,7 +483,7 @@ void Renderer::TriggerTitleChange()
// - the HRESULT of the underlying engine's UpdateTitle call.
HRESULT Renderer::_PaintTitle(IRenderEngine* const pEngine)
{
const std::wstring newTitle = _pData->GetConsoleTitle();
const auto newTitle = _pData->GetConsoleTitle();
return pEngine->UpdateTitle(newTitle);
}
@ -627,9 +627,10 @@ 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.
const auto dirtyAreas = pEngine->GetDirtyArea();
gsl::span<const til::rectangle> dirtyAreas;
LOG_IF_FAILED(pEngine->GetDirtyArea(dirtyAreas));
for (const auto dirtyRect : dirtyAreas)
for (const auto& dirtyRect : dirtyAreas)
{
auto dirty = Viewport::FromInclusive(dirtyRect);
@ -1044,7 +1045,10 @@ 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);
for (SMALL_RECT srDirty : engine.GetDirtyArea())
gsl::span<const til::rectangle> dirtyAreas;
LOG_IF_FAILED(engine.GetDirtyArea(dirtyAreas));
for (SMALL_RECT srDirty : dirtyAreas)
{
// Dirty is an inclusive rectangle, but oddly enough the IME was an exclusive one, so correct it.
srDirty.Bottom++;
@ -1101,13 +1105,14 @@ void Renderer::_PaintSelection(_In_ IRenderEngine* const pEngine)
{
try
{
auto dirtyAreas = pEngine->GetDirtyArea();
gsl::span<const til::rectangle> dirtyAreas;
LOG_IF_FAILED(pEngine->GetDirtyArea(dirtyAreas));
// Get selection rectangles
const auto rectangles = _GetSelectionRects();
for (auto rect : rectangles)
{
for (auto dirtyRect : dirtyAreas)
for (auto& dirtyRect : dirtyAreas)
{
// Make a copy as `TrimToViewport` will manipulate it and
// can destroy it for the next dirtyRect to test against.

View file

@ -66,7 +66,8 @@ using namespace Microsoft::Console::Types;
DxEngine::DxEngine() :
RenderEngineBase(),
_invalidateFullRows{ true },
_invalidMap{},
_pool{ til::pmr::get_default_resource() },
_invalidMap{ &_pool },
_invalidScroll{},
_allInvalid{ false },
_firstFrame{ true },
@ -2087,13 +2088,16 @@ float DxEngine::GetScaling() const noexcept
// Routine Description:
// - Gets the area that we currently believe is dirty within the character cell grid
// Arguments:
// - <none>
// - area - Rectangle describing dirty area in characters.
// Return Value:
// - Rectangle describing dirty area in characters.
[[nodiscard]] std::vector<til::rectangle> DxEngine::GetDirtyArea()
// - S_OK
[[nodiscard]] HRESULT DxEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
try
{
return _invalidMap.runs();
area = _invalidMap.runs();
return S_OK;
}
CATCH_RETURN();
// Routine Description:
// - Gets the current font size
@ -2141,7 +2145,7 @@ CATCH_RETURN();
// - newTitle: the new string to use for the title of the window
// Return Value:
// - S_OK
[[nodiscard]] HRESULT DxEngine::_DoUpdateTitle(_In_ const std::wstring& /*newTitle*/) noexcept
[[nodiscard]] HRESULT DxEngine::_DoUpdateTitle(_In_ const std::wstring_view /*newTitle*/) noexcept
{
if (_hwndTarget != INVALID_HANDLE_VALUE)
{

View file

@ -113,7 +113,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
[[nodiscard]] std::vector<til::rectangle> GetDirtyArea() override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
@ -130,7 +130,7 @@ namespace Microsoft::Console::Render
void UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept;
protected:
[[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring& newTitle) noexcept override;
[[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring_view newTitle) noexcept override;
[[nodiscard]] HRESULT _PaintTerminalEffects() noexcept;
[[nodiscard]] bool _FullRepaintNeeded() const noexcept;
@ -181,7 +181,8 @@ namespace Microsoft::Console::Render
bool _firstFrame;
bool _invalidateFullRows;
til::bitmap _invalidMap;
std::pmr::unsynchronized_pool_resource _pool;
til::pmr::bitmap _invalidMap;
til::point _invalidScroll;
bool _allInvalid;

View file

@ -66,12 +66,12 @@ namespace Microsoft::Console::Render
_Out_ FontInfo& Font,
const int iDpi) noexcept override;
[[nodiscard]] std::vector<til::rectangle> GetDirtyArea() override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
protected:
[[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring& newTitle) noexcept override;
[[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring_view newTitle) noexcept override;
private:
HWND _hwndTargetWindow;
@ -82,6 +82,7 @@ namespace Microsoft::Console::Render
bool _fPaintStarted;
til::rectangle _invalidCharacters;
PAINTSTRUCT _psInvalidData;
HDC _hdcMemoryContext;
bool _isTrueTypeFont;

View file

@ -12,18 +12,22 @@ using namespace Microsoft::Console::Render;
// Routine Description:
// - Gets the size in characters of the current dirty portion of the frame.
// Arguments:
// - <none>
// Return Value:
// - The character dimensions of the current dirty area of the frame.
// - area - The character dimensions of the current dirty area of the frame.
// This is an Inclusive rect.
std::vector<til::rectangle> GdiEngine::GetDirtyArea()
// Return Value:
// - S_OK or math failure
[[nodiscard]] HRESULT GdiEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
{
RECT rc = _psInvalidData.rcPaint;
SMALL_RECT sr = { 0 };
LOG_IF_FAILED(_ScaleByFont(&rc, &sr));
RETURN_IF_FAILED(_ScaleByFont(&rc, &sr));
return { sr };
_invalidCharacters = sr;
area = { &_invalidCharacters, 1 };
return S_OK;
}
// Routine Description:

View file

@ -31,6 +31,7 @@ GdiEngine::GdiEngine() :
_lastBg(INVALID_COLOR),
_lastFontItalic(false),
_fPaintStarted(false),
_invalidCharacters{},
_hfont(nullptr),
_hfontItalic(nullptr),
_pool{ til::pmr::get_default_resource() }, // It's important the pool is first so it can be given to the others on construction.
@ -392,7 +393,7 @@ GdiEngine::~GdiEngine()
// - newTitle: the new string to use for the title of the window
// Return Value:
// - S_OK if PostMessageW succeeded, otherwise E_FAIL
[[nodiscard]] HRESULT GdiEngine::_DoUpdateTitle(_In_ const std::wstring& /*newTitle*/) noexcept
[[nodiscard]] HRESULT GdiEngine::_DoUpdateTitle(_In_ const std::wstring_view /*newTitle*/) noexcept
{
// the CM_UPDATE_TITLE handler in windowproc will query the updated title.
return PostMessageW(_hwndTargetWindow, CM_UPDATE_TITLE, 0, (LPARAM) nullptr) ? S_OK : E_FAIL;

View file

@ -64,7 +64,7 @@ namespace Microsoft::Console::Render
virtual const std::vector<RenderOverlay> GetOverlays() const noexcept = 0;
virtual const bool IsGridLineDrawingAllowed() noexcept = 0;
virtual const std::wstring GetConsoleTitle() const noexcept = 0;
virtual const std::wstring_view GetConsoleTitle() const noexcept = 0;
virtual const std::wstring GetHyperlinkUri(uint16_t id) const noexcept = 0;
virtual const std::wstring GetHyperlinkCustomId(uint16_t id) const noexcept = 0;

View file

@ -71,7 +71,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] virtual HRESULT InvalidateAll() noexcept = 0;
[[nodiscard]] virtual HRESULT InvalidateCircling(_Out_ bool* const pForcePaint) noexcept = 0;
[[nodiscard]] virtual HRESULT InvalidateTitle(const std::wstring& proposedTitle) noexcept = 0;
[[nodiscard]] virtual HRESULT InvalidateTitle(const std::wstring_view proposedTitle) noexcept = 0;
[[nodiscard]] virtual HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept = 0;
@ -100,10 +100,10 @@ namespace Microsoft::Console::Render
_Out_ FontInfo& FontInfo,
const int iDpi) noexcept = 0;
virtual std::vector<til::rectangle> GetDirtyArea() = 0;
[[nodiscard]] virtual HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept = 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;
[[nodiscard]] virtual HRESULT UpdateTitle(const std::wstring_view newTitle) noexcept = 0;
};
inline Microsoft::Console::Render::IRenderEngine::~IRenderEngine() {}

View file

@ -34,9 +34,9 @@ namespace Microsoft::Console::Render
RenderEngineBase& operator=(RenderEngineBase&&) = default;
public:
[[nodiscard]] HRESULT InvalidateTitle(const std::wstring& proposedTitle) noexcept override;
[[nodiscard]] HRESULT InvalidateTitle(const std::wstring_view proposedTitle) noexcept override;
[[nodiscard]] HRESULT UpdateTitle(const std::wstring& newTitle) noexcept override;
[[nodiscard]] HRESULT UpdateTitle(const std::wstring_view newTitle) noexcept override;
[[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override;
@ -45,7 +45,7 @@ namespace Microsoft::Console::Render
void WaitUntilCanRender() noexcept override;
protected:
[[nodiscard]] virtual HRESULT _DoUpdateTitle(const std::wstring& newTitle) noexcept = 0;
[[nodiscard]] virtual HRESULT _DoUpdateTitle(const std::wstring_view newTitle) noexcept = 0;
bool _titleChanged;
std::wstring _lastFrameTitle;

View file

@ -427,12 +427,16 @@ CATCH_RETURN();
// - Gets the area that we currently believe is dirty within the character cell grid
// - Not currently used by UiaEngine.
// Arguments:
// - <none>
// - area - Rectangle describing dirty area in characters.
// Return Value:
// - Rectangle describing dirty area in characters.
[[nodiscard]] std::vector<til::rectangle> UiaEngine::GetDirtyArea()
// - S_OK.
[[nodiscard]] HRESULT UiaEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
{
return { Viewport::Empty().ToInclusive() };
// Magic static is only valid because any instance of this object has the same behavior.
// Use member variable instead if this ever changes.
const static til::rectangle empty;
area = { &empty, 1 };
return S_OK;
}
// Routine Description:
@ -465,7 +469,7 @@ CATCH_RETURN();
// - newTitle: the new string to use for the title of the window
// Return Value:
// - S_FALSE
[[nodiscard]] HRESULT UiaEngine::_DoUpdateTitle(_In_ const std::wstring& /*newTitle*/) noexcept
[[nodiscard]] HRESULT UiaEngine::_DoUpdateTitle(_In_ const std::wstring_view /*newTitle*/) noexcept
{
return S_FALSE;
}

View file

@ -69,12 +69,12 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
[[nodiscard]] std::vector<til::rectangle> GetDirtyArea() override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
protected:
[[nodiscard]] HRESULT _DoUpdateTitle(const std::wstring& newTitle) noexcept override;
[[nodiscard]] HRESULT _DoUpdateTitle(const std::wstring_view newTitle) noexcept override;
private:
bool _isEnabled;

View file

@ -57,7 +57,8 @@ XtermEngine::XtermEngine(_In_ wil::unique_hfile hPipe,
}
else
{
const auto dirty = GetDirtyArea();
gsl::span<const til::rectangle> dirty;
RETURN_IF_FAILED(GetDirtyArea(dirty));
// 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));
@ -540,7 +541,7 @@ CATCH_RETURN();
// - newTitle: the new string to use for the title of the window
// Return Value:
// - S_OK
[[nodiscard]] HRESULT XtermEngine::_DoUpdateTitle(const std::wstring& newTitle) noexcept
[[nodiscard]] HRESULT XtermEngine::_DoUpdateTitle(const std::wstring_view newTitle) noexcept
{
// inbox telnet uses xterm-ascii as its mode. If we're in ascii mode, don't
// do anything, to maintain compatibility.

View file

@ -59,7 +59,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT _MoveCursor(const COORD coord) noexcept override;
[[nodiscard]] HRESULT _DoUpdateTitle(const std::wstring& newTitle) noexcept override;
[[nodiscard]] HRESULT _DoUpdateTitle(const std::wstring_view newTitle) noexcept override;
#ifdef UNIT_TESTING
friend class VtRendererTest;

View file

@ -13,13 +13,14 @@ using namespace Microsoft::Console::Types;
// Routine Description:
// - Gets the size in characters of the current dirty portion of the frame.
// Arguments:
// - <none>
// - area - The character dimensions of the current dirty area of the frame.
// This is an Inclusive rect.
// Return Value:
// - The character dimensions of the current dirty area of the frame.
// This is an Inclusive rect.
std::vector<til::rectangle> VtEngine::GetDirtyArea()
// - S_OK.
[[nodiscard]] HRESULT VtEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
{
return _invalidMap.runs();
area = _invalidMap.runs();
return S_OK;
}
// Routine Description:

View file

@ -598,7 +598,7 @@ using namespace Microsoft::Console::Types;
// - newTitle: the new string to use for the title of the window
// Return Value:
// - S_OK
[[nodiscard]] HRESULT VtEngine::_DoUpdateTitle(const std::wstring& /*newTitle*/) noexcept
[[nodiscard]] HRESULT VtEngine::_DoUpdateTitle(const std::wstring_view /*newTitle*/) noexcept
{
return S_OK;
}

View file

@ -31,7 +31,8 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
_hFile(std::move(pipe)),
_lastTextAttributes(INVALID_COLOR, INVALID_COLOR),
_lastViewport(initialViewport),
_invalidMap(initialViewport.Dimensions()),
_pool(til::pmr::get_default_resource()),
_invalidMap(initialViewport.Dimensions(), false, &_pool),
_lastText({ 0 }),
_scrollDelta({ 0, 0 }),
_quickReturn(false),
@ -50,7 +51,10 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
_deferredCursorPos{ INVALID_COORDS },
_inResizeRequest{ false },
_trace{},
_bufferLine{}
_bufferLine{},
_buffer{},
_formatBuffer{},
_conversionBuffer{}
{
#ifndef UNIT_TESTING
// When unit testing, we can instantiate a VtEngine without a pipe.
@ -144,12 +148,8 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
// - S_OK or suitable HRESULT error from either conversion or writing pipe.
[[nodiscard]] HRESULT VtEngine::_WriteTerminalUtf8(const std::wstring_view wstr) noexcept
{
try
{
const auto converted = ConvertToA(CP_UTF8, wstr);
return _Write(converted);
}
CATCH_RETURN();
RETURN_IF_FAILED(til::u16u8(wstr, _conversionBuffer));
return _Write(_conversionBuffer);
}
// Method Description:

View file

@ -145,7 +145,7 @@ void RenderTracing::TraceInvalidateScroll(const til::point scroll) const
}
void RenderTracing::TraceStartPaint(const bool quickReturn,
const til::bitmap& invalidMap,
const til::pmr::bitmap& invalidMap,
const til::rectangle lastViewport,
const til::point scrollDelt,
const bool cursorMoved,

View file

@ -39,7 +39,7 @@ namespace Microsoft::Console::VirtualTerminal
void TraceTriggerCircling(const bool newFrame) const;
void TraceInvalidateScroll(const til::point scroll) const;
void TraceStartPaint(const bool quickReturn,
const til::bitmap& invalidMap,
const til::pmr::bitmap& invalidMap,
const til::rectangle lastViewport,
const til::point scrollDelta,
const bool cursorMoved,

View file

@ -85,7 +85,7 @@ namespace Microsoft::Console::Render
_Out_ FontInfo& Font,
const int iDpi) noexcept override;
std::vector<til::rectangle> GetDirtyArea() override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
@ -113,12 +113,14 @@ namespace Microsoft::Console::Render
std::string _buffer;
std::string _formatBuffer;
std::string _conversionBuffer;
TextAttribute _lastTextAttributes;
Microsoft::Console::Types::Viewport _lastViewport;
til::bitmap _invalidMap;
std::pmr::unsynchronized_pool_resource _pool;
til::pmr::bitmap _invalidMap;
COORD _lastText;
til::point _scrollDelta;
@ -222,7 +224,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT _WriteTerminalUtf8(const std::wstring_view str) noexcept;
[[nodiscard]] HRESULT _WriteTerminalAscii(const std::wstring_view str) noexcept;
[[nodiscard]] virtual HRESULT _DoUpdateTitle(const std::wstring& newTitle) noexcept override;
[[nodiscard]] virtual HRESULT _DoUpdateTitle(const std::wstring_view newTitle) noexcept override;
/////////////////////////// Unit Testing Helpers ///////////////////////////
#ifdef UNIT_TESTING

View file

@ -353,7 +353,7 @@ bool WddmConEngine::IsInitialized()
return S_OK;
}
std::vector<til::rectangle> WddmConEngine::GetDirtyArea()
[[nodiscard]] HRESULT WddmConEngine::GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept
{
SMALL_RECT r;
r.Bottom = _displayHeight > 0 ? (SHORT)(_displayHeight - 1) : 0;
@ -361,7 +361,12 @@ std::vector<til::rectangle> WddmConEngine::GetDirtyArea()
r.Left = 0;
r.Right = _displayWidth > 0 ? (SHORT)(_displayWidth - 1) : 0;
return { r };
_dirtyArea = r;
area = { &_dirtyArea,
1 };
return S_OK;
}
RECT WddmConEngine::GetDisplaySize()
@ -409,7 +414,7 @@ RECT WddmConEngine::GetDisplaySize()
// - newTitle: the new string to use for the title of the window
// Return Value:
// - S_OK
[[nodiscard]] HRESULT WddmConEngine::_DoUpdateTitle(_In_ const std::wstring& /*newTitle*/) noexcept
[[nodiscard]] HRESULT WddmConEngine::_DoUpdateTitle(_In_ const std::wstring_view /*newTitle*/) noexcept
{
return S_OK;
}

View file

@ -59,12 +59,12 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& fiFontInfoDesired, FontInfo& fiFontInfo, int const iDpi) noexcept override;
std::vector<til::rectangle> GetDirtyArea() override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* const pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(const std::wstring_view glyph, _Out_ bool* const pResult) noexcept override;
protected:
[[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring& newTitle) noexcept override;
[[nodiscard]] HRESULT _DoUpdateTitle(_In_ const std::wstring_view newTitle) noexcept override;
private:
HANDLE _hWddmConCtx;
@ -75,6 +75,7 @@ namespace Microsoft::Console::Render
// Variables
LONG _displayHeight;
LONG _displayWidth;
til::rectangle _dirtyArea;
PCD_IO_ROW_INFORMATION* _displayState;