shader: implement BUFFER_ATOMIC_CMPSWAP (#357)

shader: implement RDNA2 buffer atomic compare swap

Co-authored-by: nmzik <Nmzik@mail.ru>
This commit is contained in:
GodsWill Dev
2026-09-03 17:34:34 -04:00
committed by GitHub
parent 5b8e3b51b0
commit 2fe5c150bc
9 changed files with 142 additions and 49 deletions
+53
View File
@@ -12226,6 +12226,7 @@ CoverageClass ClassifyOpcode(ShaderOpcode opcode,
case Opcode::TBUFFER_STORE_FORMAT_XYZ:
case Opcode::TBUFFER_STORE_FORMAT_XYZW:
case Opcode::BUFFER_ATOMIC_SWAP:
case Opcode::BUFFER_ATOMIC_CMPSWAP:
case Opcode::BUFFER_ATOMIC_ADD:
case Opcode::BUFFER_ATOMIC_SUB:
case Opcode::BUFFER_ATOMIC_SMIN:
@@ -19450,6 +19451,52 @@ TestCase BufferAtomicVariants() {
O::S_ENDPGM}};
}
TestCase BufferAtomicCmpSwapExactRaw() {
using O = ShaderOpcode;
std::vector<u32> code;
const auto Store = [&](u32 value, u32 index) {
AppendVMovU32(&code, 31, index * sizeof(u32));
code.push_back(EncodeMubuf0(0x1cu));
code.push_back(EncodeMubuf1(value, 8, 31));
};
AppendVMovU32(&code, 4, 1);
AppendVMovLiteral(&code, 1, 0x22222222u);
AppendVMovLiteral(&code, 2, 0x11111111u);
code.push_back(0xe0c46000u);
code.push_back(0x80080104u);
Store(1, 2);
AppendVMovLiteral(&code, 1, 0x33333333u);
AppendVMovLiteral(&code, 2, 0x44444444u);
code.push_back(0xe0c46000u);
code.push_back(0x80080104u);
Store(1, 3);
Store(2, 4);
AppendVMovLiteral(&code, 1, 0x55555555u);
AppendVMovLiteral(&code, 2, 0x22222222u);
code.push_back(EncodeMubuf0(0x31u, 0, true, false));
code.push_back(0x80080104u);
Store(1, 5);
AppendEnd(&code);
TestCase test;
test.name = "BufferAtomicCmpSwapExactRaw";
test.code = std::move(code);
test.initial = {0xaaaaaaaau, 0x11111111u, 0, 0, 0, 0};
test.expected = {0xaaaaaaaau, 0x55555555u, 0x11111111u,
0x22222222u, 0x44444444u, 0x55555555u};
test.opcodes = {O::V_MOV_B32, O::BUFFER_ATOMIC_CMPSWAP,
O::BUFFER_STORE_DWORD, O::S_ENDPGM};
test.required_spirv = {"OpAtomicCompareExchange"};
const auto descriptor = MakeStructuredStorageBufferData(
sizeof(u32), static_cast<u32>(test.initial.size()));
std::copy_n(descriptor.begin(), 4, test.user_data.begin() + 32);
test.has_user_data = true;
return test;
}
TestCase BufferAtomicGlc0DoesNotReturnOldValue() {
using O = ShaderOpcode;
@@ -21654,6 +21701,7 @@ std::vector<TestCase> MakeCases() {
AddCase(DsBpermuteCapturedExecOffsetAndWrap);
AddCase(DsBpermuteWave64UsesIndependentHalves);
AddCase(BufferAtomicVariants);
AddCase(BufferAtomicCmpSwapExactRaw);
AddCase(BufferAtomicGlc0DoesNotReturnOldValue);
AddCase(BufferAtomicFMinExactRawGlcModes);
AddCase(BufferAtomicFMinSpecialValues);
@@ -25841,6 +25889,11 @@ int main(int argc, char **argv) {
RunCase(&vulkan, BufferOffsetsUsePackedLaneAndStorageFallback());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--buffer-cmpswap-only") == 0) {
VulkanHarness vulkan;
RunCase(&vulkan, BufferAtomicCmpSwapExactRaw());
return 0;
}
if (argc == 2 && std::strcmp(argv[1], "--mapped-range-only") == 0) {
VulkanHarness vulkan;
vulkan.CheckGpuMappedRangeLifecycle();
+36 -29
View File
@@ -8557,40 +8557,47 @@ void TestNewShaderRecompilerBufferLoadsGuardedByExec() {
}
void TestNewShaderRecompilerBufferAtomicsGuardedByBounds() {
const uint32_t shader[] = {
EncodeMubuf0(0x32, 0, true, true),
EncodeMubuf1(0, 0, 1), // buffer_atomic_add
EncodeSopp(0x01),
struct Case {
std::array<uint32_t, 3> shader;
const char *decoded;
const char *ir;
const char *spirv;
};
const Case cases[] = {
{{EncodeMubuf0(0x32, 0, true, true), EncodeMubuf1(0, 0, 1),
EncodeSopp(0x01)},
"BUFFER_ATOMIC_ADD", "BufferAtomicIAdd32", "OpAtomicIAdd"},
{{0xe0c46000u, 0x80080104u, EncodeSopp(0x01)},
"BUFFER_ATOMIC_CMPSWAP", "BufferAtomicCmpSwap32",
"OpAtomicCompareExchange"},
};
auto options = MakeCompileOptions(ShaderType::Compute);
options.dump_ir = true;
auto result = RecompileForTest(shader, options);
Check(Common::ContainsStr(result.decoded_dump, "BUFFER_ATOMIC_ADD"),
"buffer atomic bounds regression did not decode MUBUF atomic");
CheckSpirvBinaryValidates(result.spirv);
for (const auto &test : cases) {
const auto result = RecompileForTest(test.shader, options);
Check(Common::ContainsStr(result.decoded_dump, test.decoded),
"buffer atomic bounds regression did not decode MUBUF atomic");
Check(Common::ContainsStr(result.ir_dump, test.ir),
"buffer atomic did not lower to its native IR opcode");
CheckSpirvBinaryValidates(result.spirv);
const auto source = DisassembleSpirvBinary(result.spirv);
const auto array_length =
Common::FindIndex(source, std::string("OpArrayLength"), 0);
const auto bounds_branch = Common::FindIndex(
source, std::string("OpBranchConditional"), array_length);
const auto atomic = Common::FindIndex(source, std::string("OpAtomicIAdd"), 0);
const auto memory_barrier =
Common::FindIndex(source, std::string("OpMemoryBarrier"), atomic);
Check(array_length != Common::FIND_INVALID_INDEX,
"buffer atomic SPIR-V lacks storage buffer array-length bounds check");
Check(bounds_branch != Common::FIND_INVALID_INDEX,
"buffer atomic SPIR-V lacks storage buffer bounds branch");
Check(atomic != Common::FIND_INVALID_INDEX,
"buffer atomic SPIR-V lacks atomic operation");
Check(memory_barrier != Common::FIND_INVALID_INDEX,
"buffer atomic SPIR-V lacks memory barrier after atomic operation");
Check(bounds_branch < atomic,
"buffer atomic was emitted before bounds guard");
Check(atomic < memory_barrier,
"buffer atomic memory barrier was emitted before atomic");
const auto source = DisassembleSpirvBinary(result.spirv);
const auto array_length =
Common::FindIndex(source, std::string("OpArrayLength"), 0);
const auto bounds_branch = Common::FindIndex(
source, std::string("OpBranchConditional"), array_length);
const auto atomic = Common::FindIndex(source, std::string(test.spirv), 0);
const auto memory_barrier =
Common::FindIndex(source, std::string("OpMemoryBarrier"), atomic);
Check(array_length != Common::FIND_INVALID_INDEX &&
bounds_branch != Common::FIND_INVALID_INDEX &&
atomic != Common::FIND_INVALID_INDEX &&
memory_barrier != Common::FIND_INVALID_INDEX,
"buffer atomic SPIR-V lacks its bounds guard, operation, or barrier");
Check(bounds_branch < atomic && atomic < memory_barrier,
"buffer atomic bounds guard, operation, and barrier are misordered");
}
}
void TestCapturedBufferAtomicsX2() {