re(xbg7): the [index][vertex] layout is runtime-verified, and the indices= mystery was batching

The decoder's central unstated assumption — a block's index buffer sits
immediately before its vertex buffer (`vb - idx_count*2 - pad`, pad <= 3) — was
also the prime suspect for the residual anchor misses, since a capture-proven
`e106_eng_02_l` block was rejected outright. Measured it instead of assuming:

- extended the F10 ship capture to log each draw's index buffer (base, count,
  min/max index) and to key its de-dup on the index range, so every draw batch
  is recorded rather than only the first;
- `examples/capture_ib_truth.rs` places each drawn buffer in the container by its
  dumped positions and scores the capture against our decode.

Stage_S02, 42 drawn buffers placed: our idx_count == the sum of the draw's index
batches for 42/42, the batch union covers the vertex pool exactly for 42/42, and
all 30 single-block cases sit at pad <= 3 (20 at pad 0, 10 at pad 2). The other
12 are grouped pools, where one index pool serves the whole group. So the layout
holds, the decoded index count is exact, and eng_02_l died on the connectivity
gate (since replaced by the winding gate) — not on index location. The shipped
exact-coverage rule is independently confirmed.

The recorded "capture indices=21 vs our 246" disagreement was an artefact of the
old de-dup key: 21 was the first of two batches, 21 + 225 = 246. Any conclusion
from a pre-2026-08-13 capture's `indices=` or `vbase - ibase` is about one batch,
not about the block.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NptfmpjdpNCKEez6d2xvA9
This commit is contained in:
2026-08-13 05:57:44 +00:00
parent 1b618f500f
commit 6d92e6c114
5 changed files with 334 additions and 5 deletions

View File

@@ -1354,3 +1354,45 @@ Not settled: `e106_brg_01_b_02` ≡ `e106_brg_01_l` (51 verts). A second 51-vert
holds three near-identical 51-vertex runs, so the pair has no oracle yet.
`n006_01A``n006_01B` shows a single `vbase` in all three logs — consistent
with real reuse, but equally with only one of the two being on screen.
## ✅ The `[index buffer][vertex buffer]` layout is RUNTIME-VERIFIED (2026-08-13)
Every anchor decision in this decoder rests on one assumption nothing on disc
states: a block's index buffer sits **immediately before** its vertex buffer, at
`vb idx_count*2 pad` with `pad ≤ 3` (`anchor_pool_mesh`). It was also the
prime suspect for the residual misses — the note above recorded a capture-proven
`e106_eng_02_l` block that the decoder *rejected*, and "the index buffer is
somewhere else" would have explained it.
It is now measured, not assumed. The F10 ship capture in `xenia-canary-native`
was extended to log each draw's index buffer (`ib base=… count=… min=… max=…`,
`CaptureShipDrawForRE`), and `examples/capture_ib_truth.rs` scores it against our
decode of the same container (`Stage_S02`, load constant `0x17FE3FF4`,
[full table](../captures/stage-s02-index-buffer-truth.txt)):
| measurement, 42 drawn buffers placed in the container | result |
|---|---|
| our `idx_count` == sum of the draw's index batches | **42 / 42** |
| union of batches covers the vertex pool exactly (`max_idx == vcount1`) | **42 / 42** |
| index data at `vb 2*idx_count pad`, `pad ≤ 3` (single-block path) | **30 / 30** (20 at pad 0, 10 at pad 2) |
| the remaining 12 | all **grouped pools** — one index pool for the whole group, so the per-sub-mesh distance is larger by construction (`_rou_f105_break` sub-meshes, `n301_02B`) |
So the layout assumption is **correct**, the index count we decode is **exactly**
what the engine indexes, and `eng_02_l`'s rejection was the connectivity gate
(since replaced by the winding gate), not the index location. The coverage rule
shipped earlier (`XBG7_COVER_SLACK = 1`, "a real block reaches its last vertex")
is independently confirmed: every drawn buffer's index union ends exactly at
`vcount1`.
### The trap that hid this: the capture kept only the FIRST batch
The engine issues **several draws over one vertex buffer**, each indexing a
sub-range (29 batches here; the player craft's 10 891-vertex buffer takes 9).
The capture de-duped by `(vbase, WVP transform)`, so it recorded one batch per
placement — which is exactly the recorded mystery *"the capture's `indices=` field
disagrees with the descriptor index count (119-vert twin draws log `indices=21`
vs our 246)"*. Not a disagreement: `21` was the first of two batches, and
`21 + 225 = 246`. Both twins now read `batches 2 · idx 246 · span 492 · gap 0`.
Mixing the index range into the de-dup key (same commit) makes every batch
appear. **Any conclusion drawn from a pre-2026-08-13 capture's `indices=` value,
or from `vbase ibase`, is about one batch and not about the block.**