Repository navigation
Conversation
…fter memory writes, not on every barrier: Ocarina of Time -1.3 ms a frame on an M1 resource_vis() put MTL4VisibilityOptionResourceAlias on every barrier while an image heap was alive, about 230 barriers a frame. Ocarina of Time keeps one from its first frame (its dynamic-resolution targets are a resolution family, one storage shared by several sizes), and a counter-free Metal System Trace of its Kokiri Forest slow spot on an M1 showed the GPU's stages never overlapping: vertex work started under a running fragment phase in 0.9 % of kicks, compute under fragment in 0 %, and the GPU was idle a quarter of each frame. The relaxed draw-pass barriers allowed the overlap; the alias flush on each of them took it away. A family changes the size it draws at between frames, not inside one, so the flush is needed at a submission's start and after a write that another view of the memory might read. Now, with DM_NVN_VIS_HEAPFRAME (on by default; =0 flushes on every barrier again, ab for the DM_AB phases), an image heap's alias flush is on the first barrier of each submission (g_alias_heap_pend, set by mtl_sub_begin) and on the first barrier after an encoder that may have written memory (g_alias_pend: any compute or copy encoder, a draw pass with shader writes, g_pass_swrite). Large chunked pools without an image heap keep the flush on every barrier, as before. Measured (0400486024DEC000, Kokiri Forest, MacBook M1 8-core GPU, fullscreen, with the light-pass merge and mesh compute of seedds#2441 / seedds#2413 on, in a tree with other local changes): DM_NVN_VIS_HEAPFRAME=ab:1/3 in 300-frame phases, frame p50 36.61 / 36.25 / 37.20 / 37.27 ms with it (mean 36.83) against 38.12 / 38.16 / 38.15 without (38.14): -1.3 ms, lower in every phase. Its GPU p50 (the sum of command-buffer durations) rises to 37.7-39.7 ms, as command buffers now overlap. With it a Metal System Trace shows vertex under fragment in 32.8 % of kicks and the GPU idle 21 % of the time. Screenshots look right (the fog of seedds#2487 included). Dropping the flush altogether (a diagnostic) gave -1.5 to -1.8 ms; this keeps most of that. Not run: this branch's host in game, any test suite, the iPhone, M3 and later, other games. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
With this merged, Ocarina of Time's foliage in Kokiri Forest (the bushes and the ground ivy) is missing from most frames: whole draws are gone from about half to two thirds of frames, and they flicker in and out. Seen on an M3 Max (macOS 27.2) in a tree with other PRs merged; What's missing I made the rule switchable at run time (read once a submission, flipped through
So the missing flush follows a draw pass that writes no memory through shaders ( Two smaller gaps I found on the way, which didn't fix it either:
What it saves when correct Same spot, 300-frame windows, the same run, switching at run time:
On this machine, once the flushes the foliage needs are back, about 0.1 ms is left. The first row has two clean windows, the others five each. An M1 may gain more, but most of the measured gain seems to come from skipping flushes that are needed. I've taken it out of my build for now. |
…(no MTLGPUFamilyApple9); an M3 Max lost Ocarina of Time's foliage with it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the careful bisection. You're right: the rule skips flushes Metal asks for, since a pass's attachment stores are writes and other texture objects sit over the same bytes. I checked on an M1 (8-core GPU) at the same Kokiri Forest spot, one run, three settings alternating every 300 frames, six screenshots each:
So with the needed flushes back, nothing is left on the M1 either. The difference is that the M1 draws the foliage without them and the M3 Max doesn't. I've pushed 7f9492a: the rule is now on by default only on Macs without |
Update: on by default on M1 / M2 only
An M3 Max lost Ocarina of Time's Kokiri Forest foliage (bushes, ground ivy) in half to two thirds of frames with this rule (see the comments). The rule skips flushes Metal asks for: a draw pass's attachment stores are writes too, and the host places other texture objects over the same bytes.
Checked on an M1 (8-core GPU) at the same spot, one run, the three settings alternating every 300 frames:
So the correct version gains nothing, here as on the M3 Max. The M1 draws the foliage without the skipped flushes; the M3 Max doesn't. The rule is now on by default only without
MTLGPUFamilyApple9(M1, M2). Everything else flushes on every barrier as before.DM_NVN_VIS_HEAPFRAME=1|0|abstill overrides.Not checked: an M2.
What
resource_vis()putMTL4VisibilityOptionResourceAliason every barrier while an image heap was alive, about 230 barriers a frame in Ocarina of Time. OoT keeps one from its first frame: its dynamic-resolution targets are a resolution family, one storage shared by several sizes.A family changes the size it draws at between frames, not inside one. So the alias flush is needed at a submission's start, and after a write that another view of the memory might read. With
DM_NVN_VIS_HEAPFRAME(on by default;=0flushes on every barrier again,abfor theDM_ABphases), an image heap's alias flush goes on:g_alias_heap_pend, set bymtl_sub_begin);g_alias_pend: any compute or copy encoder, or a draw pass with shader writes,g_pass_swrite).Large chunked pools without an image heap keep the flush on every barrier, as before.
Why
A counter-free Metal System Trace of OoT's Kokiri Forest slow spot on an M1 showed the GPU's stages never overlapping, with the GPU idle a quarter of each frame. The relaxed draw-pass barriers allowed overlap; the alias flush on each of them took it away.
Performance
Ocarina of Time (0400486024DEC000), Kokiri Forest, MacBook M1 (8-core GPU), fullscreen. Measured with the light-pass merge and mesh compute of #2441 / #2413 on, in a tree with other local changes.
DM_NVN_VIS_HEAPFRAME=ab:1/3, 300-frame phases:About −1.3 ms a frame, lower in every phase. GPU p50 (the sum of command-buffer durations) rises to 37.7–39.7 ms, because command buffers now overlap. Use frame time to judge it.
Dropping the flush altogether (a diagnostic, not this change) gave −1.5 to −1.8 ms; this keeps most of that. Screenshots look right, including the fog of #2487.
Switch (in
docs/env_switches.tsv)DM_NVN_VIS_HEAPFRAME=0|1|ab: on by default.Not run
🤖 Generated with Claude Code