mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
ceccd746d8
Co-authored-by: fxpw <fxpw@outlook.com>
139 lines
5.4 KiB
C++
139 lines
5.4 KiB
C++
#ifndef EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SRTWALKER_H_
|
|
#define EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SRTWALKER_H_
|
|
|
|
#include "graphics/shader/recompiler/ir/ShaderIR.h"
|
|
|
|
#include <algorithm>
|
|
#include <span>
|
|
|
|
namespace Libs::Graphics::ShaderRecompiler::IR {
|
|
|
|
class Value;
|
|
|
|
using SrtMemoryReader = bool (*)(void* userdata, uint64_t address, std::span<uint32_t> values);
|
|
|
|
struct SrtRuntime {
|
|
std::span<const uint32_t> user_data;
|
|
uint64_t shader_base = 0;
|
|
SrtMemoryReader read_memory = nullptr;
|
|
void* userdata = nullptr;
|
|
SrtMemoryReader read_specialization_memory = nullptr;
|
|
std::span<const uint32_t> workgroup_counts;
|
|
uint64_t indirect_args_address = 0;
|
|
};
|
|
|
|
class SrtReadCapture {
|
|
public:
|
|
SrtReadCapture(SrtRuntime source, std::vector<std::pair<uint64_t, uint64_t>>& ranges):
|
|
m_source(source), m_ranges(ranges) {}
|
|
SrtRuntime ObservedRuntime();
|
|
|
|
private:
|
|
static bool ReadStrict(void* userdata, uint64_t address, std::span<uint32_t> values);
|
|
static bool ReadOrdinary(void* userdata, uint64_t address, std::span<uint32_t> values);
|
|
|
|
SrtRuntime m_source;
|
|
std::vector<std::pair<uint64_t, uint64_t>>& m_ranges;
|
|
};
|
|
|
|
// Retained reads no longer need the guest instruction PC. A clean read evaluates
|
|
// its address and bounds through the strict reader as well as the final DWORD.
|
|
struct SrtReadFlags {
|
|
uint32_t index = 0;
|
|
uint32_t clean = 0;
|
|
};
|
|
|
|
enum class RuntimeValueType { Any, Integer, ImmutableInteger };
|
|
|
|
bool ValidateRuntimeValue(const ResourcePlan& program, Value value,
|
|
RuntimeValueType type = RuntimeValueType::Any);
|
|
// Uniform across a compute wave, including values derived from its workgroup ID.
|
|
// This does not imply that the host can evaluate the value.
|
|
bool ValidateWaveUniformValue(const ResourcePlan& program, Value value);
|
|
void CompileSrtWalker(ResourcePlan& program);
|
|
// Uses the strict reader for values that affect shader specialization.
|
|
SrtRuntime CleanRuntime(SrtRuntime runtime);
|
|
|
|
// Nonnegative affine coefficients: element zero is constant, remaining elements are
|
|
// caller-selected leaves. Runtime range proofs must additionally exclude U32 wrap.
|
|
template <size_t Size>
|
|
std::optional<std::array<uint64_t, Size>> AffineIndex(
|
|
Value value, const auto& resolve, const auto& leaf, uint32_t depth = 0) {
|
|
value = resolve(value);
|
|
if (depth > 32u || value.GetType() != Type::U32) return {};
|
|
std::array<uint64_t, Size> result {};
|
|
if (value.IsImmediate()) {
|
|
result[0] = value.U32();
|
|
return result;
|
|
}
|
|
if (const auto index = leaf(value)) {
|
|
result[*index] = 1u;
|
|
return result;
|
|
}
|
|
const auto* inst = value.TryInstruction();
|
|
if (inst == nullptr) return {};
|
|
const auto op = inst->GetOpcode();
|
|
if (op != ValueOpcode::IAdd32 && op != ValueOpcode::IMul32 &&
|
|
op != ValueOpcode::ShiftLeftLogical32) return {};
|
|
auto left = AffineIndex<Size>(inst->Arg(0), resolve, leaf, depth + 1u);
|
|
auto right = AffineIndex<Size>(inst->Arg(1), resolve, leaf, depth + 1u);
|
|
if (!left || !right) return {};
|
|
const auto constant = [](const auto& coefficients) {
|
|
return std::ranges::all_of(std::span(coefficients).subspan(1u),
|
|
[](uint64_t coefficient) { return coefficient == 0u; });
|
|
};
|
|
if (op == ValueOpcode::IMul32 && !constant(*right)) std::swap(left, right);
|
|
if (op != ValueOpcode::IAdd32 && !constant(*right)) return {};
|
|
if (op == ValueOpcode::ShiftLeftLogical32) {
|
|
if ((*right)[0] >= 32u) return {};
|
|
(*right)[0] = uint64_t {1} << (*right)[0];
|
|
}
|
|
for (size_t index = 0; index < Size; ++index) {
|
|
(*left)[index] = op == ValueOpcode::IAdd32
|
|
? (*left)[index] + (*right)[index] : (*left)[index] * (*right)[0];
|
|
if ((*left)[index] > UINT32_MAX) return {};
|
|
}
|
|
return left;
|
|
}
|
|
|
|
// One memoized evaluation session shared by the entire shader resource refresh.
|
|
class SrtWalker {
|
|
public:
|
|
SrtWalker(const ResourcePlan& program, const SrtRuntime& runtime,
|
|
SrtWalker* clean_evaluator = nullptr);
|
|
~SrtWalker();
|
|
SrtWalker(const SrtWalker&) = delete;
|
|
SrtWalker& operator=(const SrtWalker&) = delete;
|
|
|
|
bool Evaluate(Value value, uint32_t& result);
|
|
bool EvaluateDescriptor(uint32_t source, DescriptorValue& result);
|
|
// Refreshes reachable scalar reads and active descriptor sources in one walk.
|
|
bool RefreshFlatBuffer(std::vector<uint32_t>& flat);
|
|
|
|
private:
|
|
SrtWalker(SrtWalker& parent, Value active_mask, bool clean = false,
|
|
SrtWalker* clean_evaluator = nullptr);
|
|
ResourcePlan::EvaluationContext& AcquireContext();
|
|
void ResetUnknowns();
|
|
static float Float32(uint64_t bits);
|
|
bool EvaluateWide(Value value, uint64_t& result);
|
|
bool Arg(const Inst& inst, size_t index, uint64_t& result);
|
|
bool EvaluatePhi(const Inst& inst, uint64_t& result);
|
|
bool EvaluateExtract(const Inst& inst, uint64_t& result);
|
|
bool EvaluateRawRead(const Inst& inst, uint64_t& result);
|
|
bool EvaluateInst(const Inst& inst, uint64_t& result);
|
|
|
|
const ResourcePlan& m_program;
|
|
SrtRuntime m_runtime;
|
|
SrtWalker* m_clean_evaluator = nullptr;
|
|
Value m_active_mask;
|
|
const std::vector<uint8_t>* m_reachable_blocks = nullptr;
|
|
const SrtWalker* m_session;
|
|
uint8_t m_read_policy;
|
|
ResourcePlan::EvaluationContext& m_context;
|
|
};
|
|
|
|
} // namespace Libs::Graphics::ShaderRecompiler::IR
|
|
|
|
#endif /* EMULATOR_INCLUDE_EMULATOR_GRAPHICS_SHADER_RECOMPILER_SRTWALKER_H_ */
|