perf(ui): memoize the input-field caret prefix width (#128)
DrawInputField re-measured the cursor prefix via Font::GetLineWidth on every frame of a focused field, even though only the blink (caretVisible) changes frame-to-frame — value, cursorPos and fontSize are stable across the vast majority of frames. Cache the measured prefix WIDTH on the InputField, keyed on (prefix bytes, fontSize). A cheap byte compare of the cached prefix guards the expensive per-glyph UTF-8 decode + advance accumulation. The cache is mutable so the const-ref draw fn can refresh it. The absolute caretX is deliberately NOT cached: it adds the layout- dependent textX (rect.x + paddingX) each frame, so caching it would give a relocated field a stale caret. Caching the width keeps that correct. Adds tests/InputFieldCaretCache pinning: the cached caret matches the uncached GetLineWidth oracle across states, a redraw (cache hit) is identical to the miss, the cache invalidates on value/cursorPos/fontSize changes, the blink never moves the caret, and a relocated field shifts the caret by exactly the rect delta. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
2c07abee00
commit
a45e793da4
5 changed files with 309 additions and 1 deletions
|
|
@ -150,7 +150,21 @@ void Crafter::DrawInputField(UIBuffer& buf, const InputField& f, Rect rect,
|
|||
|
||||
if (f.focused && caretVisible && *buf.quadCount < buf.quadCap) {
|
||||
std::string_view sub(f.value.data(), std::min(f.cursorPos, f.value.size()));
|
||||
float caretX = textX + (sub.empty() ? 0.0f : static_cast<float>(font.GetLineWidth(sub, fontSize)));
|
||||
// Memoise the prefix width (issue #128): only the blink changes between
|
||||
// frames, so re-walking the prefix every frame is wasted work. A byte
|
||||
// compare of the cached prefix (plus fontSize) is far cheaper than the
|
||||
// per-glyph UTF-8 decode + advance accumulation GetLineWidth does.
|
||||
float width;
|
||||
if (f.caretCacheFontSize_ == fontSize && f.caretCachePrefix_ == sub) {
|
||||
width = f.caretCacheWidth_;
|
||||
} else {
|
||||
width = sub.empty() ? 0.0f
|
||||
: static_cast<float>(font.GetLineWidth(sub, fontSize));
|
||||
f.caretCachePrefix_.assign(sub);
|
||||
f.caretCacheFontSize_ = fontSize;
|
||||
f.caretCacheWidth_ = width;
|
||||
}
|
||||
float caretX = textX + width;
|
||||
float caretH = fontSize * 1.1f;
|
||||
float caretY = rect.y + (rect.h - caretH) * 0.5f;
|
||||
float caretW = std::max(1.0f, fontSize / 16.0f);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue