Skip to content

Commit

Permalink
Pass through line rendition attributes over conpty (#13933)
Browse files Browse the repository at this point in the history
This PR introduces a mechanism for passing through line rendition
attributes to the conpty client, so we can support double-width and
double-height text in Windows Terminal.

Line renditions were first implemented in conhost (with the GDI
renderer) in PR #8664, and were implemented in the DX renderer in PR
#13102.

By default this won't add any additional overhead to the conpty output,
but as soon as there is any usage of double-size text, we switch to a
mode in which every line output will be prefixed with a line rendition
sequence. This is to ensure that the line attributes in the client
terminal are always in sync with the host.

Since this does add some overhead to the conpty output, we'd prefer not
to remain in this mode longer than necessary. So whenever there is a
full repaint of the entire viewport, we check to see if all of the lines
are single-width. If that is the case, we can then safely skip the line
rendition sequences in future updates.

One other small optimization is when the conpty update is only writing
out a single character (this is something we already check for). When
that is the case, we can safely skip the line rendition prefix, because
a single character update should never include a change of the line
rendition.

## Validation Steps Performed

I've manually tested that Windows Terminal now passes the double-size
tests in _Vttest_, and also confirmed various edge cases are working
correctly in my own double-size tests.

Closes #11595
  • Loading branch information
j4james authored Sep 9, 2022
1 parent 3958c93 commit 3e6abd3
Show file tree
Hide file tree
Showing 12 changed files with 72 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/renderer/atlas/AtlasEngine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ CATCH_RETURN()
return S_OK;
}

