perf(ui): transparent shaped-run cache lookup, no string copy on hit (#122) #141

Merged
catbot merged 1 commit from claude/issue-122 into master 2026-06-18 15:19:43 +02:00
Member

Problem

UIRenderer::ShapeText built ShapedRunKey{..., std::string(utf8)} before find() unconditionally — including on cache hits, on the per-frame onBuild path. N labels meant N string copies + hashes (plus heap frees for non-SSO strings) every frame, partially defeating the shaped-run cache.

Fix

  • Add is_transparent hash and equality functors to shapedRuns_.
  • Probe with a borrowing ShapedRunViewKey {const Font*, float, std::array<float,4>, std::string_view} whose text views the caller's utf8 directly.
  • Materialise the owning std::string only on a miss, for the emplace.

A cache hit now copies and hashes no string. Hash values are unchanged (the existing hash already routed text through std::hash<std::string_view>), so existing entries match identically.

Testing

crafter-build test — all 23 tests pass, including ShapeTextCache, which asserts a hit is byte-identical to the miss path, leaves the atlas clean, and that distinct color/pxSize/font are distinct entries.

Resolves #122

## Problem `UIRenderer::ShapeText` built `ShapedRunKey{..., std::string(utf8)}` before `find()` **unconditionally** — including on cache hits, on the per-frame `onBuild` path. N labels meant N string copies + hashes (plus heap frees for non-SSO strings) every frame, partially defeating the shaped-run cache. ## Fix - Add `is_transparent` hash and equality functors to `shapedRuns_`. - Probe with a borrowing `ShapedRunViewKey {const Font*, float, std::array<float,4>, std::string_view}` whose `text` views the caller's `utf8` directly. - Materialise the owning `std::string` only on a **miss**, for the `emplace`. A cache hit now copies and hashes no string. Hash values are unchanged (the existing hash already routed `text` through `std::hash<std::string_view>`), so existing entries match identically. ## Testing `crafter-build test` — all 23 tests pass, including `ShapeTextCache`, which asserts a hit is byte-identical to the miss path, leaves the atlas clean, and that distinct color/pxSize/font are distinct entries. Resolves #122
ShapeText built ShapedRunKey{..., std::string(utf8)} before find()
unconditionally — even on cache hits, on the per-frame onBuild path.
N labels meant N string copies + hashes (+ frees for non-SSO strings)
every frame, partially defeating the run cache.

Add is_transparent hash + equality functors on shapedRuns_ and probe
with a borrowing ShapedRunViewKey {const Font*, float, array<float,4>,
string_view}. The owning std::string is now materialised only on a
miss, for emplace. Cache hits copy and hash no string.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
catbot merged commit 1b57f84348 into master 2026-06-18 15:19:43 +02:00
catbot deleted branch claude/issue-122 2026-06-18 15:19:44 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Catcrafts/Crafter.Graphics!141
No description provided.