Make ours' VdSwap present path faithful to xenia-canary `VdSwap_entry` (xboxkrnl_video.cc:518-548): write the reserved 64-dword ring slot with a PM4_TYPE0 fetch-constant patch + PM4_TYPE3(PM4_XE_SWAP) + NOP padding, then let the natural drain consume the swap packet in command-stream order. Remove the synthetic CP swap-complete interrupt that `notify_xe_swap` raised out-of-band. Root found this session (the actual present-path bug): ours' `notify_xe_swap` pushed an `InterruptSource::Swap` (→ INTERRUPT_SOURCE_CP) interrupt directly from the VdSwap HLE, decoupled from the GPU command stream. When that interrupt reached the graphics ISR `sub_824BE9A0` before D3D had armed its swap-callback slot (`[gfx+10772]+16` still the `0xBADF00D` placeholder), the ISR took its error path and hit the assert "ERR[D3D]: Unanticipated CPU_INTERRUPT. Sign of a corrupt command buffer?" (`bl sub_824C5DF0; twi` at 0x824BE9DC) — 2x per run on master. Canary's VdSwap raises NO interrupt; swap-complete CP interrupts come only from in-stream PM4_INTERRUPT packets, which are naturally ordered after the callback-arming Type-0 writes. Routing the swap through the ring packet matches that ordering and eliminates the trap (2 -> 0). Canary oracle confirmation (muted, audit_mem_watch + audit_jit_prolog_pc): canary's early/loading loop is present-driven — swap counter [gfx+15160] (0xBE56CA38) advances ~per-vblank from vblank 65 onward, reaching 0xD02 (3330) in ~60s via 6184 CP source=1 interrupts, with VdSwap called only ONCE. So the present interrupts are entirely in-stream, not from the VdSwap export. This is a correctness/faithfulness fix; it does NOT cascade. draws stay 78 at 200M and 1B because the upstream gate persists: the game submits one render batch then stalls (renderer sub_82506xxx 0x; 2nd title thread 0x821748F0 never spawns). The per-frame loop sub_822F1AA8 runs ~1207 iterations on vsync but clock B (swap count) only advances ~once, so the manager update sub_821741C8 fires once. That is the iterate-2Q/2F title-pipeline gate, not a present/ interrupt bug. swaps 3 -> 4 (the in-stream PM4_XE_SWAP now drains). Deterministic in inline mode (n50m --gpu-inline --stable-digest regenerated byte-identical twice; golden re-baselined: swaps 3 -> 4). cargo test --workspace 675 passing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sylpheed regression goldens
These JSON files anchor xenia-rs check digest output for Project Sylpheed.
Files
| File | -n | Mode | Captures |
|---|---|---|---|
sylpheed_n2m.json |
2_000_000 | full digest | early boot (swaps=0, no rendering) |
sylpheed_n50m.json |
50_000_000 | stable-digest | first VdSwap pair (swaps=2 post-Phase-A) |
Stable-digest mode
sylpheed_n50m.json is captured with --stable-digest, which omits
timing-sensitive counters: packets (±2–8% lockstep noise from a GPU thread
race), resolves, interrupts_delivered, interrupts_dropped,
texture_decodes. The remaining fields are byte-identical across repeated
lockstep runs at a fixed -n.
sylpheed_n2m.json predates the stable-digest flag and uses full-digest
compare. It still works because at -n 2M the GPU pipeline has not produced any
packets yet — packets=0 is trivially deterministic.
Circularity hazard
Per ORACBUG-001/002/003, these goldens were captured by running the same code
they validate. They detect regression from a known-good snapshot, not
correctness. When a planned fix intentionally moves the digest (e.g. a
shader fix landing draws > 0 for the first time), re-baseline the golden as
a separate commit and reference the audit ID in the message.
Re-baselining
cargo build --release -p xenia-app
target/release/xenia-rs check \
"$SYLPHEED_ISO" \
-n 50000000 \
--stable-digest \
--out crates/xenia-app/tests/golden/sylpheed_n50m.json
Running the goldens
cargo test --release -p xenia-app --test sylpheed_oracles -- --ignored --nocapture
The tests are #[ignore]-gated because each run takes a few seconds, which is
unacceptable in the default cargo test cycle. The ISO path defaults to the
contributor's local ~/RE Project Sylpheed/Project Sylpheed*.iso and can be
overridden via SYLPHEED_ISO=/path/to/sylpheed.iso.
n4b canonical-invocation regression anchor (deferred)
The audit's recommended next sprint also called for a sylpheed_n4b.json
golden capturing the canonical reference invocation
xenia-rs check sylpheed.iso -n 4_000_000_000 --parallel --reservations-table.
This is deferred because:
- The
--parallel --reservations-tablecombination is empirically pathologically slow at -n 100M (>32 min per run per the audit memory). At -n 4B the run cost is many hours, not the single-session-friendly 5–15 min the original plan estimated. - Each phase that intentionally moves rendering counters (C, D, E, F) would need a re-baseline of n4b — a significant time cost compounding over the sprint.
Once the renderer-unblock phases (C+D+E) land and draws > 0 is confirmed at
-n 100M lockstep, an n4b artifact may be captured one-shot and stored under
audit-runs/post-fix/ (not as a test golden) as a manual regression anchor for
the canonical invocation.