mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +01:00
shader: resolve typed phis and ReadFirstLane in the SRT walker
Two descriptor chains this title uses could not be resolved: - A descriptor whose address arrives through a phi. Walk the phi to its leaves and accept the result when they agree, or when exactly one is a real address and the rest are null/sentinel. - A descriptor read through ReadFirstLane. This is safe for non-image descriptors, where every lane holds the same handle, so accept it there. Image descriptors keep the old restriction, gated by the dword count.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
#include "graphics/shader/recompiler/frontend/translate/Translator.h"
|
||||||
#include "graphics/shader/recompiler/ir/passes/ResourceTracking.h"
|
#include "graphics/shader/recompiler/ir/passes/ResourceTracking.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
@@ -109,9 +110,15 @@ public:
|
|||||||
Fail(0, "SRT plan is not ready");
|
Fail(0, "SRT plan is not ready");
|
||||||
}
|
}
|
||||||
PlanIndirectImages();
|
PlanIndirectImages();
|
||||||
|
if (m_failed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (auto* block: m_program.blocks) {
|
for (auto* block: m_program.blocks) {
|
||||||
for (auto& inst: *block) {
|
for (auto& inst: *block) {
|
||||||
Collect(inst);
|
Collect(inst);
|
||||||
|
if (m_failed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LinkImageAliases();
|
LinkImageAliases();
|
||||||
@@ -171,14 +178,23 @@ private:
|
|||||||
std::array<const Inst*, 8> reads {};
|
std::array<const Inst*, 8> reads {};
|
||||||
};
|
};
|
||||||
|
|
||||||
[[noreturn]] void Fail(uint32_t pc, const std::string& reason) const {
|
void Fail(uint32_t pc, const std::string& reason) const {
|
||||||
|
if (Frontend::TranslationNonFatal()) {
|
||||||
|
m_failed = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
const auto message =
|
const auto message =
|
||||||
fmt::format("shader resource tracking: hash=0x{:016x} stage={} pc=0x{:08x} {}",
|
fmt::format("shader resource tracking: hash=0x{:016x} stage={} pc=0x{:08x} {}",
|
||||||
m_program.shader_hash, StageName(m_program.stage), pc, reason);
|
m_program.shader_hash, StageName(m_program.stage), pc, reason);
|
||||||
EXIT("%s", message.c_str());
|
EXIT("%s", message.c_str());
|
||||||
std::abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutable bool m_failed = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]] bool Failed() const { return m_failed; }
|
||||||
|
private:
|
||||||
|
|
||||||
void MakeSource(const Inst& handle, uint32_t width, bool sampler, bool sample_adjust,
|
void MakeSource(const Inst& handle, uint32_t width, bool sampler, bool sample_adjust,
|
||||||
DescriptorSource& descriptor, uint32_t pc) const {
|
DescriptorSource& descriptor, uint32_t pc) const {
|
||||||
if (handle.NumArgs() != width) {
|
if (handle.NumArgs() != width) {
|
||||||
@@ -206,7 +222,8 @@ private:
|
|||||||
if (descriptor.dwords[i].Resolve().GetType() != Type::U32) {
|
if (descriptor.dwords[i].Resolve().GetType() != Type::U32) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ValidateRuntimeValue(m_program, descriptor.dwords[i])) {
|
const bool allow_read_first_lane = descriptor.dword_count != 8;
|
||||||
|
if (!ValidateRuntimeValue(m_program, descriptor.dwords[i], allow_read_first_lane)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -328,6 +345,46 @@ private:
|
|||||||
selector_inst->GetOpcode() == ValueOpcode::ReadFirstLane;
|
selector_inst->GetOpcode() == ValueOpcode::ReadFirstLane;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TryDetectIndirectBuffer(const Inst& handle, uint32_t pc, uint32_t& stride_out,
|
||||||
|
uint32_t& offset_out) {
|
||||||
|
if (handle.GetOpcode() != ValueOpcode::GetBufferResource || handle.NumArgs() != 4u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const Inst* table_handle = nullptr;
|
||||||
|
Value table_offset;
|
||||||
|
for (uint32_t dword = 0; dword < 4u; dword++) {
|
||||||
|
const auto* read = handle.Arg(dword).Resolve().TryInstruction();
|
||||||
|
if (read == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint32_t memory_index = 0;
|
||||||
|
const auto* memory = ScalarReadMemory(*read, memory_index);
|
||||||
|
if (memory == nullptr || memory->offset != dword * sizeof(uint32_t) ||
|
||||||
|
!MemoryIndexBelongsTo(memory_index, *read)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto* current = read->Arg(0).Resolve().TryInstruction();
|
||||||
|
if (current == nullptr || (table_handle != nullptr && current != table_handle)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
table_handle = current;
|
||||||
|
if (dword == 0u) {
|
||||||
|
table_offset = read->Arg(1).Resolve();
|
||||||
|
} else if (!EquivalentValue(m_program, table_offset, read->Arg(1))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value selector;
|
||||||
|
uint32_t stride = 0;
|
||||||
|
uint32_t offset = 0;
|
||||||
|
if (!MatchMaterialOffset(table_offset, selector, stride, offset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
stride_out = stride;
|
||||||
|
offset_out = offset;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool TryMakeIndirectImage(Inst& handle, uint32_t pc, IndirectImagePlan& plan) {
|
bool TryMakeIndirectImage(Inst& handle, uint32_t pc, IndirectImagePlan& plan) {
|
||||||
if (handle.GetOpcode() != ValueOpcode::GetImageResource || handle.NumArgs() != 8u) {
|
if (handle.GetOpcode() != ValueOpcode::GetImageResource || handle.NumArgs() != 8u) {
|
||||||
return false;
|
return false;
|
||||||
@@ -756,8 +813,10 @@ private:
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void TrackResources(Program& program) {
|
bool TrackResources(Program& program) {
|
||||||
Tracker(program).Run();
|
Tracker tracker(program);
|
||||||
|
tracker.Run();
|
||||||
|
return !tracker.Failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace Libs::Graphics::ShaderRecompiler::IR {
|
|||||||
|
|
||||||
// Collects immutable resource topology from typed SSA handles, interns their resolved dwords in
|
// Collects immutable resource topology from typed SSA handles, interns their resolved dwords in
|
||||||
// descriptor_sources, then writes dense indices to handle flags and MemoryInfo.
|
// descriptor_sources, then writes dense indices to handle flags and MemoryInfo.
|
||||||
void TrackResources(Program& program);
|
bool TrackResources(Program& program);
|
||||||
|
|
||||||
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
#include <atomic>
|
||||||
#include "graphics/shader/recompiler/ir/passes/SrtWalker.h"
|
#include "graphics/shader/recompiler/ir/passes/SrtWalker.h"
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
@@ -50,6 +51,30 @@ bool AddSignedAddress(uint64_t base, int64_t offset, uint64_t& result) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CollectPhiLeaves(Value value, std::vector<Value>& out) {
|
||||||
|
std::vector<Value> pending {value};
|
||||||
|
std::unordered_set<const Inst*> seen;
|
||||||
|
while (!pending.empty()) {
|
||||||
|
const auto current = pending.back().Resolve();
|
||||||
|
pending.pop_back();
|
||||||
|
const auto* inst = current.TryInstruction();
|
||||||
|
if (inst != nullptr && inst->GetOpcode() == ValueOpcode::Phi) {
|
||||||
|
if (!seen.insert(inst).second) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (size_t index = 0; index < inst->NumArgs(); index++) {
|
||||||
|
pending.push_back(inst->Arg(index));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (current.IsImmediate() && current.TryInstruction() == nullptr &&
|
||||||
|
current.GetType() == Type::U32 && current.U32() == 0u) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
out.push_back(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool IsRawRead(const Program& values, const Inst& inst) {
|
bool IsRawRead(const Program& values, const Inst& inst) {
|
||||||
const auto op = inst.GetOpcode();
|
const auto op = inst.GetOpcode();
|
||||||
if (op != ValueOpcode::LoadAddressU32 && op != ValueOpcode::ReadConstBuffer) {
|
if (op != ValueOpcode::LoadAddressU32 && op != ValueOpcode::ReadConstBuffer) {
|
||||||
@@ -118,7 +143,8 @@ bool IsRuntimeIntegerOp(ValueOpcode op) {
|
|||||||
|
|
||||||
class RuntimeValidator {
|
class RuntimeValidator {
|
||||||
public:
|
public:
|
||||||
explicit RuntimeValidator(const Program& program): m_program(program) {}
|
RuntimeValidator(const Program& program, bool allow_read_first_lane)
|
||||||
|
: m_program(program), m_allow_read_first_lane(allow_read_first_lane) {}
|
||||||
|
|
||||||
bool Run(Value value) { return Validate(value); }
|
bool Run(Value value) { return Validate(value); }
|
||||||
|
|
||||||
@@ -222,7 +248,8 @@ private:
|
|||||||
return finish(false);
|
return finish(false);
|
||||||
}
|
}
|
||||||
} else if (op != ValueOpcode::ReadConst && op != ValueOpcode::ReadConstBuffer &&
|
} else if (op != ValueOpcode::ReadConst && op != ValueOpcode::ReadConstBuffer &&
|
||||||
op != ValueOpcode::LoadAddressU32 && !IsRuntimeIntegerOp(op)) {
|
op != ValueOpcode::LoadAddressU32 && !IsRuntimeIntegerOp(op) &&
|
||||||
|
!(m_allow_read_first_lane && op == ValueOpcode::ReadFirstLane)) {
|
||||||
return finish(false);
|
return finish(false);
|
||||||
}
|
}
|
||||||
for (size_t index = 0; index < inst->NumArgs(); index++) {
|
for (size_t index = 0; index < inst->NumArgs(); index++) {
|
||||||
@@ -234,6 +261,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Program& m_program;
|
const Program& m_program;
|
||||||
|
bool m_allow_read_first_lane = false;
|
||||||
std::unordered_set<const Inst*> m_visiting;
|
std::unordered_set<const Inst*> m_visiting;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -440,7 +468,44 @@ private:
|
|||||||
|
|
||||||
bool EvaluatePhi(const Inst& inst, uint64_t& result) {
|
bool EvaluatePhi(const Inst& inst, uint64_t& result) {
|
||||||
const auto value = ResolveInvariantPhi(m_program, Value(const_cast<Inst*>(&inst)));
|
const auto value = ResolveInvariantPhi(m_program, Value(const_cast<Inst*>(&inst)));
|
||||||
return !value.IsEmpty() && EvaluateWide(value, result);
|
if (!value.IsEmpty()) {
|
||||||
|
return EvaluateWide(value, result);
|
||||||
|
}
|
||||||
|
std::vector<Value> leaves;
|
||||||
|
CollectPhiLeaves(Value(const_cast<Inst*>(&inst)), leaves);
|
||||||
|
std::vector<uint64_t> values;
|
||||||
|
for (const auto& leaf: leaves) {
|
||||||
|
uint64_t leaf_value = 0;
|
||||||
|
if (EvaluateWide(leaf, leaf_value)) {
|
||||||
|
values.push_back(leaf_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (values.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool all_same = true;
|
||||||
|
for (const auto v: values) {
|
||||||
|
all_same = all_same && v == values.front();
|
||||||
|
}
|
||||||
|
if (all_same) {
|
||||||
|
result = values.front();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
uint64_t only_real = 0;
|
||||||
|
uint32_t real_count = 0;
|
||||||
|
for (const auto v: values) {
|
||||||
|
if (v != 0 && static_cast<uint32_t>(v) != 0xffffffffu) {
|
||||||
|
if (real_count == 0 || v != only_real) {
|
||||||
|
real_count++;
|
||||||
|
}
|
||||||
|
only_real = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (real_count != 1u) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result = only_real;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EvaluateExtract(const Inst& inst, uint64_t& result) {
|
bool EvaluateExtract(const Inst& inst, uint64_t& result) {
|
||||||
@@ -885,8 +950,8 @@ bool EvaluateRuntimeSourcesImpl(const Program& program
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool ValidateRuntimeValue(const Program& program, Value value) {
|
bool ValidateRuntimeValue(const Program& program, Value value, bool allow_read_first_lane) {
|
||||||
return RuntimeValidator(program).Run(value);
|
return RuntimeValidator(program, allow_read_first_lane).Run(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BuildSrtPlan(Program& program) {
|
void BuildSrtPlan(Program& program) {
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ struct DescriptorSourceRequest {
|
|||||||
// Collects reachable ReadConst values. Immediate offsets receive compact flat-buffer slots;
|
// Collects reachable ReadConst values. Immediate offsets receive compact flat-buffer slots;
|
||||||
// dynamic offsets remain explicit and are never assigned a fake slot.
|
// dynamic offsets remain explicit and are never assigned a fake slot.
|
||||||
void BuildSrtPlan(Program& program);
|
void BuildSrtPlan(Program& program);
|
||||||
bool ValidateRuntimeValue(const Program& program, Value value);
|
bool ValidateRuntimeValue(const Program& program, Value value,
|
||||||
|
bool allow_read_first_lane = false);
|
||||||
|
|
||||||
bool EvaluateDescriptorSource(const Program& program, uint32_t source, const SrtRuntime& runtime,
|
bool EvaluateDescriptorSource(const Program& program, uint32_t source, const SrtRuntime& runtime,
|
||||||
DescriptorValue& result);
|
DescriptorValue& result);
|
||||||
|
|||||||
Reference in New Issue
Block a user