Repository navigation
Conversation
…l NVN queues in the class of work it is in Each piece the queue scheduler replays is its own Metal command buffer. Between pieces of different NVN queues, the order is the host's choice wherever no event or sync wait orders them, and round robin alternates them. Ocarina of Time's queue 5 holds all of its frame's compute (139 dispatches in 11 pieces, no render pass) and queue 1 every render pass. Round robin replays them render, compute, render, compute: 22 switches between compute and render a frame from the pieces alone, at 35-46 us median each on an M2, with a long compute -> render tail. With the switch on, a ready command piece whose first class (render or compute) differs from the class the last replayed piece ended in is held. Meanwhile, another queue's ready wait (bookkeeping) or ready piece of the class in progress goes first. The held piece runs at once when: - a CPU thread waits on a fence (nvn_sync_wait); - it has been held DM_NVN_QDEFER_US (default 3000); - the alternative is a present or another frame's work; - nothing else qualifies. NVN orders two queues' work only by event and sync waits. The scan only takes pieces whose waits are met, and one queue's order never changes. Pieces are classed at record time by their ops: draws and texture clears are render; dispatches, copies and buffer clears are compute. A "[nvn] queue defer" line reports holds and why held pieces ran. nvn_mtl.m exports its ab switch helpers (mtl_sw_mode / mtl_sw_on) for it. Offline, one OoT frame's queue order replayed with its waits goes from 22 switches to 5, the fewest those waits allow. Measured on an M2 Air: Ocarina of Time at 1440x810, route4 to frame 12000, DM_NVN_QDEFER=ab with DM_AB=240 (21 + 21 phases, bootstrap 95 %, on a cut7 build): - GPU idle within the frame: -0.72 ms [-4.28, -0.49] - GPU span: -2.19 ms [-4.28, -0.03] - frame p90: -3.31 ms [-10.2, -0.12] - frame p50: -1.77 ms [-4.16, +0.28] (noise) - GPU busy: +0.15 (noise) Tests: a t_wait_event queue-defer case (every policy reason, the cap's edge, op classes, a recorded copy-only piece classed compute). check_t_wait_event and check_t_queue_lifetime also run with DM_NVN_QDEFER=1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…_NVN_BARRIER=game) With draw_barriers game, a draw pass after a draw pass that ended with the relaxed end barrier skips the full entry barrier unless the game's nvnCommandBufferBarrier bits since then ask for it (e775054). The relaxed end barrier orders later fragment, dispatch and copy stages only, not vertex or indirect fetch. That is enough within one queue, where the game's own barrier bits say what it needs. g_last_end_relaxed is global, though, and survived a switch to another NVN queue. That queue's work after an event or sync wait must see all of the first queue's writes, vertex and indirect fetch included, and it issues no barrier for them. mtl_queue_activate now clears the flag when another queue's work begins, so that queue's first pass takes the full barrier. Round robin already hands render work from one queue to another. DM_NVN_QDEFER makes it more common: it holds a compute piece so that two queues' render pieces run back to back. No cost to Ocarina of Time, the one profile with draw_barriers game, whose second queue is compute only. Test: t_queue_origin draws on queue 0, then queue 1, then queue 1 again. The second pass takes the full barrier. With DM_NVN_BARRIER=game (a new check_t_queue_origin line), the third takes the relaxed one, which shows the check is sensitive. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
DM_NVN_QDEFER=1, a ready piece of the other class is held while another queue offers a ready wait or a piece of the class in progress. Offline, one OoT frame's queue order replayed with its waits goes from 22 switches to 5, the fewest those waits allow.nvn_sync_wait);DM_NVN_QDEFER_US(default 3000).[nvn] queue deferline gives the holds a frame and why held pieces ran.With #2250 and e775054 (in this branch's base; not in the measured build)
g_blit_opsreset inblit_open, and each piece is its own command buffer, whose first copy encoder opens with a full queue barrier.draw_barriers game):g_last_end_relaxed, global across NVN queues. A queue's pass right after another queue's pass could skip the full entry barrier, although its work after an event wait must see the other queue's writes in its vertex and indirect stages too.mtl_queue_activate, with a t_queue_origin case.draw_barriers gameprofile) it costs nothing: queue 5 is compute only.t_queue_origincase (the second commit), not by a measurement.Measurements
Apple M2 MacBook Air (16 GB), macOS 27.0. Ocarina of Time 0400486024DEC000 v131072 on our cut7 build (a406a81f: an older main plus OoT-specific host changes, shader chain bf6035002b98), handheld 1440x810, 60 fps cap. Route4 to frame 12000, in-run A/B
DM_NVN_QDEFER=abwithDM_AB=240: 21 phases on and 21 off, each on phase against the mean of its two off neighbours, bootstrap 95 %.hangs.pyCLEAN) and no new[RED]kinds.Tests
t_wait_event: a new queue-defer case (each policy reason, the cap's edge, op classes, a recorded copy-only piece classed compute).check_t_wait_eventandcheck_t_queue_lifetime: also run withDM_NVN_QDEFER=1.test_env_switches: OK.t_queue_origin: the queue-switch barrier case, also run withDM_NVN_BARRIER=game(sensitivity).Regression (rule:
scripts/regress.py check --games)🤖 Generated with Claude Code