handoff: VSync/event-wedge fixes + iterate 2.A–2.BC research notes

Source changes (dormant parity infra, retained from iterate 2.AI/2.AO):
- xenia-kernel/exports.rs: nt_create_event manual_reset polarity +
  related event wiring
- xenia-gpu/mmio_region.rs: D1MODE_VBLANK_VLINE_STATUS hardcode parity

Also lands the audit-runs/ analysis notes (.md/.txt/.json digests) for the
iterate 2.x VSync/0x10e8/0x1004 wedge investigation. Raw trace dumps
(.jsonl/.gz/.csv/.stdout) and agent worktrees (.claude/) are gitignored as
regenerable local artifacts — see memory + HANDOFF for the running findings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-05 07:19:08 +02:00
parent acd1656753
commit ef93a4fa14
620 changed files with 108303 additions and 1 deletions

View File

@@ -0,0 +1,127 @@
# Phase Host-Audio-Eager — Re-validation (2026-05-19)
## Progression metric (primary gate)
| metric | pre-fix baseline | post-fix | delta |
|-------:|:----------------:|:--------:|:-----:|
| **swaps** | 1 | **1** | **0** |
| **draws** | 0 | **0** | **0** |
**The progression metric did NOT move.** Despite landing the eager-seed
implementation cleanly with 3× reproducibility, neither swaps nor draws
advanced. This matches the prior agent's diagnosis: the audio worker
ordering issue is real, but the deeper root cause is voice-struct state
divergence — the audio callback at `0x824D6640` in ours blocks on
`KeWaitForMultipleObjects([0x82928B04, 0x82928AE0])` immediately
because the voice struct at `[r31+356]` reads `0x01` (ours) vs `0x00`
(canary). Pre-seeding 8 fires lets `try_inject_audio_callback` deliver
the first callback earlier, but the callback still blocks on the same
guest dispatchers — fires 2-8 sit in the queue because
`interrupts.is_in_callback()` stays true.
## 3× determinism
```
73e99d60029128b4d5c3dd98e540457d82a52b8a962e7495132be2be31411aca /tmp/digest_eager_1.json
73e99d60029128b4d5c3dd98e540457d82a52b8a962e7495132be2be31411aca /tmp/digest_eager_2.json
73e99d60029128b4d5c3dd98e540457d82a52b8a962e7495132be2be31411aca /tmp/digest_eager_3.json
```
All three cold runs produce byte-identical digest JSON. The
seed-at-register implementation is fully deterministic in lockstep
mode (the ticker accumulator gets pre-populated synchronously inside
the register handler, no host-thread non-determinism).
## Digest JSON
```json
{
"instructions": 50000007,
"imports": 40390,
"unimpl": 0,
"draws": 0,
"swaps": 1,
"unique_render_targets": 0,
"shader_blobs_live": 0,
"texture_cache_entries": 0
}
```
`imports`/`unimpl` unchanged from the C+22 baseline (40390/0).
## Phase B invariant
```
image_loaded_sha256 = ea8d160e9369328a5b922258a92113efb8d7ce3e1a5c12cc521e375985c91c18
```
UNCHANGED — Phase B is not affected by audio-runtime changes.
## Per-chain matched-prefix
100M-instruction cold trace vs canary baseline
(`xenia-rs/audit-runs/phase-d-stage1/canary-cvaroff-trunc.jsonl`
pre Phase D D-extension absorber, so main reads at the C+18
102,424 value, NOT the post-D-extension 105,046).
| chain | pre-fix | post-fix | delta | first divergence |
|------:|--------:|---------:|------:|:-----------------|
| canary tid=4 → ours tid=11 | 11 | **11** | 0 | (preserved) |
| canary tid=6 → ours tid=1 | 102,424 | **102,424** | **0** | `NtQueryFullAttributesFile` (C+18-era) |
| canary tid=7 → ours tid=2 | 32 | **32** | 0 | (preserved) |
| canary tid=12 → ours tid=7 | 4 | **4** | 0 | C+23 idx=4 |
| canary tid=14 → ours tid=9 | 41 | **41** | 0 | (no advance — primary target) |
| canary tid=15 → ours tid=10 | 16 | **16** | 0 | (no advance — primary target) |
The two primary targets (tid=14→9 and tid=15→10) were the audio worker
guest threads spinning on the uninitialized voice struct. Their
matched-prefix did NOT advance.
## Kernel tests
- Pre: 217 passed
- Post: **221 passed** (+4 new `seed_fires_for_*` tests)
- Failures: 0
- All existing tests pass
## Build
`cargo build --release` clean. One pre-existing dead-code warning
unrelated to this fix.
## Total LOC
| file | added | removed |
|------|------:|--------:|
| `crates/xenia-kernel/src/xaudio.rs` | 86 | 0 |
| `crates/xenia-kernel/src/exports.rs` | 18 | 5 |
| **total** | **104** | **5** |
Net ~100 LOC, of which ~60 LOC are tests + doc comments. Engine logic
delta is ~25 LOC.
## Conclusion
The implementation lands cleanly:
- 3× cold-deterministic
- Phase B unchanged
- All tests pass
- Sister chains preserved
But the progression metric (swaps/draws) did NOT move. This is an
HONEST NEGATIVE RESULT: the eager-seed approach addresses the symptom
(ticker delays the first callback) but not the root cause (the
callback at 0x824D6640 still blocks on guest dispatchers that only
tid=9/10 can signal, and tid=9/10 are stuck on a voice-struct field
that the callback would need to clear — but doesn't, because canary's
callback takes a DIFFERENT control-flow path that doesn't reach the
KeWaitForMultipleObjects in the first place).
The deeper fix requires either:
- Identifying the guest write that initializes `[r31+356]` to 0 in
canary's boot path and ensuring ours produces the same write.
- A true host-side audio worker thread that can run the callback
in a host context (substantial threading-model rework).
Both are out of scope for this session per the brief's "Don't widen
scope" tripstone.