diff --git a/core/libs/prx/libSceAgcDriver/tests/MaskedSelect.cpp b/core/libs/prx/libSceAgcDriver/tests/MaskedSelect.cpp index 8e1cd2e3f..119f87925 100644 --- a/core/libs/prx/libSceAgcDriver/tests/MaskedSelect.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/MaskedSelect.cpp @@ -336,5 +336,43 @@ int main() { return sample(IrShaderStage::Pixel, RdnaImageSampleFlagLevelZero) && sample(IrShaderStage::Mesh, 0u); }); + const auto guarded = [](IrOpcode opcode, bool sameExec, bool asActive) { + Builder b; + auto& wide = b.mask(48u); + auto& exec = b.logicalAnd(wide, b.mask(16u)); + auto& old = b.lane(); + auto& written = b.select(exec, b.add(old, 1u), old); + IrValue& active = asActive ? b.emit(IrOpcode::INotEqual32, IrType::Bool, {&written, &b.constant(0u)}) : sameExec ? exec : wide; + switch (opcode) { + case IrOpcode::WriteSharedU32: + b.emit(opcode, IrType::Void, {&old, &written, &active}); + break; + case IrOpcode::LoadBufferU32: + b.keep(b.select(exec, b.emit(opcode, IrType::U32, {&b.emit(IrOpcode::GetBufferResource, IrType::BufferResource, {}), &written, &b.constant(0u), &b.constant(0u), &active}), old)); + break; + default: + b.emit(opcode, IrType::Void, {&b.emit(IrOpcode::CompositeConstructU32x4, IrType::U32x4, {&written, &old, &old, &old}), &active}); + break; + } + b.eliminate(); + return removed(written); + }; + + passed &= run("a store, load or export whose exec implies the write's exec drops the select", [&] { + return guarded(IrOpcode::WriteSharedU32, true, false) && guarded(IrOpcode::LoadBufferU32, true, false) && guarded(IrOpcode::SetAttribute, true, false); + }); + + passed &= run("a store, load or export under a wider exec keeps the select", [&] { + return !guarded(IrOpcode::WriteSharedU32, false, false) && !guarded(IrOpcode::LoadBufferU32, false, false) && !guarded(IrOpcode::SetAttribute, false, false); + }); + + passed &= run("a value used as a store's exec keeps the select", [&] { + return !guarded(IrOpcode::WriteSharedU32, true, true); + }); + + passed &= run("an atomic keeps the select", [&] { + return !guarded(IrOpcode::SharedAtomicIAdd32, true, false); + }); + return passed ? 0 : 1; } diff --git a/core/shader/recompiler/Optimization/src/MaskedSelectEliminator.cpp b/core/shader/recompiler/Optimization/src/MaskedSelectEliminator.cpp index 6ab6062c4..74f4cda6f 100644 --- a/core/shader/recompiler/Optimization/src/MaskedSelectEliminator.cpp +++ b/core/shader/recompiler/Optimization/src/MaskedSelectEliminator.cpp @@ -175,6 +175,39 @@ bool isLaneLocal(IrOpcode opcode) { } } +bool readsOnlyWhereActive(IrOpcode opcode) { + switch (opcode) { + case IrOpcode::LoadBufferU8: + case IrOpcode::LoadBufferU16: + case IrOpcode::LoadBufferU32: + case IrOpcode::LoadBufferU32x2: + case IrOpcode::LoadBufferU32x3: + case IrOpcode::LoadBufferU32x4: + case IrOpcode::StoreBufferU8: + case IrOpcode::StoreBufferU16: + case IrOpcode::StoreBufferU32: + case IrOpcode::StoreBufferU32x2: + case IrOpcode::StoreBufferU32x3: + case IrOpcode::StoreBufferU32x4: + case IrOpcode::LoadSharedU8: + case IrOpcode::LoadSharedU16: + case IrOpcode::LoadSharedU32: + case IrOpcode::LoadSharedU32x2: + case IrOpcode::LoadSharedU32x3: + case IrOpcode::LoadSharedU32x4: + case IrOpcode::WriteSharedU8: + case IrOpcode::WriteSharedU16: + case IrOpcode::WriteSharedU32: + case IrOpcode::WriteSharedU32x2: + case IrOpcode::WriteSharedU32x3: + case IrOpcode::WriteSharedU32x4: + case IrOpcode::SetAttribute: + return true; + default: + return false; + } +} + bool isExplicitLodSample(const IrProgram& program, const IrValue& value) { if (value.Opcode() != IrOpcode::ImageSampleRaw) return false; if (program.Resources().stage != IrShaderStage::Pixel) return true; @@ -316,6 +349,7 @@ bool unobservedWhereMasked(const IrProgram& program, const std::unordered_setParent() == nullptr || !once.contains(user->Parent())) return false; if (isSelect(user->Opcode()) && use.operand == 1u && implies(user->Argument(0), mask, waveSize)) continue; if (user->Opcode() == IrOpcode::LogicalAnd && use.operand < 2u && implies(user->Argument(1u - use.operand), mask, waveSize)) continue; + if (readsOnlyWhereActive(user->Opcode()) && use.operand + 1u < user->ArgumentCount() && implies(user->Argument(user->ArgumentCount() - 1u), mask, waveSize)) continue; if (!user->IsPhi() && !isLaneLocal(user->Opcode()) && !isExplicitLodSample(program, *user)) return false; if (visited.insert(user).second) { if (visited.size() > VisitLimit) return false;