Repository navigation
feat(shader): implement the 64-bit LDS atomics - #573
Conversation
ds_add/sub/rsub/inc/dec_u64, ds_min/max_i64/u64, ds_and/or/xor/mskor_b64, ds_cmpst_b64/f64, ds_min/max_f64, their _rtn forms and ds_wrxchg_rtn_b64 threw in the decoder. LDS is a 32-bit workgroup array, so the two dwords are read and written under a lock dword placed after the guest LDS, which is cleared with a workgroup barrier at entry. The lock is acquired and released in the same loop iteration, so lanes of one subgroup cannot deadlock on it. Stages whose LDS is a function variable update it directly. 64-bit GDS atomics throw. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1591f13 to
d78eea1
Compare
|
Thanks, this is a careful one. I checked the 35 opcodes with llvm-mc for gfx1013: the test shader disassembles to exactly the ops in One thing to watch when CI runs (it still needs approval): in the contended part, all 32 lanes write 0 to the two shared slots and then do the
Small optional note: the lock dword adds 4 bytes, so a shader that already uses the device's whole shared memory would go over the limit. That's an edge I don't expect to see. PPSA21564 has no 64-bit LDS atomics, so this doesn't affect my runs. Ready to merge from my side once CI is green. |
Progress report (f83ee12 → 7907e87)📈 GPU shader instructions: 86.88% (+3.0%, +35 implemented instructions) ✅ 35 implemented
|
On a device whose subgroups are narrower than the wave, the recompiled wave runs as several subgroups, so one subgroup's zeroing write could land after another's atomics, and a read could run before another subgroup's atomics. An s_barrier after the zeroing writes and another after the atomics order them on any subgroup width. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for adding the barriers. I owe you a correction on my earlier note: the Linux job had already passed on d78eea1 ( The two
|
Conflicts resolved: - libSceAudioPropagation/Export.cpp: upstream's boykopovar#482 + boykopovar#489 version taken; it is the same no-propagation model as astrobot's stand-in, with SourceRender and the handle-only UnregisterMaterial already in it. - State.cpp (boykopovar#453): astrobot's resident depth model kept; it already skips the tests of an absent plane (boykopovar#453's test is on astrobot since e087a4f). - Graphics.cpp, State.hpp, ShaderResources.cpp: astrobot's DepthTarget model kept (exportIndex, no DepthSurface.hpp), upstream's ThreadOwned include added. - BdaResources.cpp: astrobot's form with boykopovar#390's message. - VulkanDevice.cpp, Context.hpp, PacketExecution.cpp: both sides kept (calibrated timestamps, host query reset, GDS, memory budget; pipeline stats). - Spirv: astrobot's split-wave rules first, then boykopovar#608's wave-mask ballot and boykopovar#587's host subgroup lane; astrobot's function LDS sizing and boykopovar#573's LDS lock both kept; conditionalDepth kept in the atomic update loops; upstream's buffer atomics (boykopovar#321 boykopovar#332 boykopovar#594) and 64-bit LDS atomics taken. - TechnicalDebt.md: both sides, duplicate lines dropped. Build fixes folded in: the upstream boykopovar#453 test block duplicated next to astrobot's adapted copy removed; a lost closing brace after the host query reset block restored; lookupLog() is a function since boykopovar#590; the duplicate MUTEX_TYPE_ADAPTIVE constant (astrobot's 93b2d0e equals boykopovar#156) removed; the ops boykopovar#596 drops removed from the masked-select list and the BDA test message updated (the gate-batch-2 integ commits 559e4fab6 and 2ec968fb5).
The LDS array bound (3cbea50) counted every shared access that is not a vector load or store as one dword. boykopovar#573's 64-bit LDS atomics read and write two, so a pixel shader's last 64-bit atomic could reach past the array. SharedComponentCount (boykopovar#573) already gives the dword count of every shared op; the bound now uses it.
What
The 35 64-bit DS atomics threw
DS opcode is not supported:ds_add/sub/rsub/inc/dec_u64,ds_min/max_i64,ds_min/max_u64,ds_and/or/xor/mskor_b64,ds_cmpst_b64/f64,ds_min/max_f64, their_rtnforms, andds_wrxchg_rtn_b64(DS 0x40-0x4c, 0x50-0x53, 0x60-0x6d, 0x70-0x73).LDS is emitted as a 32-bit
Workgrouparray, and 64-bit atomics on it would needVK_KHR_workgroup_memory_explicit_layoutplusshaderSharedInt64Atomics. Instead, each op reads and writes its two dwords under a lock dword placed after the guest LDS:Semantics follow the 32-bit forms (commit c8af889):
cmpststoresdata1when memory equalsdata0, andmskorcomputes(old & ~data0) | data1.incanddecwrap as inds_inc_u32andds_dec_u32. The f64min/max/cmpstops are computed on the bits with the NaN and signed-zero rules measured for the f32 forms. This is recorded in TechnicalDebt together with the lock's scope: it orders 64-bit atomics against each other, not against concurrent 32-bit access to the same dwords.tools/progress.py: shaders 1013/1166 (86.88%), +35.Tested
Windows 11, MinGW GCC 15.2.0 r7, NVIDIA Vulkan,
-DANYPS5_ENABLE_SPIRV_TOOLS=ON.agc_driver_lds_atomics64runs one wave32 compute dispatch:ds_add_rtn_u64andds_max_rtn_u64. The test checks the total and that the returned values form one serial order.ds_min_i64's compare fails its lane checks.ctest: 212/213 pass.agc_shader_memoryfails identically without this change (SPIR-V validationOpSelectvector size).Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code