Files
xenia-rs/crates/xenia-app/tests/golden
MechaCat02 a91f4c550b [iterate-2W] Sustain the title present loop: viewport-size register + ISR CPU impersonation
The title's per-frame loop (sub_822F1AA8) is clock-B-paced and only re-fires
when the swap count [controller+88] changes, which advances only on source=1
CP swap-complete interrupts. Each present batch the guest submits (via the
sub_824CE348 -> sub_824BF4D0 builder) ends with a WAIT_REG_MEM on a per-CPU
swap-acknowledge fence [GCTX+0] (GCTX = [device+10772]); the GPU parks there
until the graphics ISR (sub_824BE9A0) clears that CPU's bit. Two coupled gaps
kept ours emitting only ONE source=1 then dead-locking (draws plateaued at 28,
run halted ~19.27M):

1. GPU MMIO register 0x1961 (AVIVO_D1MODE_VIEWPORT_SIZE) read as 0. The swap
   callback sub_824CE2B8 divides by its low 12 bits (display height) as a
   refresh-pacing term, so a 0 read tripped its `twi` divide-by-zero guard and
   aborted the ISR before it reached the fence-clear. Mirror canary
   GraphicsSystem::ReadRegister (graphics_system.cc:311): return 0x050002D0
   (1280x720).

2. The ISR ran on an arbitrary borrowed thread, so [r13+268] (the PCR
   processor number) did not match the interrupt's target CPU. The ISR clears
   `1 << current_cpu` from the fence; running on the wrong CPU cleared the
   wrong bit and the fence (bit 2, from cpu_mask 0x4) never reached 0. Carry
   the target CPU through the interrupt queue (bit index of the PM4_INTERRUPT
   cpu_mask for CP, 2 for vsync per canary DispatchInterruptCallback(0, 2)) and
   impersonate it on the borrowed thread's PCR around the ISR, mirroring canary
   EmulateCPInterruptDPC -> XThread::SetActiveCpu.

With both fixes the fence clears, the GPU drains each present batch, source=1
sustains per-present, clock B advances, and the loop runs continuously. Draws
climb linearly with the budget (no re-stall): 50M 28->718, 200M ->3411,
1B ->18734; swaps 2->147/950/6060. No "Unanticipated CPU_INTERRUPT" trap.
Inline-deterministic (--stable-digest byte-identical x2); n50m golden
re-baselined. 675 tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 20:49:32 +02:00
..

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 (±28% 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:

  1. The --parallel --reservations-table combination 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 515 min the original plan estimated.
  2. 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.