shader: drop inactive pixel values under whole-quad masks

This commit is contained in:
nmzik
2026-09-06 14:38:32 +04:00
parent 66c1663495
commit 533176f9ff
2 changed files with 29 additions and 6 deletions
@@ -125,9 +125,17 @@ bool IsSelect(ValueOpcode opcode) {
bool Implies(Value condition, Value required, uint32_t depth = 0) { bool Implies(Value condition, Value required, uint32_t depth = 0) {
condition = condition.Resolve(); condition = condition.Resolve();
required = required.Resolve();
if (condition == required) return true; if (condition == required) return true;
if (depth >= 64u) return false;
const auto* target = required.TryInstruction();
if (target != nullptr && target->GetOpcode() == ValueOpcode::LogicalOr &&
(Implies(condition, target->Arg(0), depth + 1u) ||
Implies(condition, target->Arg(1), depth + 1u))) {
return true;
}
const auto* inst = condition.TryInstruction(); const auto* inst = condition.TryInstruction();
return depth < 64u && inst != nullptr && inst->GetOpcode() == ValueOpcode::LogicalAnd && return inst != nullptr && inst->GetOpcode() == ValueOpcode::LogicalAnd &&
(Implies(inst->Arg(0), required, depth + 1u) || (Implies(inst->Arg(0), required, depth + 1u) ||
Implies(inst->Arg(1), required, depth + 1u)); Implies(inst->Arg(1), required, depth + 1u));
} }
+20 -5
View File
@@ -6036,10 +6036,13 @@ void TestPixelAncillaryLayerInput() {
void TestMaskedValueDemand() { void TestMaskedValueDemand() {
using namespace ShaderRecompiler::IR; using namespace ShaderRecompiler::IR;
enum class Use { Guarded, Conjunction, TrueArm, Unguarded, OtherGuard, Predicate, enum class Use { Guarded, Conjunction, Wqm, WqmConjunction, WqmOtherGuard,
TrueArm, Unguarded, OtherGuard, Predicate,
ReadLane, ExplicitLod, ExplicitGradient, LevelZero, ImplicitLod, ReadLane, ExplicitLod, ExplicitGradient, LevelZero, ImplicitLod,
Mixed, Phi, Loop }; Mixed, Phi, Loop };
for (const auto use : {Use::Guarded, Use::Conjunction, Use::TrueArm, Use::Unguarded, for (const auto use : {Use::Guarded, Use::Conjunction, Use::Wqm,
Use::WqmConjunction, Use::WqmOtherGuard,
Use::TrueArm, Use::Unguarded,
Use::OtherGuard, Use::Predicate, Use::ReadLane, Use::OtherGuard, Use::Predicate, Use::ReadLane,
Use::ExplicitLod, Use::ExplicitGradient, Use::LevelZero, Use::ExplicitLod, Use::ExplicitGradient, Use::LevelZero,
Use::ImplicitLod, Use::Mixed, Use::Phi, Use::Loop}) { Use::ImplicitLod, Use::Mixed, Use::Phi, Use::Loop}) {
@@ -6061,8 +6064,17 @@ void TestMaskedValueDemand() {
{input, Value(0u)})); {input, Value(0u)}));
const auto other = Value(&entry.AppendNewInst(ValueOpcode::IEqual32, const auto other = Value(&entry.AppendNewInst(ValueOpcode::IEqual32,
{input, Value(2u)})); {input, Value(2u)}));
auto mask = predicate;
if (use == Use::Wqm || use == Use::WqmConjunction || use == Use::WqmOtherGuard) {
// WQM includes the original active lanes and additional helper lanes.
const auto helpers = Value(&entry.AppendNewInst(ValueOpcode::GetUserData,
{Value(static_cast<ScalarReg>(1))}));
const auto helper = Value(&entry.AppendNewInst(ValueOpcode::INotEqual32,
{helpers, Value(0u)}));
mask = Value(&entry.AppendNewInst(ValueOpcode::LogicalOr, {predicate, helper}));
}
auto &masked = entry.AppendNewInst(ValueOpcode::SelectU32, auto &masked = entry.AppendNewInst(ValueOpcode::SelectU32,
{predicate, Value(10u), Value(20u)}); {mask, Value(10u), Value(20u)});
Value value(&masked); Value value(&masked);
if (use == Use::Phi || use == Use::Loop) { if (use == Use::Phi || use == Use::Loop) {
auto &phi = join.AppendNewInst(ValueOpcode::Phi, {}, static_cast<uint64_t>(Type::U32)); auto &phi = join.AppendNewInst(ValueOpcode::Phi, {}, static_cast<uint64_t>(Type::U32));
@@ -6107,8 +6119,10 @@ void TestMaskedValueDemand() {
value = Value(&join.AppendNewInst(ValueOpcode::CompositeExtractU32x4, {value, Value(0u)})); value = Value(&join.AppendNewInst(ValueOpcode::CompositeExtractU32x4, {value, Value(0u)}));
} }
auto guard = predicate; auto guard = predicate;
if (use == Use::Conjunction) { if (use == Use::Conjunction || use == Use::WqmConjunction) {
guard = Value(&join.AppendNewInst(ValueOpcode::LogicalAnd, {predicate, other})); guard = Value(&join.AppendNewInst(ValueOpcode::LogicalAnd, {predicate, other}));
} else if (use == Use::WqmOtherGuard) {
guard = Value(&join.AppendNewInst(ValueOpcode::IEqual32, {input, Value(0u)}));
} else if (use == Use::OtherGuard) { } else if (use == Use::OtherGuard) {
guard = other; guard = other;
} else if (use == Use::Unguarded) { } else if (use == Use::Unguarded) {
@@ -6125,7 +6139,8 @@ void TestMaskedValueDemand() {
if (use == Use::Mixed) join.AppendNewInst(ValueOpcode::SetAttribute, {value, Value(true)}); if (use == Use::Mixed) join.AppendNewInst(ValueOpcode::SetAttribute, {value, Value(true)});
EliminateMaskedValues(program); EliminateMaskedValues(program);
const bool removed = Value(&masked).Resolve() == Value(10u); const bool removed = Value(&masked).Resolve() == Value(10u);
const bool safe = use == Use::Guarded || use == Use::Conjunction || use == Use::TrueArm || const bool safe = use == Use::Guarded || use == Use::Conjunction ||
use == Use::Wqm || use == Use::WqmConjunction || use == Use::TrueArm ||
use == Use::ExplicitLod || use == Use::ExplicitGradient || use == Use::ExplicitLod || use == Use::ExplicitGradient ||
use == Use::LevelZero || use == Use::Phi; use == Use::LevelZero || use == Use::Phi;
Check(removed == safe, "masked value demand crossed an unguarded or nonlocal use"); Check(removed == safe, "masked value demand crossed an unguarded or nonlocal use");