Merge branch 'claude/hopeful-darwin-ae8b87' — Phase A.5 SHIP + Quality Preset system

Phase A.5 — Two-tier Streaming + Horizon LOD shipped.

Headline: 2.3 km terrain horizon (radius=4 near + 12 far) with off-thread
mesh build, fog blend at N₁, mipmaps + 16x AF, MSAA 4x + A2C foliage,
depth-write audit, BUDGET_OVER diag, Quality Preset system (Low/Medium/
High/Ultra) with env-var overrides + F11 mid-session re-apply.

~999 tests pass, 8 pre-existing physics/input failures unchanged.

Two structural-to-A.5 bug fixes shipped post-T26:
- Bug A (9217fd9): far-tier worker strips entities (T13/T16 had only
  wired the controller side; far-tier was loading full entity layers,
  ~71K entities instead of ~10K, 5x perf regression).
- Bug B (0ad8c99): WalkEntities scratch list reused across frames
  (was 480 KB / frame allocation).

Tier 1 entity-classification cache attempted as polish (3639a6f),
reverted (9b49009) — broke animation by caching mutable per-frame
state. Retry deferred to post-A.5 polish phase (ISSUE #53).

Deferred to post-A.5 polish:
- Tier 1 retry with animation-mutation audit (ISSUE #53)
- Lifestone missing visual (ISSUE #52)
- JobKind plumbing through BuildLandblockForStreaming (ISSUE #54)
- Tier 2 (static/dynamic split) + Tier 3 (GPU compute cull) —
  separate multi-week phases. Roadmap at
  docs/plans/2026-05-10-perf-tiers-2-3-roadmap.md.

SHIP commit: 9245db5.
This commit is contained in:
Erik 2026-05-10 10:09:03 +02:00
commit d3d78fa14f
37 changed files with 6001 additions and 281 deletions

View file

@ -46,6 +46,74 @@ Copy this block when adding a new issue:
# 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
**Status:** OPEN