From 09a9654458f25337a2db2c7dacec7803706050b0 Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 22 Oct 2026 00:40:07 +0200 Subject: [PATCH] shader: compute DPP permute, rotate, mirror and share lanes with bit-field and XOR ops --- .../backend/spirv/spirvEmitterHelpers.cpp | 83 ++++++------------- tests/ShaderRecompilerComputeTests.cpp | 33 ++++++++ 2 files changed, 57 insertions(+), 59 deletions(-) diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterHelpers.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterHelpers.cpp index bd1f1e7e2..0496db425 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterHelpers.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterHelpers.cpp @@ -4,25 +4,16 @@ namespace Libs::Graphics::ShaderRecompiler::Spirv::Emitter { DppTargetLane EmitDppPermTargetLane(EmitterState& state, uint32_t subid, uint32_t control, uint32_t lane_bits) { - const auto lane_mask = (1u << lane_bits) - 1u; - const auto group_base = state.builder.AllocateId(); - const auto lane = state.builder.AllocateId(); - const auto shift = state.builder.AllocateId(); - const auto selected0 = state.builder.AllocateId(); - const auto selected = state.builder.AllocateId(); - const auto target = state.builder.AllocateId(); - state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), group_base, subid, - ConstantU32(state, ~lane_mask)); - state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), lane, subid, - ConstantU32(state, lane_mask)); - state.builder.AddFunction(spv::OpIMul, TypeU32(state), shift, lane, - ConstantU32(state, lane_bits)); - state.builder.AddFunction(spv::OpShiftRightLogical, TypeU32(state), selected0, - ConstantU32(state, control), shift); - state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), selected, selected0, - ConstantU32(state, lane_mask)); - state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, group_base, selected); - return {target, ConstantBool(state, true)}; + // Each lane of a group takes its lane_bits-wide source index from control. + const auto lane = Binary(state, spv::OpBitwiseAnd, TypeU32(state), subid, + ConstantU32(state, (1u << lane_bits) - 1u)); + const auto selected = EmitNative( + state, ConstantU32(state, control), + Binary(state, spv::OpIMul, TypeU32(state), lane, ConstantU32(state, lane_bits)), + ConstantU32(state, lane_bits)); + return {EmitNative( + state, subid, selected, ConstantU32(state, 0), ConstantU32(state, lane_bits)), + ConstantBool(state, true)}; } DppTargetLane EmitDppRowShiftTargetLane(EmitterState& state, uint32_t subid, uint32_t amount, @@ -53,42 +44,19 @@ DppTargetLane EmitDppRowShiftTargetLane(EmitterState& state, uint32_t subid, uin DppTargetLane EmitDppRowRotateRightTargetLane(EmitterState& state, uint32_t subid, uint32_t amount) { - const auto row = state.builder.AllocateId(); - const auto lane = state.builder.AllocateId(); - const auto in_high = state.builder.AllocateId(); - const auto minus = state.builder.AllocateId(); - const auto plus = state.builder.AllocateId(); - const auto selected = state.builder.AllocateId(); - const auto target = state.builder.AllocateId(); - state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), row, subid, - ConstantU32(state, 0xfffffff0u)); - state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), lane, subid, - ConstantU32(state, 15)); - state.builder.AddFunction(spv::OpUGreaterThanEqual, TypeBool(state), in_high, lane, - ConstantU32(state, amount)); - state.builder.AddFunction(spv::OpISub, TypeU32(state), minus, lane, ConstantU32(state, amount)); - state.builder.AddFunction(spv::OpIAdd, TypeU32(state), plus, lane, - ConstantU32(state, 16u - amount)); - state.builder.AddFunction(spv::OpSelect, TypeU32(state), selected, in_high, minus, plus); - state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, row, selected); - return {target, ConstantBool(state, true)}; + // Rotation wraps within the row, so only the low four lane bits change. + const auto rotated = Binary(state, spv::OpISub, TypeU32(state), subid, ConstantU32(state, amount)); + return {EmitNative(state, subid, rotated, + ConstantU32(state, 0), + ConstantU32(state, 4)), + ConstantBool(state, true)}; } DppTargetLane EmitDppMirrorTargetLane(EmitterState& state, uint32_t subid, bool half_row) { - const auto base_mask = half_row ? 0xfffffff8u : 0xfffffff0u; - const auto lane_mask = half_row ? 7u : 15u; - const auto base = state.builder.AllocateId(); - const auto lane = state.builder.AllocateId(); - const auto mirrored = state.builder.AllocateId(); - const auto target = state.builder.AllocateId(); - state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), base, subid, - ConstantU32(state, base_mask)); - state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), lane, subid, - ConstantU32(state, lane_mask)); - state.builder.AddFunction(spv::OpISub, TypeU32(state), mirrored, ConstantU32(state, lane_mask), - lane); - state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, base, mirrored); - return {target, ConstantBool(state, true)}; + // Mirroring lane i of a row or half row reads lane (size - 1 - i), which flips its low bits. + return {Binary(state, spv::OpBitwiseXor, TypeU32(state), subid, + ConstantU32(state, half_row ? 7u : 15u)), + ConstantBool(state, true)}; } DppTargetLane EmitDppTargetLane(EmitterState& state, const IR::DppMoveFlags& flags) { @@ -117,13 +85,10 @@ DppTargetLane EmitDppTargetLane(EmitterState& state, const IR::DppMoveFlags& fla } // row_share:N reads lane N of the caller's row of 16. if (control >= 0x150u && control <= 0x15fu) { - const auto row = state.builder.AllocateId(); - const auto target = state.builder.AllocateId(); - state.builder.AddFunction(spv::OpBitwiseAnd, TypeU32(state), row, subid, - ConstantU32(state, 0xfffffff0u)); - state.builder.AddFunction(spv::OpBitwiseOr, TypeU32(state), target, row, - ConstantU32(state, control & 0xfu)); - return {target, ConstantBool(state, true)}; + return {EmitNative( + state, subid, ConstantU32(state, control & 0xfu), ConstantU32(state, 0), + ConstantU32(state, 4)), + ConstantBool(state, true)}; } if (control >= 0x160u && control <= 0x16fu) { const auto target = state.builder.AllocateId(); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index f5fe6a0ee..fb7ffefd9 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -27298,6 +27298,35 @@ TestCase VectorDppRowShare(u32 wave_size) { return test; } +// row_mirror, row_half_mirror and row_ror read lanes within the row; v1 = 100 + lane. +TestCase VectorDppMirrorAndRotate(u32 wave_size) { + using O = ShaderOpcode; + TestCase test; + test.name = wave_size == 32 ? "VectorDppMirrorAndRotateWave32" : "VectorDppMirrorAndRotateWave64"; + auto &code = test.code; + AppendVMovU32(&code, 1, 100); + code.push_back(EncodeVop2(0x25, 1, Vgpr(0), 1)); + for (const u32 control : {0x140u, 0x141u, 0x121u, 0x125u, 0x12fu}) { + code.push_back(EncodeVop1(0x01, 2, 250)); + code.push_back(EncodeVop1Dpp(1, control)); + AppendStoreVgprAtLaneDwordOffset(&code, 2, 0, u32(test.expected.size())); + for (u32 lane = 0; lane < wave_size; ++lane) { + const u32 source = control == 0x140u ? (lane | 15u) - (lane & 15u) + : control == 0x141u ? (lane | 7u) - (lane & 7u) + : (lane & ~15u) | ((lane - (control & 15u)) & 15u); + test.expected.push_back(100 + source); + } + } + AppendEnd(&code); + test.opcodes = {O::V_MOV_B32, O::V_ADD_NC_U32, O::BUFFER_STORE_DWORD, O::S_ENDPGM}; + test.compute_info.wave_size = wave_size; + test.compute_info.threads_num[0] = wave_size; + test.compute_info.threads_num[1] = test.compute_info.threads_num[2] = 1; + test.compute_info.thread_ids_num = 1; + test.has_compute_info = true; + return test; +} + TestCase VectorDppBankMaskPreservesDestination() { using O = ShaderOpcode; @@ -43058,6 +43087,8 @@ std::vector MakeCases() { AddCase(VectorDppRowXmask); cases.push_back(VectorDppRowShare(32)); cases.push_back(VectorDppRowShare(64)); + cases.push_back(VectorDppMirrorAndRotate(32)); + cases.push_back(VectorDppMirrorAndRotate(64)); cases.push_back(ComputeWave64Minimum(false)); cases.push_back(ComputeWave64Minimum(true)); AddCase(VectorDppBankMaskPreservesDestination); @@ -49148,6 +49179,8 @@ int main(int argc, char **argv) { RunCase(&vulkan, VectorDppRowXmask()); RunCase(&vulkan, VectorDppRowShare(32)); RunCase(&vulkan, VectorDppRowShare(64)); + RunCase(&vulkan, VectorDppMirrorAndRotate(32)); + RunCase(&vulkan, VectorDppMirrorAndRotate(64)); RunCase(&vulkan, VectorDppBankMaskPreservesDestination()); RunCase(&vulkan, VectorDppBoundsControlZeroPreservesDestination()); return 0;