From dacba43fcc69d14d01bb12550ebc6bbb12548405 Mon Sep 17 00:00:00 2001 From: nmzik Date: Mon, 28 Sep 2026 05:30:22 +0400 Subject: [PATCH] shader: handle scalar guards for image bounds --- .../recompiler/ir/passes/ResourceTracking.cpp | 32 ++++++++----------- tests/ResourceTrackingTests.cpp | 23 ++++++++++--- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp index cb976372f..d24ef382d 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp @@ -705,22 +705,10 @@ private: return false; } const auto* previous = block->ImmPredecessors()[0]; - const auto current_it = std::ranges::find(m_program.blocks, block); - const auto previous_it = std::ranges::find(m_program.blocks, previous); - if (current_it == m_program.blocks.end() || previous_it == m_program.blocks.end()) { - return false; - } - const auto& info = m_program.block_info[previous_it - m_program.blocks.begin()]; - const auto id = m_program.block_info[current_it - m_program.blocks.begin()].id; - const auto& term = info.terminator; - if (term.kind == CFG::TerminatorKind::ConditionalBranch && - (term.true_block == id) != (term.false_block == id)) { - bool positive = term.true_block == id; - const auto* test = info.condition.Resolve().TryInstruction(); - while (test != nullptr && test->GetOpcode() == ValueOpcode::LogicalNot) { - positive = !positive; - test = test->Arg(0).Resolve().TryInstruction(); - } + Value condition; + bool positive = false; + if (ConditionalEdge(previous, block, condition, positive)) { + const auto* test = condition.TryInstruction(); if (test != nullptr && test->NumArgs() == 2u && ((test->GetOpcode() == ValueOpcode::INotEqual32 && positive) || (test->GetOpcode() == ValueOpcode::IEqual32 && !positive))) { @@ -801,9 +789,17 @@ private: condition = info.condition; positive = term.true_block == id; while (const auto* inst = condition.Resolve().TryInstruction()) { - if (inst->GetOpcode() != ValueOpcode::LogicalNot) break; + if (inst->GetOpcode() == ValueOpcode::LogicalNot) { + positive = !positive; + } else if (inst->GetOpcode() == ValueOpcode::ConditionRef) { + // SCC is uniform across the guest wave; EXEC and VCC are lane predicates. + const auto kind = inst->Flags(); + if (kind != CFG::BranchCondition::SccZero && + kind != CFG::BranchCondition::SccNonZero) break; + } else { + break; + } condition = inst->Arg(0); - positive = !positive; } condition = condition.Resolve(); return true; diff --git a/tests/ResourceTrackingTests.cpp b/tests/ResourceTrackingTests.cpp index 811c44848..f3a79e368 100644 --- a/tests/ResourceTrackingTests.cpp +++ b/tests/ResourceTrackingTests.cpp @@ -561,9 +561,10 @@ void TestInvariantIndirectImageMaterialization() { void TestGuardedDirectImageTable() { namespace CFG = Libs::Graphics::ShaderRecompiler::CFG; - enum class Guard { Nonzero, Zero, Unrelated, Bypass }; + enum class Guard { Nonzero, SccNonZero, Plain, Zero, Unrelated, Bypass, ExecZero, VccZero }; const auto make_plan = [](Guard guard) { Fixture fixture(ShaderType::Pixel); + fixture.program.wave_size = 64u; auto *entry = fixture.block; auto *middle = fixture.AddBlock(); auto *before_sample = fixture.AddBlock(); @@ -580,8 +581,19 @@ void TestGuardedDirectImageTable() { const auto nonzero = fixture.Emit( ValueOpcode::INotEqual32, {Value(0u), guard == Guard::Unrelated ? fixture.UserData(2) : mask}); - fixture.program.block_info[0].condition = - fixture.Emit(ValueOpcode::LogicalNot, {nonzero}); + const auto kind = guard == Guard::ExecZero ? CFG::BranchCondition::ExecZero + : guard == Guard::VccZero ? CFG::BranchCondition::VccZero + : CFG::BranchCondition::SccZero; + auto condition = nonzero; + if (guard == Guard::SccNonZero) { + condition = fixture.Emit(ValueOpcode::ConditionRef, {condition}, + CFG::BranchCondition::SccNonZero); + } + condition = fixture.Emit(ValueOpcode::LogicalNot, {condition}); + if (guard != Guard::Plain && guard != Guard::SccNonZero) { + condition = fixture.Emit(ValueOpcode::ConditionRef, {condition}, kind); + } + fixture.program.block_info[0].condition = condition; fixture.program.block_info[0].terminator = { .kind = CFG::TerminatorKind::ConditionalBranch, .true_block = guard == Guard::Zero ? 1u : 4u, @@ -645,7 +657,10 @@ void TestGuardedDirectImageTable() { }; auto plan = make_plan(Guard::Nonzero); - for (const auto guard : {Guard::Zero, Guard::Unrelated, Guard::Bypass}) { + make_plan(Guard::SccNonZero); + make_plan(Guard::Plain); + for (const auto guard : {Guard::Zero, Guard::Unrelated, Guard::Bypass, + Guard::ExecZero, Guard::VccZero}) { CheckFatal([&] { make_plan(guard); }, "not a valid runtime value", "direct table accepted a selector without a dominating nonzero guard"); }