fix(shaders): a subgroup barrier between a warp's shared memory store and its next load #2524
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2524
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/lds-after-sts-subgroup-barrier"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
On NVIDIA's native cooperative-matrix path, the game's own upscaler below 100% Render resolution (Auto included) draws flickering bright bars. Each bar is 16 px wide and 2 rows tall, at
x % 16 == 0andy % 8 in {0, 1}of the output, in different places every frame (#2455, tracked in #2511). OoT's programs store to shared memory and read it back, other lanes' words included, with no BAR between: the Switch runs a warp's shared memory accesses in order. Vulkan orders another lane's store and this lane's load only through a barrier.Cause
The 4/3 gameplay network
fd029a317a9a4b94ends with 8 STS and then 16 LDS (one per output pixel it writes) with no barrier between them. I dumped its SPIR-V withRX_VK_DUMP_SPV. The main function's shared memory accesses, barriers and matrix ops in program order:The last
S8 L16has nothing between them, so the driver's compiler is free to move the loads before other lanes' stores. That is the same mechanism as LDSM's (lower_to_maxwell.h, nxnative's "LDSM reads rows other lanes stored"), but with a plain LDS. It shows on the cooperative-matrix path. On the software HMMA path (subgroup shuffles everywhere) it did not show in my runs.Change
lower_to_maxwell.h, STS/LDS (0x388 / 0x984): after an STS, the next LDS gets a subgroup barrier first (MEMBAR bit 20,SubgroupBarrier, as LDSM's), but only when the LDS is unpredicated, so every lane of the warp reaches it. A BAR.SYNC clears the pending store. In this network it adds exactly oneOpControlBarrier Subgroupbetween theS8and theL16, in the same block as the program's workgroup barriers (so in uniform control flow).RX_VK_SHARED_NO_LDS_BARRIER=1turns it off, as before. It is under theRX_VK_SHAREDprefix, so it is intranslator_stamp(test_program_stamp_knobspasses).tests/test_lower_lds_after_sts.pylowers STS/LDS/BAR sequences against the real header: a barrier between a store and its next load, one per store, none after a BAR.SYNC, none before a predicated LDS, none with the switch. It fails on main (FAIL: count(one, 'B') == 1).Tested
RTX 2060 SUPER (Turing), driver 610.62, Windows 11. Kokiri Forest from a save, in a separate copy of the game folder. The game was driven through the control bridge (headless,
option_set, 10 shots at 2048x1152 per setting). The bars were counted by a detector: 16x2, brighter than the rows above and below, ending sharply on both sides, counting only places seen in at most half of the shots. The same patchedhookshot_vk.dll(built withRX_VK_ONLY=1onb744b1ec, the exe's API version) was used for both the on and off runs, each from a fresh program cache. Every run's log has the network's dispatch (and its pipeline compiled) before the shots.RX_VK_SHARED_NO_LDS_BARRIER=1fd029a317a9a4b94)6676619641320ba7)For reference on main: the 75% stand-in (
RX_VK_TAAU_KEYS=fd029a317a9a4b94) gave 0, and AMD FSR 3.1 at 75% gave 1.GPU time a frame at 75% (the log's
GPU time, median of the last 12 lines): 12.8 ms on main, 12.3 ms with this change, 12.3 ms with it switched off. No measurable cost.tests/run_all.py(Windows, llvm-mingw 20261104): everything passes excepttest_vram_budget_row, which fails the same way without this change (-Werror,-Wunused-const-variableonkVramMinimumOnly, from #1859).Not tested
Fixes #2455 (part of #2511).
[Investigated with the help of an AI agent (Hermes) on my machine; I reviewed the change and the numbers.]
Thanks, this is a careful one: the dumped SPIR-V shape, a test that fails on main, and on/off numbers from the same DLL make it easy to follow. The change matches the LDSM barrier already on main (same MEMBAR bit 20 →
SubgroupBarrier). The off switch is underRX_VK_SHARED, so it is in the program stamp. Old cached programs get rebuilt because the translator sources feedHOOKSHOT_SHADER_STAMP. The test declaresRUN_ALL_ARGSand passes on both platforms in CI, and the head already includes #2443's change to this file, so there is no conflict there.Two notes, neither blocking:
if(after an STS) would get anOpControlBarrier Subgroupin non-uniform control flow. LDSM doesn't have this problem because it is warp-collective. This network is fine (you checked that the barrier lands next to its workgroup barriers), but the rule applies to every program. If you can, log how many programs it fires in during a run (or note whether any of them have a BRA between the STS and the LDS). That would show how far it reaches beyond this network, which matters more since AMD, Intel and Linux are untested.Approving: CI is green and the fix is narrow, with a switch to turn it off.
native_predicate == 7covers this instruction's predicate, but not a divergent branch around it. An LDS in aniftaken by some lanes still gets the subgroup barrier. Fine forfd029a317a9a4b94; see the review note about checking the other programs it fires in.github-sync referenced this pull request2026-11-12 06:22:54 +00:00