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:
@@ -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 == vcount−1`) | **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
|
||||
`vcount−1`.
|
||||
|
||||
### 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 (2–9 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.**
|
||||
|
||||
Reference in New Issue
Block a user