Repository navigation
Conversation
Event and sync waits were ignored, allowing consumers to read stale input when another guest queue had not yet submitted its producer. This implements ready-prefix scheduling: a blocked queue yields so its producer can be submitted later, including by the same CPU thread. CPU-visible event values are published after GPU completion. Preserve render targets, conditional rendering, visibility queries and submission-mode timestamps across event boundaries. Reuse command allocators across chunks without resetting GPU-owned data, while keeping the allocator pool bounded. Wait-only submissions retain the producer's completion fence. Validation covers forward and bidirectional dependencies, CPU signals, queue ordering, resource retirement, allocator pressure, both timestamp modes and capture compatibility. Scheduler tests also pass with AddressSanitizer and UndefinedBehaviorSanitizer; GPU tests pass with Metal validation enabled. Front-ends: break: rebuild host and replay tools together for the updated capture layout. Older captures are rejected; raw replay of event/sync waits remains unsupported.
|
Tested this on The Legend of Zelda: Ocarina of Time (Switch 2, 0400486024DEC000, v131072) on a Mac (M4 Pro, macOS 27.2). It fixes a long-standing flicker there that nothing else had touched. Symptom: while turning the camera, big chunks of the shadowing (sunlit steps and grass in Kokiri Forest) switched between lit and shadowed from one frame to the next. It went away whenever the game ran slowly, and no setting or host switch changed it. Cause: OoT uses several NVN queues. Every frame, queue 1 signals timeline events at points spread through its submission. Queue 5's command buffers wait on those one by one and signal back, and queues 6 and 8 wait on queue 1 too. Some of the waits fall in the middle of a command buffer. With waits treated as no-ops, a logging probe found more than half of them not yet satisfied when the host replayed them, so their work read the previous frame's data. How often that happened depended on thread timing. Holding back whole submissions doesn't fix it, because the queues depend on each other within one submission, so splitting at each wait the way this PR does is what OoT needs. Result: I applied this PR onto a local OoT branch (four conflicts in nvn_mtl.m, all resolved). The shadow flicker is completely gone. The frame rate holds at 60 (frame time p50 about 16.7 ms), and the log shows no new errors. The PR's own tests and the NVN suite pass on that branch. Thanks for this! |
|
Tested on Ocarina of Time (0400486024DEC000 v131072) on an M4 Pro, macOS 27. #1245 fixes every one-frame shadow glitch we had chased in #1703, and with it the game no longer needs its resolution pinned. Setup. I merged #1245 onto main
Deterministic replays use the same key recordings and saves, with
In play. The player played without the resolution pin, at the rock, the bridge, the mossy passages and around Kokiri. They saw:
Thanks @sn0wst0rm, and @padsiwono for pointing us here. |
|
Data point for the queue-order fix, from a port of this PR onto our stack (535f2e3b): Ocarina of Time v131072 on an M5 Pro, fixed 720p internal resolution, MetalFX with jitter on. The character lighting flips we saw before are gone over about 18 minutes of play. The session ended in an unrelated guest crash: |
…1245, on this tree's submission start, hang watchdog and queue render-target snapshots)
|
Measured on an iPad Pro (M5) with Ocarina of Time (0400486024DEC000 v131072), Docked 1536x864, this PR merged onto our stack (main of 2026-11-08 plus our changes; the Picture: it fixes two things. The Kokiri ground-shadow drop (#1703), and the game's whole-screen one-frame fog flicker: the frame-start fog compute binds the sun cascade right after a Cost on the iPad: the chunk granularity.
GPU time per frame is the same. The frame listing shows 35 chunks for 6 guest submissions, alternating queue 5 (compute) and queue 1 (graphics) about 12 times, cut by 19 signals and 26 waits all satisfied by the scheduler before the next chunk; the GPU profile shows 1.6 ms of idle before the 760-draw colour pass (the segment counter restarts per chunk, so the pass is committed whole where it used to be fed in 64-draw segments) and about 60 smaller gaps at the other boundaries. It is not a CPU wait on the previous chunk's fence ( Follow-up offered as #1749, on this PR's head: |
|
Queue scheduler (ea0c98d): CPU cost on Ocarina of Time, M5 Pro ea0c98d copies, snapshots and replays every submission on a worker thread. On Ocarina of Time (0400486024DEC000 v131072) headless on an M5 Pro, same saves, against our port of #1245 (the scheduler only when waits are recorded, one allocator lease per logical submission), steady 300-frame windows:
GPU time is the same (Field 13.8 ms, Market 16-16.5 ms); instructions per frame are +16-17 % at about 3,100 draws a frame. Reading the code (no profile): Possible fixes, not tried: nodes of one submission share their first node's allocator; replay a submission's leading nodes with no wait inline from the guest's bytes; double the buffer in Same sentence as the commit message: broad NVN scheduling, regression reach uncertain. This is one game and one machine; Three Houses, Octopath and Okami were not run here. |
|
A controlled A/B of this PR against main's ea0c98d, on an M4 Pro (Mac16,8, macOS 27.0.1) with Ocarina of Time v131072. Setup. Both hosts share the same shader chain (058136f61e9e) and the same tree: main 1794a35 + #1707 + #1742 + #1752. Host B reverts ea0c98d and merges this PR; the conflicts were resolved as in my earlier comment. Replay of a fixed Kokiri route with
On that light route ea0c98d paces better and has less GPU idle. Real play (Kokiri village, handheld,
That matches lunks's M5 Pro numbers above. Not measured: 04419bc, which landed after this build. It fixes the stale frame-start timestamp and the WRITE-signal waits, but I don't see it changing the per-submission CPU cost. |
Event and sync waits were ignored, allowing consumers to read stale input when another guest queue had not yet submitted its producer. This implements ready-prefix scheduling: a blocked queue yields so its producer can be submitted later, including by the same CPU thread. CPU-visible event values are published after GPU completion.
Preserve render targets, conditional rendering, visibility queries and submission-mode timestamps across event boundaries. Reuse command allocators across chunks without resetting GPU-owned data, while keeping the allocator pool bounded. Wait-only submissions retain the producer's completion fence.
Validation covers forward and bidirectional dependencies, CPU signals, queue ordering, resource retirement, allocator pressure, both timestamp modes and capture compatibility. Scheduler tests also pass with AddressSanitizer and UndefinedBehaviorSanitizer; GPU tests pass with Metal validation enabled.
Front-ends: break: rebuild host and replay tools together for the updated capture layout. Older captures are rejected; raw replay of event/sync waits remains unsupported.