perf(ui): memoize typography specs on the schema generation

Every Typography text, textWrapped and textColored call rebuilt all 15
type specs to read one uppercase flag: 28 ui.toml element lookups, 13
of them copying the element with its string map. With M such labels on
screen, that was 28M lookups and 13M copies per frame.

The specs are now built once per UI-schema generation, which every
ui.toml load, hot reload and skin or theme switch bumps. DPI, font
scale and language are not inputs, so nothing else invalidates them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-10 09:50:03 -05:00
co-authored by Claude Opus 5.5
parent 1ced49d622
commit e025a314d1
+10 -2
View File
@@ -34,13 +34,21 @@ namespace material {
const TypeSpec* Typography::getTypeSpecs()
{
// Rebuild each call to pick up hot-reloaded values
// Memoized on the schema generation. Every input (typography.* and fonts.*) is read via
// schema::UI().drawElement, and UISchema bumps generation() on each loadFromFile / loadFromString /
// mergeOverlayFromFile (startup, hot-reload, skin/theme switch). ~0u sentinel: generation_ starts at 0,
// so the first call always builds (pre-load defaults), then the first load rebuilds.
// DPI / font scale / language are NOT inputs (kFont*() are raw schema px). UI thread only, as before.
static TypeSpec specs[kNumStyles];
static uint32_t s_builtGen = ~0u;
// Helper: read typography entry from UISchema
auto& S = schema::UI();
const uint32_t gen = S.generation();
if (gen == s_builtGen) return specs;
s_builtGen = gen;
auto tspec = [&](const char* key, float defLS, float defLH, bool defUpper) -> TypeSpec {
auto e = S.drawElement("typography", key);
const auto& e = S.drawElement("typography", key); // ref into styleCache_: never let it escape this lambda
float ls = e.getFloat("letter-spacing", defLS);
float lh = e.getFloat("line-height", defLH);
// text-transform stored as extraColors (string map)