From 19799b3079df56f83a128ec8fae628012078d84c Mon Sep 17 00:00:00 2001 From: nmzik Date: Tue, 13 Oct 2026 04:54:13 +0200 Subject: [PATCH] shader: simplify invariant execution masks in descriptor proofs --- .../recompiler/ir/passes/ResourceTracking.cpp | 4 +- tests/ResourceTrackingTests.cpp | 46 ++++++++++++++----- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp index 7998a6fe2..bbcd44e0d 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp @@ -1151,7 +1151,7 @@ private: } bool HasActiveLane(Value mask, const Block* use) const { - mask = mask.Resolve(); + mask = SimplifyGuard(mask); if (mask.IsImmediate()) return mask.GetType() == Type::U1 && mask.U1(); const auto incoming_is_nonempty = [&](const Block* from, const Block* to, Value incoming, const Block* header) { @@ -1186,7 +1186,7 @@ private: bool Implies(Value guard, Value required) const { guard = SimplifyGuard(guard); - required = required.Resolve(); + required = SimplifyGuard(required); if (required == Value(true) || EquivalentValue(m_program, guard, required)) return true; const auto* test = required.TryInstruction(); if (test != nullptr && test->GetOpcode() == ValueOpcode::INotEqual32) { diff --git a/tests/ResourceTrackingTests.cpp b/tests/ResourceTrackingTests.cpp index 210bdd677..68df81d5f 100644 --- a/tests/ResourceTrackingTests.cpp +++ b/tests/ResourceTrackingTests.cpp @@ -2562,10 +2562,12 @@ void TestCountedRowImageSelector() { KeyStride, CountImmediate, KeyImmediate, DifferentBackedgeBound, Masked, EmptyCountMask, VaryingRow, EmptyMask, UnprovenMask, WrongMask, AnyBound, MaskedStepTwo, Spilled, - MergedInitial, MissingBound, PreviousIteration }; + MergedInitial, MissingBound, PreviousIteration, + InvariantMask, PossiblyEmptyMask }; const auto make_plan = [](Variant variant = Variant::Valid, uint32_t capacity = 16u) { Fixture fixture; const bool masked = variant >= Variant::Masked; + const bool cyclic_mask = variant >= Variant::InvariantMask; const bool stable_index = variant == Variant::DifferentBackedgeBound; auto *entry = fixture.block; auto *header = fixture.AddBlock(); @@ -2606,17 +2608,35 @@ void TestCountedRowImageSelector() { "wave-uniform workgroup data escaped the host evaluation boundary"); if (variant == Variant::VaryingRow) row = fixture.Emit(ValueOpcode::IAdd32, {row, local}); - active = fixture.Emit(ValueOpcode::INotEqual32, {local, Value(0u)}); - entry->condition = fixture.Emit(ValueOpcode::ConditionRef, {active}, - CFG::BranchCondition::ExecNonZero); - if (variant == Variant::UnprovenMask) { - entry->condition = fixture.Emit(ValueOpcode::INotEqual32, - {fixture.UserData(13u), Value(0u)}); + if (cyclic_mask) { + branch(entry, preheader); + auto &mask = header->AppendNewInst(ValueOpcode::Phi, {}, uint64_t(Type::U1)); + auto *restore = fixture.AddBlock(); + body->AddBranch(restore); + body->condition = fixture.Emit(ValueOpcode::INotEqual32, + {fixture.UserData(13u), Value(0u)}, 0, body); + body->terminator = {.kind = CFG::TerminatorKind::ConditionalBranch, + .true_block = latch, .false_block = restore}; + branch(restore, latch); + auto &next = latch->AppendNewInst(ValueOpcode::Phi, {}, uint64_t(Type::U1)); + next.AddPhiOperand(body, Value(&mask)); + next.AddPhiOperand(restore, Value(variant == Variant::InvariantMask)); + mask.AddPhiOperand(preheader, Value(true)); + mask.AddPhiOperand(latch, Value(&next)); + active = Value(&mask); + } else { + active = fixture.Emit(ValueOpcode::INotEqual32, {local, Value(0u)}); + entry->condition = fixture.Emit(ValueOpcode::ConditionRef, {active}, + CFG::BranchCondition::ExecNonZero); + if (variant == Variant::UnprovenMask) { + entry->condition = fixture.Emit(ValueOpcode::INotEqual32, + {fixture.UserData(13u), Value(0u)}); + } + entry->AddBranch(prepare); + entry->AddBranch(exit); + entry->terminator = {.kind = CFG::TerminatorKind::ConditionalBranch, + .true_block = prepare, .false_block = exit}; } - entry->AddBranch(prepare); - entry->AddBranch(exit); - entry->terminator = {.kind = CFG::TerminatorKind::ConditionalBranch, - .true_block = prepare, .false_block = exit}; if (variant == Variant::EmptyMask) active = Value(false); } Value initial(0u); @@ -2741,7 +2761,7 @@ void TestCountedRowImageSelector() { const bool valid = variant == Variant::Valid || variant == Variant::Masked || variant == Variant::Spilled || variant == Variant::EmptyCountMask || variant == Variant::MergedInitial || variant == Variant::StepTwo || variant == Variant::MaskedStepTwo || - variant == Variant::SkippedIteration || stable_index; + variant == Variant::SkippedIteration || variant == Variant::InvariantMask || stable_index; Check((selector && selector->rows.has_value()) == valid, "counted-row selector lost its proof or accepted mismatched row/induction bounds"); if (valid) @@ -2761,6 +2781,8 @@ void TestCountedRowImageSelector() { make_plan(Variant::Spilled); make_plan(Variant::EmptyCountMask); make_plan(Variant::DifferentBackedgeBound); + make_plan(Variant::InvariantMask); + make_plan(Variant::PossiblyEmptyMask); for (const auto variant : {Variant::Signed, Variant::StepTwo, Variant::SkippedIteration, Variant::WrongRow, Variant::CountStride, Variant::KeyStride, Variant::CountImmediate, Variant::KeyImmediate, Variant::VaryingRow,