From 51489e34db7180c54fcc957bf6298f7277e320ad Mon Sep 17 00:00:00 2001 From: MechaCat02 Date: Sat, 6 Jun 2026 19:22:03 +0200 Subject: [PATCH] =?UTF-8?q?Iterate-2.BE=20Path=20=CE=B2:=20tick=20vsync=20?= =?UTF-8?q?from=20coord=5Fidle=5Fadvance?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The iterate-2.BE host-driven synchronous ISR dispatcher relies on something queueing v-syncs. In lockstep that's `tick_vsync_instr`, called from `coord_pre_round` per round. If the scheduler stalls into `coord_idle_advance` (no Ready threads), the instruction counter freezes — the accumulator stops incrementing, the ticker stops queueing, and the dispatcher is left starved for the duration of the idle wait. Tick `tick_vsync_wallclock` at the top of `coord_idle_advance` so v-syncs keep firing on host time even when the guest scheduler is parked. The dispatcher in the outer loop drains whatever we queue on the next iteration. Same MMIO `D1MODE_VBLANK_VLINE_STATUS` bit-set as the production path. Note: empirically in Sylpheed at 50M/500M instruction horizons, `coord_idle_advance` is never reached (tids 9/10/12 stay Ready through the early-boot deadlock), so this commit doesn't move `gpu.interrupt.delivered{source=0}` off 54 for this title at these horizons. It is the correct fix for the documented starvation pattern and will activate as soon as the kernel reaches a state where Ready threads drop to zero with timers/waits pending. Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/xenia-app/src/main.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/crates/xenia-app/src/main.rs b/crates/xenia-app/src/main.rs index 1b14bbc..004dad6 100644 --- a/crates/xenia-app/src/main.rs +++ b/crates/xenia-app/src/main.rs @@ -2016,6 +2016,24 @@ fn coord_idle_advance( shutdown: &Option>, stats: &ExecStats, ) -> RoundCtl { + // Path β (iterate-2.BE follow-up): when the scheduler has no Ready + // threads, `coord_pre_round`'s instruction-count vsync ticker stops + // advancing (instruction_count is frozen). That starves the + // host-driven graphics ISR dispatcher: queue stays empty, no + // deliveries occur, and the very stall we're trying to break out of + // gets worse. Tick vsync from wallclock here unconditionally — it's + // a host-clock read, independent of instruction count, and the + // dispatcher in the outer loop will drain whatever we queue on the + // next pass. Mirrors the `--parallel` ticker choice in + // `coord_pre_round` (`tick_vsync_wallclock` branch). + if kernel.interrupts.tick_vsync_wallclock() { + use std::sync::atomic::Ordering; + let mmio = kernel.gpu.mmio(); + let prev = mmio.d1mode_vblank_vline_status.load(Ordering::Relaxed); + mmio.d1mode_vblank_vline_status + .store(prev | 0x1, Ordering::Relaxed); + } + let next_timer = kernel.earliest_timer_deadline(); let next_wait = kernel.scheduler.earliest_wait_deadline(); let target = match (next_timer, next_wait) {