diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp index 25286e3dc..7aed831a8 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterProgram.cpp @@ -243,6 +243,12 @@ void Invoke(Return (*emit)(Context&, Args...), ValueEmitContext& ctx, const IR:: } void EmitDirectInstruction(ValueEmitContext& ctx, const IR::Inst& inst) { + if (ctx.half != 0 && (inst.GetOpcode() == IR::ValueOpcode::Ballot || + inst.GetOpcode() == IR::ValueOpcode::ReadFirstLane)) { + // Both operations already combine both emulated halves into one whole-wave result. + ctx.Define(inst, ctx.other_half->Result(inst)); + return; + } switch (inst.GetOpcode()) { #define VALUE_OPCODE(name, ...) \ case IR::ValueOpcode::name: return Invoke(Emit##name, ctx, inst); diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 40dea625b..b1b0d9ada 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -28392,6 +28392,27 @@ TestCase Wave64CrossHalfLaneAndLds() { return test; } +void CheckWave64WholeWaveResults() { + const auto test = Wave64CrossHalfLaneAndLds(); + const auto native = CompileCase(test, 64); + const auto emulated = CompileCase(test, 32); + spvtools::SpirvTools tools(SPV_ENV_VULKAN_1_2); + std::string native_text, emulated_text; + Require(test.name, "whole-wave disassembly", + tools.Disassemble(native.spirv, &native_text) && + tools.Disassemble(emulated.spirv, &emulated_text), + "failed to disassemble whole-wave operations"); + const auto ballots = CountText(native_text, "OpGroupNonUniformBallot "); + Require(test.name, "whole-wave ballot reuse", + ballots != 0 && + CountText(emulated_text, "OpGroupNonUniformBallot ") == ballots * 2, + "emulated halves repeated a whole-wave ballot"); + // READLANE still emits two shuffles per half; READFIRSTLANE needs one shared pair. + Require(test.name, "whole-wave first-lane reuse", + CountText(emulated_text, "OpGroupNonUniformShuffle ") == 6, + "emulated halves repeated the first-active-lane broadcast"); +} + TestCase Wave64RawMasksAndScalarBranch() { using O = ShaderOpcode; std::vector code; @@ -36487,6 +36508,7 @@ int main(int argc, char **argv) { return 0; } if (argc == 2 && std::strcmp(argv[1], "--wave64-only") == 0) { + CheckWave64WholeWaveResults(); VulkanHarness vulkan; RunCase(&vulkan, Wave32VccMasksPreserveOtherHalf()); RunCase(&vulkan, DsBpermuteWave64UsesIndependentHalves()); @@ -37011,6 +37033,7 @@ int main(int argc, char **argv) { CheckPixelParameterAliases(); CheckRectListShaders(); CheckIndirectImageKeySwitch(); + CheckWave64WholeWaveResults(); CheckPs5GameExampleImageClearRuntimeShape(); vulkan.CheckSchedulerTimeline(); vulkan.CheckHostImageAllocation();