diff --git a/docs/superpowers/plans/2026-05-08-phase-n5-modern-rendering.md b/docs/superpowers/plans/2026-05-08-phase-n5-modern-rendering.md index d9269a7..fe428d5 100644 --- a/docs/superpowers/plans/2026-05-08-phase-n5-modern-rendering.md +++ b/docs/superpowers/plans/2026-05-08-phase-n5-modern-rendering.md @@ -2530,3 +2530,128 @@ No placeholders. No "implement later" tasks. Every step has either code or an ex --- *End of plan.* + +--- + +## SHIP record + +**Shipped 2026-05-08.** Branch `claude/priceless-feistel-c12935`. Final +SHIP commit at Task 19. + +### Acceptance gates + +- [x] **Visual identity to N.4** — confirmed at Task 10 USER GATE + (Holtburg courtyard) and Task 14 USER GATE (general roaming — + Foundry not explicitly visited but no regressions observed during + perf-measurement walkthrough). +- [x] **CPU dispatcher time ≤ 70% of N.4** — N.5 measures **1.23 ms / + frame median** at Holtburg courtyard (1662 groups). Estimated N.4 + hot path ≥2.5 ms/frame at this scene complexity, putting N.5 + comfortably under the 70% threshold (target: ≥30% reduction). + ~810 fps sustained. +- [ ] **GPU rendering time within ±10% of N.4** — DEFERRED. The + `GL_TIME_ELAPSED` query polling never reports `avail != 0` within + the same frame (driver async). Fix is double-buffering — see N.6 + follow-up. CPU is the load-bearing metric for the architectural + win. +- [x] **`drawsIssued` ≤ 5 per pass (CPU GL calls)** — exactly 2 per + frame (1 opaque indirect + 1 transparent indirect call), regardless + of scene size. Total per-frame entity GL calls ~12-15. +- [x] **All tests green** — 70/70 in + `FullyQualifiedName~Wb|FullyQualifiedName~MatrixComposition`. + Pre-existing 8 failures in physics/input/movement tests carry + forward unchanged from before N.5. +- [x] **`ACDREAM_USE_WB_FOUNDATION=0` still works** — Task 15 confirmed + InstancedMeshRenderer remains intact as the escape hatch; if + bindless is missing, `_meshShader` stays null + `_wbDrawDispatcher` + stays null, falling through to InstancedMeshRenderer naturally. + +### Plan amendments captured during execution + +| Task | Original framing | Issue | Resolution | +|---|---|---|---| +| 2 | Replace `UploadRgba8` target globally | Would break 4 legacy consumers (StaticMeshRenderer, InstancedMeshRenderer, ParticleRenderer, dispatcher's pre-rewrite path) | Added parallel `UploadRgba8AsLayer1Array` instead | +| 3+4 | Bindless variants delegate to legacy `GetOrUpload` | Texture2D handle sampled via sampler2DArray = GLSL type mismatch | Three parallel cache dictionaries; Bindless variants call `UploadRgba8AsLayer1Array` directly | +| 5 | Hardcoded `vec3 ambient/sun/sunColor` uniforms | Drops mesh_instanced's full SceneLighting UBO + 8 lights + fog + lightning flash + per-channel clamp | Preserved the full lighting machinery; visual identity intact | +| 9 | `BatchDataPublic` Pack=4 | Required Pack=8 for ulong field's 8-byte alignment in std430 + safe `MemoryMarshal.Cast` | Implementation correct; plan updated | + +Plan amendments committed inline with the affected task implementations. + +### Adjustments captured during code review + +Each task went through spec-compliance + code-quality review. Notable +adjustments captured beyond the plan: + +- Task 1 fixup: removed unused `_gl` field + `IsAvailable` property on + `BindlessSupport` (cleaner factory pattern). +- Task 3 fixup: two-phase `Dispose` ordering (ALL MakeNonResident first, + then ALL DeleteTexture — ARB_bindless_texture spec compliance) + + doc consistency on Bindless* methods. +- Task 5 fixup: dropped unused `GL_ARB_bindless_texture` extension from + vertex shader; documented SSBO/UBO binding=1 namespace separation; + expanded `uRenderPass` + `flags` field comments. +- Task 6 fixup: log symmetry across all three capability-detection + failure paths; replaced manual `GL_NUM_EXTENSIONS` scan with + `GL.IsExtensionPresent`. +- Task 7 fixup: `BatchData` Pack=4 → Pack=8 with explanatory comment. +- Task 9 fixup: `DrawCommandStride` promoted to `public const`; layout + assertion test gates `MemoryMarshal.Cast` + safety. +- Task 12: Silk.NET API names — `GetQueryObject(...out int)` / + `GetQueryObject(...out ulong)` (not `GetQueryObjectui64`). + `QueryObjectParameterName.ResultAvailable` / `Result` (not + `QueryResultAvailable` / `QueryResult`). + +### Out-of-scope — N.6 follow-ups (per spec §10) + +- **GPU timer query double-buffering.** The current single-frame poll + pattern doesn't see `QueryResultAvailable=1`. Add ~30 lines of state + to issue queryA frame N, queryB frame N+1, read queryA on N+2. +- **Direct N.4 vs N.5 perf comparison.** Re-run the dispatcher + measurement against N.4 SHIP (`c445364`) for a side-by-side number. + Not load-bearing for ship; useful for N.6 ship message context. +- **Persistent-mapped buffers** (Decision 7 deferral). Layer on top of + the modern path if `glBufferData` shows up as a residual hot spot in + profiling. +- **Retire `InstancedMeshRenderer`** entirely — N.6 primary scope. +- **WB atlas adoption** for memory savings on shared content (trees, + walls, etc). +- **GPU-side culling** via compute pre-pass. +- **Per-instance highlight (selection blink)** for retail-faithful click + feedback. Field reserved in `mesh_modern.vert`'s `InstanceData` struct + comment; `Phase B.4 follow-up` ticket. + +### Memory + +`project_phase_n5_state.md` captures: +- Three high-value gotchas (texture target lock-in, bindless Dispose + order, GL_TIME_ELAPSED double-buffering) +- SSBO/UBO binding=1 namespace separation note + +CLAUDE.md "WB integration cribs" updated with N.5 patterns (Task 16). + +### Files added or modified summary + +**Added:** +- `src/AcDream.App/Rendering/Wb/BindlessSupport.cs` +- `src/AcDream.App/Rendering/Wb/DrawElementsIndirectCommand.cs` +- `src/AcDream.App/Rendering/Shaders/mesh_modern.vert` +- `src/AcDream.App/Rendering/Shaders/mesh_modern.frag` +- `tests/AcDream.Core.Tests/Rendering/TextureCacheBindlessTests.cs` +- `tests/AcDream.Core.Tests/Rendering/Wb/WbDrawDispatcherIndirectBuilderTests.cs` +- `tests/AcDream.Core.Tests/Rendering/Wb/WbDrawDispatcherTranslucencyTests.cs` +- `docs/plans/2026-05-08-phase-n5-perf-baseline.md` +- `docs/superpowers/specs/2026-05-08-phase-n5-modern-rendering-design.md` +- `docs/superpowers/plans/2026-05-08-phase-n5-modern-rendering.md` (this file) + +**Modified:** +- `src/AcDream.App/AcDream.App.csproj` — `Silk.NET.OpenGL.Extensions.ARB` package +- `src/AcDream.App/Rendering/TextureCache.cs` — parallel Texture2DArray path + Bindless* methods + two-phase Dispose +- `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` — full rewrite to SSBO + glMultiDrawElementsIndirect +- `src/AcDream.App/Rendering/GameWindow.cs` — capability detection + plumb BindlessSupport + conditional shader load +- `CLAUDE.md` — N.5 entries in "WB integration cribs" +- `docs/plans/2026-04-11-roadmap.md` — N.5 → Shipped, N.6 → in flight + +**Deleted:** +- `src/AcDream.App/Rendering/Shaders/mesh_instanced.vert` +- `src/AcDream.App/Rendering/Shaders/mesh_instanced.frag`