From 83f8c193c980de696f2b593ee7802be75e190003 Mon Sep 17 00:00:00 2001 From: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com> Date: Sat, 10 Oct 2026 22:26:51 +0200 Subject: [PATCH] fix(shader): treat an image read as a use that only reads where its exec is active An ImageRead carries a trailing exec argument like the buffer loads, but it was in neither the read-only-where-active list nor the lane-local list, so a masked select feeding its address was kept. Co-Authored-By: Claude Sonnet 5.5 --- core/libs/prx/libSceAgcDriver/tests/MaskedSelect.cpp | 11 +++++++++-- .../Optimization/src/MaskedSelectEliminator.cpp | 1 + 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/core/libs/prx/libSceAgcDriver/tests/MaskedSelect.cpp b/core/libs/prx/libSceAgcDriver/tests/MaskedSelect.cpp index 56dc21e87b..ecf3a581e4 100644 --- a/core/libs/prx/libSceAgcDriver/tests/MaskedSelect.cpp +++ b/core/libs/prx/libSceAgcDriver/tests/MaskedSelect.cpp @@ -636,6 +636,13 @@ int main() { 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; + case IrOpcode::ImageRead: { + auto& address = b.emit(IrOpcode::MakeImageAddress, IrType::ImageAddress, {&written, &old}); + auto& image = b.emit(IrOpcode::GetImageResource, IrType::ImageResource, {}); + auto& read = b.emit(opcode, IrType::U32x4, {&image, &address, &active}); + b.keep(b.select(exec, b.emit(IrOpcode::CompositeExtractU32x4, IrType::U32, {&read, &b.constant(0u)}), old)); + break; + } default: b.emit(opcode, IrType::Void, {&b.emit(IrOpcode::CompositeConstructU32x4, IrType::U32x4, {&written, &old, &old, &old}), &active}); break; @@ -645,11 +652,11 @@ int main() { }; 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); + return guarded(IrOpcode::WriteSharedU32, true, false) && guarded(IrOpcode::LoadBufferU32, true, false) && guarded(IrOpcode::ImageRead, 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); + return !guarded(IrOpcode::WriteSharedU32, false, false) && !guarded(IrOpcode::LoadBufferU32, false, false) && !guarded(IrOpcode::ImageRead, false, false) && !guarded(IrOpcode::SetAttribute, false, false); }); passed &= run("a value used as a store's exec keeps the select", [&] { diff --git a/core/shader/recompiler/Optimization/src/MaskedSelectEliminator.cpp b/core/shader/recompiler/Optimization/src/MaskedSelectEliminator.cpp index be902084dd..30331f633b 100644 --- a/core/shader/recompiler/Optimization/src/MaskedSelectEliminator.cpp +++ b/core/shader/recompiler/Optimization/src/MaskedSelectEliminator.cpp @@ -204,6 +204,7 @@ bool readsOnlyWhereActive(IrOpcode opcode) { case IrOpcode::WriteSharedU32x3: case IrOpcode::WriteSharedU32x4: case IrOpcode::SetAttribute: + case IrOpcode::ImageRead: return true; default: return false;