mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 00:12:40 +00:00
add srt walker
This commit is contained in:
1 parent
730e1a3cf3
commit
f848d2761b
7 files changed
+269
-5
No files matched your search
@@ -3,6 +3,7 @@
|
||||
#include "graphics/shader/recompiler/BufferFormat.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include <map>
|
||||
#include <new>
|
||||
#include <unordered_map>
|
||||
@@ -99,6 +100,9 @@ bool EquivalentValue(const ResourcePlan& program, Value left, Value right,
|
||||
|
||||
} // namespace
|
||||
|
||||
ResourcePlan::ResourcePlan() = default;
|
||||
ResourcePlan::ResourcePlan(ResourcePlan&&) noexcept = default;
|
||||
|
||||
ResourcePlan::~ResourcePlan() {
|
||||
for (auto& inst: value_storage) {
|
||||
inst.Invalidate();
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace Xbyak {
|
||||
class CodeGenerator;
|
||||
}
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
|
||||
enum class ResourceKind {
|
||||
@@ -573,12 +577,12 @@ struct ResourcePlan {
|
||||
uint64_t generation = 0;
|
||||
};
|
||||
|
||||
ResourcePlan() = default;
|
||||
ResourcePlan();
|
||||
~ResourcePlan();
|
||||
|
||||
ResourcePlan(const ResourcePlan&) = delete;
|
||||
ResourcePlan& operator=(const ResourcePlan&) = delete;
|
||||
ResourcePlan(ResourcePlan&&) noexcept = default;
|
||||
ResourcePlan(ResourcePlan&&) noexcept;
|
||||
ResourcePlan& operator=(ResourcePlan&& other) noexcept;
|
||||
|
||||
ShaderType stage = ShaderType::Unknown;
|
||||
@@ -597,6 +601,7 @@ struct ResourcePlan {
|
||||
bool resource_tracking_complete = false;
|
||||
ShaderInfo info;
|
||||
UniformFillPlan uniform_fill;
|
||||
std::unique_ptr<Xbyak::CodeGenerator> srt_code;
|
||||
// GPU-thread scratch for nested clean/EXEC memos, activity and material keys.
|
||||
mutable std::deque<EvaluationContext> evaluation_contexts;
|
||||
mutable uint32_t evaluation_value_count = 0;
|
||||
|
||||
@@ -1064,6 +1064,7 @@ ResourcePlan ExtractResourcePlan(const Program& program) {
|
||||
}
|
||||
if (capture_indirect_reads || !plan.source_reads.empty())
|
||||
plan.resource_tracking_complete &= !program.has_address_writes;
|
||||
CompileSrtWalker(plan);
|
||||
return plan;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
#include "graphics/shader/recompiler/ir/passes/SrtWalker.h"
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/assert.h"
|
||||
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
||||
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Libs::Graphics::ShaderRecompiler::IR {
|
||||
@@ -359,6 +364,131 @@ private:
|
||||
|
||||
} // namespace
|
||||
|
||||
void CompileSrtWalker(ResourcePlan& program) {
|
||||
if (!program.resource_tracking_complete || !program.srt_plan_complete ||
|
||||
program.capture_specialization_reads || !program.control_flow.empty() ||
|
||||
program.srt_reads.empty()) return;
|
||||
|
||||
using Reads = std::map<int32_t, const SrtRead*>;
|
||||
std::map<uint32_t, Reads> roots;
|
||||
std::unordered_map<const Inst*, Reads> pointer_uses;
|
||||
std::unordered_map<const Inst*, int32_t> offsets;
|
||||
for (const auto& read: program.srt_reads) {
|
||||
const auto* inst = read.value.Resolve().TryInstruction();
|
||||
if (inst == nullptr || inst->GetOpcode() != ValueOpcode::LoadAddressU32 ||
|
||||
inst->NumArgs() < 2 || !IsRawRead(program, *inst) ||
|
||||
read.flat_offset >= program.srt_reads.size()) return;
|
||||
const auto offset = inst->Arg(1).Resolve();
|
||||
if (!offset.IsImmediate() || offset.GetType() != Type::U32) return;
|
||||
const auto& memory = program.memory_info[inst->Flags<SrtReadFlags>().index];
|
||||
const auto relative =
|
||||
(static_cast<int64_t>(static_cast<int32_t>(memory.offset)) & ~int64_t {3}) +
|
||||
static_cast<int64_t>(offset.U32() & ~3u);
|
||||
if (relative < INT32_MIN || relative > INT32_MAX ||
|
||||
!offsets.emplace(inst, static_cast<int32_t>(relative)).second) return;
|
||||
}
|
||||
uint32_t user_data_count = 0;
|
||||
for (const auto& read: program.srt_reads) {
|
||||
const auto* inst = read.value.ResolveInstruction();
|
||||
const auto* handle = inst->Arg(0).Resolve().TryInstruction();
|
||||
if (handle == nullptr || handle->GetOpcode() != ValueOpcode::GetAddressResource ||
|
||||
handle->NumArgs() != 2) return;
|
||||
const auto* low = handle->Arg(0).Resolve().TryInstruction();
|
||||
const auto* high = handle->Arg(1).Resolve().TryInstruction();
|
||||
if (low == nullptr || high == nullptr) return;
|
||||
Reads* uses = nullptr;
|
||||
if (low->GetOpcode() == ValueOpcode::GetUserData &&
|
||||
high->GetOpcode() == ValueOpcode::GetUserData && low->NumArgs() == 1 &&
|
||||
high->NumArgs() == 1 && low->Arg(0).GetType() == Type::ScalarReg &&
|
||||
high->Arg(0).GetType() == Type::ScalarReg) {
|
||||
const auto reg = RegIndex(low->Arg(0).ScalarRegister());
|
||||
if (reg < program.user_data_base ||
|
||||
RegIndex(high->Arg(0).ScalarRegister()) != reg + 1u) return;
|
||||
const auto base = reg - program.user_data_base;
|
||||
user_data_count = std::max(user_data_count, base + 2u);
|
||||
uses = &roots[base];
|
||||
} else {
|
||||
const auto lo = offsets.find(low);
|
||||
const auto hi = offsets.find(high);
|
||||
if (lo == offsets.end() || hi == offsets.end() ||
|
||||
static_cast<int64_t>(lo->second) + 4 != hi->second ||
|
||||
!EquivalentValue(program, low->Arg(0), high->Arg(0))) return;
|
||||
uses = &pointer_uses[low];
|
||||
}
|
||||
if (!uses->emplace(offsets.at(inst), &read).second) return;
|
||||
}
|
||||
std::unordered_set<const Inst*> visited;
|
||||
const auto reachable = [&](auto&& self, const Reads& reads) -> bool {
|
||||
for (const auto& [offset, read]: reads) {
|
||||
const auto* inst = read->value.ResolveInstruction();
|
||||
const auto child = pointer_uses.find(inst);
|
||||
if (child != pointer_uses.end() &&
|
||||
(!visited.insert(inst).second || !self(self, child->second))) return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
for (const auto& [reg, reads]: roots) {
|
||||
if (!reachable(reachable, reads)) return;
|
||||
}
|
||||
if (visited.size() != pointer_uses.size()) return;
|
||||
|
||||
program.srt_code = std::make_unique<Xbyak::CodeGenerator>(4096, Xbyak::AutoGrow);
|
||||
auto& code = *program.srt_code;
|
||||
Xbyak::Label failed, finished;
|
||||
code.push(code.rbp);
|
||||
code.mov(code.rbp, code.rsp);
|
||||
code.cmp(code.r8, user_data_count);
|
||||
code.jb(failed, Xbyak::CodeGenerator::T_NEAR);
|
||||
code.mov(code.r9, AddressMask & ~uint64_t {3});
|
||||
// Follow shadPS4's VisitPointer: copy one table level in offset order, then
|
||||
// descend through its child pointers. Seed the descriptor evaluator's memo.
|
||||
const auto visit = [&](auto&& self, int32_t pointer_offset, const Reads& reads) -> void {
|
||||
code.push(code.rdi);
|
||||
code.mov(code.rdi, code.qword[code.rdi + pointer_offset]);
|
||||
code.and_(code.rdi, code.r9);
|
||||
const auto first = reads.begin()->first;
|
||||
const auto last = reads.rbegin()->first;
|
||||
if (first < 0) {
|
||||
code.mov(code.r10, -static_cast<int64_t>(first));
|
||||
code.cmp(code.rdi, code.r10);
|
||||
code.jb(failed, Xbyak::CodeGenerator::T_NEAR);
|
||||
}
|
||||
if (last > 0) {
|
||||
code.mov(code.r10, AddressMask - last);
|
||||
code.cmp(code.rdi, code.r10);
|
||||
code.ja(failed, Xbyak::CodeGenerator::T_NEAR);
|
||||
}
|
||||
for (const auto& [offset, read]: reads) {
|
||||
const auto index = read->value.ResolveInstruction()->EvaluationIndex(program.evaluation_value_count);
|
||||
using Entry = ResourcePlan::EvaluationContext::Entry;
|
||||
const auto slot = code.rdx + index * sizeof(Entry);
|
||||
code.mov(code.r10d, code.dword[code.rdi + offset]);
|
||||
code.mov(code.dword[code.rsi + read->flat_offset * sizeof(uint32_t)], code.r10d);
|
||||
code.mov(code.qword[slot + offsetof(Entry, value)], code.r10);
|
||||
code.mov(code.qword[slot + offsetof(Entry, generation)], code.rcx);
|
||||
}
|
||||
for (const auto& [offset, read]: reads) {
|
||||
const auto child = pointer_uses.find(read->value.ResolveInstruction());
|
||||
if (child == pointer_uses.end()) continue;
|
||||
self(self, offset, child->second);
|
||||
}
|
||||
code.pop(code.rdi);
|
||||
};
|
||||
for (const auto& [reg, reads]: roots) {
|
||||
visit(visit, reg * sizeof(uint32_t), reads);
|
||||
}
|
||||
code.mov(code.eax, 1);
|
||||
code.jmp(finished, Xbyak::CodeGenerator::T_NEAR);
|
||||
code.L(failed);
|
||||
code.xor_(code.eax, code.eax);
|
||||
code.L(finished);
|
||||
code.mov(code.rsp, code.rbp);
|
||||
code.pop(code.rbp);
|
||||
code.ret();
|
||||
code.readyRE();
|
||||
EXIT_IF(Xbyak::GetError() != 0);
|
||||
}
|
||||
|
||||
SrtWalker::SrtWalker(const ResourcePlan& program, const SrtRuntime& runtime,
|
||||
SrtWalker* clean_evaluator, Value active_mask)
|
||||
: m_program(program), m_runtime(runtime), m_clean_evaluator(clean_evaluator),
|
||||
@@ -954,6 +1084,15 @@ bool SrtWalker::EvaluateDescriptor(uint32_t source, DescriptorValue& result) {
|
||||
|
||||
bool SrtWalker::RefreshFlatBuffer(std::vector<uint32_t>& flat) {
|
||||
if (!m_program.srt_plan_complete) return false;
|
||||
if (m_program.srt_code && m_runtime.read_memory == nullptr) {
|
||||
flat.resize(m_program.srt_reads.size());
|
||||
m_program.active_sources.clear();
|
||||
m_context.values.resize(m_program.evaluation_value_count);
|
||||
using Walker = bool KYTY_SYSV_ABI (*)(const uint32_t*, uint32_t*,
|
||||
ResourcePlan::EvaluationContext::Entry*, uint64_t, size_t);
|
||||
return m_program.srt_code->getCode<Walker>()(m_runtime.user_data.data(), flat.data(),
|
||||
m_context.values.data(), m_context.generation, m_runtime.user_data.size());
|
||||
}
|
||||
const auto refresh = [&](uint32_t slot) {
|
||||
if (slot >= m_program.srt_reads.size()) return false;
|
||||
const auto& read = m_program.srt_reads[slot];
|
||||
|
||||
@@ -45,6 +45,7 @@ enum class RuntimeValueType { Any, Integer, ImmutableInteger };
|
||||
|
||||
bool ValidateRuntimeValue(const ResourcePlan& program, Value value,
|
||||
RuntimeValueType type = RuntimeValueType::Any);
|
||||
void CompileSrtWalker(ResourcePlan& program);
|
||||
// Uses the strict reader for values that affect shader specialization.
|
||||
SrtRuntime CleanRuntime(SrtRuntime runtime);
|
||||
|
||||
|
||||
Reference in new issue
Block a user