mirror of
https://github.com/microsoft/terminal.git
synced 2026-02-05 21:24:53 +00:00
Recycle assorted rendering components to accelerate drawing (#6483)
This saves an awful lot of construction/destruction and memory allocation, especially with text that changes a lot (see: cacafire). Three things: 1. Recycling the text layouts. This holds onto the `CustomTextLayout` so all the things that don't change related to drawing targets and whatnot aren't freed and recreated every frame. 2. Reordering the runs in place. This saves a vector allocation/copy/delete every time OrderRuns is called. They can be rearranged in place. 3. Only clip once per row. This reduces the clip push/pop to only one time per row. Since we're always redrawing an entire row at a time, this saves a lot of alloc/free of the clip frame, dramatically reduces queued commands, and makes less work on the flush since clipping requires staging the drawing and then bringing it back to the main surface.
This commit is contained in:
@@ -20,14 +20,12 @@ using namespace Microsoft::Console::Render;
|
||||
// - analyzer - DirectWrite text analyzer from the factory that has been cached at a level above this layout (expensive to create)
|
||||
// - format - The DirectWrite format object representing the size and other text properties to be applied (by default) to a layout
|
||||
// - font - The DirectWrite font face to use while calculating layout (by default, will fallback if necessary)
|
||||
// - clusters - From the backing buffer, the text to be displayed clustered by the columns it should consume.
|
||||
// - width - The count of pixels available per column (the expected pixel width of every column)
|
||||
// - boxEffect - Box drawing scaling effects that are cached for the base font across layouts.
|
||||
CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory,
|
||||
gsl::not_null<IDWriteTextAnalyzer1*> const analyzer,
|
||||
gsl::not_null<IDWriteTextFormat*> const format,
|
||||
gsl::not_null<IDWriteFontFace1*> const font,
|
||||
std::basic_string_view<Cluster> const clusters,
|
||||
size_t const width,
|
||||
IBoxDrawingEffect* const boxEffect) :
|
||||
_factory{ factory.get() },
|
||||
@@ -47,8 +45,37 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
|
||||
// Fetch the locale name out once now from the format
|
||||
_localeName.resize(gsl::narrow_cast<size_t>(format->GetLocaleNameLength()) + 1); // +1 for null
|
||||
THROW_IF_FAILED(format->GetLocaleName(_localeName.data(), gsl::narrow<UINT32>(_localeName.size())));
|
||||
}
|
||||
|
||||
_textClusterColumns.reserve(clusters.size());
|
||||
//Routine Description:
|
||||
// - Resets this custom text layout to the freshly allocated state in terms of text analysis.
|
||||
// Arguments:
|
||||
// - <none>, modifies internal state
|
||||
// Return Value:
|
||||
// - S_OK or suitable memory management issue
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::Reset() noexcept
|
||||
try
|
||||
{
|
||||
_runs.clear();
|
||||
_breakpoints.clear();
|
||||
_runIndex = 0;
|
||||
_isEntireTextSimple = false;
|
||||
_textClusterColumns.clear();
|
||||
_text.clear();
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
// Routine Description:
|
||||
// - Appends text to this layout for analysis/processing.
|
||||
// Arguments:
|
||||
// - clusters - From the backing buffer, the text to be displayed clustered by the columns it should consume.
|
||||
// Return Value:
|
||||
// - S_OK or suitable memory management issue.
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE CustomTextLayout::AppendClusters(const std::basic_string_view<::Microsoft::Console::Render::Cluster> clusters)
|
||||
try
|
||||
{
|
||||
_textClusterColumns.reserve(_textClusterColumns.size() + clusters.size());
|
||||
|
||||
for (const auto& cluster : clusters)
|
||||
{
|
||||
@@ -64,7 +91,10 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
|
||||
|
||||
_text += text;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
// Routine Description:
|
||||
// - Figures out how many columns this layout should take. This will use the analyze step only.
|
||||
@@ -1827,21 +1857,13 @@ void CustomTextLayout::_SplitCurrentRun(const UINT32 splitPosition)
|
||||
// - <none>
|
||||
void CustomTextLayout::_OrderRuns()
|
||||
{
|
||||
const size_t totalRuns = _runs.size();
|
||||
std::vector<LinkedRun> runs;
|
||||
runs.resize(totalRuns);
|
||||
|
||||
UINT32 nextRunIndex = 0;
|
||||
for (UINT32 i = 0; i < totalRuns; ++i)
|
||||
std::sort(_runs.begin(), _runs.end(), [](auto& a, auto& b) { return a.textStart < b.textStart; });
|
||||
for (UINT32 i = 0; i < _runs.size() - 1; ++i)
|
||||
{
|
||||
runs.at(i) = _runs.at(nextRunIndex);
|
||||
runs.at(i).nextRunIndex = i + 1;
|
||||
nextRunIndex = _runs.at(nextRunIndex).nextRunIndex;
|
||||
til::at(_runs, i).nextRunIndex = i + 1;
|
||||
}
|
||||
|
||||
runs.back().nextRunIndex = 0;
|
||||
|
||||
_runs.swap(runs);
|
||||
_runs.back().nextRunIndex = 0;
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
@@ -24,10 +24,13 @@ namespace Microsoft::Console::Render
|
||||
gsl::not_null<IDWriteTextAnalyzer1*> const analyzer,
|
||||
gsl::not_null<IDWriteTextFormat*> const format,
|
||||
gsl::not_null<IDWriteFontFace1*> const font,
|
||||
const std::basic_string_view<::Microsoft::Console::Render::Cluster> clusters,
|
||||
size_t const width,
|
||||
IBoxDrawingEffect* const boxEffect);
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE AppendClusters(const std::basic_string_view<::Microsoft::Console::Render::Cluster> clusters);
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE Reset() noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE GetColumns(_Out_ UINT32* columns);
|
||||
|
||||
// IDWriteTextLayout methods (but we don't actually want to implement them all, so just this one matching the existing interface)
|
||||
|
||||
@@ -409,6 +409,40 @@ CATCH_RETURN()
|
||||
::Microsoft::WRL::ComPtr<ID2D1DeviceContext> d2dContext;
|
||||
RETURN_IF_FAILED(drawingContext->renderTarget->QueryInterface(d2dContext.GetAddressOf()));
|
||||
|
||||
// Determine clip rectangle
|
||||
D2D1_RECT_F clipRect;
|
||||
clipRect.top = origin.y;
|
||||
clipRect.bottom = clipRect.top + drawingContext->cellSize.height;
|
||||
clipRect.left = 0;
|
||||
clipRect.right = drawingContext->targetSize.width;
|
||||
|
||||
// If we already have a clip rectangle, check if it different than the previous one.
|
||||
if (_clipRect.has_value())
|
||||
{
|
||||
const auto storedVal = _clipRect.value();
|
||||
// If it is different, pop off the old one and push the new one on.
|
||||
if (storedVal.top != clipRect.top || storedVal.bottom != clipRect.bottom ||
|
||||
storedVal.left != clipRect.left || storedVal.right != clipRect.right)
|
||||
{
|
||||
d2dContext->PopAxisAlignedClip();
|
||||
|
||||
// Clip all drawing in this glyph run to where we expect.
|
||||
// We need the AntialiasMode here to be Aliased to ensure
|
||||
// that background boxes line up with each other and don't leave behind
|
||||
// stray colors.
|
||||
// See GH#3626 for more details.
|
||||
d2dContext->PushAxisAlignedClip(clipRect, D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
_clipRect = clipRect;
|
||||
}
|
||||
}
|
||||
// If we have no clip rectangle, it's easy. Push it on and go.
|
||||
else
|
||||
{
|
||||
// See above for aliased flag explanation.
|
||||
d2dContext->PushAxisAlignedClip(clipRect, D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
_clipRect = clipRect;
|
||||
}
|
||||
|
||||
// Draw the background
|
||||
// The rectangle needs to be deduced based on the origin and the BidiDirection
|
||||
const auto advancesSpan = gsl::make_span(glyphRun->glyphAdvances, glyphRun->glyphCount);
|
||||
@@ -425,18 +459,6 @@ CATCH_RETURN()
|
||||
}
|
||||
rect.right = rect.left + totalSpan;
|
||||
|
||||
// Clip all drawing in this glyph run to where we expect.
|
||||
// We need the AntialiasMode here to be Aliased to ensure
|
||||
// that background boxes line up with each other and don't leave behind
|
||||
// stray colors.
|
||||
// See GH#3626 for more details.
|
||||
d2dContext->PushAxisAlignedClip(rect, D2D1_ANTIALIAS_MODE_ALIASED);
|
||||
|
||||
// Ensure we pop it on the way out
|
||||
auto popclip = wil::scope_exit([&d2dContext]() noexcept {
|
||||
d2dContext->PopAxisAlignedClip();
|
||||
});
|
||||
|
||||
d2dContext->FillRectangle(rect, drawingContext->backgroundBrush);
|
||||
|
||||
RETURN_IF_FAILED(_drawCursor(d2dContext.Get(), rect, *drawingContext, true));
|
||||
@@ -635,6 +657,22 @@ CATCH_RETURN()
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::EndClip(void* clientDrawingContext) noexcept
|
||||
try
|
||||
{
|
||||
DrawingContext* drawingContext = static_cast<DrawingContext*>(clientDrawingContext);
|
||||
RETURN_HR_IF(E_INVALIDARG, !drawingContext);
|
||||
|
||||
if (_clipRect.has_value())
|
||||
{
|
||||
drawingContext->renderTarget->PopAxisAlignedClip();
|
||||
_clipRect = std::nullopt;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN()
|
||||
|
||||
[[nodiscard]] HRESULT CustomTextRenderer::_DrawBasicGlyphRun(DrawingContext* clientDrawingContext,
|
||||
D2D1_POINT_2F baselineOrigin,
|
||||
DWRITE_MEASURING_MODE measuringMode,
|
||||
|
||||
@@ -18,18 +18,20 @@ namespace Microsoft::Console::Render
|
||||
IDWriteFactory* dwriteFactory,
|
||||
const DWRITE_LINE_SPACING spacing,
|
||||
const D2D_SIZE_F cellSize,
|
||||
const D2D_SIZE_F targetSize,
|
||||
const std::optional<CursorOptions>& cursorInfo,
|
||||
const D2D1_DRAW_TEXT_OPTIONS options = D2D1_DRAW_TEXT_OPTIONS_NONE) noexcept
|
||||
const D2D1_DRAW_TEXT_OPTIONS options = D2D1_DRAW_TEXT_OPTIONS_NONE) noexcept :
|
||||
renderTarget(renderTarget),
|
||||
foregroundBrush(foregroundBrush),
|
||||
backgroundBrush(backgroundBrush),
|
||||
forceGrayscaleAA(forceGrayscaleAA),
|
||||
dwriteFactory(dwriteFactory),
|
||||
spacing(spacing),
|
||||
cellSize(cellSize),
|
||||
targetSize(targetSize),
|
||||
cursorInfo(cursorInfo),
|
||||
options(options)
|
||||
{
|
||||
this->renderTarget = renderTarget;
|
||||
this->foregroundBrush = foregroundBrush;
|
||||
this->backgroundBrush = backgroundBrush;
|
||||
this->forceGrayscaleAA = forceGrayscaleAA;
|
||||
this->dwriteFactory = dwriteFactory;
|
||||
this->spacing = spacing;
|
||||
this->cellSize = cellSize;
|
||||
this->cursorInfo = cursorInfo;
|
||||
this->options = options;
|
||||
}
|
||||
|
||||
ID2D1RenderTarget* renderTarget;
|
||||
@@ -39,6 +41,7 @@ namespace Microsoft::Console::Render
|
||||
IDWriteFactory* dwriteFactory;
|
||||
DWRITE_LINE_SPACING spacing;
|
||||
D2D_SIZE_F cellSize;
|
||||
D2D_SIZE_F targetSize;
|
||||
std::optional<CursorOptions> cursorInfo;
|
||||
D2D1_DRAW_TEXT_OPTIONS options;
|
||||
};
|
||||
@@ -98,6 +101,8 @@ namespace Microsoft::Console::Render
|
||||
BOOL isRightToLeft,
|
||||
IUnknown* clientDrawingEffect) noexcept override;
|
||||
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE EndClip(void* clientDrawingContext) noexcept;
|
||||
|
||||
private:
|
||||
[[nodiscard]] HRESULT _FillRectangle(void* clientDrawingContext,
|
||||
IUnknown* clientDrawingEffect,
|
||||
@@ -128,5 +133,7 @@ namespace Microsoft::Console::Render
|
||||
DWRITE_MEASURING_MODE measuringMode,
|
||||
_In_ const DWRITE_GLYPH_RUN* glyphRun,
|
||||
_In_opt_ const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription) noexcept;
|
||||
|
||||
std::optional<D2D1_RECT_F> _clipRect;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -97,7 +97,9 @@ DxEngine::DxEngine() :
|
||||
_scale{ 1.0f },
|
||||
_prevScale{ 1.0f },
|
||||
_chainMode{ SwapChainMode::ForComposition },
|
||||
_customRenderer{ ::Microsoft::WRL::Make<CustomTextRenderer>() }
|
||||
_customLayout{},
|
||||
_customRenderer{ ::Microsoft::WRL::Make<CustomTextRenderer>() },
|
||||
_drawingContext{}
|
||||
{
|
||||
const auto was = _tracelogCount.fetch_add(1);
|
||||
if (0 == was)
|
||||
@@ -692,6 +694,44 @@ void DxEngine::_ReleaseDeviceResources() noexcept
|
||||
CATCH_LOG();
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Calculates whether or not we should force grayscale AA based on the
|
||||
// current renderer state.
|
||||
// Arguments:
|
||||
// - <none> - Uses internal state of _antialiasingMode, _defaultTextBackgroundOpacity,
|
||||
// _backgroundColor, and _defaultBackgroundColor.
|
||||
// Return Value:
|
||||
// - True if we must render this text in grayscale AA as cleartype simply won't work. False otherwise.
|
||||
[[nodiscard]] bool DxEngine::_ShouldForceGrayscaleAA() noexcept
|
||||
{
|
||||
// GH#5098: If we're rendering with cleartype text, we need to always
|
||||
// render onto an opaque background. If our background's opacity is
|
||||
// 1.0f, that's great, we can use that. Otherwise, we need to force the
|
||||
// text renderer to render this text in grayscale. In
|
||||
// UpdateDrawingBrushes, we'll set the backgroundColor's a channel to
|
||||
// 1.0 if we're in cleartype mode and the background's opacity is 1.0.
|
||||
// Otherwise, at this point, the _backgroundColor's alpha is <1.0.
|
||||
//
|
||||
// Currently, only text with the default background color uses an alpha
|
||||
// of 0, every other background uses 1.0
|
||||
//
|
||||
// DANGER: Layers slow us down. Only do this in the specific case where
|
||||
// someone has chosen the slower ClearType antialiasing (versus the faster
|
||||
// grayscale antialiasing)
|
||||
const bool usingCleartype = _antialiasingMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
|
||||
const bool usingTransparency = _defaultTextBackgroundOpacity != 1.0f;
|
||||
// Another way of naming "bgIsDefault" is "bgHasTransparency"
|
||||
const auto bgIsDefault = (_backgroundColor.a == _defaultBackgroundColor.a) &&
|
||||
(_backgroundColor.r == _defaultBackgroundColor.r) &&
|
||||
(_backgroundColor.g == _defaultBackgroundColor.g) &&
|
||||
(_backgroundColor.b == _defaultBackgroundColor.b);
|
||||
const bool forceGrayscaleAA = usingCleartype &&
|
||||
usingTransparency &&
|
||||
bgIsDefault;
|
||||
|
||||
return forceGrayscaleAA;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Helper to create a DirectWrite text layout object
|
||||
// out of a string.
|
||||
@@ -734,6 +774,7 @@ CATCH_RETURN()
|
||||
try
|
||||
{
|
||||
_sizeTarget = Pixels;
|
||||
|
||||
_invalidMap.resize(_sizeTarget / _glyphCell, true);
|
||||
return S_OK;
|
||||
}
|
||||
@@ -1064,6 +1105,24 @@ try
|
||||
|
||||
_d2dDeviceContext->BeginDraw();
|
||||
_isPainting = true;
|
||||
|
||||
{
|
||||
// Get the baseline for this font as that's where we draw from
|
||||
DWRITE_LINE_SPACING spacing;
|
||||
RETURN_IF_FAILED(_dwriteTextFormat->GetLineSpacing(&spacing.method, &spacing.height, &spacing.baseline));
|
||||
|
||||
// Assemble the drawing context information
|
||||
_drawingContext = std::make_unique<DrawingContext>(_d2dDeviceContext.Get(),
|
||||
_d2dBrushForeground.Get(),
|
||||
_d2dBrushBackground.Get(),
|
||||
_ShouldForceGrayscaleAA(),
|
||||
_dwriteFactory.Get(),
|
||||
spacing,
|
||||
_glyphCell,
|
||||
_d2dDeviceContext->GetSize(),
|
||||
std::nullopt,
|
||||
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
@@ -1087,6 +1146,9 @@ try
|
||||
{
|
||||
_isPainting = false;
|
||||
|
||||
// If there's still a clip hanging around, remove it. We're all done.
|
||||
LOG_IF_FAILED(_customRenderer->EndClip(_drawingContext.get()));
|
||||
|
||||
hr = _d2dDeviceContext->EndDraw();
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
@@ -1362,56 +1424,11 @@ try
|
||||
const D2D1_POINT_2F origin = til::point{ coord } * _glyphCell;
|
||||
|
||||
// Create the text layout
|
||||
CustomTextLayout layout(_dwriteFactory.Get(),
|
||||
_dwriteTextAnalyzer.Get(),
|
||||
_dwriteTextFormat.Get(),
|
||||
_dwriteFontFace.Get(),
|
||||
clusters,
|
||||
_glyphCell.width(),
|
||||
_boxDrawingEffect.Get());
|
||||
|
||||
// Get the baseline for this font as that's where we draw from
|
||||
DWRITE_LINE_SPACING spacing;
|
||||
RETURN_IF_FAILED(_dwriteTextFormat->GetLineSpacing(&spacing.method, &spacing.height, &spacing.baseline));
|
||||
|
||||
// GH#5098: If we're rendering with cleartype text, we need to always
|
||||
// render onto an opaque background. If our background's opacity is
|
||||
// 1.0f, that's great, we can use that. Otherwise, we need to force the
|
||||
// text renderer to render this text in grayscale. In
|
||||
// UpdateDrawingBrushes, we'll set the backgroundColor's a channel to
|
||||
// 1.0 if we're in cleartype mode and the background's opacity is 1.0.
|
||||
// Otherwise, at this point, the _backgroundColor's alpha is <1.0.
|
||||
//
|
||||
// Currently, only text with the default background color uses an alpha
|
||||
// of 0, every other background uses 1.0
|
||||
//
|
||||
// DANGER: Layers slow us down. Only do this in the specific case where
|
||||
// someone has chosen the slower ClearType antialiasing (versus the faster
|
||||
// grayscale antialiasing)
|
||||
const bool usingCleartype = _antialiasingMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
|
||||
const bool usingTransparency = _defaultTextBackgroundOpacity != 1.0f;
|
||||
// Another way of naming "bgIsDefault" is "bgHasTransparency"
|
||||
const auto bgIsDefault = (_backgroundColor.a == _defaultBackgroundColor.a) &&
|
||||
(_backgroundColor.r == _defaultBackgroundColor.r) &&
|
||||
(_backgroundColor.g == _defaultBackgroundColor.g) &&
|
||||
(_backgroundColor.b == _defaultBackgroundColor.b);
|
||||
const bool forceGrayscaleAA = usingCleartype &&
|
||||
usingTransparency &&
|
||||
bgIsDefault;
|
||||
|
||||
// Assemble the drawing context information
|
||||
DrawingContext context(_d2dDeviceContext.Get(),
|
||||
_d2dBrushForeground.Get(),
|
||||
_d2dBrushBackground.Get(),
|
||||
forceGrayscaleAA,
|
||||
_dwriteFactory.Get(),
|
||||
spacing,
|
||||
_glyphCell,
|
||||
_frameInfo.cursorInfo,
|
||||
D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT);
|
||||
RETURN_IF_FAILED(_customLayout->Reset());
|
||||
RETURN_IF_FAILED(_customLayout->AppendClusters(clusters));
|
||||
|
||||
// Layout then render the text
|
||||
RETURN_IF_FAILED(layout.Draw(&context, _customRenderer.Get(), origin.x, origin.y));
|
||||
RETURN_IF_FAILED(_customLayout->Draw(_drawingContext.get(), _customRenderer.Get(), origin.x, origin.y));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -1510,6 +1527,9 @@ CATCH_RETURN()
|
||||
[[nodiscard]] HRESULT DxEngine::PaintSelection(const SMALL_RECT rect) noexcept
|
||||
try
|
||||
{
|
||||
// If a clip rectangle is in place from drawing the text layer, remove it here.
|
||||
LOG_IF_FAILED(_customRenderer->EndClip(_drawingContext.get()));
|
||||
|
||||
const auto existingColor = _d2dBrushForeground->GetColor();
|
||||
|
||||
_d2dBrushForeground->SetColor(_selectionBackground);
|
||||
@@ -1631,6 +1651,17 @@ CATCH_RETURN()
|
||||
}*/
|
||||
}
|
||||
|
||||
// If we have a drawing context, it may be choosing its antialiasing based
|
||||
// on the colors. Update it if it exists.
|
||||
// We only need to do this here because this is called all the time on painting frames
|
||||
// and will update it in a timely fashion. Changing the AA mode or opacity do affect
|
||||
// it, but we will always hit updating the drawing brushes so we don't
|
||||
// need to update this in those locations.
|
||||
if (_drawingContext)
|
||||
{
|
||||
_drawingContext->forceGrayscaleAA = _ShouldForceGrayscaleAA();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -1656,6 +1687,9 @@ try
|
||||
// Calculate and cache the box effect for the base font. Scale is 1.0f because the base font is exactly the scale we want already.
|
||||
RETURN_IF_FAILED(CustomTextLayout::s_CalculateBoxEffect(_dwriteTextFormat.Get(), _glyphCell.width(), _dwriteFontFace.Get(), 1.0f, &_boxDrawingEffect));
|
||||
|
||||
// Prepare the text layout
|
||||
_customLayout = WRL::Make<CustomTextLayout>(_dwriteFactory.Get(), _dwriteTextAnalyzer.Get(), _dwriteTextFormat.Get(), _dwriteFontFace.Get(), _glyphCell.width(), _boxDrawingEffect.Get());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
@@ -1783,17 +1817,11 @@ try
|
||||
|
||||
const Cluster cluster(glyph, 0); // columns don't matter, we're doing analysis not layout.
|
||||
|
||||
// Create the text layout
|
||||
CustomTextLayout layout(_dwriteFactory.Get(),
|
||||
_dwriteTextAnalyzer.Get(),
|
||||
_dwriteTextFormat.Get(),
|
||||
_dwriteFontFace.Get(),
|
||||
{ &cluster, 1 },
|
||||
_glyphCell.width(),
|
||||
_boxDrawingEffect.Get());
|
||||
RETURN_IF_FAILED(_customLayout->Reset());
|
||||
RETURN_IF_FAILED(_customLayout->AppendClusters({ &cluster, 1 }));
|
||||
|
||||
UINT32 columns = 0;
|
||||
RETURN_IF_FAILED(layout.GetColumns(&columns));
|
||||
RETURN_IF_FAILED(_customLayout->GetColumns(&columns));
|
||||
|
||||
*pResult = columns != 1;
|
||||
|
||||
@@ -2284,6 +2312,6 @@ CATCH_LOG()
|
||||
// - S_OK
|
||||
[[nodiscard]] HRESULT DxEngine::PrepareRenderInfo(const RenderFrameInfo& info) noexcept
|
||||
{
|
||||
_frameInfo = info;
|
||||
_drawingContext->cursorInfo = info.cursorInfo;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <wrl.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
#include "CustomTextLayout.h"
|
||||
#include "CustomTextRenderer.h"
|
||||
|
||||
#include "../../types/inc/Viewport.hpp"
|
||||
@@ -173,6 +174,7 @@ namespace Microsoft::Console::Render
|
||||
::Microsoft::WRL::ComPtr<IDWriteTextFormat> _dwriteTextFormat;
|
||||
::Microsoft::WRL::ComPtr<IDWriteFontFace1> _dwriteFontFace;
|
||||
::Microsoft::WRL::ComPtr<IDWriteTextAnalyzer1> _dwriteTextAnalyzer;
|
||||
::Microsoft::WRL::ComPtr<CustomTextLayout> _customLayout;
|
||||
::Microsoft::WRL::ComPtr<CustomTextRenderer> _customRenderer;
|
||||
::Microsoft::WRL::ComPtr<ID2D1StrokeStyle> _strokeStyle;
|
||||
|
||||
@@ -195,6 +197,7 @@ namespace Microsoft::Console::Render
|
||||
DXGI_SWAP_CHAIN_DESC1 _swapChainDesc;
|
||||
::Microsoft::WRL::ComPtr<IDXGISwapChain1> _dxgiSwapChain;
|
||||
wil::unique_handle _swapChainFrameLatencyWaitableObject;
|
||||
std::unique_ptr<DrawingContext> _drawingContext;
|
||||
|
||||
// Terminal effects resources.
|
||||
bool _retroTerminalEffects;
|
||||
@@ -215,8 +218,6 @@ namespace Microsoft::Console::Render
|
||||
|
||||
float _defaultTextBackgroundOpacity;
|
||||
|
||||
RenderFrameInfo _frameInfo;
|
||||
|
||||
// DirectX constant buffers need to be a multiple of 16; align to pad the size.
|
||||
__declspec(align(16)) struct
|
||||
{
|
||||
@@ -233,6 +234,8 @@ namespace Microsoft::Console::Render
|
||||
|
||||
void _ReleaseDeviceResources() noexcept;
|
||||
|
||||
bool _ShouldForceGrayscaleAA() noexcept;
|
||||
|
||||
[[nodiscard]] HRESULT _CreateTextLayout(
|
||||
_In_reads_(StringLength) PCWCHAR String,
|
||||
_In_ size_t StringLength,
|
||||
|
||||
Reference in New Issue
Block a user