Skip to content

Commit

Permalink
Optimize rendering runs of spaces when only the foreground changes
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
DHowett committed Mar 10, 2020
1 parent 852f0aa commit d7b39c8
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 2 deletions.
8 changes: 8 additions & 0 deletions src/buffer/out/TextAttribute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<COLORREF> colorTable,
COLORREF defaultColor) const noexcept;
Expand Down
16 changes: 14 additions & 2 deletions src/renderer/base/renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit d7b39c8

Please sign in to comment.