perf(text): cache shaped runs in ShapeText (#52)
ShapeText re-decoded UTF-8, re-looked-up every glyph, and recomputed layout every frame for static strings (DrawText / EmitCenteredLabel run inside per-frame onBuild). DrawText and EmitCenteredLabel also did a second full pass over the glyphs to apply alignment. Memoize the shaped run, keyed by (Font*, pxSize, color, utf8), as an origin-relative vector<GlyphItem>. A cache hit skips decode/lookup/layout and just translates the cached run into the output buffer by (x + alignShift, baselineY). The alignment shift (Center = -advance/2, Right = -advance) is folded into ShapeText's single pass, dropping the second loop in both callers. TextAlign moves to the :UI partition so ShapeText can take it. Pure CPU memoization — the glyph buffer is fully rewritten and re-flushed every frame, so a hit is byte-equivalent. A miss still calls Ensure so new glyphs rasterise and dirty the atlas; hits don't (atlas entries are never evicted). InvalidateFont drops a font's runs before it is destroyed (the cache, like FontAtlas's glyph cache, keys on the Font pointer). A soft cap bounds memory against pathological per-frame-unique text. Adds the ShapeTextCache test (15 checks): hit == miss byte-for-byte, hits don't touch the atlas, translate/alignment/truncation/InvalidateFont. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
58e9e0a747
commit
ca48f79465
7 changed files with 391 additions and 40 deletions
|
|
@ -58,17 +58,12 @@ namespace {
|
|||
GlyphItem* writePos = buf.glyphs + before;
|
||||
float baseline = centerY + fontSize * 0.32f;
|
||||
float advance = 0.0f;
|
||||
// ShapeText folds the -advance/2 centering shift into its own pass.
|
||||
std::uint32_t n = buf.renderer->ShapeText(
|
||||
font, fontSize, centerX, baseline,
|
||||
label, color, writePos, cap, &advance
|
||||
label, color, writePos, cap, &advance, TextAlign::Center
|
||||
);
|
||||
*buf.glyphCount = before + n;
|
||||
|
||||
// Center: shift each glyph's x by -advance/2.
|
||||
float shift = -advance * 0.5f;
|
||||
for (std::uint32_t i = 0; i < n; ++i) {
|
||||
writePos[i].x += shift;
|
||||
}
|
||||
return advance;
|
||||
}
|
||||
}
|
||||
|
|
@ -202,16 +197,12 @@ float Crafter::DrawText(UIBuffer& buf, std::string_view text,
|
|||
|
||||
GlyphItem* writePos = buf.glyphs + before;
|
||||
float advance = 0.0f;
|
||||
// Alignment is applied inside ShapeText (folded into its single pass).
|
||||
std::uint32_t n = buf.renderer->ShapeText(
|
||||
font, fontSize, x, baselineY,
|
||||
text, color, writePos, cap, &advance
|
||||
text, color, writePos, cap, &advance, align
|
||||
);
|
||||
*buf.glyphCount = before + n;
|
||||
|
||||
if (align != TextAlign::Left && n > 0) {
|
||||
float shift = (align == TextAlign::Center) ? -advance * 0.5f : -advance;
|
||||
for (std::uint32_t i = 0; i < n; ++i) writePos[i].x += shift;
|
||||
}
|
||||
return advance;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue