docs(opstack): handoff -- 001 chains into 002, and PERF is now aimed

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 15:46:30 +02:00
parent 7313201e91
commit 7909c4f2ca
+39 -15
View File
@@ -95,19 +95,29 @@ collision**. The warning is written at the site you land on when you add one.
`|AB| ≥ K`, so the running minimum saturates at `K` below the smallest term. Without that
observation the slack would scale with the ~88 tunnels in a cache and the criterion would be dead.)
## First actions — one build to read, one task to hand Codex
## First actions — one build to read, two tasks to hand Codex
### (a) Hand Codex `CODEX-TASK-001-tile-skip-stats.md` — this is the priority
### (a) Hand Codex `CODEX-TASK-001-tile-skip-stats.md`, then `-002-` — 001 first, they chain
Everything in this refactor has been proved in an automation harness on 40 sampled tiles, and
**nothing has ever been observed in the running game.** The task adds a `stat VoxelForge` group with
`TilesClassified / TilesSkippedAllSolid / TilesSkippedAllAir / TilesMeshed`.
**nothing has ever been observed in the running game.** Task 001 adds a `stat VoxelForge` group with
`TilesClassified / TilesSkippedAllSolid / TilesSkippedAllAir / TilesMeshed`, **plus
`TilesOpStackSolid / TilesOpStackAir` at a second site**. Task 002 adds `ColumnMemoHit / Miss` to the
same group; it needs 001's header to exist, and both should land in one build.
Its deliverable is a **before/after that constitutes the production proof of T1.d**: with no strate
opted in, `TilesSkippedAllSolid` underground must read **0**; after ticking `bUseOperatorStack` on
one `TunnelNetwork` strate and flying the same route, it must be **non-zero**. The spec carries the
invariants — most importantly that `bTrivialEmpty` decides whether a tile has **collision**, and that
`GenerateTileResult` runs on **worker threads** so a plain `static int32++` is a data race.
Its deliverable is a **before/after that constitutes the production proof of T1.d**: after ticking
`bUseOperatorStack` on one `TunnelNetwork` strate and flying the same route, **`TilesOpStackSolid`
must go non-zero**. The spec carries the invariants — most importantly that `bTrivialEmpty` decides
whether a tile has **collision**, and that `GenerateTileResult` runs on **worker threads** so a plain
`static int32++` is a data race.
⚠️ **Corrected 2026-08-16 — the earlier version of this bar was unmeasurable.** It said
`TilesSkippedAllSolid` would read 0 underground with no strate opted in. It will not: `ClassifyTile`
also proves `AllSolid` on its **hand-written** path (a bedrock-gap chunk sets `bCanAir = false`,
`VoxelGenerator.cpp` ~2835), which fires with nothing ticked at all. That is the same "~84 %" caveat
below, which the old bar quoted and then contradicted. The op-stack-only counters are zero **by
construction** — the cave branch returns `Mixed` at `UsesOperatorStackForChunk` — so they are the
ones that prove anything.
Interim answer if Jahni wants it before that lands: **Unreal Insights already shows this.** The trace
scopes `VoxelForge_ClassifyTile` and `VoxelForge_GenerateMesh` exist at the site; a skipped tile is a
@@ -135,12 +145,26 @@ Everything before it is built and green.
## Then, in order
1. **PERF — still unparked, and now the biggest open item.** The op path is measurably slower. One
cause found and fixed (the column memo discarded itself every chunk). Remaining suspects in order:
the hashed column lookup vs `GSurfColCache`'s direct-indexed box, then per-voxel virtual dispatch.
Also measured and stated: the gate is tested twelve times per voxel instead of once (stage B5's
deliberate trade). **Measure before optimising** — that is the §C10 lesson, and this session
re-learned it the hard way.
1. **PERF — the biggest open item, and now AIMED (2026-08-16). Read this before touching it.**
The op path is measurably slower; one cause was already found and fixed (the column memo
discarded itself every chunk). Three things were worked out since, all still **unmeasured**:
- **The A/B needs no new code.** `VoxelForge_ClassifyTile` and `VoxelForge_GenerateMesh` already
exist, and the world is deterministic, so two Insights traces — flag off, then on, same seed and
route — are a clean before/after.
- **But it is unreadable without task 001.** With the stack on, tiles get *skipped*, so
`GenerateMesh` runs fewer times; a total conflates "cheaper per tile" with "fewer tiles" and
those pull opposite ways. `TilesMeshed` is the denominator. **⇒ 001 is a PREREQUISITE here, not
a parallel item.** Order: 001 → traces → attribution → fix.
- **The suspects don't share an archetype**, so measure one ticked strate at a time.
*SurfaceWorld* = the hashed column memo. *TunnelNetwork* = **19 virtual calls per voxel**
(16 from `BuildTunnelNetworkStack` + 3 from `AppendStructuralPost`), plus the known 12× gate
re-test (stage B5's deliberate trade).
`CODEX-TASK-002` tests the SurfaceWorld suspect and **fixes nothing on purpose** — the derivation
says the direct-mapped 4096-slot table evicts ~25 % of columns *every Z plane* (the mesher
pre-samples **Z-outermost**), for a derived ~9× on column work. **Derived, not measured.**
**Measure before optimising** — the §C10 lesson, re-learned the hard way last session.
2. **The warp squeeze — PARKED with its ceiling measured, my recommendation is leave it.** The
`WARP SHARE` line says over half the remaining blocking is the query-box dilation, not geometry
(production: rooms 0.9 → 0.4, tunnels 2.4 → 1.1 with the dilation zeroed). The only remaining