perf(rendering): retain synchronous frame scratch

Reuse the PView frame input, publish mutation-invalidated landblock views, and avoid constructing optional shadow iterators while preserving title and lifecycle visibility facts.
This commit is contained in:
Erik 2026-07-25 05:28:30 +02:00
parent b9cbf5e040
commit b3427554c3
10 changed files with 397 additions and 129 deletions

View file

@ -0,0 +1,69 @@
# Slice H-a4 — retained frame scratch and diagnostic traversal
## Scope
This is a behavior-preserving ownership/allocation correction. It does not
change retail visibility, PView construction, spatial ordering, collision
debug drawing, title facts, or lifecycle checkpoint facts.
## Findings
Three independent steady-frame allocation sources remained:
1. `WorldSceneRenderer` constructed one `RetailPViewFrameInput` reference
object for every PView frame even though `DrawInside` consumes it
synchronously.
2. `GpuWorldState.LandblockEntries` and `LandblockBounds` were C# iterator
methods, so merely obtaining each sequence constructed fresh iterator
state. The underlying spatial membership changed only at update-thread
publication boundaries.
3. `WorldSceneDiagnosticsController` passed
`ShadowObjectRegistry.AllEntriesForDebug()` to the DebugVM publisher even
when no developer UI consumer existed. The publisher correctly avoided
enumeration, but the yield sequence itself had already been constructed.
The execution plan initially described landblock visibility counting as an
unconsumed diagnostic. That was incorrect in the current application:
`WorldRenderFrameOutcome` feeds the always-visible FPS/title `lb V/T` values
and connected lifecycle snapshots. Returning a neutral count would therefore
change observable behavior and invalidate lifecycle evidence. The count is
retained; only its iterator allocation is removed.
## Implementation
- `WorldSceneRenderer` owns one `RetailPViewFrameInput` and resets every field
before the synchronous `DrawInside` call. The input is publicly read-only
and internally mutable so downstream render owners cannot change borrowed
frame facts.
- `GpuWorldState` exposes retained entry, no-animated-index entry, and bounds
lists. They rebuild only after landblock membership, resident entity-list,
animated-index, or AABB changes. Existing render-traversal slot order is
preserved exactly.
- Landblock diagnostic counting uses indexed access over the retained bounds
view.
- Shadow enumeration is constructed only when the DebugVM is mounted. The
independent collision-wireframe switch keeps its existing enumeration.
- `InteriorEntityPartition` remains untouched while the G4 referee is still
retained. Its removal and issue #241 close remain G5 work after the user
accepts the G4 visual gate.
## Conformance
- Stable retained render-view getters allocate zero bytes over 1,000 polls.
- A spatial mutation refreshes the same borrowed list identity and publishes
the new count.
- Two successive PView frames receive the same input object, reset with the
current frame values.
- Existing quiescence, render traversal, animated-index, PView executor,
abort/recovery, and DebugVM tests remain unchanged in behavior.
## Rollback boundary
This work is independent of the G4 production-source cutover. If tomorrow's
visual gate rejects G4, use:
```text
git revert ef1d263337997bb030eadb7b8e71d73dc659907a
```
Do not revert this Slice H-a4 commit, G3, or the portal-warmup corrections.