Repository navigation
Conversation
… cooperative matrix load and store The staged matrix route that follows writes cooperative matrices back to shared memory, and on the Vulkan memory model its loads and stores of workgroup memory must carry the NonPrivatePointer operand. Sirit had the load only, without memory operands. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts (16x16x16 cooperative matrices) On AMD cards the game's anti-aliasing and upscaling networks never run as on the console: the 100% network runs its matrix multiplies in a slow software function, and the 75/67/58/50/33% networks and the cutscene upscalers are left out for stand-ins (issue 1284, tracking 1006). The networks use HMMA on 16x8x16 fragments; RDNA3/4 on RADV offers only 16x16x16 cooperative matrices. The multiplies now go through those matrices staged through shared memory (mma_staged.h): each HMMA's A and B into 16x16 tiles, f16 x f16 -> f32 products with a zero accumulator, and the console's two roundings, D = f16(f16(C + S1) + S2), kept with NoContraction so the driver cannot fold them. mma_route.h plans the route for each program from its shared memory and workgroup size (native, staged, or the software function). At start the renderer runs a self-test of the route's own function on 1024 random problems (vk_mma_staged_test.h) against the software function's arithmetic and HMMA's rounding bound, behind the driver fault guard; the route is used only when it passes. With it every network translates, the 67/58/50% networks are gameplay's, and a network that still fails falls back as before. On by default with Mesa RADV only, where it was tested; RX_VK_MMA_STAGED=1 asks for it on any driver (AMD's own lists 16x16x16 on RDNA2 too, without matrix hardware), RX_VK_MMA_STAGED=0 never. NVIDIA keeps its native route. The route is in the program cache's stamp. Tests: nx2/test_hmma_staged_spirv.py builds programs on each route and the self-test's module and runs spirv-val on them, with the staged route's Vulkan memory model rules; tests/test_mma_staged_driver.py holds the driver gate ahead of the self-test; tests/test_upscale_fallback.py and tests/test_taau_env_lookup.py follow the new route. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Players on AMD cards need to know the game's own anti-aliasing now runs there, how to tell from the log, and how to turn it off if the self-test stops the start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
this one is huge, thanks for the amazing work 👍 |
hckrman101
left a comment
There was a problem hiding this comment.
Really nice work. The design holds up: it picks a route for each program from one place (PlanMatrixRoute) and the translator, vk_shader.cpp and vk_draw.cpp all decide by it, it checks itself at start before turning on, it is limited to RADV by default, and the cache stamp covers it. I went through the routing logic and found nothing wrong:
left_outis unchanged from main on every non-staged path (NVIDIA native,mma_software,RX_VK_NO_F16).taau_keygives the same answers as before off the staged route and checks the key before it asks for the route.upscale_stub's new!full_size_passis equivalent to the old!gameplay_network(key) || gameplay_taau.profile()andprofile_pinned()agree onSupportsSubgroupStage, so the renderer and the translator can't pick different routes.Infois serialized whole, so cached programs get the same route.- The scratch sizing (
threads/32 * kStagedBytes, X a multiple of 32,pin_subgroupasking for full 32-lane subgroups, andstaged_multipliesrequiringsg32_stages+computeFullSubgroups) is consistent.
One thing I'd fix before merge:
Not every barrier gets MakeAvailable/MakeVisible on the staged route. Only EmitBarrier was changed. EmitSubgroupBarrier, which the Ampere lowering emits as MEMBAR bit 20 for LDSM (the warp's shared-memory stores followed by the fragment loads), and MemoryBarrier (MEMBAR.CTA/GL) still send AcquireRelease | WorkgroupMemory with no availability or visibility. Under the Vulkan memory model, which these programs now declare, the non-private STS -> LDSM exchange inside a subgroup is therefore not formally ordered. The networks are the HMMA programs that do exactly this. RADV/NIR doesn't really act on those bits, which would explain why the self-test and the game run fine there. A driver that follows the model strictly (RX_VK_MMA_STAGED=1 elsewhere, which the readme invites) could read stale fragments. The fix is to OR in the same visibility mask in EmitSubgroupBarrier and MemoryBarrier when ctx.workgroup_non_private, and to add a ir.SubgroupBarrier() to tests/hmma_staged_ir.cpp, so that nx2/test_hmma_staged_spirv.py's "every barrier makes memory available and visible" check covers that barrier too. At the moment the fixture only has ir.Barrier().
Smaller points:
- CI hasn't run on this head (
no checks reported, presumably the fork's workflows need approval), so the newnx2/test_hmma_staged_spirv.pyand the updatedtest_taau_env_lookup.py/test_upscale_fallback.pyhaven't run in CI yet. Your localrun_allresult (336 passed) is good to have, but someone should approve the run. - On the staged route every movie network now translates as well, not only the gameplay ones, and
RX_VK_NETWORK_KEEPno longer applies to them. The description already lists movies as untested. Given the history of issue 26 (AMD compile times on these networks), it would be worth one cutscene run on RADV, checking compile stalls and the 40 ms movie fallback, before this goes in. - Nit: the self-test module declares
SignedZeroInfNanPreservefor 16 and 32 bits. If the game's own staged modules don't, the test isn't checking quite the same float mode as the programs it vouches for. That's harmless on RADV, but you may want them to match.
The RDNA3 and Windows/AMDVLK results are still open, as the description says. The merge decision for a change this large is NotRexed's.
…ble and visible On the staged matrix route the program's own shared memory is on the Vulkan memory model, where a barrier orders non-private accesses only when its semantics make memory available and visible. EmitBarrier did; EmitSubgroupBarrier (the warp's stores before Ampere's LDSM, MEMBAR bit 20) and the MEMBAR.CTA/GL memory barriers did not, so the STS -> LDSM exchange inside a subgroup was not formally ordered. RADV does not act on those bits, which is why the self-test and the game ran right; a driver that follows the model strictly could read stale fragments. All three now take the same mask when ctx.workgroup_non_private; the GLSL450 and native routes are unchanged. tests/hmma_staged_ir.cpp's program gains a subgroup barrier and a MEMBAR.CTA, and the module inspection counts OpMemoryBarrier too, so nx2/test_hmma_staged_spirv.py's "every barrier makes memory available and visible" covers them (3 of 5 without this change). The fixture's profile keeps the float modes vk_shader.cpp's make_profile keeps (SignedZeroInfNanPreserve 16 and 32), and the test checks that the staged modules and the self-test's declare the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…/staged-hmma # Conflicts: # gpu/vk/vk_draw.cpp # gpu/vk/vk_shader.cpp # tests/test_taau_env_lookup.py
…/staged-hmma # Conflicts: # gpu/vk/vk_draw.cpp
|
Thanks for the careful review. You were right about the barrier gap. It's fixed in 4dd396d ("every barrier of a staged program makes shared memory available and visible"), on top of the commits you reviewed. Main is merged in (5aa3971), with #2042's switch changes: the staged route keeps its own Barriers. SignedZeroInfNanPreserve. These already match. Movies. RADV, RX 9070 XT, Mesa 26.2.6, this branch before the merge (on main 07b0359): the intro movie plays at every start, so every lab run covers it. With shaders prepared at build time, as the builder does, the movie's networks run on the staged route throughout: 14 dispatches a frame, 6.4-7.6 ms of GPU time a frame at 2560x1440 output, 60 fps from the second 5-second block on. No waits for shaders or pipelines, no draws left out, and the 40 ms movie fallback never triggered (no "movie upscaling uses the linear compute path"). I haven't run it with an empty program cache, where compile time would show. After the merge. I ran a game build of the merged branch: the self-test passes, the networks run on the staged route, and the game network's picture matches the earlier capture of the same setting (1-px outlines 6400 vs 6201 per megapixel, standing flicker 5.00 vs 5.20). CI. It still needs a maintainer to approve the workflows for this fork. |
Joey-Magnus
left a comment
There was a problem hiding this comment.
4dd396d fixes the barrier gap: EmitSubgroupBarrier and MemoryBarrier now make memory available and visible like EmitBarrier, and the IR fixture covers all three. The movie run on the 9070 XT answers the other open point.
It conflicts with main again, at one place. In gpu/vk/vk_shader.cpp around line 1238, #2343 (GCN f16 lowering) added || !dev.vk12.shaderFloat16 to the network left-out condition, and your branch replaces that line with the PlanMatrixRoute version. When you resolve it, keep #2343's term in your left_out, i.e. env_on("RX_VK_NO_F16") || !dev.vk12.shaderFloat16. Otherwise GCN cards without f16 stop leaving those networks out. I resolved it that way locally: run_all gives 377 passed, 0 failed, including test_mma_staged_driver, test_taau_env_lookup, test_upscale_fallback and test_amd_subgroup_profiles.
The Windows "Python source checks part 1/2" failure is on a base from before #2397 (the Windows test_native_astc_texture_count build fix), so I don't think it's this PR. A re-run after the rebase should show. Results on RDNA3 and on Windows/AMDVLK are still missing, as the description says. The final call on a change this size is NotRexed's.
|
This now conflicts with main in |
…/staged-hmma # Conflicts: # gpu/vk/vk_coopmat.cpp # gpu/vk/vk_shader.cpp
|
Main is merged in (01faa82, then a7ec84b for the four commits after it), so the PR is mergeable again. Three conflicts:
Checked on the merge (RX 9070 XT, Mesa RADV 26.2.6, Linux):
Thanks for the reviews. RDNA3 and Windows/AMDVLK results are still open, as the description says. |
Joey-Magnus
left a comment
There was a problem hiding this comment.
The point from my last review is fixed. The network left-out condition at gpu/vk/vk_shader.cpp:1288-1290 keeps #2343's env_on("RX_VK_NO_F16") || !dev.vk12.shaderFloat16. Moving the staged route's cache flags to 256/512 (vk_shader.cpp:1189) is also the right call, because #2457 now uses bits 8 to 128.
I merged the branch into current main (51da046) and ran tests/run_all.py with the Vulkan and SPIR-V headers: 470 passed. The only failures were test_lift_cache, which fails on main on this machine too, and a load timeout in test_recorder_sync, which passes when run alone. The red Python checks on GitHub are test_env_on from main at your base, and a re-run should clear them. I also merged it together with #2306, which touches spirv_emit_context.cpp and emit_spirv_atomic.cpp: no conflicts. #2742 changes the cooperative matrix probe in vk_coopmat.cpp, so whichever of the two lands second should re-check the self-test path.
Still open, as the description says: the route is on by default for every RADV card with 16x16x16 matrices, so RDNA3 (Tier 1) too, and neither RDNA3 nor Windows/AMDVLK has been run. Whether to take a change this size is NotRexed's call.
|
Tested on RDNA3 with AMD's own driver on Windows, head a7ec84b (built here with make_game.py, llvm-mingw, shaders prepared at the first start). This is the first result on RDNA3 and on Windows/AMD's driver. Testing: RX 7800 XT 16 GB (RDNA3), AMD Adrenalin 26.10.1 (Windows driver 32.0.32015.7029; the log says "AMD proprietary driver 26.10.1 (LLPC)", Vulkan 1.4), Ryzen 7 5800X3D, 32 GB, Windows 11 Home 10.0.26300. Windowed, output 2560x1440, Render resolution 75%, Upscaler "the game's", frame generation Auto (it was on in my options file), empty Results
I do not take the fps difference as a measurement of the route: the scenes differ, and the
Not tested: RADV on RDNA3, the default ( 🤖 Generated with Claude Code |
On AMD cards the game's own anti-aliasing never runs as on the console: at 100% its network runs on a slow software fallback, and at 75% and below (or Auto) the network is left out and an FXAA stand-in takes its place, which is visibly softer. This runs the game's networks on the matrix units of AMD RDNA3/4 cards, so every Render resolution gets the game's own anti-aliasing and upscaling. Closes #1284 (tracking: #1006).
Why it did not run: the networks multiply with HMMA on NVIDIA's 16x8x16 fragments. RDNA3/4 on RADV offers only 16x16x16 cooperative matrices (compute stage, subgroup scope), so
vk_coopmat.cppfell back to the software function, and the lowering left out every network but the 100% one.What changed
gpu/vk/sirit:OpCooperativeMatrixStoreKHR, and memory operands on the cooperative matrix load and store (NonPrivatePointer, which the Vulkan memory model needs for workgroup memory).backend/spirv/mma_staged.h,mma_route.h(new), and the SPIR-V back end: a third route for HMMA. Each HMMA's A and B are staged through shared memory into 16x16 tiles, multiplied as f16 x f16 -> f32 with a zero accumulator, and the console's two roundings are kept:D = f16(f16(C + S1) + S2), with NoContraction so the driver cannot fold them.PlanMatrixRoutepicks native, staged or the software function for each program from its shared memory and workgroup size.vk_coopmat.cpp,vk_mma_staged_test.h(new): before the route is used, a self-test at start runs the route's own function on 1024 random problems (131,072 results) against the software function's arithmetic and HMMA's rounding bound, behindvk_draw.cpp's driver fault guard. If it fails, the software function is used as before. On this card:31 differ from the software function by a rounding (max 2 f16 ulps of D, 0.95 of the bound of HMMA's roundings), 0 beyond it.vk_shader.cpp,vk_draw.cpp: on the staged route every network translates, the 67/58/50% networks count as gameplay's, and a network that still fails falls back as before (the RGB9E5 decode keeps fix(vk): decode gameplay RGB9E5 upscale fallback #1552's rule: every network but the full-size pass). The route is in the program cache's stamp.RX_VK_MMA_STAGED=1.RX_VK_MMA_STAGED=0turns it off. NVIDIA keeps its native route and its code path is unchanged; Intel's matrices are a different shape, so nothing changes there.Testing: RX 9070 XT 16 GB, Mesa RADV 26.2.6 (Vulkan 1.4), Ryzen 9 9950X3D, 30 GB, Linux (KDE Plasma, Wayland). Output resolution 2560x1440, Render resolution 75%,
RX_VK_BATCH=0. Kokiri Forest from a save, standing, then 28 s of camera turning (frames fromhookshot_log.txt).nx2/test_hmma_staged_spirv.pychecks synthetic programs on each route and the self-test's module in CI.python -I -B tests/run_all.py: 336 passed, 0 failed, 11 skipped (Linux), on this branch rebased onto fc375e9. The game runs above were on 8a8ac27; the branch rebased onto fc375e9 without conflicts.Screenshots: the same frame at 75%, 2x crops: main's FXAA stand-in on the left, the game's network with this PR on the right.
Not tested: RDNA3 on RADV (only RDNA4 here), the Steam Deck, AMD's own driver (off by default there;
RX_VK_MMA_STAGED=1to try), Windows, NVIDIA (native route untouched), cutscene upscalers in a movie (they now translate; if one takes the GPU over 40 ms a frame, the existing movie fallback applies), and Render resolution Auto.New checks:
nx2/test_hmma_staged_spirv.py(routes, memory model, spirv-val),tests/test_mma_staged_driver.py(the RADV default ahead of the self-test; fails without the change).tests/test_upscale_fallback.pyandtests/test_taau_env_lookup.pyfollow the new route.Suggested row for
docs/tested-configurations.md:| 2026-11-10 | main 8a8ac27 + this PR | Gentoo Linux, KDE Plasma 6 Wayland | RX 9070 XT 16 GB, Mesa RADV 26.2.6 | Ryzen 9 9950X3D, 30 GB | 2560x1440 output, Render resolution 75%, RX_VK_BATCH=0 | Kokiri Forest, camera turning | 57.3 fps (main: 58.2 with the FXAA stand-in); the game's network at every Render resolution |🤖 Generated with Claude Code