mirror of
https://github.com/microsoft/terminal.git
synced 2026-02-08 21:29:19 +00:00
Skip glyph shaping analysis when the entire text is simple (#6206)
## Summary of the Pull Request As the title suggests, this PR will make CustomTextLayout skip glyph shaping analysis when the entire text is detected as simple. ## References My main reference is [DirectX Factor - Who’s Afraid of Glyph Runs?](https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/november/directx-factor-who%e2%80%99s-afraid-of-glyph-runs) And also #2959 ## PR Checklist * [x] Closes @skyline75489's continuous drive for perf gainz. * [x] CLA signed. * [x] Manual tests. * [x] Nah on docs. * [x] Discussed with core contributors in this PR. ## Detailed Description of the Pull Request / Additional comments This can be seen as a followup of #2959. The idea is the same: make use of simple text (which makes up I think 95% of all terminal text) as much as possible. The performance boost is huge. Cacafire is actually on fire this time (and remember I'm using 4K!). The frame rate is also boosted since more CPU time can be used for actual drawing. Before:  After:  ## Validation Steps Performed Manually validated.
This commit is contained in:
@@ -41,7 +41,8 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
|
||||
_runs{},
|
||||
_breakpoints{},
|
||||
_runIndex{ 0 },
|
||||
_width{ width }
|
||||
_width{ width },
|
||||
_isEntireTextSimple{ false }
|
||||
{
|
||||
// Fetch the locale name out once now from the format
|
||||
_localeName.resize(gsl::narrow_cast<size_t>(format->GetLocaleNameLength()) + 1); // +1 for null
|
||||
@@ -76,6 +77,7 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
|
||||
RETURN_HR_IF_NULL(E_INVALIDARG, columns);
|
||||
*columns = 0;
|
||||
|
||||
RETURN_IF_FAILED(_AnalyzeTextComplexity());
|
||||
RETURN_IF_FAILED(_AnalyzeRuns());
|
||||
RETURN_IF_FAILED(_ShapeGlyphRuns());
|
||||
|
||||
@@ -106,6 +108,7 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
|
||||
FLOAT originX,
|
||||
FLOAT originY) noexcept
|
||||
{
|
||||
RETURN_IF_FAILED(_AnalyzeTextComplexity());
|
||||
RETURN_IF_FAILED(_AnalyzeRuns());
|
||||
RETURN_IF_FAILED(_ShapeGlyphRuns());
|
||||
RETURN_IF_FAILED(_CorrectGlyphRuns());
|
||||
@@ -119,6 +122,44 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Uses the internal text information and the analyzers/font information from construction
|
||||
// to determine the complexity of the text. If the text is determined to be entirely simple,
|
||||
// we'll have more chances to optimize the layout process.
|
||||
// Arguments:
|
||||
// - <none> - Uses internal state
|
||||
// Return Value:
|
||||
// - S_OK or suitable DirectWrite or STL error code
|
||||
[[nodiscard]] HRESULT CustomTextLayout::_AnalyzeTextComplexity() noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
const auto textLength = gsl::narrow<UINT32>(_text.size());
|
||||
|
||||
BOOL isTextSimple = FALSE;
|
||||
UINT32 uiLengthRead = 0;
|
||||
|
||||
// Start from the beginning.
|
||||
const UINT32 glyphStart = 0;
|
||||
|
||||
_glyphIndices.resize(textLength);
|
||||
|
||||
const HRESULT hr = _analyzer->GetTextComplexity(
|
||||
_text.c_str(),
|
||||
textLength,
|
||||
_font.Get(),
|
||||
&isTextSimple,
|
||||
&uiLengthRead,
|
||||
&_glyphIndices.at(glyphStart));
|
||||
|
||||
RETURN_IF_FAILED(hr);
|
||||
|
||||
_isEntireTextSimple = isTextSimple && uiLengthRead == textLength;
|
||||
}
|
||||
CATCH_RETURN();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Routine Description:
|
||||
// - Uses the internal text information and the analyzers/font information from construction
|
||||
// to determine the complexity of the text inside this layout, compute the subsections (or runs)
|
||||
@@ -149,11 +190,7 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
|
||||
// Allocate enough room to have one breakpoint per code unit.
|
||||
_breakpoints.resize(_text.size());
|
||||
|
||||
BOOL isTextSimple = FALSE;
|
||||
UINT32 uiLengthRead = 0;
|
||||
RETURN_IF_FAILED(_analyzer->GetTextComplexity(_text.c_str(), textLength, _font.Get(), &isTextSimple, &uiLengthRead, NULL));
|
||||
|
||||
if (!(isTextSimple && uiLengthRead == _text.size()))
|
||||
if (!_isEntireTextSimple)
|
||||
{
|
||||
// Call each of the analyzers in sequence, recording their results.
|
||||
RETURN_IF_FAILED(_analyzer->AnalyzeLineBreakpoints(this, 0, textLength, this));
|
||||
@@ -274,6 +311,39 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
|
||||
_glyphIndices.resize(totalGlyphsArrayCount);
|
||||
}
|
||||
|
||||
if (_isEntireTextSimple)
|
||||
{
|
||||
// When the entire text is simple, we can skip GetGlyphs and directly retrieve glyph indices and
|
||||
// advances(in font design unit). With the help of font metrics, we can calculate the actual glyph
|
||||
// advances without the need of GetGlyphPlacements. This shortcut will significantly reduce the time
|
||||
// needed for text analysis.
|
||||
DWRITE_FONT_METRICS1 metrics;
|
||||
run.fontFace->GetMetrics(&metrics);
|
||||
|
||||
// With simple text, there's only one run. The actual glyph count is the same as textLength.
|
||||
_glyphDesignUnitAdvances.resize(textLength);
|
||||
_glyphAdvances.resize(textLength);
|
||||
_glyphOffsets.resize(textLength);
|
||||
|
||||
USHORT designUnitsPerEm = metrics.designUnitsPerEm;
|
||||
|
||||
RETURN_IF_FAILED(_font->GetDesignGlyphAdvances(
|
||||
textLength,
|
||||
&_glyphIndices.at(glyphStart),
|
||||
&_glyphDesignUnitAdvances.at(glyphStart),
|
||||
run.isSideways));
|
||||
|
||||
for (size_t i = glyphStart; i < _glyphAdvances.size(); i++)
|
||||
{
|
||||
_glyphAdvances.at(i) = (float)_glyphDesignUnitAdvances.at(i) / designUnitsPerEm * _format->GetFontSize() * run.fontScale;
|
||||
}
|
||||
|
||||
run.glyphCount = textLength;
|
||||
glyphStart += textLength;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
std::vector<DWRITE_SHAPING_TEXT_PROPERTIES> textProps(textLength);
|
||||
std::vector<DWRITE_SHAPING_GLYPH_PROPERTIES> glyphProps(maxGlyphCount);
|
||||
|
||||
@@ -371,6 +441,12 @@ CustomTextLayout::CustomTextLayout(gsl::not_null<IDWriteFactory1*> const factory
|
||||
{
|
||||
try
|
||||
{
|
||||
// For simple text, there is no need to correct runs.
|
||||
if (_isEntireTextSimple)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Correct each run separately. This is needed whenever script, locale,
|
||||
// or reading direction changes.
|
||||
for (UINT32 runIndex = 0; runIndex < _runs.size(); ++runIndex)
|
||||
@@ -508,9 +584,6 @@ try
|
||||
|
||||
// We're going to walk through and check for advances that don't match the space that we expect to give out.
|
||||
|
||||
DWRITE_FONT_METRICS1 metrics;
|
||||
run.fontFace->GetMetrics(&metrics);
|
||||
|
||||
// Glyph Indices represents the number inside the selected font where the glyph image/paths are found.
|
||||
// Text represents the original text we gave in.
|
||||
// Glyph Clusters represents the map between Text and Glyph Indices.
|
||||
|
||||
@@ -134,6 +134,7 @@ namespace Microsoft::Console::Render
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE _AnalyzeBoxDrawing(gsl::not_null<IDWriteTextAnalysisSource*> const source, UINT32 textPosition, UINT32 textLength);
|
||||
[[nodiscard]] HRESULT STDMETHODCALLTYPE _SetBoxEffect(UINT32 textPosition, UINT32 textLength);
|
||||
|
||||
[[nodiscard]] HRESULT _AnalyzeTextComplexity() noexcept;
|
||||
[[nodiscard]] HRESULT _AnalyzeRuns() noexcept;
|
||||
[[nodiscard]] HRESULT _ShapeGlyphRuns() noexcept;
|
||||
[[nodiscard]] HRESULT _ShapeGlyphRun(const UINT32 runIndex, UINT32& glyphStart) noexcept;
|
||||
@@ -180,6 +181,9 @@ namespace Microsoft::Console::Render
|
||||
|
||||
// Glyph shaping results
|
||||
|
||||
// Whether the entire text is determined to be simple and does not require full script shaping.
|
||||
bool _isEntireTextSimple;
|
||||
|
||||
std::vector<DWRITE_GLYPH_OFFSET> _glyphOffsets;
|
||||
|
||||
// Clusters are complicated. They're in respect to each individual run.
|
||||
@@ -191,6 +195,9 @@ namespace Microsoft::Console::Render
|
||||
// This appears to be the index of the glyph inside each font.
|
||||
std::vector<UINT16> _glyphIndices;
|
||||
|
||||
// This is for calculating glyph advances when the entire text is simple.
|
||||
std::vector<INT32> _glyphDesignUnitAdvances;
|
||||
|
||||
std::vector<float> _glyphAdvances;
|
||||
|
||||
struct ScaleCorrection
|
||||
|
||||
Reference in New Issue
Block a user