diff --git a/docs/superpowers/plans/2026-05-08-phase-n4-rendering-foundation.md b/docs/superpowers/plans/2026-05-08-phase-n4-rendering-foundation.md index f31e820..d977b5b 100644 --- a/docs/superpowers/plans/2026-05-08-phase-n4-rendering-foundation.md +++ b/docs/superpowers/plans/2026-05-08-phase-n4-rendering-foundation.md @@ -66,23 +66,28 @@ This plan is the **execution source of truth** for N.4. It is updated as tasks l Status: **Living document — work in progress, started 2026-05-08.** -**Progress (2026-05-08):** Week 1 ✅ COMPLETE. Tasks 1-10 shipped. Foundation types + WbMeshAdapter constructed against real WB pipeline (`OpenGLGraphicsDevice` + `DefaultDatReaderWriter` + `ObjectMeshManager`). `InstancedMeshRenderer.EnsureUploaded` routes through the adapter under `ACDREAM_USE_WB_FOUNDATION=1`; sentinel entry marks "this gfxObj lives in WB now" and the draw loop skips sentinel entries (Task 22's `WbDrawDispatcher` will draw them eventually). Conformance tests pin `GfxObjMesh.Build` + `SetupMesh.Flatten` behavior. Build green, 901 tests pass, 8 pre-existing failures only (unchanged from main). +**Progress (2026-05-08):** Weeks 1 + 2 ✅ COMPLETE. WB pipeline running flag-on (constructed + ref-counted + per-frame Tick draining its queues). Three architectural adjustments documented: 1 (DefaultDatReaderWriter discovery, no bridge needed), 2 (renderer is tier-blind; routing belongs in spawn callbacks), 3 (FPS regression root-caused as dual-pipeline cost; Task 22's dispatcher will allow the legacy-renderer short-circuit). Build green, 912 tests pass, 8 pre-existing failures only. -**Next: Task 11** — `LandblockSpawnAdapter` (streaming-loader hook for ref-count lifecycle). +**Next: Task 16** (Week 3) — `AnimatedEntityState` type + per-instance customization path. | Task | Status | Commit | |---|---|---| | 1 — WbFoundationFlag scaffold | ✅ | `81b5ed8` | | 2 — AcSurfaceMetadata + Table | ✅ | `46deed6` | | 3 — Mesh-extraction conformance | ✅ | `ed73fc5` | -| 4 — Setup-flatten conformance | ✅ | `ed73fc5` (combined with #3) | +| 4 — Setup-flatten conformance | ✅ | `ed73fc5` | | 5 — WbMeshAdapter stub + IWbMeshAdapter | ✅ | (post-`ed73fc5`) | -| 6 — WbDatReaderAdapter | ✅ OBSOLETED | `502c3a8` | +| 6 — WbDatReaderAdapter | ✅ OBSOLETED (Adj. 1) | `502c3a8` | | 7 — GameWindow wiring under flag | ✅ | `502c3a8` | | 8 — CLAUDE.md pointer | ✅ | `506b86b` (preemptive) | -| 9 — Real WB pipeline + InstancedMeshRenderer routing | ✅ | `4ad7a98` | -| 10 — Week 1 wrap-up | ✅ | (this commit) | -| 11–15 — Week 2: streaming integration | pending | — | +| 9 — Real WB pipeline + InstancedMeshRenderer routing | ✅ partial / Adj. 2 reverted | `4ad7a98` + `4f318bc` | +| 10 — Week 1 wrap-up | ✅ | `c49c6ed` | +| 11 — LandblockSpawnAdapter | ✅ | `669768d` | +| 12 — Wire into GpuWorldState | ✅ | `931a690` | +| 13 — Memory budget verification | ✅ deferred to Task 22 (Adj. 3) | — | +| 14 — Pending-spawn integration test | ✅ | `f4f0101` | +| Tick — drain WB pipeline queues | ✅ added per Adj. 3 | `bf53cb4` | +| 15 — Week 2 wrap-up | ✅ | (this commit) | | 16–21 — Week 3: per-instance + animation | pending | — | | 22–28 — Week 4: draw dispatcher + ship | pending | — | @@ -878,6 +883,58 @@ construction in `WbMeshAdapter` (verified working under flag-off). flag-off visually identical." Routing arrives in Week 2 (Task 11) at the correct layer. Smoke verification is now: flag-on === flag-off. +--- + +### Adjustment 3 (2026-05-08): flag-on FPS regression — root-caused, deferred to Task 22 + +**Discovered during Task 13 stress test** (radius 7, flag-on). Visible +FPS drop + rising frame latency vs flag-off baseline. Initial guess +was the staged-upload queue leaking memory; we shipped +`WbMeshAdapter.Tick()` (commit `bf53cb4`) to drain +`_meshManager.StagedMeshData` + `_graphicsDevice._glThreadQueue` per +frame. Result: leak fixed, but **FPS unchanged**. + +**Real cause: dual-pipeline cost.** Flag-on runs both rendering +pipelines in parallel without yet collecting any savings: + +1. **Background workers (4-wide).** `ObjectMeshManager` spins up + `MaxParallelLoads = 4` worker threads decoding GfxObj polygons, + building texture atlases, encoding batches. Contends with the + render thread for CPU cores. +2. **Duplicate GL upload.** `Tick()` calls `UploadMeshData` per + staged mesh, creating VAO/VBO/IBO + atlas texture uploads. Real + per-call GL state churn on the render thread. +3. **Duplicate I/O.** `DefaultDatReaderWriter` opens its own dat file + handles and rebuilds its own index cache (~50-100 MB) alongside + our existing `DatCollection`. Memory bandwidth + GC churn. +4. **Legacy renderer keeps doing the same work.** Per Adjustment 2, + `InstancedMeshRenderer` is tier-blind — it still uploads VAO / + VBO / IBO for the same atlas-tier content WB is also building. + **We literally double the prep cost** for every atlas-tier GfxObj. + +The savings from WB's atlas batching only materialize when **Task 22 +(`WbDrawDispatcher`) lands and the legacy renderer can short-circuit +its upload for atlas-tier content**. At that point WB owns atlas-tier +draw and `InstancedMeshRenderer` skips its own upload + draw work +for those entities. Until then, flag-on pays both costs. + +**Decision: do not fix now.** Plan Risk #5 explicitly anticipated this: + +> Performance regression during integration of week 1's "atlas for +> static scenery, old path for everything else" mixed state. +> Mitigation: keep the feature gate `ACDREAM_USE_WB_FOUNDATION=1` +> during weeks 1-3; default-off until week 4 visual verification. + +Default-off (the user's daily experience) is byte-identical to +pre-N.4. Flag-on is dev-only until Week 4. Task 22 must wire the +legacy-renderer short-circuit for atlas-tier content as part of +landing the dispatcher; the cost cannot be amortized any earlier +without violating Adjustment 2's tier-blind-renderer principle. + +`Tick()` stays — it fixed a real memory leak and is required +infrastructure for Task 22 anyway. We just paid for it without +seeing FPS recovery yet. + ### Task 6 (original — kept for history) **Files:**