Repository navigation
Conversation
…dispatch moves ahead of the pass past occlusion-query resets and reports (samples passed) whose memory it neither reads nor writes, and past draws whose storage buffers are read-only, where every scan used to stop at the first query; and clears with no draw wait across a dispatch that touches none of their targets, for the next pass to take as its load action, instead of a clear-only pass that stores both targets for the next to load again Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dering too: Ocarina of Time draws each object only if its occlusion query passed, and every scan stopped at the first render-enable switch, so no dispatch moved. Conditional rendering skips draws and clears, never a dispatch; a dispatch that writes the two counters a condition compares stops the scan. On an M1 one dispatch a frame now moves ahead of the pass it used to split (300 in 300 frames, 0 before) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…updates and game barriers too: once conditional rendering passed, Ocarina of Time's scans stopped at its uniform updates (about 80 a frame in the scene passes) and then at its barriers (about 70). A dispatch moves past an update whose bytes it neither reads nor writes. A barrier orders nothing the moved dispatch depends on: the draws before it write no memory and no attachment it samples, moved dispatches keep a barrier between each other, and the pass that opens after them starts behind a full barrier; replay still runs the barrier in place (DM_NVN_CSH_BARRIER=0 stops the scan there). The stats now log which ops stop the scans. On an M1 at 1366x768: 760-900 dispatches moved per 300 frames (300 before), the heavy submission 30-35 → 25-27 ms of GPU, heavy-area play 18-22 → 27-30 fps Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
iPhone test: no dispatch moved Setup: same as my #1970 report. iPhone Duo (iOS 27.2), Ocarina v131072, docked, 30 fps, the game's own NDSR upscaler (not MetalFX), main pass 2048x1152, Deku Tree, ~8 min. This PR was picked together with #1970 onto main 37f076a + a few fixes.
So here the game's dispatches seem to sit right after a render-target change, at the start of a pass, and the scan never gets past it. Your run was handheld with MetalFX. Could docked plus the game's own upscaler change where the dispatches land? Happy to run a build with more logging if that helps. No picture issue seen (nothing moved, as expected). |
…h to the targets the pass already has: replay continues the pass across such a switch (same-target continuation), but the hoist's scans stopped there. In Ocarina of Time almost every scan stopped at OP_SET_RT (about 27,000 per 300 frames on an M1; 25,978 in a docked iPhone session, where no dispatch moved at all), and the passes on its 1440x810 scene targets split around the compute after each switch. A switch to other targets, with the continuation off, or at a deferred pass's start still stops the scan. The hoist's stats line adds the same-target switches the scans went past. Not yet run Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks, this matches what I'm seeing on the Mac. There, about 27,000 scans per 300 frames stopped at op 8 ( The game sets its scene targets again before each group of compute between its draws. Replay already continues the pass across a switch to the same targets (same-target continuation), but the hoist's scan stopped at every I've added a commit: the scan goes past a render-target switch when replay would continue the pass across it, meaning the same targets, the continuation on, and not a deferred pass's start. Any other switch still stops it. The stats line now also prints
🤖 Generated with Claude Code |
… ahead of Ocarina of Time's scene passes past queries, conditional rendering, uniform updates, game barriers and same-target switches (M1: heavy-area play 18-22 -> 27-30 fps) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Aax1HnchwCweUDRNWjhHx9 # Conflicts: # dm/gfx/nvn/nvn_mtl.m
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Aax1HnchwCweUDRNWjhHx9
…e passes past occlusion queries and conditional rendering; clears wait across dispatches Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… and left out of the table test_env_switches failed on the integrated tree with it missing; the meaning is from the code comment at cs_hoist_try. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…f by default PR seedds#1952's commit 6a512fa lets a dispatch move ahead of the pass past an explicit game barrier. t_perf_order's 'explicit game barrier prevents hoist' check fails with it on (the integrated tree failed make check_t_perf_order; the PR never ran that GPU test and left it as it was). Off, a barrier stops the scan as on main and the check passes. The rest of seedds#1952 stays on. The PR measured the barrier part at 760-900 dispatches moved per 300 frames, 30-35 -> 25-27 ms for the heavy submission and 18-22 -> 27-30 fps in the heavy area (M1, 1366x768, Ocarina of Time), so it is worth an A/B run: DM_NVN_CSH_BARRIER=1. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
seedds#1901 seedds#1459 seedds#1928 seedds#1942 seedds#1940 seedds#1952 seedds#1611 seedds#2065 seedds#2083 seedds#2008 seedds#2079 seedds#2022 seedds#2075 seedds#2077 seedds#2020 seedds#2021 onto main 04811a6 Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014eD437mxrMnW2UwLZhpwc7 Upstream pull requests applied in this commit (their authors' work, brought onto this tree with a three-way merge; the description is in each PR): - seedds#1459 by PromethOps (open): DM_UPSCALER=metalfx takes agl's agl_ndsr_vr.pck networks too, and a variant with no curve and no power is drawn as one colour space: Ocarina of Time's upscaler is MetalFX's - seedds#1611 by Esteban-V (open): An argument block whose only change is a uniform buffer is patched instead of encoded again - seedds#1774 by uncraftablexd (closed): Compute threads that exit before a barrier keep arriving at it, and every shader loop has a cap (GPU hang defences), chain change - seedds#1824 by cocoman93 (open): Clamp texel fetches past a sampled 2D texture's edge: no hard edge in Ocarina of Time's mist - seedds#1901 by FifteenPen (open): The game's compute dispatches with no game barrier between them run side by side (profile key dispatch_overlap, on for Ocarina of Time): Kokiri Forest GPU p50 15.63 → 14.76 ms - seedds#1928 by FeReuss (open): Ocarina of Time: enc_thread on, the replay encodes on a thread of its own - seedds#1929 by FeReuss (open): Unused border corrections are compiled out of pipelines that bind no sampler with a twin (function constant DMSH_BORDER_FC) - seedds#1930 by FeReuss (open): A float add to storage memory with no result read is one native atomic add instead of a compare-and-swap loop - seedds#1940 by uncraftablexd (open): A prewarm list's lines after its #@after_first_frame line are built after the first frame, in the background, instead of in the prepare step (issue seedds#1747) - seedds#1942 by uncraftablexd (open): Frame step: late frames with the GPU idle do not step up, and a longer hold the GPU does not explain is probed - seedds#1952 by AdyaGMD (open): Compute moves ahead of scene passes past occlusion queries and conditional rendering; clears wait across dispatches - seedds#2008 by bquilty25 (open): nvn: reactive memory watchdog, 16-way view cache, and texture upload batching - seedds#2020 by padsiwono (open): Ocarina of Time v131072: Output Native draws the full output size instead of the game's 80 % frame (`DM_OUTPUT`) - seedds#2021 by padsiwono (open): Ocarina of Time v131072 renders at the launcher's Resolution (`DM_RENDER_SIZE`), docked - seedds#2022 by sleepyeyedjoe (closed): Ocarina of Time: large models no longer go black for one frame twice, 4–5 frames apart, when the game streams new textures into them (a texture ID a shader has read keeps its read mark when it gets another texture) - seedds#2030 by humanperzeus (open): A NaN colour at alpha 0 is returned as 0, as NVIDIA's blender takes a zero factor: Ocarina of Time's white clouds over the ferns are gone - seedds#2035 by Know-It-All-Brother (open): The shader build runs as worker processes, and no shader waits for a batch (Ocarina of Time's cold Mac map: 3,002 s to 1,298 s) - seedds#2065 by Raymon82777 (closed): A vertex library with layer entry points of its own gets bases for its main0 pairs: Ocarina of Time's 230 ms camera-turn hitch in Hyrule Field goes to 39 ms - seedds#2075 by sleepyeyedjoe (closed): Ocarina of Time: a patch of scenery no longer draws dark for one frame when the game gives it a sharper texture (a pool texture the walk does not make holds a stand-in of its own colours until its first read) - seedds#2077 by roandegraaf (closed): Ocarina of Time: fix the "plaid" terrain / missing texture tiles (GPU-drawn textures now reach the storage the game copies) - seedds#2079 by uncraftablexd (open): Render pipelines that share a vertex function are taken from the archive or compiled one at a time: the AGX driver crashes in createVertexProgramVariant's completion block - seedds#2083 by FifteenPen (closed): Ocarina of Time's prewarm list gets 41 more task / mesh pipelines from later play
…s, and t_perf_order follows seedds#1952 pool_heap_texture (DM_NVN_POOL_HEAP) ran before rh_texture (DM_NVN_RT_RECYCLE, 7f50222) in tex_mtl_ and took almost every render target it covers, so the recycling that keeps Ocarina of Time's dynamic resolution from adding hundreds of MB per 300 frames never ran; upstream also found that placing by pool offset corrupts the picture while that resolution moves. DM_NVN_POOL_HEAP=1 turns it back on. On the Mac, t_rt_recycle failed 8 of 18, t_image_alias 1 of 336 and t_pool_heap off 1 of 3 with the heap first; t_pool_heap now asks for the heap, and its off mode turns rh_texture off too. t_perf_order: since seedds#1952 a game barrier after draws that write no memory no longer stops the compute hoist (DM_NVN_CSH_BARRIER=0 restores the stop), so the hazard case expects the move unless that switch is off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Aax1HnchwCweUDRNWjhHx9
|
Data point from an M4 Pro (MacBook Pro, 24 GB, macOS 26.7.2), Ocarina of Time v131072. This change, at 6a512fa, was ported onto our tree (an older main): 7 of 8 hunks applied as is, and the Tests:
Game: Kokiri Forest route in a window, from the same save copy each time, comparing this change with
About 3 dispatches a frame moved, which matches uncraftablexd's M4 Pro count above. On this GPU there was no measurable gain, so for now our build for the player leaves the change out. These numbers are for 6a512fa. The render-target switch commit that came after it (7d26a8d) was not tested. |
|
At head 7d26a8d, This PR lets the hoist pass a game barrier when the draws before it write no memory ( A one-line fix that keeps both behaviours covered: expect a hoist exactly when - CHECK(hazard?"explicit game barrier prevents hoist":"independent dispatch hoisted once",g_csh_moved-before==(cshoist_on() && !hazard));
+ CHECK(hazard?"explicit game barrier: hoist only when barriers can be passed":"independent dispatch hoisted once",g_csh_moved-before==(cshoist_on() && (!hazard || csh_bar_pass())));With that change the test passes here. I also ran the PR on Ocarina of Time (0400486024DEC000 v131072) on a MacBook Air M2 (10-core GPU, 16 GB), macOS 27.0: handheld at 1440x810, Kokiri walk, in-run A/B, 8 pairs at ~3.0 GHz. GPU p50 went down 0.05 ms [−0.09, −0.035] and the frame went down ~0.035 ms. That's small but consistent. On this M2 the scans mostly stop at render-target switches, not at the cases this PR handles. 🤖 Generated with Claude Code |
|
This PR conflicts with main in
I've played Ocarina of Time on that merge for several days on an M4 Max, with no stutter and no picture problem. |
What and why
In Ocarina of Time (
0400486024DEC000) the heavy frames on an M1 run about 150 encoders and 110 passes, and the GPU spends about 19% of its time in gaps between them. Compute work splits the scene passes, andcs_hoist_try, which moves a dispatch ahead of the pass it would split, never moved one. Every scan stopped at one of these:Each case, and what this change does:
Queries: a dispatch draws no samples, so it moves past samples-passed resets and reports as long as it reads and writes none of the reports' memory (16 bytes each).
Conditional rendering: it skips draws and clears, never a dispatch, so a dispatch moves past it. If a dispatch writes the two counters a condition compares, the scan stops.
Storage buffers: a draw's read-only storage buffer counts as a read range instead of stopping the scan.
Clears:
Uniform updates: a dispatch moves past an update whose bytes it neither reads nor writes. The updated range joins the ranges the scan checks.
Barriers: a barrier orders nothing the moved dispatch depends on, for three reasons:
Replay still runs the barrier at its original place, for the draws after it.
DM_NVN_CSH_BARRIER=0restores the old stop.Stats: a new log line names the ops that stopped the most scans. In Ocarina of Time, render-target changes are now almost all of them, and those really do end the pass.
Checked
0400486024DEC000v131072, Mac app, M1, macOS 26, handheld, 30 fps target, opening and gameplay. The pass-dump and gpuprof data come from the same game.compute dispatches moved ahead of their pass 0.waiting clears taken by a later pass: about 300 in 300 frames. The clears that still land on their own are cleared right before a dispatch that uses them.dm/gfx/tests/t_perf_order.mwas updated forcsh_cs's new arguments. The GPU tests were not run locally (Apple-silicon Metal tests; CI has no Metal 4).regress.pyreport was made. The new paths only add moves past ops the scan used to stop at. Each is guarded by the memory ranges it reads or writes, except barriers, which rely on the ordering described above.Docs
nvn_mtl.m, described in its comments🤖 Generated with Claude Code