mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 13:56:59 +01:00
shader: remove masked-value pass and normalize ancillary fields
This commit is contained in:
@@ -651,9 +651,6 @@ TranslateResult TranslateProgram(std::span<const uint32_t> code, const CompileOp
|
|||||||
IR::ResolveControlFlowIdentities(ir);
|
IR::ResolveControlFlowIdentities(ir);
|
||||||
IR::RemoveIdentities(ir.blocks);
|
IR::RemoveIdentities(ir.blocks);
|
||||||
IR::EliminateDeadCode(ir.blocks);
|
IR::EliminateDeadCode(ir.blocks);
|
||||||
IR::EliminateMaskedValues(ir);
|
|
||||||
IR::RemoveIdentities(ir.blocks);
|
|
||||||
IR::EliminateDeadCode(ir.blocks);
|
|
||||||
const auto read_lane_stats = IR::EliminateReadLane(ir, ir.wave_size);
|
const auto read_lane_stats = IR::EliminateReadLane(ir, ir.wave_size);
|
||||||
if (read_lane_stats.rewritten_reads != 0) {
|
if (read_lane_stats.rewritten_reads != 0) {
|
||||||
LOGF("%s read-lane elimination: reads=%" PRIu32 "\n", GetDumpLabel(options),
|
LOGF("%s read-lane elimination: reads=%" PRIu32 "\n", GetDumpLabel(options),
|
||||||
|
|||||||
@@ -268,20 +268,13 @@ void Translator::S_MOV_B64(const Decoder::Instruction& inst) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Translator::S_WQM_B64(const Decoder::Instruction& inst) {
|
void Translator::S_WQM_B64(const Decoder::Instruction& inst) {
|
||||||
const auto source_mask = ReadMask(inst.src0);
|
|
||||||
const auto mask_valid = ReadMaskValid(inst.src0);
|
const auto mask_valid = ReadMaskValid(inst.src0);
|
||||||
const auto result =
|
const auto result =
|
||||||
IR::U64(ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src0, IR::Type::U64)}));
|
IR::U64(ir.Emit(IR::ValueOpcode::WqmU64, {ReadOperand(inst.src0, IR::Type::U64)}));
|
||||||
// WQM activates whole quads and cannot clear an already active lane.
|
|
||||||
const auto widened_mask = ir.LogicalOr(source_mask, ThreadBit(ExtractU64(result)));
|
|
||||||
WriteOperand(DestinationOperand(inst), result);
|
WriteOperand(DestinationOperand(inst), result);
|
||||||
if (inst.dst.kind == Decoder::OperandKind::ExecLo) {
|
if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
|
||||||
ir.SetExec(widened_mask);
|
|
||||||
} else if (inst.dst.kind == Decoder::OperandKind::VccLo) {
|
|
||||||
ir.SetVcc(widened_mask);
|
|
||||||
} else if (inst.dst.kind == Decoder::OperandKind::Sgpr) {
|
|
||||||
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
|
const auto dst = static_cast<IR::ScalarReg>(inst.dst.reg);
|
||||||
ir.SetThreadBitScalarReg(dst, widened_mask);
|
ir.SetThreadBitScalarReg(dst, ThreadBit(ExtractU64(result)));
|
||||||
ir.SetScalarMaskTag(dst, mask_valid);
|
ir.SetScalarMaskTag(dst, mask_valid);
|
||||||
}
|
}
|
||||||
ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
|
ir.SetScc(IR::U1(ir.Emit(IR::ValueOpcode::INotEqual64, {result, IR::Value(uint64_t {0})})));
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <bit>
|
#include <bit>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <unordered_map>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||||
namespace {
|
namespace {
|
||||||
@@ -118,95 +118,6 @@ bool FoldSelect(Inst& inst) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsSelect(ValueOpcode opcode) {
|
|
||||||
return opcode == ValueOpcode::SelectU1 || opcode == ValueOpcode::SelectU32 ||
|
|
||||||
opcode == ValueOpcode::SelectF32;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Implies(Value condition, Value required, uint32_t depth = 0) {
|
|
||||||
condition = condition.Resolve();
|
|
||||||
required = required.Resolve();
|
|
||||||
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();
|
|
||||||
return inst != nullptr && inst->GetOpcode() == ValueOpcode::LogicalAnd &&
|
|
||||||
(Implies(inst->Arg(0), required, depth + 1u) ||
|
|
||||||
Implies(inst->Arg(1), required, depth + 1u));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsAcyclic(const BlockList& blocks) {
|
|
||||||
std::unordered_map<const Block*, bool> completed;
|
|
||||||
const auto visit = [&](auto&& self, const Block* block) -> bool {
|
|
||||||
const auto [entry, added] = completed.emplace(block, false);
|
|
||||||
if (!added) return entry->second;
|
|
||||||
for (const auto* successor: block->ImmSuccessors()) {
|
|
||||||
if (!self(self, successor)) return false;
|
|
||||||
}
|
|
||||||
completed[block] = true;
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
for (const auto* block: blocks) {
|
|
||||||
if (!visit(visit, block)) return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MaskedUses {
|
|
||||||
public:
|
|
||||||
MaskedUses(const Program& program, Value condition)
|
|
||||||
: program(program), condition(condition.Resolve()) {}
|
|
||||||
|
|
||||||
bool OnlyWhenActive(const Inst& inst) {
|
|
||||||
const auto [entry, added] = completed.emplace(&inst, false);
|
|
||||||
if (!added) return entry->second;
|
|
||||||
for (const auto& use: inst.Uses()) {
|
|
||||||
const auto& user = *use.user;
|
|
||||||
const auto op = user.GetOpcode();
|
|
||||||
if (IsSelect(op)) {
|
|
||||||
if (use.operand == 0) return false;
|
|
||||||
if (use.operand == 1 && Implies(user.Arg(0), condition)) continue;
|
|
||||||
} else if (op == ValueOpcode::SetAttribute) {
|
|
||||||
if (use.operand == 0 && Implies(user.Arg(1), condition)) continue;
|
|
||||||
return false;
|
|
||||||
} else if (!IsLaneLocal(user, use.operand)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!OnlyWhenActive(user)) return false;
|
|
||||||
}
|
|
||||||
completed[&inst] = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool IsLaneLocal(const Inst& inst, size_t operand) const {
|
|
||||||
const auto op = inst.GetOpcode();
|
|
||||||
// These opcode-table groups contain only construction, conversion and arithmetic.
|
|
||||||
if ((op >= ValueOpcode::BitCastU16F16 && op <= ValueOpcode::BitFieldSExtract) ||
|
|
||||||
(op >= ValueOpcode::WqmU64 && op <= ValueOpcode::FPFract32) ||
|
|
||||||
op == ValueOpcode::Identity || op == ValueOpcode::Phi ||
|
|
||||||
op == ValueOpcode::MakeImageAddress) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (op != ValueOpcode::ImageSampleRaw || operand != 2) return false;
|
|
||||||
// Explicit LOD or gradients keep coordinates local; implicit derivatives read neighbors.
|
|
||||||
const auto index = inst.Flags<MemoryFlags>().index;
|
|
||||||
return index < program.memory_info.size() &&
|
|
||||||
(program.memory_info[index].image_sample_flags &
|
|
||||||
(Decoder::ImageSampleFlagLod | Decoder::ImageSampleFlagLevelZero |
|
|
||||||
Decoder::ImageSampleFlagDerivative)) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Program& program;
|
|
||||||
Value condition;
|
|
||||||
std::unordered_map<const Inst*, bool> completed;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool FoldPhi(Inst& inst) {
|
bool FoldPhi(Inst& inst) {
|
||||||
Value same;
|
Value same;
|
||||||
for (size_t index = 0; index < inst.NumArgs(); index++) {
|
for (size_t index = 0; index < inst.NumArgs(); index++) {
|
||||||
@@ -283,7 +194,8 @@ bool FoldCompositeExtract(Inst& inst, ValueOpcode construct, size_t components)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FoldInstruction(Block& block, Block::iterator instruction) {
|
void FoldInstruction(Block& block, Block::iterator instruction,
|
||||||
|
std::unordered_set<Inst*>& lowered_ancillary) {
|
||||||
auto& inst = *instruction;
|
auto& inst = *instruction;
|
||||||
switch (inst.GetOpcode()) {
|
switch (inst.GetOpcode()) {
|
||||||
case ValueOpcode::Phi: FoldPhi(inst); return;
|
case ValueOpcode::Phi: FoldPhi(inst); return;
|
||||||
@@ -315,7 +227,7 @@ void FoldInstruction(Block& block, Block::iterator instruction) {
|
|||||||
const auto value = Arg(inst, 0);
|
const auto value = Arg(inst, 0);
|
||||||
const auto offset = Arg(inst, 1);
|
const auto offset = Arg(inst, 1);
|
||||||
const auto count = Arg(inst, 2);
|
const auto count = Arg(inst, 2);
|
||||||
const auto* source = value.TryInstruction();
|
auto* source = value.TryInstruction();
|
||||||
if (source != nullptr && source->GetOpcode() == ValueOpcode::GetBuiltin &&
|
if (source != nullptr && source->GetOpcode() == ValueOpcode::GetBuiltin &&
|
||||||
source->Arg(0) == Value(static_cast<uint32_t>(StageInputKind::PackedAncillary)) &&
|
source->Arg(0) == Value(static_cast<uint32_t>(StageInputKind::PackedAncillary)) &&
|
||||||
IsImmediate(offset, Type::U32) && IsImmediate(count, Type::U32) && count.U32() != 0u) {
|
IsImmediate(offset, Type::U32) && IsImmediate(count, Type::U32) && count.U32() != 0u) {
|
||||||
@@ -333,6 +245,7 @@ void FoldInstruction(Block& block, Block::iterator instruction) {
|
|||||||
{Value(static_cast<uint32_t>(field.kind)), Value(0u)});
|
{Value(static_cast<uint32_t>(field.kind)), Value(0u)});
|
||||||
inst.SetArg(0, Value(&*input));
|
inst.SetArg(0, Value(&*input));
|
||||||
inst.SetArg(1, Value(offset.U32() - field.start));
|
inst.SetArg(1, Value(offset.U32() - field.start));
|
||||||
|
lowered_ancillary.insert(source);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -697,23 +610,25 @@ void FoldInstruction(Block& block, Block::iterator instruction) {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ConstantPropagationPass(const BlockList& blocks) {
|
void ConstantPropagationPass(const BlockList& blocks) {
|
||||||
|
std::unordered_set<Inst*> lowered_ancillary;
|
||||||
for (auto* block: blocks) {
|
for (auto* block: blocks) {
|
||||||
for (auto inst = block->begin(); inst != block->end(); ++inst) {
|
for (auto inst = block->begin(); inst != block->end(); ++inst) {
|
||||||
FoldInstruction(*block, inst);
|
FoldInstruction(*block, inst, lowered_ancillary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Normalize retained PHI/select values only after every supported field read has
|
||||||
|
// been lowered; direct raw consumers remain unsupported.
|
||||||
|
for (auto* source: lowered_ancillary) {
|
||||||
|
const bool retained_only = std::ranges::all_of(source->Uses(), [](const Use& use) {
|
||||||
|
const auto& user = *use.user;
|
||||||
|
if (!user.HasUses() && !user.MayHaveSideEffects()) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return user.GetOpcode() == ValueOpcode::Phi ||
|
||||||
void EliminateMaskedValues(Program& program) {
|
(user.GetOpcode() == ValueOpcode::SelectU32 && use.operand == 2u);
|
||||||
// A loop-carried value may have been produced under a previous iteration's predicate.
|
});
|
||||||
if (!IsAcyclic(program.blocks)) return;
|
if (retained_only) {
|
||||||
for (auto block = program.blocks.rbegin(); block != program.blocks.rend(); ++block) {
|
Replace(*source, Value(0u));
|
||||||
auto& instructions = (*block)->Instructions();
|
|
||||||
for (auto inst = instructions.rbegin(); inst != instructions.rend(); ++inst) {
|
|
||||||
if (IsSelect(inst->GetOpcode()) &&
|
|
||||||
MaskedUses(program, inst->Arg(0)).OnlyWhenActive(*inst)) {
|
|
||||||
Replace(*inst, inst->Arg(1));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,6 @@
|
|||||||
|
|
||||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||||
|
|
||||||
struct Program;
|
|
||||||
|
|
||||||
void ConstantPropagationPass(const BlockList& blocks);
|
void ConstantPropagationPass(const BlockList& blocks);
|
||||||
void EliminateMaskedValues(Program& program);
|
|
||||||
|
|
||||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||||
|
|||||||
+29
-114
@@ -6007,6 +6007,30 @@ void TestPixelAncillaryLayerInput() {
|
|||||||
CheckSpirvBinaryValidates(wqm_result.spirv);
|
CheckSpirvBinaryValidates(wqm_result.spirv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uint32_t loop[] = {
|
||||||
|
EncodeSop1(0x04, 8, 126), // Save the original EXEC mask.
|
||||||
|
EncodeSop1(0x0a, 126, 8), // Widen for helper lanes.
|
||||||
|
EncodeSop1(0x24, 12, 0), // Narrow EXEC while saving the widened mask.
|
||||||
|
EncodeSop1(0x04, 10, 126), // Save the final export mask.
|
||||||
|
EncodeSopp(0x08, 6), // Skip the body when EXEC is zero.
|
||||||
|
EncodeVop3Word0(0x148, 5), EncodeVop3Word1(5 + 256, 128 + 16, 128 + 11),
|
||||||
|
EncodeSMovB32(2, 128),
|
||||||
|
EncodeSop2(0x00, 2, 2, 129),
|
||||||
|
EncodeSopc(0x0a, 2, 131),
|
||||||
|
EncodeSopp(0x05, 0xfffdu), // Repeat the scalar loop three times.
|
||||||
|
EncodeSop1(0x04, 126, 12), // Pack under the widened mask.
|
||||||
|
EncodeVop2(0x2f, 6, 5 + 256, 5),
|
||||||
|
EncodeSop1(0x04, 126, 10), // Export only lanes that entered the body.
|
||||||
|
EncodeExp0(0x00, 0x1, true, false, true), EncodeExp1(6, 0, 0, 0), EncodeSopp(0x01),
|
||||||
|
};
|
||||||
|
auto loop_result = RecompileForTest(loop, options);
|
||||||
|
Check(ProgramHasInput(loop_result.program, StageInputKind::Layer) &&
|
||||||
|
!ProgramHasInput(loop_result.program, StageInputKind::PackedAncillary),
|
||||||
|
"loop and skipped body retained the symbolic ancillary input after field lowering");
|
||||||
|
Check(SpirvContainsOpcode(loop_result.spirv, 246u),
|
||||||
|
"ancillary regression did not retain its scalar loop");
|
||||||
|
CheckSpirvBinaryValidates(loop_result.spirv);
|
||||||
|
|
||||||
const uint32_t unused[] = {EncodeSopp(0x01)};
|
const uint32_t unused[] = {EncodeSopp(0x01)};
|
||||||
auto unused_result = RecompileForTest(unused, options);
|
auto unused_result = RecompileForTest(unused, options);
|
||||||
Check(!ProgramHasInput(unused_result.program, StageInputKind::Layer) &&
|
Check(!ProgramHasInput(unused_result.program, StageInputKind::Layer) &&
|
||||||
@@ -6031,122 +6055,14 @@ void TestPixelAncillaryLayerInput() {
|
|||||||
shader[1] = EncodeVop3Word1(5 + 256, 128 + 12, 128 + 4);
|
shader[1] = EncodeVop3Word1(5 + 256, 128 + 12, 128 + 4);
|
||||||
ExpectFatal([&] { (void)RecompileForTest(shader, options); },
|
ExpectFatal([&] { (void)RecompileForTest(shader, options); },
|
||||||
"unsupported live ancillary field was silently replaced");
|
"unsupported live ancillary field was silently replaced");
|
||||||
|
shader[1] = EncodeVop3Word1(5 + 256, 128 + 16, 128 + 11);
|
||||||
|
shader[4] = EncodeExp0(0x00, 0x7);
|
||||||
|
shader[5] = EncodeExp1(6, 7, 5, 0);
|
||||||
|
ExpectFatal([&] { (void)RecompileForTest(shader, options); },
|
||||||
|
"direct raw ancillary export was silently replaced after field lowering");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestMaskedValueDemand() {
|
|
||||||
using namespace ShaderRecompiler::IR;
|
|
||||||
enum class Use { Guarded, Conjunction, Wqm, WqmConjunction, WqmOtherGuard,
|
|
||||||
TrueArm, Unguarded, OtherGuard, Predicate,
|
|
||||||
ReadLane, ExplicitLod, ExplicitGradient, LevelZero, ImplicitLod,
|
|
||||||
Mixed, Phi, Loop };
|
|
||||||
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::ExplicitLod, Use::ExplicitGradient, Use::LevelZero,
|
|
||||||
Use::ImplicitLod, Use::Mixed, Use::Phi, Use::Loop}) {
|
|
||||||
Program program;
|
|
||||||
for (uint32_t i = 0; i < 4; ++i) {
|
|
||||||
program.block_storage.push_back(std::make_unique<Block>());
|
|
||||||
program.blocks.push_back(program.block_storage.back().get());
|
|
||||||
}
|
|
||||||
auto &entry = *program.blocks[0];
|
|
||||||
auto &join = *program.blocks[3];
|
|
||||||
entry.AddBranch(program.blocks[1]);
|
|
||||||
entry.AddBranch(program.blocks[2]);
|
|
||||||
program.blocks[1]->AddBranch(&join);
|
|
||||||
program.blocks[2]->AddBranch(&join);
|
|
||||||
if (use == Use::Loop) join.AddBranch(&entry);
|
|
||||||
const auto input = Value(&entry.AppendNewInst(ValueOpcode::GetUserData,
|
|
||||||
{Value(static_cast<ScalarReg>(0))}));
|
|
||||||
const auto predicate = Value(&entry.AppendNewInst(ValueOpcode::INotEqual32,
|
|
||||||
{input, Value(0u)}));
|
|
||||||
const auto other = Value(&entry.AppendNewInst(ValueOpcode::IEqual32,
|
|
||||||
{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,
|
|
||||||
{mask, Value(10u), Value(20u)});
|
|
||||||
Value value(&masked);
|
|
||||||
if (use == Use::Phi || use == Use::Loop) {
|
|
||||||
auto &phi = join.AppendNewInst(ValueOpcode::Phi, {}, static_cast<uint64_t>(Type::U32));
|
|
||||||
phi.AddPhiOperand(program.blocks[1], value);
|
|
||||||
phi.AddPhiOperand(program.blocks[2], Value(0u));
|
|
||||||
value = Value(&phi);
|
|
||||||
}
|
|
||||||
value = Value(&join.AppendNewInst(ValueOpcode::BitCastF32U32, {value}));
|
|
||||||
value = Value(&join.AppendNewInst(ValueOpcode::FPMul32, {value, Value::F32(2.f)}));
|
|
||||||
value = Value(&join.AppendNewInst(ValueOpcode::BitCastU32F32, {value}));
|
|
||||||
if (use == Use::ReadLane) {
|
|
||||||
value = Value(&join.AppendNewInst(ValueOpcode::ReadLane, {value, Value(0u)}));
|
|
||||||
}
|
|
||||||
if (use == Use::ExplicitLod || use == Use::ExplicitGradient ||
|
|
||||||
use == Use::LevelZero || use == Use::ImplicitLod) {
|
|
||||||
const auto zero = Value(0u);
|
|
||||||
auto &address = join.AppendNewInst(ValueOpcode::MakeImageAddress,
|
|
||||||
{value, zero, zero, zero, zero, zero, zero, zero, zero, zero, zero, zero, zero});
|
|
||||||
auto &image = entry.AppendNewInst(ValueOpcode::GetImageResource,
|
|
||||||
{zero, zero, zero, zero, zero, zero, zero, zero});
|
|
||||||
auto &sampler = entry.AppendNewInst(ValueOpcode::GetSamplerResource,
|
|
||||||
{zero, zero, zero, zero});
|
|
||||||
auto &memory = program.memory_info.emplace_back();
|
|
||||||
switch (use) {
|
|
||||||
case Use::ExplicitLod:
|
|
||||||
memory.image_sample_flags =
|
|
||||||
ShaderRecompiler::Decoder::ImageSampleFlagLod;
|
|
||||||
break;
|
|
||||||
case Use::ExplicitGradient:
|
|
||||||
memory.image_sample_flags =
|
|
||||||
ShaderRecompiler::Decoder::ImageSampleFlagDerivative;
|
|
||||||
break;
|
|
||||||
case Use::LevelZero:
|
|
||||||
memory.image_sample_flags =
|
|
||||||
ShaderRecompiler::Decoder::ImageSampleFlagLevelZero;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
value = Value(&join.AppendNewInst(ValueOpcode::ImageSampleRaw,
|
|
||||||
{Value(&image), Value(&sampler), Value(&address)}));
|
|
||||||
value = Value(&join.AppendNewInst(ValueOpcode::CompositeExtractU32x4, {value, Value(0u)}));
|
|
||||||
}
|
|
||||||
auto guard = predicate;
|
|
||||||
if (use == Use::Conjunction || use == Use::WqmConjunction) {
|
|
||||||
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) {
|
|
||||||
guard = other;
|
|
||||||
} else if (use == Use::Unguarded) {
|
|
||||||
guard = Value(true);
|
|
||||||
} else if (use == Use::Predicate) {
|
|
||||||
guard = Value(&join.AppendNewInst(ValueOpcode::INotEqual32, {value, Value(0u)}));
|
|
||||||
} else if (use == Use::TrueArm) {
|
|
||||||
value = Value(&join.AppendNewInst(ValueOpcode::SelectU32, {predicate, value, Value(0u)}));
|
|
||||||
guard = Value(true);
|
|
||||||
}
|
|
||||||
value = Value(&join.AppendNewInst(ValueOpcode::CompositeConstructU32x4,
|
|
||||||
{value, Value(0u), Value(0u), Value(0u)}));
|
|
||||||
join.AppendNewInst(ValueOpcode::SetAttribute, {value, guard});
|
|
||||||
if (use == Use::Mixed) join.AppendNewInst(ValueOpcode::SetAttribute, {value, Value(true)});
|
|
||||||
EliminateMaskedValues(program);
|
|
||||||
const bool removed = Value(&masked).Resolve() == Value(10u);
|
|
||||||
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::LevelZero || use == Use::Phi;
|
|
||||||
Check(removed == safe, "masked value demand crossed an unguarded or nonlocal use");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestGraphicsCreateInterpolantMapping() {
|
void TestGraphicsCreateInterpolantMapping() {
|
||||||
ShaderRegister regs[32] = {};
|
ShaderRegister regs[32] = {};
|
||||||
|
|
||||||
@@ -12787,7 +12703,6 @@ int main() {
|
|||||||
TestNewShaderRecompilerRejectsDppOn64BitCompares();
|
TestNewShaderRecompilerRejectsDppOn64BitCompares();
|
||||||
TestPsInputCountRegisterDecode();
|
TestPsInputCountRegisterDecode();
|
||||||
TestPixelAncillaryLayerInput();
|
TestPixelAncillaryLayerInput();
|
||||||
TestMaskedValueDemand();
|
|
||||||
TestNewShaderRecompilerUnbasedFlatUsesBda();
|
TestNewShaderRecompilerUnbasedFlatUsesBda();
|
||||||
TestNewShaderRecompilerFlatUserPointerUsesDma();
|
TestNewShaderRecompilerFlatUserPointerUsesDma();
|
||||||
TestNewShaderRecompilerFlatAddressDomainsUseDma();
|
TestNewShaderRecompilerFlatAddressDomainsUseDma();
|
||||||
|
|||||||
Reference in New Issue
Block a user