terminal/src/renderer/vt/vtrenderer.hpp
Leonard Hecker 2353349fe5
Introduce AtlasEngine - A new text rendering prototype (#11623)
This commit introduces "AtlasEngine", a new text renderer based on DxEngine.
But unlike it, DirectWrite and Direct2D are only used to rasterize glyphs.
Blending and placing these glyphs into the target view is being done using
Direct3D and a simple HLSL shader. Since this new renderer more aggressively
assumes that the text is monospace, it simplifies the implementation:
The viewport is divided into cells, and its data is stored as a simple matrix.
Modifications to this matrix involve only simple pointer arithmetic and is easy
to understand. But just like with DxEngine however, DirectWrite
related code remains extremely complex and hard to understand.

Supported features:
* Basic text rendering with grayscale AA
* Foreground and background colors
* Emojis, including zero width joiners
* Underline, dotted underline, strikethrough
* Custom font axes and features
* Selections
* All cursor styles
* Full alpha support for all colors
* _Should_ work with Windows 7

Unsupported features:
* A more conservative GPU memory usage
  The backing texture atlas for glyphs is grow-only and will not shrink.
  After 256MB of memory is used up (~20k glyphs) text output
  will be broken until the renderer is restarted.
* ClearType
* Remaining gridlines (left, right, top, bottom, double underline)
* Hyperlinks don't get full underlines if hovered in WT
* Softfonts
* Non-default line renditions

Performance:
* Runs at up to native display refresh rate
  Unfortunately the frame rate often drops below refresh rate, due us
  fighting over the buffer lock with other parts of the application.
* CPU consumption is up to halved compared to DxEngine
  AtlasEngine is still highly unoptimized. Glyph hashing
  consumes up to a third of the current CPU time.
* No regressions in WT performance
  VT parsing and related buffer management takes up most of the CPU time (~85%),
  due to which the AtlasEngine can't show any further improvements.
* ~2x improvement in raw text throughput in OpenConsole
  compared to DxEngine running at 144 FPS
* ≥10x improvement in colored VT output in WT/OpenConsole
  compared to DxEngine running at 144 FPS
2021-11-13 00:10:06 +00:00

223 lines
10 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- VtRenderer.hpp
Abstract:
- This is the definition of the VT specific implementation of the renderer.
Author(s):
- Michael Niksa (MiNiksa) 24-Jul-2017
- Mike Griese (migrie) 01-Sept-2017
--*/
#pragma once
#include "../inc/RenderEngineBase.hpp"
#include "../../inc/ITerminalOutputConnection.hpp"
#include "../../inc/ITerminalOwner.hpp"
#include "../../types/inc/Viewport.hpp"
#include "tracing.hpp"
#include <string>
#include <functional>
// fwdecl unittest classes
#ifdef UNIT_TESTING
namespace TerminalCoreUnitTests
{
class ConptyRoundtripTests;
};
#endif
namespace Microsoft::Console::Render
{
class VtEngine : public RenderEngineBase, public Microsoft::Console::ITerminalOutputConnection
{
public:
// See _PaintUtf8BufferLine for explanation of this value.
static const size_t ERASE_CHARACTER_STRING_LENGTH = 8;
static const COORD INVALID_COORDS;
VtEngine(_In_ wil::unique_hfile hPipe,
const Microsoft::Console::Types::Viewport initialViewport);
// IRenderEngine
[[nodiscard]] HRESULT StartPaint() noexcept override;
[[nodiscard]] HRESULT EndPaint() noexcept override;
[[nodiscard]] HRESULT Present() noexcept override;
[[nodiscard]] HRESULT PrepareForTeardown(_Out_ bool* pForcePaint) noexcept override;
[[nodiscard]] HRESULT Invalidate(const SMALL_RECT* psrRegion) noexcept override;
[[nodiscard]] HRESULT InvalidateCursor(const SMALL_RECT* psrRegion) noexcept override;
[[nodiscard]] HRESULT InvalidateSystem(const RECT* prcDirtyClient) noexcept override;
[[nodiscard]] HRESULT InvalidateSelection(const std::vector<SMALL_RECT>& rectangles) noexcept override;
[[nodiscard]] HRESULT InvalidateAll() noexcept override;
[[nodiscard]] HRESULT InvalidateCircling(_Out_ bool* pForcePaint) noexcept override;
[[nodiscard]] HRESULT PaintBackground() noexcept override;
[[nodiscard]] HRESULT PaintBufferLine(gsl::span<const Cluster> clusters, COORD coord, bool fTrimLeft, bool lineWrapped) noexcept override;
[[nodiscard]] HRESULT PaintBufferGridLines(GridLineSet lines, COLORREF color, size_t cchLine, COORD coordTarget) noexcept override;
[[nodiscard]] HRESULT PaintSelection(SMALL_RECT rect) noexcept override;
[[nodiscard]] HRESULT PaintCursor(const CursorOptions& options) noexcept override;
[[nodiscard]] HRESULT UpdateFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo) noexcept override;
[[nodiscard]] HRESULT UpdateDpi(int iDpi) noexcept override;
[[nodiscard]] HRESULT UpdateViewport(SMALL_RECT srNewViewport) noexcept override;
[[nodiscard]] HRESULT GetProposedFont(const FontInfoDesired& FontInfoDesired, _Out_ FontInfo& FontInfo, int iDpi) noexcept override;
[[nodiscard]] HRESULT GetDirtyArea(gsl::span<const til::rectangle>& area) noexcept override;
[[nodiscard]] HRESULT GetFontSize(_Out_ COORD* pFontSize) noexcept override;
[[nodiscard]] HRESULT IsGlyphWideByFont(std::wstring_view glyph, _Out_ bool* pResult) noexcept override;
// VtEngine
[[nodiscard]] HRESULT SuppressResizeRepaint() noexcept;
[[nodiscard]] HRESULT RequestCursor() noexcept;
[[nodiscard]] HRESULT InheritCursor(const COORD coordCursor) noexcept;
[[nodiscard]] HRESULT WriteTerminalUtf8(const std::string_view str) noexcept;
[[nodiscard]] virtual HRESULT WriteTerminalW(const std::wstring_view str) noexcept = 0;
void SetTerminalOwner(Microsoft::Console::ITerminalOwner* const terminalOwner);
void BeginResizeRequest();
void EndResizeRequest();
void SetResizeQuirk(const bool resizeQuirk);
[[nodiscard]] virtual HRESULT ManuallyClearScrollback() noexcept;
[[nodiscard]] HRESULT RequestWin32Input() noexcept;
protected:
wil::unique_hfile _hFile;
std::string _buffer;
std::string _formatBuffer;
std::string _conversionBuffer;
TextAttribute _lastTextAttributes;
Microsoft::Console::Types::Viewport _lastViewport;
std::pmr::unsynchronized_pool_resource _pool;
til::pmr::bitmap _invalidMap;
COORD _lastText;
til::point _scrollDelta;
bool _quickReturn;
bool _clearedAllThisFrame;
bool _cursorMoved;
bool _resized;
bool _suppressResizeRepaint;
SHORT _virtualTop;
bool _circled;
bool _firstPaint;
bool _skipCursor;
bool _newBottomLine;
COORD _deferredCursorPos;
bool _pipeBroken;
HRESULT _exitResult;
Microsoft::Console::ITerminalOwner* _terminalOwner;
Microsoft::Console::VirtualTerminal::RenderTracing _trace;
bool _inResizeRequest{ false };
std::optional<short> _wrappedRow{ std::nullopt };
bool _delayedEolWrap{ false };
bool _resizeQuirk{ false };
std::optional<TextColor> _newBottomLineBG{ std::nullopt };
[[nodiscard]] HRESULT _Write(std::string_view const str) noexcept;
[[nodiscard]] HRESULT _Flush() noexcept;
template<typename S, typename... Args>
[[nodiscard]] HRESULT _WriteFormatted(S&& format, Args&&... args)
try
{
fmt::basic_memory_buffer<char, 64> buf;
fmt::format_to(std::back_inserter(buf), std::forward<S>(format), std::forward<Args>(args)...);
return _Write({ buf.data(), buf.size() });
}
CATCH_RETURN()
void _OrRect(_Inout_ SMALL_RECT* const pRectExisting, const SMALL_RECT* const pRectToOr) const;
bool _AllIsInvalid() const;
[[nodiscard]] HRESULT _StopCursorBlinking() noexcept;
[[nodiscard]] HRESULT _StartCursorBlinking() noexcept;
[[nodiscard]] HRESULT _HideCursor() noexcept;
[[nodiscard]] HRESULT _ShowCursor() noexcept;
[[nodiscard]] HRESULT _EraseLine() noexcept;
[[nodiscard]] HRESULT _InsertDeleteLine(const short sLines, const bool fInsertLine) noexcept;
[[nodiscard]] HRESULT _DeleteLine(const short sLines) noexcept;
[[nodiscard]] HRESULT _InsertLine(const short sLines) noexcept;
[[nodiscard]] HRESULT _CursorForward(const short chars) noexcept;
[[nodiscard]] HRESULT _EraseCharacter(const short chars) noexcept;
[[nodiscard]] HRESULT _CursorPosition(const COORD coord) noexcept;
[[nodiscard]] HRESULT _CursorHome() noexcept;
[[nodiscard]] HRESULT _ClearScreen() noexcept;
[[nodiscard]] HRESULT _ClearScrollback() noexcept;
[[nodiscard]] HRESULT _ChangeTitle(const std::string& title) noexcept;
[[nodiscard]] HRESULT _SetGraphicsRendition16Color(const BYTE index,
const bool fIsForeground) noexcept;
[[nodiscard]] HRESULT _SetGraphicsRendition256Color(const BYTE index,
const bool fIsForeground) noexcept;
[[nodiscard]] HRESULT _SetGraphicsRenditionRGBColor(const COLORREF color,
const bool fIsForeground) noexcept;
[[nodiscard]] HRESULT _SetGraphicsRenditionDefaultColor(const bool fIsForeground) noexcept;
[[nodiscard]] HRESULT _SetGraphicsDefault() noexcept;
[[nodiscard]] HRESULT _ResizeWindow(const short sWidth, const short sHeight) noexcept;
[[nodiscard]] HRESULT _SetBold(const bool isBold) noexcept;
[[nodiscard]] HRESULT _SetFaint(const bool isFaint) noexcept;
[[nodiscard]] HRESULT _SetUnderlined(const bool isUnderlined) noexcept;
[[nodiscard]] HRESULT _SetDoublyUnderlined(const bool isUnderlined) noexcept;
[[nodiscard]] HRESULT _SetOverlined(const bool isOverlined) noexcept;
[[nodiscard]] HRESULT _SetItalic(const bool isItalic) noexcept;
[[nodiscard]] HRESULT _SetBlinking(const bool isBlinking) noexcept;
[[nodiscard]] HRESULT _SetInvisible(const bool isInvisible) noexcept;
[[nodiscard]] HRESULT _SetCrossedOut(const bool isCrossedOut) noexcept;
[[nodiscard]] HRESULT _SetReverseVideo(const bool isReversed) noexcept;
[[nodiscard]] HRESULT _SetHyperlink(const std::wstring_view& uri, const std::wstring_view& customId, const uint16_t& numberId) noexcept;
[[nodiscard]] HRESULT _EndHyperlink() noexcept;
[[nodiscard]] HRESULT _RequestCursor() noexcept;
[[nodiscard]] HRESULT _RequestWin32Input() noexcept;
[[nodiscard]] virtual HRESULT _MoveCursor(const COORD coord) noexcept = 0;
[[nodiscard]] HRESULT _RgbUpdateDrawingBrushes(const TextAttribute& textAttributes) noexcept;
[[nodiscard]] HRESULT _16ColorUpdateDrawingBrushes(const TextAttribute& textAttributes) noexcept;
bool _WillWriteSingleChar() const;
// buffer space for these two functions to build their lines
// so they don't have to alloc/free in a tight loop
std::wstring _bufferLine;
[[nodiscard]] HRESULT _PaintUtf8BufferLine(gsl::span<const Cluster> const clusters,
const COORD coord,
const bool lineWrapped) noexcept;
[[nodiscard]] HRESULT _PaintAsciiBufferLine(gsl::span<const Cluster> const clusters,
const COORD coord) noexcept;
[[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_view newTitle) noexcept override;
/////////////////////////// Unit Testing Helpers ///////////////////////////
#ifdef UNIT_TESTING
std::function<bool(const char* const, size_t const)> _pfnTestCallback;
bool _usingTestCallback;
friend class VtRendererTest;
friend class ConptyOutputTests;
friend class TerminalCoreUnitTests::ConptyRoundtripTests;
#endif
void SetTestCallback(_In_ std::function<bool(const char* const, size_t const)> pfn);
};
}