docs(A.5 T27): roadmap + ISSUES + CLAUDE.md updates for A.5 ship

Roadmap (2026-04-11-roadmap.md):
- Status header updated to 2026-05-10 / A.5 as the shipped phase.
- A.5 row added to shipped table (after A.3): two-tier streaming,
  QualityPreset, Bug A/B fixes, deferred items, plan archive link.
- A.5 sub-piece in Phase A section marked SHIPPED with archive link
  (replaces the old "not yet brainstormed" entry).
- N.6 bullet changed from "Currently in flight" to "Planned
  (post-A.5 polish takes priority)"; A.5's landing means the
  "direct higher-radius comparison once A.5 lands" item is now
  available.

ISSUES.md:
- #52 (A.5/lifestone-missing): Holtburg lifestone not rendering since
  A.5 dev; two root-cause candidates; investigation approach.
- #53 (A.5/tier1-redo): classification cache reverted at 9b49009;
  animation-mutation audit required before retry; 1-week estimate.
- #54 (A.5/jobkind-plumbing): Bug A's post-load strip wastes worker
  CPU; proper fix plumbs JobKind through BuildLandblockForStreaming;
  30 min–1 hour estimate.

CLAUDE.md:
- "Currently in flight" paragraph updated from N.6 to Post-A.5 polish
  (issues #52/#53/#54) with note that N.6 follows.
- A.5 shipped paragraph added (mirrors N.5b/N.5/N.4 format).
- WB integration cribs: new bullet documenting the two-tier streaming
  architecture (StreamingRegion / StreamingController / LandblockStreamer
  / GpuWorldState, N₁/N₂ defaults, QualitySettings, spec pointer).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-10 10:06:40 +02:00
parent a28a5b7583
commit d93d823539
3 changed files with 102 additions and 18 deletions

View file

@ -110,6 +110,21 @@ ourselves".
uvec2 + `sampler2DArray(handle)` constructor pattern (NOT the direct uvec2 + `sampler2DArray(handle)` constructor pattern (NOT the direct
`uniform sampler2DArray` + `glProgramUniformHandleARB` form, which `uniform sampler2DArray` + `glProgramUniformHandleARB` form, which
GL_INVALID_OPERATIONs on at least one driver). GL_INVALID_OPERATIONs on at least one driver).
- **Two-tier streaming architecture (Phase A.5, shipped 2026-05-10).**
`src/AcDream.App/Streaming/` owns the full streaming pipeline. Key types:
`StreamingRegion` (two-radius Chebyshev window: N₁=near, N₂=far; produces
`TwoTierDiff` with 5 transition lists per tick), `StreamingController`
(render-thread coordinator: routes `TwoTierDiff` to the worker queue and
drains completions up to `MaxCompletionsPerFrame` per frame),
`LandblockStreamer` (single background worker thread: `LoadFar` = heightmap
+ mesh only, `LoadNear` = heightmap + `LandBlockInfo` + scenery + mesh,
`PromoteToNear` = `LandBlockInfo` + scenery only),
`GpuWorldState` (render-thread entity state: `AddEntitiesToExistingLandblock`
for promotions, `RemoveEntitiesFromLandblock` for demotions).
Default: N₁=4 (81 near LBs, full detail), N₂=12 (544 far LBs, terrain
only). Quality Preset system (`QualitySettings.From(preset)`) controls
both radii and MSAA/anisotropic/A2C/completions-per-frame as a unit.
Spec: `docs/superpowers/specs/2026-05-09-phase-a5-two-tier-streaming-design.md`.
**Execution phases:** R1→R8 in the architecture doc. Each phase has clear **Execution phases:** R1→R8 in the architecture doc. Each phase has clear
goals, test criteria, and builds on the previous. Don't skip phases. goals, test criteria, and builds on the previous. Don't skip phases.
@ -510,19 +525,19 @@ acdream's plan lives in two files committed to the repo:
acceptance criteria. Do not drift from the spec without explicit user acceptance criteria. Do not drift from the spec without explicit user
approval. approval.
**Currently in flight: Phase N.6 — Perf polish.** **Currently in flight: Post-A.5 polish — Tier 1 retry + lifestone fix + JobKind plumbing.**
Roadmap entry at [`docs/plans/2026-04-11-roadmap.md`](docs/plans/2026-04-11-roadmap.md). Open issues: #52 (lifestone missing), #53 (Tier 1 entity cache redo with animation-mutation
Builds on N.5 + N.5b. Legacy renderers (`InstancedMeshRenderer`, audit), #54 (JobKind plumbing through BuildLandblockForStreaming for proper far-tier skip).
`StaticMeshRenderer`, `WbFoundationFlag`) were retired in the N.5 ship After those three close, the next planned phase is N.6 (perf polish) — see roadmap for scope.
amendment, and the terrain legacy renderer (`TerrainChunkRenderer` +
`TerrainRenderer` + legacy `terrain.vert/.frag`) was retired in N.5b. **Phase A.5 (Two-tier Streaming + Horizon LOD) shipped 2026-05-10.**
N.6 scope is perf-only: WB atlas adoption, persistent-mapped buffers N₁=4 near-tier (81 LBs, full detail) + N₂=12 far-tier (544 LBs, terrain only); fog
(strong candidate after N.5b's per-frame DEIC `BufferSubData`), horizon; QualityPreset system (Low/Medium/High/Ultra) with env-var overrides; F11
GPU-side culling via compute pre-pass, GL_TIME_ELAPSED query mid-session reapply. Two post-ship-prep bugs fixed: Bug A (far-tier worker was loading
double-buffering, direct higher-radius perf comparison once A.5 lands, full entity layer — ~71K entities, ~5x perf regression vs spec), Bug B (WalkEntities
legacy `Texture2D`/`sampler2D` TextureCache path retirement (Sky / Debug per-frame list alloc — ~480 KB/frame GC pressure). Tier 1 entity cache reverted (animation
remain on the legacy path now that Terrain has migrated). regression; see #53). Plan archived at
Plan + spec written when work begins. [`docs/superpowers/plans/2026-05-09-phase-a5-two-tier-streaming.md`](docs/superpowers/plans/2026-05-09-phase-a5-two-tier-streaming.md).
**Phase N.5b (Terrain on Modern Rendering Path) shipped 2026-05-09.** **Phase N.5b (Terrain on Modern Rendering Path) shipped 2026-05-09.**
`TerrainModernRenderer` mirrors WB's `TerrainRenderManager` pattern `TerrainModernRenderer` mirrors WB's `TerrainRenderManager` pattern

View file

@ -46,6 +46,74 @@ Copy this block when adding a new issue:
# Active issues # Active issues
## #54 — A.5/jobkind-plumbing: far-tier worker loads full entity layer then strips
**Status:** OPEN
**Severity:** LOW (correctness/perf; worker wastes CPU on far-tier LandBlockInfo + scenery generation that is immediately discarded)
**Filed:** 2026-05-10
**Component:** streaming / LandblockStreamer
**Description:** Bug A's fix (commit `9217fd9`) patches at the worker output — after a far-tier job completes the full `LoadNear` path, the result's entity list is stripped before posting to the completion queue. This means far-tier LBs still load `LandBlockInfo` + run `SceneryGenerator` + call `LandblockLoader.BuildEntitiesFromInfo` even though those results are thrown away. At N₂=12, that is ~544 far-tier LBs × unnecessary dat reads + scenery math on promotion sequences.
**Proper fix:** plumb `LandblockStreamJobKind` through `BuildLandblockForStreaming` so far-tier jobs call only `LandBlock` heightmap read + `LandblockMesh.Build`, skipping `LandBlockInfo` + `SceneryGenerator` entirely. The function signature change is ~5 lines; wiring is ~10 lines. Estimated 30 min1 hour total.
**Files:**
- `src/AcDream.App/Streaming/LandblockStreamer.cs``HandleJob` + `BuildLandblockForStreaming`
**Acceptance:** Far-tier LB worker path reads only the `LandBlock` dat file (no `LandBlockInfo`, no `SceneryGenerator` call). Verified by adding a counter diagnostic or via dotnet-trace showing the dat-read call count per job kind.
---
## #53 — A.5/tier1-redo: entity-classification cache broke animation (reverted)
**Status:** OPEN
**Severity:** MEDIUM (perf gap; the classification cache would save ~1-2ms/frame but cannot land until animation-mutation audit is done)
**Filed:** 2026-05-10
**Component:** rendering / WbDrawDispatcher / AnimationSequencer
**Description:** Tier 1 entity-classification cache (commit `3639a6f`) was reverted at `9b49009` due to an animation regression. The cache stored `meshRef.PartTransform` at first-classify time. For static entities this is stable. For animated entities, `AnimationSequencer` mutates `meshRef.PartTransform` every frame to apply the current skeletal pose. The cache froze the pose, causing NPCs and some animated entities to stop animating (some buildings also showed at wrong positions, likely entities incorrectly flagged as animated).
**Root cause:** the "trust MeshRefs as the source of truth" comment in the dispatcher gave false confidence — MeshRefs IS the source of truth, but it is mutated EVERY frame for animated entities.
**Next attempt needs:**
1. Audit `AnimationSequencer` + `AnimationHookRouter` to identify ALL per-frame mutations of `MeshRef` state (not just `PartTransform` — are any other fields mutated?).
2. Redesign cache to: (a) bypass animated entities entirely (classify them each frame, cache only static entities), OR (b) cache only the animation-invariant subset of the classification key (group key, texture handle, blend mode) while reading the per-frame pose from the live `MeshRef`.
3. Test specifically with a moving animated NPC visible on screen before shipping.
**Estimated:** 1 week including audit + redesign + retest.
**Files:**
- `src/AcDream.App/Rendering/Wb/WbDrawDispatcher.cs` — dispatcher classification logic
- `src/AcDream.Core/Animation/AnimationSequencer.cs` — mutation source
- `src/AcDream.Core/Animation/AnimationHookRouter.cs` — secondary mutation source
---
## #52 — A.5/lifestone-missing: Holtburg lifestone not rendering
**Status:** OPEN
**Severity:** MEDIUM (visible missing landmark; lifestone is the player's respawn anchor and should always be visible)
**Filed:** 2026-05-10
**Component:** streaming / rendering
**Description:** The Holtburg lifestone (spinning blue crystal) has not rendered since earlier in A.5 development. Reproduce: launch live client, walk to Holtburg town center, look toward the lifestone position. Should see the spinning blue crystal; instead see nothing.
**Root cause (suspected, two candidates):**
1. Bug A's far-tier strip (commit `9217fd9`) may be incorrectly stripping a near-tier entity. The lifestone's server GUID is `0x5000000A`; its dat object may be registering via the `LandBlockInfo` path but getting stripped as if it were a far-tier entity due to a tier-classification race or incorrect LB-tier tracking.
2. Separate regression from earlier in the A.5 development chain — possibly introduced when entity registration was restructured during T13/T16 streaming controller wiring.
**Investigation approach:**
1. Add a `[STREAMING-DIAG]` log line when far-tier stripping drops an entity — log the entity's GfxObj ID and LB address so the lifestone's GfxObj ID appears in the log if it is being stripped.
2. If not in the strip log, check whether the lifestone's LB is registering as near-tier at all during first-tick bootstrap.
3. Bisect to find the commit that broke it if the above two checks don't isolate the cause.
**Acceptance:** Launch live, walk to Holtburg center, spinning blue crystal visible at the lifestone position. No regression on other static entities in the area.
---
## #50 — Road-edge tree at 0xA9B1 visible in acdream but not retail ## #50 — Road-edge tree at 0xA9B1 visible in acdream but not retail
**Status:** OPEN **Status:** OPEN

View file

@ -1,6 +1,6 @@
# acdream — strategic roadmap # acdream — strategic roadmap
**Status:** Living document. Updated 2026-05-09 for Phase N.5b shipping (terrain on the modern rendering path via Path C — mirror WB's `TerrainRenderManager` pattern, consume `LandblockMesh.Build` for retail formula compliance; closes ISSUE #51). N.6 (perf polish) remains the in-flight phase. **Status:** Living document. Updated 2026-05-10 for Phase A.5 shipping (two-tier streaming N₁=4/N₂=12 + QualityPreset system + Bug A/B fixes; closes the two-tier streaming spec). Post-A.5 polish (Tier 1 retry + lifestone fix + JobKind plumbing) is now the in-flight work.
**Purpose:** One source of truth for where the project is and where it's going. Every observed defect or missing feature has a named phase that owns it; when something looks wrong in-game, look here to find the phase that'll address it. Implementation details live in per-phase specs under `docs/superpowers/specs/`, not in this file. **Purpose:** One source of truth for where the project is and where it's going. Every observed defect or missing feature has a named phase that owns it; when something looks wrong in-game, look here to find the phase that'll address it. Implementation details live in per-phase specs under `docs/superpowers/specs/`, not in this file.
--- ---
@ -31,6 +31,7 @@
| A.1 | Streaming landblock loader — runtime-configurable visible window (default 5×5, `ACDREAM_STREAM_RADIUS`), camera-centered offline / player-centered live, hysteresis-based unloads, pending-spawn list for late CreateObject events | Live ✓ | | A.1 | Streaming landblock loader — runtime-configurable visible window (default 5×5, `ACDREAM_STREAM_RADIUS`), camera-centered offline / player-centered live, hysteresis-based unloads, pending-spawn list for late CreateObject events | Live ✓ |
| A.2 | Frustum culling — per-landblock AABB test (Gribb-Hartmann), terrain + static-mesh renderers skip culled landblocks, perf overlay in window title | Visual ✓ | | A.2 | Frustum culling — per-landblock AABB test (Gribb-Hartmann), terrain + static-mesh renderers skip culled landblocks, perf overlay in window title | Visual ✓ |
| A.3 | Background net receive thread — dedicated daemon thread buffers UDP into Channel, render thread drains | Visual ✓ | | A.3 | Background net receive thread — dedicated daemon thread buffers UDP into Channel, render thread drains | Visual ✓ |
| A.5 | Two-tier streaming + horizon LOD — N₁=4 (full detail, 81 LBs) + N₂=12 (terrain only, 544 LBs); fog blend at N₁; per-LB entity dispatcher walk tightened (Change #1 animated-walk fix + Change #2 cached AABB); single-worker off-thread mesh build; mipmaps + 16x anisotropic on TerrainAtlas; A2C with MSAA 4x on foliage; depth-write audit + lock-in test; **NEW T22.5: QualityPreset system** (Low/Medium/High/Ultra) with per-preset radii + MSAA + anisotropic + A2C + completions; env-var overrides per field; F11 mid-session re-apply. **Bug fixes post-T26 ship-prep**: (Bug A) far-tier worker now strips entities from far-tier loads — without this fix, far-tier LBs were loading their full entity layer (~71K entities) defeating the two-tier optimization; (Bug B) WalkEntities switched from per-frame fresh-list allocation to caller-provided scratch list (eliminated ~480 KB/frame GC pressure). **Deferred to post-A.5**: Tier 1 entity-classification cache (first attempt broke animation; revert + redo with animation-mutation audit), lifestone visual (missing in render), JobKind plumbing through BuildLandblockForStreaming (proper Bug A fix), Tier 2/3 perf optimizations (roadmap at docs/plans/2026-05-10-perf-tiers-2-3-roadmap.md). Plan archived at docs/superpowers/plans/2026-05-09-phase-a5-two-tier-streaming.md. | Live ✓ |
| B.3 | Physics MVP resolver foundation — terrain contact, CellSurface prototype, streaming-populated collision inputs, and first `PhysicsEngine` resolver path. Not the complete retail collision system. | Tests ✓ | | B.3 | Physics MVP resolver foundation — terrain contact, CellSurface prototype, streaming-populated collision inputs, and first `PhysicsEngine` resolver path. Not the complete retail collision system. | Tests ✓ |
| B.2 | Player movement mode — Tab-toggled WASD ground walking, walk/run/idle animations, third-person chase camera, MoveToState + AutonomousPosition outbound, portal entry. Outdoor-only MVP. | Live ✓ | | B.2 | Player movement mode — Tab-toggled WASD ground walking, walk/run/idle animations, third-person chase camera, MoveToState + AutonomousPosition outbound, portal entry. Outdoor-only MVP. | Live ✓ |
| D.1 | 2D ortho overlay + font rendering (StbTrueTypeSharp atlas + TextRenderer + DebugOverlay) | Visual ✓ | | D.1 | 2D ortho overlay + font rendering (StbTrueTypeSharp atlas + TextRenderer + DebugOverlay) | Visual ✓ |
@ -82,7 +83,7 @@ Plus polish that doesn't get its own phase number:
- **✓ SHIPPED — A.2 — Frustum culling.** Per-landblock AABB test (Gribb-Hartmann plane extraction + positive-vertex AABB test) in both `TerrainRenderer.Draw` and `StaticMeshRenderer.Draw`. Per-entity culling deferred. LOD deferred to Phase C. Performance overlay in window title shows FPS, frame time, visible/total landblock ratio, entity count, animated count. ~160fps uncapped at 5×5 radius. - **✓ SHIPPED — A.2 — Frustum culling.** Per-landblock AABB test (Gribb-Hartmann plane extraction + positive-vertex AABB test) in both `TerrainRenderer.Draw` and `StaticMeshRenderer.Draw`. Per-entity culling deferred. LOD deferred to Phase C. Performance overlay in window title shows FPS, frame time, visible/total landblock ratio, entity count, animated count. ~160fps uncapped at 5×5 radius.
- **✓ SHIPPED — A.3 — Background net receive thread.** Dedicated daemon thread continuously pulls raw UDP datagrams from the kernel buffer into a `Channel<byte[]>`. Render thread's `Tick()` drains the channel. All decode, fragment assembly, ISAAC crypto, event dispatch, and ack-sending remain on the render thread — minimal change that prevents packet drops during frame stalls. Thread starts after `EnterWorld()` completes; `PumpOnce()` during handshake still reads the socket directly. - **✓ SHIPPED — A.3 — Background net receive thread.** Dedicated daemon thread continuously pulls raw UDP datagrams from the kernel buffer into a `Channel<byte[]>`. Render thread's `Tick()` drains the channel. All decode, fragment assembly, ISAAC crypto, event dispatch, and ack-sending remain on the render thread — minimal change that prevents packet drops during frame stalls. Thread starts after `EnterWorld()` completes; `PumpOnce()` during handshake still reads the socket directly.
- **A.4 — Async dat decoding.** Folded into the streaming worker — it's the worker's read path, not a separate subsystem. Called out here because regressions in dat caching could land on this surface. - **A.4 — Async dat decoding.** Folded into the streaming worker — it's the worker's read path, not a separate subsystem. Called out here because regressions in dat caching could land on this surface.
- **A.5 — Two-tier streaming + terrain horizon LOD.** Split `ACDREAM_STREAM_RADIUS` into two: `ACDREAM_TERRAIN_RADIUS` (large, 8-12 cells = 1.5-2.3km) for terrain mesh + `ACDREAM_ENTITY_RADIUS` (small, 2-3 cells, current default) for entities + scenery. Distant landblocks render terrain only — no NPCs, no procedural scenery, no static objects. Tune `SceneLightingUbo`'s `uFogParams` so the far edge fades into sky color (eliminates the hard streaming boundary visible at higher radii). Optional: terrain LOD via mesh decimation for very distant chunks (combine 2×2 landblocks into one decimated mesh; cribs from `references/WorldBuilder/Chorizite.OpenGLSDLBackend/Lib/TerrainRenderManager.cs`). Motivation: at radius=5 today, perf scales from ~810 fps → ~200-300 fps because everything stays full-detail; both retail and WorldBuilder render terrain way out and strip entities/scenery at distance. Enables WB-style horizon visibility. **Estimate: 3-5 days for the radius split + fog tuning; +1 week if terrain LOD is included.** Not yet brainstormed. - **✓ SHIPPED — A.5 — Two-tier streaming + horizon LOD.** Shipped 2026-05-10. See shipped table above for full description. Plan archived at `docs/superpowers/plans/2026-05-09-phase-a5-two-tier-streaming.md`.
**Acceptance:** **Acceptance:**
- Walk across 10+ landblocks in any direction, no crashes, no empty voids. - Walk across 10+ landblocks in any direction, no crashes, no empty voids.
@ -664,7 +665,7 @@ for our deletions/additions; merge upstream `master` periodically.
manifest at higher radius. Spec acceptance criterion #5 was wrong; manifest at higher radius. Spec acceptance criterion #5 was wrong;
amended via `docs/plans/2026-05-09-phase-n5b-perf-baseline.md`. Plan amended via `docs/plans/2026-05-09-phase-n5b-perf-baseline.md`. Plan
archived at `docs/superpowers/plans/2026-05-09-phase-n5b-terrain-modern.md`. archived at `docs/superpowers/plans/2026-05-09-phase-n5b-terrain-modern.md`.
- **N.6 — Perf polish.** **Currently in flight.** - **N.6 — Perf polish.** **Planned (post-A.5 polish takes priority).**
Builds on N.5 + N.5b. Legacy renderer retirement was pulled forward Builds on N.5 + N.5b. Legacy renderer retirement was pulled forward
into N.5 ship amendment — `InstancedMeshRenderer`, `StaticMeshRenderer`, into N.5 ship amendment — `InstancedMeshRenderer`, `StaticMeshRenderer`,
`WbFoundationFlag` are gone — and the terrain legacy renderer `WbFoundationFlag` are gone — and the terrain legacy renderer
@ -675,8 +676,8 @@ for our deletions/additions; merge upstream `master` periodically.
is a candidate), GPU-side culling via compute pre-pass (eliminates is a candidate), GPU-side culling via compute pre-pass (eliminates
the per-frame slot walk + DEIC build entirely), GL_TIME_ELAPSED query the per-frame slot walk + DEIC build entirely), GL_TIME_ELAPSED query
double-buffering (deferred from N.5 — diagnostic shows `gpu_us=0/0` double-buffering (deferred from N.5 — diagnostic shows `gpu_us=0/0`
under `ACDREAM_WB_DIAG=1`), direct higher-radius perf comparison once under `ACDREAM_WB_DIAG=1`), direct higher-radius perf comparison (A.5
A.5 lands (where modern's architectural wins manifest), retire the has now landed — modern's architectural wins are measurable), retire the
legacy `Texture2D`/`sampler2D` path in `TextureCache` (currently kept legacy `Texture2D`/`sampler2D` path in `TextureCache` (currently kept
for Sky + Debug + particle paths now that Terrain has migrated). for Sky + Debug + particle paths now that Terrain has migrated).
Plan + spec written when work begins. **Estimate: 1-2 weeks.** Plan + spec written when work begins. **Estimate: 1-2 weeks.**