Handoff snapshot of the env-gated diagnostic scaffolding used across the
intro-video RE. Kept out of the milestone commits (645feb8..5573ac1) to keep
those clean; committed here so nothing is lost on handoff.
New — XENIA_PROFILE wall-time profiler (crates/xenia-gpu/src/prof.rs):
Coarse buckets attributing playback wall time to interpreter (step_block),
kernel HLE (call_export), block decode/cache (lookup_or_build), texture
decode, host draw, and present; prints periodic snapshots (every 500M guest
instr, or every 500 presents) + a clean-exit report. Hot path is gated on a
cached is_on() (one relaxed load) so it is zero-cost when XENIA_PROFILE is
unset. Call sites: main.rs run_superblock / parallel worker (step_block,
lookup_or_build, call_export), texture_cache ensure_cached, render.rs present
+ dispatch_xenos_draws.
First profile (movie playback, headless single-thread lockstep): effective
~35 MIPS; interpreter body ~40% @ ~95-102 MIPS; texture decode 0.3% (cache
works); present ~0%; the rest is per-block dispatch + scheduler plumbing
(~13 instr/block over 229M blocks). Overhead-bound, not interpreter-body
bound; the levers are coarser execution units (superblock chaining) and
ultimately a JIT.
Pre-existing read-only probe knobs (were uncommitted; env-gated, observe-only):
XENIA_RET_CAPTURE_PC/_REG/_MEM, LOG_RESUMES, LOG_WAITS, LOG_SIGNAL,
FORCE_TID, STARVE_LIMIT, INCUMBENT_PICK, INSTR_PER_MS, DUMP_FRAME,
DUMP_WGSL, BIND_LOG, CONST_LOG, DISPATCH_REC, AUDIT_PC_TRACE.
Tooling: sylph-run.sh (movie oracle loop, 180s default timeout),
zq.py (DuckDB disasm/xref helper).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three layered root causes kept ADV.wmv from playing. This lands the first
three fixes: the intro video now decodes end-to-end and uploads its YUV
planes. The on-screen composite still needs a multi-texture render path
(root #3, tracked separately — the shader interpreter binds one texture slot
but the YUV->RGB pass samples three).
1. Clock scale (xenia-kernel/state.rs): INSTRUCTIONS_PER_MS 10_000 -> 1_000_000.
KeTimeStampBundle tick_count = global_clock / INSTRUCTIONS_PER_MS. At 10_000
(~10 MIPS; global_clock further inflated ~58x by the serialized scheduler
summing busy-spin) the movie handler's 2000 ms software-decode deadline
(sub_821B4968 @0x821b68c0) tripped on a legitimate ~20M-instruction
720p-YUV420 decode and aborted playback (canary never enters that wait
loop). 1_000_000 sits in the validated [~600k, ~2.77M] window that fits
both the movie (decode <=2000 ms) and boot (the worker-hub +66 ms gate
still elapses before the movie, ~66M instr). XENIA_INSTR_PER_MS overrides.
2. Scheduler fairness (xenia-cpu/scheduler.rs): pick_runnable's equal-priority
tiebreak now prefers the incumbent (running_idx) so decrement_quantum's
quantum rotation sticks. Previously each round re-picked the lowest index,
so co-located equal-priority threads never alternated and the movie's demux
feeder (tid24, co-located on hw=1) starved until the STARVE_LIMIT=4096
backstop -- the decode ring never refilled. Priority preemption unaffected.
XENIA_INCUMBENT_PICK=0 rolls back.
3. k_8 texture decode (xenia-gpu/texture_cache.rs + xenia-ui/texture_cache_host.rs):
the video uploads its YUV420 planes as linear k_8 textures (Y 1280x720,
U/V 640x360); with no k_8 decoder ensure_cached rejected them and the frames
never reached the GPU. Adds decode_k8 (1 byte/texel expanded to Rgba8Unorm)
+ the host Rgba8Unorm mapping.
Read-only diagnostic probe knobs used to find these remain uncommitted in the
working tree. Boot goldens re-baselined in a follow-up commit (the clock change
intentionally moves the digest; see tests/golden/README.md).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>