[[nodiscard]] HRESULT AtlasEngine::PrepareLineTransform(const LineRendition lineRendition, const size_t targetRow, const size_t viewportLeft) noexcept
[[nodiscard]] HRESULT AtlasEngine::PrepareLineTransform(const LineRendition lineRendition, const til::CoordType targetRow, const til::CoordType viewportLeft) noexcept
{
return S_OK;
}
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/atlas/AtlasEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT NotifyNewText(const std::wstring_view newText) noexcept override;
[[nodiscard]] HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept override;
[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
[[nodiscard]] HRESULT PrepareLineTransform(LineRendition lineRendition, size_t targetRow, size_t viewportLeft) noexcept override;
[[nodiscard]] HRESULT PrepareLineTransform(LineRendition lineRendition, til::CoordType targetRow, til::CoordType viewportLeft) noexcept override;
[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override;
[[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, til::point coordTarget) noexcept override;
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/base/RenderEngineBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ HRESULT RenderEngineBase::ResetLineTransform() noexcept
}

HRESULT RenderEngineBase::PrepareLineTransform(const LineRendition /*lineRendition*/,
const size_t /*targetRow*/,
const size_t /*viewportLeft*/) noexcept
const til::CoordType /*targetRow*/,
const til::CoordType /*viewportLeft*/) noexcept
{
return S_FALSE;
}
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/dx/DxRenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2343,8 +2343,8 @@ CATCH_RETURN();
// Return Value:
// - S_OK if successful. S_FALSE if already set. E_FAIL if there was an error.
[[nodiscard]] HRESULT DxEngine::PrepareLineTransform(const LineRendition lineRendition,
const size_t targetRow,
const size_t viewportLeft) noexcept
const til::CoordType targetRow,
const til::CoordType viewportLeft) noexcept
{
auto lineTransform = D2D1::Matrix3x2F{ 0, 0, 0, 0, 0, 0 };
const auto fontSize = _fontRenderData->GlyphCell();
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/dx/DxRenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ namespace Microsoft::Console::Render

[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
[[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition,
const size_t targetRow,
const size_t viewportLeft) noexcept override;
const til::CoordType targetRow,
const til::CoordType viewportLeft) noexcept override;

[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(const gsl::span<const Cluster> clusters,
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/gdi/gdirenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ namespace Microsoft::Console::Render

[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
[[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition,
const size_t targetRow,
const size_t viewportLeft) noexcept override;
const til::CoordType targetRow,
const til::CoordType viewportLeft) noexcept override;

[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(const gsl::span<const Cluster> clusters,
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/gdi/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,8 +210,8 @@ GdiEngine::~GdiEngine()
// Return Value:
// - S_OK if successful. S_FALSE if already set. E_FAIL if there was an error.
[[nodiscard]] HRESULT GdiEngine::PrepareLineTransform(const LineRendition lineRendition,
const size_t targetRow,
const size_t viewportLeft) noexcept
const til::CoordType targetRow,
const til::CoordType viewportLeft) noexcept
{
XFORM lineTransform = {};
// The X delta is to account for the horizontal viewport offset.
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/inc/IRenderEngine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ namespace Microsoft::Console::Render
[[nodiscard]] virtual HRESULT NotifyNewText(const std::wstring_view newText) noexcept = 0;
[[nodiscard]] virtual HRESULT PrepareRenderInfo(const RenderFrameInfo& info) noexcept = 0;
[[nodiscard]] virtual HRESULT ResetLineTransform() noexcept = 0;
[[nodiscard]] virtual HRESULT PrepareLineTransform(LineRendition lineRendition, size_t targetRow, size_t viewportLeft) noexcept = 0;
[[nodiscard]] virtual HRESULT PrepareLineTransform(LineRendition lineRendition, til::CoordType targetRow, til::CoordType viewportLeft) noexcept = 0;
[[nodiscard]] virtual HRESULT PaintBackground() noexcept = 0;
[[nodiscard]] virtual HRESULT PaintBufferLine(gsl::span<const Cluster> clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept = 0;
[[nodiscard]] virtual HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, til::point coordTarget) noexcept = 0;
Expand Down
4 changes: 2 additions & 2 deletions src/renderer/inc/RenderEngineBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ namespace Microsoft::Console::Render

[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
[[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition,
const size_t targetRow,
const size_t viewportLeft) noexcept override;
const til::CoordType targetRow,
const til::CoordType viewportLeft) noexcept override;

[[nodiscard]] virtual bool RequiresContinuousRedraw() noexcept override;

Expand Down
51 changes: 51 additions & 0 deletions src/renderer/vt/paint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ using namespace Microsoft::Console::Types;
return S_FALSE;
}

// If we're using line renditions, and this is a full screen paint, we can
// potentially stop using them at the end of this frame.
_stopUsingLineRenditions = _usingLineRenditions && _AllIsInvalid();

// If there's nothing to do, quick return
auto somethingToDo = _invalidMap.any() ||
_scrollDelta != til::point{ 0, 0 } ||
Expand Down Expand Up @@ -75,6 +79,14 @@ using namespace Microsoft::Console::Types;
}
_circled = false;

// If _stopUsingLineRenditions is still true at the end of the frame, that
// means we've refreshed the entire viewport with every line being single
// width, so we can safely stop using them from now on.
if (_stopUsingLineRenditions)
{
_usingLineRenditions = false;
}

// If we deferred a cursor movement during the frame, make sure we put the
// cursor in the right place before we end the frame.
if (_deferredCursorPos != INVALID_COORDS)
Expand All @@ -100,6 +112,45 @@ using namespace Microsoft::Console::Types;
return S_FALSE;
}

[[nodiscard]] HRESULT VtEngine::ResetLineTransform() noexcept
{
return S_FALSE;
}

[[nodiscard]] HRESULT VtEngine::PrepareLineTransform(const LineRendition lineRendition,
const til::CoordType targetRow,
const til::CoordType /*viewportLeft*/) noexcept
{
// We don't want to waste bandwidth writing out line rendition attributes
// until we know they're in use. But once they are in use, we have to keep
// applying them on every line until we know they definitely aren't being
// used anymore (we check that at the end of any fullscreen paint).
if (lineRendition != LineRendition::SingleWidth)
{
_stopUsingLineRenditions = false;
_usingLineRenditions = true;
}
// One simple optimization is that we can skip sending the line attributes
// when _quickReturn is true. That indicates that we're writing out a single
// character, which should preclude there being a rendition switch.
if (_usingLineRenditions && !_quickReturn)
{
RETURN_IF_FAILED(_MoveCursor({ _lastText.x, targetRow }));
switch (lineRendition)
{
case LineRendition::SingleWidth:
return _Write("\x1b#5");
case LineRendition::DoubleWidth:
return _Write("\x1b#6");
case LineRendition::DoubleHeightTop:
return _Write("\x1b#3");
case LineRendition::DoubleHeightBottom:
return _Write("\x1b#4");
}
}
return S_OK;
}

// Routine Description:
// - Paints the background of the invalid area of the frame.
// Arguments:
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/vt/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ VtEngine::VtEngine(_In_ wil::unique_hfile pipe,
const Viewport initialViewport) :
RenderEngineBase(),
_hFile(std::move(pipe)),
_usingLineRenditions(false),
_stopUsingLineRenditions(false),
_lastTextAttributes(INVALID_COLOR, INVALID_COLOR),
_lastViewport(initialViewport),
_pool(til::pmr::get_default_resource()),
Expand Down
4 changes: 4 additions & 0 deletions src/renderer/vt/vtrenderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ namespace Microsoft::Console::Render
[[nodiscard]] HRESULT InvalidateSelection(const std::vector<til::rect>& rectangles) noexcept override;
[[nodiscard]] HRESULT InvalidateAll() noexcept override;
[[nodiscard]] HRESULT InvalidateFlush(_In_ const bool circled, _Out_ bool* const pForcePaint) noexcept override;
[[nodiscard]] HRESULT ResetLineTransform() noexcept override;
[[nodiscard]] HRESULT PrepareLineTransform(const LineRendition lineRendition, const til::CoordType targetRow, const til::CoordType viewportLeft) noexcept override;
[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> clusters, til::point coord, bool fTrimLeft, bool lineWrapped) noexcept override;
[[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, til::point coordTarget) noexcept override;
Expand Down Expand Up @@ -96,6 +98,8 @@ namespace Microsoft::Console::Render
std::string _formatBuffer;
std::string _conversionBuffer;

bool _usingLineRenditions;
bool _stopUsingLineRenditions;
TextAttribute _lastTextAttributes;

std::function<void(bool)> _pfnSetLookingForDSR;
Expand Down

0 comments on commit 3e6abd3

Please sign in to comment.