diff --git a/src/host/ut_host/AliasTests.cpp b/src/host/ut_host/AliasTests.cpp index 6eed3ac10..2249b2b4c 100644 --- a/src/host/ut_host/AliasTests.cpp +++ b/src/host/ut_host/AliasTests.cpp @@ -164,10 +164,7 @@ class AliasTests wcscpy_s(rgwchTargetBefore.get(), cchTarget, rgwchTarget.get()); size_t cbTargetUsed = 0; - auto const cbTargetUsedBefore = cbTargetUsed; - DWORD dwLines = 0; - auto const dwLinesBefore = dwLines; // Register the wrong alias name before we try. std::wstring exe(L"exe.exe"); @@ -306,7 +303,6 @@ class AliasTests auto rgwchTargetBefore = std::make_unique(cchTarget); wcscpy_s(rgwchTargetBefore.get(), cchTarget, rgwchTarget.get()); - const size_t cbTarget = cchTarget * sizeof(wchar_t); size_t cbTargetUsed = 0; auto const cbTargetUsedBefore = cbTargetUsed; diff --git a/src/host/ut_host/TextBufferTests.cpp b/src/host/ut_host/TextBufferTests.cpp index c87c740fd..92c07f85f 100644 --- a/src/host/ut_host/TextBufferTests.cpp +++ b/src/host/ut_host/TextBufferTests.cpp @@ -899,7 +899,6 @@ void TextBufferTests::TestUnBold() const auto& row = tbi.GetRowByOffset(y); const auto attrRow = &row.GetAttrRow(); - const auto len = tbi.GetSize().Width(); const std::vector attrs{ attrRow->begin(), attrRow->end() }; const auto attrA = attrs[x - 2]; const auto attrB = attrs[x - 1]; @@ -951,7 +950,6 @@ void TextBufferTests::TestUnBoldRgb() const auto& row = tbi.GetRowByOffset(y); const auto attrRow = &row.GetAttrRow(); - const auto len = tbi.GetSize().Width(); const std::vector attrs{ attrRow->begin(), attrRow->end() }; const auto attrA = attrs[x - 2]; const auto attrB = attrs[x - 1]; @@ -1011,7 +1009,6 @@ void TextBufferTests::TestComplexUnBold() const auto& row = tbi.GetRowByOffset(y); const auto attrRow = &row.GetAttrRow(); - const auto len = tbi.GetSize().Width(); const std::vector attrs{ attrRow->begin(), attrRow->end() }; const auto attrA = attrs[x - 6]; const auto attrB = attrs[x - 5]; @@ -1094,7 +1091,6 @@ void TextBufferTests::CopyAttrs() const auto& row = tbi.GetRowByOffset(0); const auto attrRow = &row.GetAttrRow(); - const auto len = tbi.GetSize().Width(); const std::vector attrs{ attrRow->begin(), attrRow->end() }; const auto attrA = attrs[0]; const auto attrB = attrs[1]; @@ -1146,7 +1142,6 @@ void TextBufferTests::EmptySgrTest() const auto& row = tbi.GetRowByOffset(y); const auto attrRow = &row.GetAttrRow(); - const auto len = tbi.GetSize().Width(); const std::vector attrs{ attrRow->begin(), attrRow->end() }; const auto attrA = attrs[x - 3]; const auto attrB = attrs[x - 2]; @@ -1208,7 +1203,6 @@ void TextBufferTests::TestReverseReset() const auto& row = tbi.GetRowByOffset(y); const auto attrRow = &row.GetAttrRow(); - const auto len = tbi.GetSize().Width(); const std::vector attrs{ attrRow->begin(), attrRow->end() }; const auto attrA = attrs[x - 3]; const auto attrB = attrs[x - 2]; @@ -1310,7 +1304,6 @@ void TextBufferTests::CopyLastAttr() const ROW& row1 = tbi.GetRowByOffset(y + 1); const ROW& row2 = tbi.GetRowByOffset(y + 2); const ROW& row3 = tbi.GetRowByOffset(y + 3); - const auto len = tbi.GetSize().Width(); const std::vector attrs1{ row1.GetAttrRow().begin(), row1.GetAttrRow().end() }; const std::vector attrs2{ row2.GetAttrRow().begin(), row2.GetAttrRow().end() }; diff --git a/src/host/ut_host/VtIoTests.cpp b/src/host/ut_host/VtIoTests.cpp index f180d03e0..07ec963be 100644 --- a/src/host/ut_host/VtIoTests.cpp +++ b/src/host/ut_host/VtIoTests.cpp @@ -456,6 +456,7 @@ void VtIoTests::RendererDtorAndThreadAndDx() // which is what CI uses. /*Sleep(500);*/ + (void)dxEngine->Enable(); pThread->EnablePainting(); pRenderer->TriggerTeardown(); pRenderer.reset(); diff --git a/src/renderer/base/Cluster.cpp b/src/renderer/base/Cluster.cpp deleted file mode 100644 index a2aa8139f..000000000 --- a/src/renderer/base/Cluster.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#include "precomp.h" - -#include "../inc/Cluster.hpp" -#include "../../inc/unicode.hpp" -#include "../types/inc/convert.hpp" - -using namespace Microsoft::Console::Render; - -// Routine Description: -// - Instantiates a new cluster structure -// Arguments: -// - text - This is a view of the text that forms this cluster (one or more wchar_t*s) -// - columns - This is the number of columns in the grid that the cluster should consume when drawn -Cluster::Cluster(const std::wstring_view text, const size_t columns) : - _text(text), - _columns(columns) -{ -} - -// Routine Description: -// - Provides the embedded text as a single character -// - This might replace the string with the replacement character if it doesn't fit as one wchar_t -// Arguments: -// - -// Return Value: -// - The only wchar_t in the string or the Unicode replacement character as appropriate. -const wchar_t Cluster::GetTextAsSingle() const noexcept -{ - try - { - return Utf16ToUcs2(_text); - } - CATCH_LOG(); - return UNICODE_REPLACEMENT; -} - -// Routine Description: -// - Provides the string of wchar_ts for this cluster. -// Arguments: -// - -// Return Value: -// - String view of wchar_ts. -const std::wstring_view& Cluster::GetText() const noexcept -{ - return _text; -} - -// Routine Description: -// - Gets the number of columns in the grid that this character should consume -// visually when rendered onto a line. -// Arguments: -// - -// Return Value: -// - Number of columns to use when drawing (not a pixel count). -const size_t Cluster::GetColumns() const noexcept -{ - return _columns; -} diff --git a/src/renderer/base/lib/base.vcxproj b/src/renderer/base/lib/base.vcxproj index 2a9ee32cb..7cf585346 100644 --- a/src/renderer/base/lib/base.vcxproj +++ b/src/renderer/base/lib/base.vcxproj @@ -11,7 +11,6 @@ - diff --git a/src/renderer/base/lib/base.vcxproj.filters b/src/renderer/base/lib/base.vcxproj.filters index baff50352..f20d76811 100644 --- a/src/renderer/base/lib/base.vcxproj.filters +++ b/src/renderer/base/lib/base.vcxproj.filters @@ -45,9 +45,6 @@ Source Files - - Source Files - diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index e5103bc3f..9654df2aa 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -16,6 +16,12 @@ static constexpr auto maxRetriesForRenderEngine = 3; // The renderer will wait this number of milliseconds * how many tries have elapsed before trying again. static constexpr auto renderBackoffBaseTimeMilliseconds{ 150 }; +#define FOREACH_ENGINE(var) \ + for (auto var : _engines) \ + if (!var) \ + break; \ + else + // Routine Description: // - Creates a new renderer controller for a console. // Arguments: @@ -23,22 +29,17 @@ static constexpr auto renderBackoffBaseTimeMilliseconds{ 150 }; // - pEngine - The output engine for targeting each rendering frame // Return Value: // - An instance of a Renderer. -// NOTE: CAN THROW IF MEMORY ALLOCATION FAILS. Renderer::Renderer(IRenderData* pData, _In_reads_(cEngines) IRenderEngine** const rgpEngines, const size_t cEngines, std::unique_ptr thread) : _pData(THROW_HR_IF_NULL(E_INVALIDARG, pData)), _pThread{ std::move(thread) }, - _destructing{ false }, - _clusterBuffer{}, _viewport{ pData->GetViewport() } { for (size_t i = 0; i < cEngines; i++) { - IRenderEngine* engine = rgpEngines[i]; - // NOTE: THIS CAN THROW IF MEMORY ALLOCATION FAILS. - AddRenderEngine(engine); + AddRenderEngine(rgpEngines[i]); } } @@ -50,6 +51,7 @@ Renderer::Renderer(IRenderData* pData, // - Renderer::~Renderer() { + // IRenderThread blocks until it has shut down. _destructing = true; _pThread.reset(); } @@ -62,12 +64,7 @@ Renderer::~Renderer() // - HRESULT S_OK, GDI error, Safe Math error, or state/argument errors. [[nodiscard]] HRESULT Renderer::PaintFrame() { - if (_destructing) - { - return S_FALSE; - } - - for (IRenderEngine* const pEngine : _rgpEngines) + FOREACH_ENGINE(pEngine) { auto tries = maxRetriesForRenderEngine; while (tries > 0) @@ -93,6 +90,7 @@ Renderer::~Renderer() // abort applications that host us. return S_FALSE; } + // Add a bit of backoff. // Sleep 150ms, 300ms, 450ms before failing out and disabling the renderer. Sleep(renderBackoffBaseTimeMilliseconds * (maxRetriesForRenderEngine - tries)); @@ -202,9 +200,10 @@ void Renderer::_NotifyPaintFrame() // - void Renderer::TriggerSystemRedraw(const RECT* const prcDirtyClient) { - std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { + FOREACH_ENGINE(pEngine) + { LOG_IF_FAILED(pEngine->InvalidateSystem(prcDirtyClient)); - }); + } _NotifyPaintFrame(); } @@ -235,9 +234,10 @@ void Renderer::TriggerRedraw(const Viewport& region) if (view.TrimToViewport(&srUpdateRegion)) { view.ConvertToOrigin(&srUpdateRegion); - std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { + FOREACH_ENGINE(pEngine) + { LOG_IF_FAILED(pEngine->Invalidate(&srUpdateRegion)); - }); + } _NotifyPaintFrame(); } @@ -286,7 +286,7 @@ void Renderer::TriggerRedrawCursor(const COORD* const pcoord) if (cursorView.IsValid()) { const SMALL_RECT updateRect = view.ConvertToOrigin(cursorView).ToExclusive(); - for (IRenderEngine* pEngine : _rgpEngines) + FOREACH_ENGINE(pEngine) { LOG_IF_FAILED(pEngine->InvalidateCursor(&updateRect)); } @@ -305,9 +305,10 @@ void Renderer::TriggerRedrawCursor(const COORD* const pcoord) // - void Renderer::TriggerRedrawAll() { - std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { + FOREACH_ENGINE(pEngine) + { LOG_IF_FAILED(pEngine->InvalidateAll()); - }); + } _NotifyPaintFrame(); } @@ -325,7 +326,7 @@ void Renderer::TriggerTeardown() noexcept _pThread->WaitForPaintCompletionAndDisable(INFINITE); // Then walk through and do one final paint on the caller's thread. - for (IRenderEngine* const pEngine : _rgpEngines) + FOREACH_ENGINE(pEngine) { bool fEngineRequestsRepaint = false; HRESULT hr = pEngine->PrepareForTeardown(&fEngineRequestsRepaint); @@ -349,7 +350,7 @@ void Renderer::TriggerSelection() try { // Get selection rectangles - const auto rects = _GetSelectionRects(); + auto rects = _GetSelectionRects(); // Restrict all previous selection rectangles to inside the current viewport bounds for (auto& sr : _previousSelection) @@ -367,12 +368,13 @@ void Renderer::TriggerSelection() sr = Viewport::FromInclusive(rc).ToExclusive(); } - std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { + FOREACH_ENGINE(pEngine) + { LOG_IF_FAILED(pEngine->InvalidateSelection(_previousSelection)); LOG_IF_FAILED(pEngine->InvalidateSelection(rects)); - }); + } - _previousSelection = rects; + _previousSelection = std::move(rects); _NotifyPaintFrame(); } @@ -390,36 +392,25 @@ bool Renderer::_CheckViewportAndScroll() SMALL_RECT const srOldViewport = _viewport.ToInclusive(); SMALL_RECT const srNewViewport = _pData->GetViewport().ToInclusive(); - COORD coordDelta; - coordDelta.X = srOldViewport.Left - srNewViewport.Left; - coordDelta.Y = srOldViewport.Top - srNewViewport.Top; - - for (auto engine : _rgpEngines) + if (srOldViewport == srNewViewport) { - LOG_IF_FAILED(engine->UpdateViewport(srNewViewport)); + return false; } _viewport = Viewport::FromInclusive(srNewViewport); - // If we're keeping some buffers between calls, let them know about the viewport size - // so they can prepare the buffers for changes to either preallocate memory at once - // (instead of growing naturally) or shrink down to reduce usage as appropriate. - const size_t lineLength = gsl::narrow_cast(til::rectangle{ srNewViewport }.width()); - til::manage_vector(_clusterBuffer, lineLength, _shrinkThreshold); + COORD coordDelta; + coordDelta.X = srOldViewport.Left - srNewViewport.Left; + coordDelta.Y = srOldViewport.Top - srNewViewport.Top; - if (coordDelta.X != 0 || coordDelta.Y != 0) + FOREACH_ENGINE(engine) { - for (auto engine : _rgpEngines) - { - LOG_IF_FAILED(engine->InvalidateScroll(&coordDelta)); - } - - _ScrollPreviousSelection(coordDelta); - - return true; + LOG_IF_FAILED(engine->UpdateViewport(srNewViewport)); + LOG_IF_FAILED(engine->InvalidateScroll(&coordDelta)); } - return false; + _ScrollPreviousSelection(coordDelta); + return true; } // Routine Description: @@ -448,9 +439,10 @@ void Renderer::TriggerScroll() // - void Renderer::TriggerScroll(const COORD* const pcoordDelta) { - std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { + FOREACH_ENGINE(pEngine) + { LOG_IF_FAILED(pEngine->InvalidateScroll(pcoordDelta)); - }); + } _ScrollPreviousSelection(*pcoordDelta); @@ -468,7 +460,7 @@ void Renderer::TriggerCircling() { const auto rects = _GetSelectionRects(); - for (IRenderEngine* const pEngine : _rgpEngines) + FOREACH_ENGINE(pEngine) { bool fEngineRequestsRepaint = false; HRESULT hr = pEngine->InvalidateCircling(&fEngineRequestsRepaint); @@ -493,7 +485,7 @@ void Renderer::TriggerCircling() void Renderer::TriggerTitleChange() { const auto newTitle = _pData->GetConsoleTitle(); - for (IRenderEngine* const pEngine : _rgpEngines) + FOREACH_ENGINE(pEngine) { LOG_IF_FAILED(pEngine->InvalidateTitle(newTitle)); } @@ -522,10 +514,11 @@ HRESULT Renderer::_PaintTitle(IRenderEngine* const pEngine) // - void Renderer::TriggerFontChange(const int iDpi, const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) { - std::for_each(_rgpEngines.begin(), _rgpEngines.end(), [&](IRenderEngine* const pEngine) { + FOREACH_ENGINE(pEngine) + { LOG_IF_FAILED(pEngine->UpdateDpi(iDpi)); LOG_IF_FAILED(pEngine->UpdateFont(FontInfoDesired, FontInfo)); - }); + } _NotifyPaintFrame(); } @@ -547,16 +540,19 @@ void Renderer::UpdateSoftFont(const gsl::span bitPattern, const const auto softFontCharCount = cellSize.cy ? bitPattern.size() / cellSize.cy : 0; _lastSoftFontChar = _firstSoftFontChar + softFontCharCount - 1; - for (const auto pEngine : _rgpEngines) + FOREACH_ENGINE(pEngine) { LOG_IF_FAILED(pEngine->UpdateSoftFont(bitPattern, cellSize, centeringHint)); } TriggerRedrawAll(); } +// We initially tried to have a "_isSoftFontChar" member function, but MSVC +// failed to inline it at _all_ call sites (check invocations inside loops). +// This issue strangely doesn't occur with static functions. bool Renderer::s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSoftFontChar, const size_t lastSoftFontChar) { - return v.size() == 1 && v.front() >= firstSoftFontChar && v.front() <= lastSoftFontChar; + return v.size() == 1 && v[0] >= firstSoftFontChar && v[0] <= lastSoftFontChar; } // Routine Description: @@ -570,21 +566,11 @@ bool Renderer::s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSo // - S_OK if set successfully or relevant GDI error via HRESULT. [[nodiscard]] HRESULT Renderer::GetProposedFont(const int iDpi, const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) { - // If there's no head, return E_FAIL. The caller should decide how to - // handle this. - // Currently, the only caller is the WindowProc:WM_GETDPISCALEDSIZE handler. - // It will assume that the proposed font is 1x1, regardless of DPI. - if (_rgpEngines.size() < 1) - { - return E_FAIL; - } - // There will only every really be two engines - the real head and the VT // renderer. We won't know which is which, so iterate over them. // Only return the result of the successful one if it's not S_FALSE (which is the VT renderer) // TODO: 14560740 - The Window might be able to get at this info in a more sane manner - FAIL_FAST_IF(!(_rgpEngines.size() <= 2)); - for (IRenderEngine* const pEngine : _rgpEngines) + FOREACH_ENGINE(pEngine) { const HRESULT hr = LOG_IF_FAILED(pEngine->GetProposedFont(FontInfoDesired, FontInfo, iDpi)); // We're looking for specifically S_OK, S_FALSE is not good enough. @@ -592,7 +578,7 @@ bool Renderer::s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSo { return hr; } - }; + } return E_FAIL; } @@ -615,14 +601,13 @@ bool Renderer::IsGlyphWideByFont(const std::wstring_view glyph) // renderer. We won't know which is which, so iterate over them. // Only return the result of the successful one if it's not S_FALSE (which is the VT renderer) // TODO: 14560740 - The Window might be able to get at this info in a more sane manner - FAIL_FAST_IF(!(_rgpEngines.size() <= 2)); - for (IRenderEngine* const pEngine : _rgpEngines) + FOREACH_ENGINE(pEngine) { const HRESULT hr = LOG_IF_FAILED(pEngine->IsGlyphWideByFont(glyph, &fIsFullWidth)); // We're looking for specifically S_OK, S_FALSE is not good enough. if (hr == S_OK) { - return fIsFullWidth; + break; } } @@ -691,6 +676,12 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine) for (const auto& dirtyRect : dirtyAreas) { + // Shortcut: don't bother redrawing if the width is 0. + if (dirtyRect.left() == dirtyRect.right()) + { + continue; + } + auto dirty = Viewport::FromInclusive(dirtyRect); // Shift the origin of the dirty region to match the underlying buffer so we can @@ -702,47 +693,43 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine) // 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) + // Retrieve the text buffer so we can read information out of it. + const auto& buffer = _pData->GetTextBuffer(); + + // Now walk through each row of text that we need to redraw. + for (auto row = redraw.Top(); row < redraw.BottomExclusive(); row++) { - // Retrieve the text buffer so we can read information out of it. - const auto& buffer = _pData->GetTextBuffer(); + // 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 screenLine = SMALL_RECT{ redraw.Left(), row, redraw.RightInclusive(), row }; - // 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 screenLine = SMALL_RECT{ redraw.Left(), row, redraw.RightInclusive(), row }; + // Convert the screen coordinates of the line to an equivalent + // range of buffer cells, taking line rendition into account. + const auto lineRendition = buffer.GetLineRendition(row); + const auto bufferLine = Viewport::FromInclusive(ScreenToBufferLine(screenLine, lineRendition)); - // Convert the screen coordinates of the line to an equivalent - // range of buffer cells, taking line rendition into account. - const auto lineRendition = buffer.GetLineRendition(row); - const auto bufferLine = Viewport::FromInclusive(ScreenToBufferLine(screenLine, lineRendition)); + // 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 line 1 because it is dirty but the viewport + // is actually looking at line 26 relative to the buffer. This means that we need line 27 out + // of the backing buffer to fill in line 1 of the screen. + const auto screenPosition = bufferLine.Origin() - COORD{ 0, view.Top() }; - // 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 line 1 because it is dirty but the viewport - // is actually looking at line 26 relative to the buffer. This means that we need line 27 out - // of the backing buffer to fill in line 1 of the screen. - const auto screenPosition = bufferLine.Origin() - COORD{ 0, view.Top() }; + // Retrieve the cell information iterator limited to just this line we want to redraw. + auto it = buffer.GetCellDataAt(bufferLine.Origin(), bufferLine); - // Retrieve the cell information iterator limited to just this line we want to redraw. - auto it = buffer.GetCellDataAt(bufferLine.Origin(), bufferLine); + // 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).WasWrapForced()) && + (bufferLine.RightExclusive() == buffer.GetSize().Width()); - // 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).WasWrapForced()) && - (bufferLine.RightExclusive() == buffer.GetSize().Width()); + // Prepare the appropriate line transform for the current row and viewport offset. + LOG_IF_FAILED(pEngine->PrepareLineTransform(lineRendition, screenPosition.Y, view.Left())); - // Prepare the appropriate line transform for the current row and viewport offset. - LOG_IF_FAILED(pEngine->PrepareLineTransform(lineRendition, screenPosition.Y, view.Left())); - - // Ask the helper to paint through this specific line. - _PaintBufferOutputHelper(pEngine, it, screenPosition, lineWrapped); - } + // Ask the helper to paint through this specific line. + _PaintBufferOutputHelper(pEngine, it, screenPosition, lineWrapped); } } } @@ -750,7 +737,7 @@ void Renderer::_PaintBufferOutput(_In_ IRenderEngine* const pEngine) static bool _IsAllSpaces(const std::wstring_view v) { // first non-space char is not found (is npos) - return v.find_first_not_of(L" ") == decltype(v)::npos; + return v.find_first_not_of(L' ') == decltype(v)::npos; } void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, @@ -839,7 +826,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, // Walk through the text data and turn it into rendering clusters. // Keep the columnCount as we go to improve performance over digging it out of the vector at the end. - size_t columnCount = 0; + size_t columnCount = it->Columns(); // If we're on the first cluster to be added and it's marked as "trailing" // (a.k.a. the right half of a two column character), then we need some special handling. @@ -850,14 +837,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, // And tell the next function to trim off the left half of it. trimLeft = true; // And add one to the number of columns we expect it to take as we insert it. - columnCount = it->Columns() + 1; - _clusterBuffer.emplace_back(it->Chars(), columnCount); - } - // Otherwise if it's not a special case, just insert it as is. - else - { - columnCount = it->Columns(); - _clusterBuffer.emplace_back(it->Chars(), columnCount); + ++columnCount; } if (columnCount > 1) @@ -866,6 +846,7 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, } // Advance the cluster and column counts. + _clusterBuffer.emplace_back(it->Chars(), columnCount); it += std::max(it->Columns(), 1); // prevent infinite loop for no visible columns cols += columnCount; @@ -1263,6 +1244,7 @@ std::vector Renderer::_GetSelectionRects() const Viewport view = _pData->GetViewport(); std::vector result; + result.reserve(rects.size()); for (auto rect : rects) { @@ -1322,7 +1304,17 @@ void Renderer::_ScrollPreviousSelection(const til::point delta) void Renderer::AddRenderEngine(_In_ IRenderEngine* const pEngine) { THROW_HR_IF_NULL(E_INVALIDARG, pEngine); - _rgpEngines.push_back(pEngine); + + for (auto& p : _engines) + { + if (!p) + { + p = pEngine; + return; + } + } + + THROW_HR_MSG(E_UNEXPECTED, "engines array is full"); } // Method Description: @@ -1354,7 +1346,7 @@ void Renderer::UpdateLastHoveredInterval(const std::optionalWaitUntilCanRender(); } diff --git a/src/renderer/base/renderer.hpp b/src/renderer/base/renderer.hpp index eb8eadf62..10d853d85 100644 --- a/src/renderer/base/renderer.hpp +++ b/src/renderer/base/renderer.hpp @@ -87,73 +87,39 @@ namespace Microsoft::Console::Render void UpdateLastHoveredInterval(const std::optional::interval>& newInterval); private: - std::deque _rgpEngines; - - IRenderData* _pData; // Non-ownership pointer - - std::unique_ptr _pThread; - bool _destructing = false; - - std::optional::interval> _hoveredInterval; + static IRenderEngine::GridLines s_GetGridlines(const TextAttribute& textAttribute) noexcept; + static bool s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSoftFontChar, const size_t lastSoftFontChar); void _NotifyPaintFrame(); - [[nodiscard]] HRESULT _PaintFrameForEngine(_In_ IRenderEngine* const pEngine) noexcept; - bool _CheckViewportAndScroll(); - [[nodiscard]] HRESULT _PaintBackground(_In_ IRenderEngine* const pEngine); - void _PaintBufferOutput(_In_ IRenderEngine* const pEngine); - - void _PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, - TextBufferCellIterator it, - const COORD target, - const bool lineWrapped); - - static IRenderEngine::GridLines s_GetGridlines(const TextAttribute& textAttribute) noexcept; - - void _PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngine, - const TextAttribute textAttribute, - const size_t cchLine, - const COORD coordTarget); - + void _PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, TextBufferCellIterator it, const COORD target, const bool lineWrapped); + void _PaintBufferOutputGridLineHelper(_In_ IRenderEngine* const pEngine, const TextAttribute textAttribute, const size_t cchLine, const COORD coordTarget); void _PaintSelection(_In_ IRenderEngine* const pEngine); void _PaintCursor(_In_ IRenderEngine* const pEngine); - void _PaintOverlays(_In_ IRenderEngine* const pEngine); void _PaintOverlay(IRenderEngine& engine, const RenderOverlay& overlay); - - [[nodiscard]] HRESULT _UpdateDrawingBrushes(_In_ IRenderEngine* const pEngine, - const TextAttribute attr, - const bool usingSoftFont, - const bool isSettingDefaultBrushes); - + [[nodiscard]] HRESULT _UpdateDrawingBrushes(_In_ IRenderEngine* const pEngine, const TextAttribute attr, const bool usingSoftFont, const bool isSettingDefaultBrushes); [[nodiscard]] HRESULT _PerformScrolling(_In_ IRenderEngine* const pEngine); - - Microsoft::Console::Types::Viewport _viewport; - - static constexpr float _shrinkThreshold = 0.8f; - std::vector _clusterBuffer; - std::vector _GetSelectionRects() const; void _ScrollPreviousSelection(const til::point delta); - std::vector _previousSelection; - [[nodiscard]] HRESULT _PaintTitle(IRenderEngine* const pEngine); - [[nodiscard]] std::optional _GetCursorInfo(); [[nodiscard]] HRESULT _PrepareRenderInfo(_In_ IRenderEngine* const pEngine); - const size_t _firstSoftFontChar = 0xEF20; + std::array _engines{}; + IRenderData* _pData = nullptr; // Non-ownership pointer + std::unique_ptr _pThread; + static constexpr size_t _firstSoftFontChar = 0xEF20; size_t _lastSoftFontChar = 0; - static bool s_IsSoftFontChar(const std::wstring_view& v, const size_t firstSoftFontChar, const size_t lastSoftFontChar); - - // Helper functions to diagnose issues with painting and layout. - // These are only actually effective/on in Debug builds when the flag is set using an attached debugger. - bool _fDebug = false; - + std::optional::interval> _hoveredInterval; + Microsoft::Console::Types::Viewport _viewport; + std::vector _clusterBuffer; + std::vector _previousSelection; std::function _pfnRendererEnteredErrorState; + bool _destructing = false; #ifdef UNIT_TESTING friend class ConptyOutputTests; diff --git a/src/renderer/base/sources.inc b/src/renderer/base/sources.inc index e3f3d2171..cc66054dd 100644 --- a/src/renderer/base/sources.inc +++ b/src/renderer/base/sources.inc @@ -24,7 +24,6 @@ PRECOMPILED_CXX = 1 PRECOMPILED_INCLUDE = ..\precomp.h SOURCES = \ - ..\Cluster.cpp \ ..\BlinkingState.cpp \ ..\FontInfo.cpp \ ..\FontInfoBase.cpp \ diff --git a/src/renderer/inc/Cluster.hpp b/src/renderer/inc/Cluster.hpp index 6f335313d..094aadc0c 100644 --- a/src/renderer/inc/Cluster.hpp +++ b/src/renderer/inc/Cluster.hpp @@ -16,24 +16,52 @@ Author(s): #pragma once +#include + namespace Microsoft::Console::Render { class Cluster { public: - Cluster(const std::wstring_view text, const size_t columns); + constexpr Cluster() noexcept = default; + constexpr Cluster(const std::wstring_view text, const size_t columns) noexcept : + _text{ text }, + _columns{ columns } + { + } - const wchar_t GetTextAsSingle() const noexcept; + // Provides the embedded text as a single character + // This might replace the string with the replacement character if it doesn't fit as one wchar_t. + constexpr wchar_t GetTextAsSingle() const noexcept + { + if (_text.size() == 1) + { + return til::at(_text, 0); + } + else + { + return UNICODE_REPLACEMENT; + } + } - const std::wstring_view& GetText() const noexcept; + // Provides the string of wchar_ts for this cluster. + constexpr std::wstring_view GetText() const noexcept + { + return _text; + } - const size_t GetColumns() const noexcept; + // Gets the number of columns in the grid that this character should consume + // visually when rendered onto a line. + constexpr size_t GetColumns() const noexcept + { + return _columns; + } private: // This is the UTF-16 string of characters that form a particular drawing cluster - const std::wstring_view _text; + std::wstring_view _text; // This is how many columns we're expecting this cluster to take in the display grid - const size_t _columns; + size_t _columns = 0; }; }