perf(ui): retain shaped report text

This commit is contained in:
Erik 2026-07-25 05:11:12 +02:00
parent c0bec56dfe
commit f2a015be8e
11 changed files with 525 additions and 38 deletions

View file

@ -267,7 +267,7 @@ hooks, collision, landing, and movement-wire cadence remain unchanged.
## #236 — UiText re-runs full word-wrap shaping every visible frame
**Status:** OPEN
**Status:** DONE — 2026-07-25, Modern Runtime Slice H-a1
**Severity:** MEDIUM
**Filed:** 2026-07-24
**Component:** ui (retained)
@ -291,6 +291,14 @@ to plan Slice H-a.
`src/AcDream.App/UI/Layout/ItemAppraisalReport.cs:137-183`;
`src/AcDream.App/UI/Layout/CharacterStatController.cs:297,306`.
**Resolution:** `UiTextLayoutCache<T>` now retains shaped lines across stable
draws and invalidates on semantic content, width, padding, color, palette, or
font changes. Appraisal and effect controllers publish content through that
cache; character information is invalidated by the current character's
object/local-state events and panel-show lifecycle. Stable provider polling is
covered by a zero-managed-allocation test. Evidence:
`docs/research/2026-07-25-slice-h-a1-ui-text-cache.md`.
---
## #237 — PhysicsEngine allocates a fresh Transition graph per resolve call

View file

@ -25,7 +25,7 @@ Do not reset history and do not revert G3. The portal-warmup corrections
## 2. H-a — retained UI, live attachments, diagnostics, and frame scratch
### H-a1 — cached shaped text
### H-a1 — cached shaped text — COMPLETE
1. Add a small retained text-layout cache keyed by source revision/value plus
width, padding, color, and font identity.
@ -42,6 +42,9 @@ Do not reset history and do not revert G3. The portal-warmup corrections
Gate: the existing appraisal/character/effect behavior tests remain unchanged,
and repeated stable provider polls allocate no report/wrap objects.
Landed evidence:
[`../research/2026-07-25-slice-h-a1-ui-text-cache.md`](../research/2026-07-25-slice-h-a1-ui-text-cache.md).
### H-a2 — visible cooldown participants
1. Keep the shared retail heartbeat and one cooldown result per represented

View file

@ -0,0 +1,39 @@
# Slice H-a1 — retained UI text cache gate
## Problem
`UiText` deliberately asks its `LinesProvider` for current content when it
draws. Appraisal, character information, and effect details had bound that
cheap question directly to expensive report construction and word wrapping.
At uncapped presentation rates, an unchanged visible panel rebuilt strings,
line lists, and wrapping measurements every frame.
## Implemented ownership
- `UiTextLayoutCache<T>` owns one semantic value and its shaped line
projection.
- A projection is reused while content, width, padding, default color, DAT
font, bitmap font, and appraisal font-color palette are unchanged.
- Appraisal publishes item reports and string fields through caches.
- Effect selection/enchantment events publish the detail string once.
- Character information subscribes to the current character's object/property
and local-character state. It rebuilds the report on the next draw after an
event or after the panel is shown.
- `UiText.LinesProvider` remains the renderer seam. Dynamic one-line fields are
not globally cached and no draw/input behavior changed.
## Gate
- Focused appraisal, character, effects, and cache tests: 43 passed.
- App Release: 3,779 passed / 3 skipped.
- Complete Release solution: 8,263 passed / 5 skipped.
- A 1,000-poll stable-provider test observes zero managed bytes allocated on
the calling thread.
- Tests prove value-equal strings reuse the same line collection, source
changes rebuild it, and resize changes reshape without rebuilding the
character report.
- `IndicatorDetailText.Shape` and `ItemAppraisalTextLayout.Shape` no longer sit
directly behind an uncached per-frame provider.
The complete build remained warning-free for production projects; the solution
test build reported the same 16 pre-existing test-project warnings.