From d7b39c86c67d2e60de77e293cc4cceedf5e858da Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Tue, 10 Mar 2020 15:21:33 -0700 Subject: [PATCH] Optimize rendering runs of spaces when only the foreground changes cmatrix is somewhat of a pathological case for our infrastructure: it prints out a bunch of green and white characters and then updates them a million times a second. It also maintains a column of space between every green character. When it prints this column, it prints it in "default" or "white". This ends up making runs of text that look like this: (def: G=green B=bright white W=white *=matrix char =space) G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W G W As characters trickle in: G*W G*W G*W G*W G*W G*W G*W B*W G*W G*W G*W G*W G*W G*W G*W G W G*W G*W G*W B*W G*W G*W G*W G W G*W B*W G*W G W G*W G*W G*W G*W G*W G W G*W G W G*W B*W G*W G*W B*W G W G*W G W G*W G W B*W G*W G W G W G*W G W G*W G W G W B*W G W G W B*W G W G*W G W G W G W Every one of those color transitions causes us to break up the run of text and start rendering it again. This impacts GDI, Direct2D *and* ConPTY. The problem is, printing a space doesn't **use** the foreground color! This commit introduces an optimization. When we're about to break a text cluster becuase its attributes changed, we make sure that it's not just filled with spaces and differs only in the foreground parameter. This lets us optimize both the rendering _and_ the PTY output to look like this: G* * * * * * * B* G* * * * * * * G* * * B* * * * G* B* * * * * * G* * * B* * * B* * * B* * G * * B* G B* * Text will be printed at best line-by-line and at worst only when the visible properties of the screen actually change. This speeds up cmatrix remarkably. --- src/buffer/out/TextAttribute.hpp | 8 ++++++++ src/renderer/base/renderer.cpp | 16 ++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/buffer/out/TextAttribute.hpp b/src/buffer/out/TextAttribute.hpp index 36fcc6c1d80..38f8aefde4a 100644 --- a/src/buffer/out/TextAttribute.hpp +++ b/src/buffer/out/TextAttribute.hpp @@ -163,6 +163,14 @@ class TextAttribute final return _foreground.IsRgb() || _background.IsRgb(); } + // In certain scenarios, we don't care about specifically the foreground color. + constexpr bool EqualsExceptForeground(const TextAttribute& other) const noexcept + { + return (_wAttrLegacy & META_ATTRS) == (other._wAttrLegacy & META_ATTRS) && + _background == other._background && + _extendedAttrs == other._extendedAttrs; + } + private: COLORREF _GetRgbForeground(std::basic_string_view colorTable, COLORREF defaultColor) const noexcept; diff --git a/src/renderer/base/renderer.cpp b/src/renderer/base/renderer.cpp index ae6a25ab364..e9fe6784e57 100644 --- a/src/renderer/base/renderer.cpp +++ b/src/renderer/base/renderer.cpp @@ -610,6 +610,12 @@ 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; +} + void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, TextBufferCellIterator it, const COORD target, @@ -661,8 +667,14 @@ void Renderer::_PaintBufferOutputHelper(_In_ IRenderEngine* const pEngine, { if (color != it->TextAttr()) { - color = it->TextAttr(); - break; + auto newAttr{ it->TextAttr() }; + // foreground doesn't matter for runs of spaces (!) + // if we trick it . . . we call Paint far fewer times for cmatrix + if (!_IsAllSpaces(it->Chars()) || !newAttr.EqualsExceptForeground(color)) + { + color = newAttr; + break; // vend this run + } } // Walk through the text data and turn it into rendering clusters.