feat(shader): add srt walker impl + refactor

This commit is contained in:
boykopovar 2026-09-18 11:31:21 +03:00
parent 3dc20758a5
commit 3400a2b6e5
17 changed files with 1234 additions and 35 deletions

View File

@ -62,7 +62,14 @@ function(add_shader_recompiler target runtimeTarget)
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Optimization/src/BindingAllocator.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Optimization/src/ShaderInfoCollector.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Optimization/src/SharedMemoryBarrierInserter.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Optimization/src/SrtWalker.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Optimization/src/SrtWalker/SrtAddressArithmetic.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Optimization/src/SrtWalker/SrtDescriptorEvaluation.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Optimization/src/SrtWalker/SrtDiagnostics.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Optimization/src/SrtWalker/SrtEvaluator.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Optimization/src/SrtWalker/SrtInstructionPredicates.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Optimization/src/SrtWalker/SrtPlanBuilder.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Optimization/src/SrtWalker/SrtRuntimeValidator.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/Optimization/src/SrtWalker/SrtWalker.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SpirvBackend/src/SpirvModule.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SpirvBackend/src/SpirvAnalysis.cpp
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/SpirvBackend/src/SpirvAluEmitter.cpp

View File

@ -0,0 +1,14 @@
#ifndef CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTADDRESSARITHMETIC_HPP
#define CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTADDRESSARITHMETIC_HPP
#include <cstdint>
namespace ShaderRecompiler::Detail {
inline constexpr std::uint64_t AddressMask = 0x0000ffffffffffffull;
bool AddSignedAddress(std::uint64_t base, std::int64_t offset, std::uint64_t& result);
}
#endif

View File

@ -0,0 +1,17 @@
#ifndef CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTDESCRIPTOREVALUATION_HPP
#define CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTDESCRIPTOREVALUATION_HPP
#include "IntermediateRepresentation/IrProgram.hpp"
#include "Optimization/SrtWalker.hpp"
#include <cstdint>
#include <span>
#include <vector>
namespace ShaderRecompiler::Detail {
bool EvaluateRuntimeSourcesImpl(const IrResourcePlan& program, std::span<const std::uint32_t> sources, const SrtRuntime& runtime, std::vector<DescriptorValue>& results, std::vector<std::uint32_t>& flat, bool evaluateFlat, std::span<const std::uint8_t> cleanFlatSlots, std::vector<std::uint8_t>& activeSources);
}
#endif

View File

@ -0,0 +1,17 @@
#ifndef CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTDIAGNOSTICS_HPP
#define CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTDIAGNOSTICS_HPP
#include "IntermediateRepresentation/IrProgram.hpp"
#include <cstdint>
#include <string>
namespace ShaderRecompiler::Detail {
const char* StageName(IrShaderStage stage);
std::string Diagnostic(const IrResourcePlan& program, std::uint32_t pc, const std::string& message);
[[noreturn]] void Fail(const IrResourcePlan& program, std::uint32_t pc, const std::string& message);
}
#endif

View File

@ -0,0 +1,42 @@
#ifndef CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTEVALUATOR_HPP
#define CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTEVALUATOR_HPP
#include "IntermediateRepresentation/IrProgram.hpp"
#include "Optimization/SrtWalker.hpp"
#include <cstdint>
#include <span>
#include <unordered_map>
#include <vector>
namespace ShaderRecompiler::Detail {
class Evaluator {
public:
Evaluator(const IrResourcePlan& program, const SrtRuntime& runtime, std::span<const std::uint8_t> cleanFlatSlots = {}, Evaluator* cleanEvaluator = nullptr, IrValue* activeMask = nullptr) : _program(program), _runtime(runtime), _cleanFlatSlots(cleanFlatSlots), _cleanEvaluator(cleanEvaluator), _activeMask(activeMask != nullptr ? activeMask->Resolve() : nullptr) {}
bool Evaluate(IrValue* value, std::uint32_t& result);
bool EvaluateWide(IrValue* raw, std::uint64_t& result);
private:
static float Float32(std::uint64_t bits);
static std::uint64_t Float32Bits(float value);
bool Arg(IrValue& inst, std::size_t index, std::uint64_t& result);
bool EvaluatePhi(IrValue& inst, std::uint64_t& result);
bool EvaluateExtract(IrValue& inst, std::uint64_t& result);
bool EvaluateRawRead(IrValue& inst, std::uint64_t& result);
bool EvaluateInst(IrValue& inst, std::uint64_t& result);
const IrResourcePlan& _program;
const SrtRuntime& _runtime;
std::span<const std::uint8_t> _cleanFlatSlots;
Evaluator* _cleanEvaluator = nullptr;
IrValue* _activeMask = nullptr;
std::unordered_map<IrValue*, std::uint64_t> _cache;
std::vector<IrValue*> _visiting;
};
}
#endif

View File

@ -0,0 +1,15 @@
#ifndef CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTINSTRUCTIONPREDICATES_HPP
#define CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTINSTRUCTIONPREDICATES_HPP
#include "IntermediateRepresentation/IrProgram.hpp"
namespace ShaderRecompiler::Detail {
bool IsRawRead(const IrResourcePlan& program, const IrValue& inst);
bool IsDescriptorHandle(IrOpcode opcode);
bool IsRuntimeSelect(IrOpcode opcode);
bool IsRuntimeUniformOp(IrOpcode opcode);
}
#endif

View File

@ -0,0 +1,35 @@
#ifndef CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTPLANBUILDER_HPP
#define CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTPLANBUILDER_HPP
#include "IntermediateRepresentation/IrProgram.hpp"
#include <cstdint>
#include <vector>
namespace ShaderRecompiler::Detail {
class PlanBuilder {
public:
explicit PlanBuilder(IrProgram& program) : _program(program) {}
void Run();
private:
struct Patch {
IrValue* inst = nullptr;
std::uint32_t slot = 0;
bool keep = false;
};
void Collect(IrValue* raw, std::uint32_t usePc);
void PatchReads();
IrProgram& _program;
std::vector<IrValue*> _visiting;
std::vector<IrValue*> _visited;
std::vector<Patch> _patches;
};
}
#endif

View File

@ -0,0 +1,30 @@
#ifndef CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTRUNTIMEVALIDATOR_HPP
#define CORE_SHADER_RECOMPILIER_OPTIMIZATION_SRTWALKER_SRTRUNTIMEVALIDATOR_HPP
#include "IntermediateRepresentation/IrProgram.hpp"
#include "Optimization/SrtWalker.hpp"
#include <unordered_set>
namespace ShaderRecompiler::Detail {
class RuntimeValidator {
public:
RuntimeValidator(const IrResourcePlan& program, RuntimeValueType type) : _program(program), _type(type) {}
bool Run(IrValue* value) { return Validate(value); }
private:
bool ValidateArguments(IrValue& inst, bool requireUniform);
bool Validate(IrValue* raw, bool requireUniform = true);
const IrResourcePlan& _program;
RuntimeValueType _type;
IrValue* _activeMask = nullptr;
std::unordered_set<IrValue*> _visiting;
std::unordered_set<IrValue*> _validatedDependencies;
};
}
#endif

View File

@ -1,34 +0,0 @@
#include "Optimization/SrtWalker.hpp"
#include <stdexcept>
namespace ShaderRecompiler {
void SrtWalker::BuildPlan(IrProgram& program) const {
throw std::runtime_error("SrtWalker::BuildPlan not implemented");
}
bool SrtWalker::ValidateRuntimeValue(const IrResourcePlan& program, const IrValue* value, RuntimeValueType type) const {
throw std::runtime_error("SrtWalker::ValidateRuntimeValue not implemented");
}
void SrtWalker::EvaluateUniformValues(const IrResourcePlan& program, std::span<IrValue* const> values, const SrtRuntime& runtime, std::span<std::uint32_t> results) const {
throw std::runtime_error("SrtWalker::EvaluateUniformValues not implemented");
}
void SrtWalker::EvaluateDescriptorSource(const IrResourcePlan& program, std::uint32_t source, const SrtRuntime& runtime, DescriptorValue& result) const {
throw std::runtime_error("SrtWalker::EvaluateDescriptorSource not implemented");
}
void SrtWalker::EvaluateDescriptorSources(const IrResourcePlan& program, std::span<const std::uint32_t> sources, const SrtRuntime& runtime, std::vector<DescriptorValue>& results) const {
throw std::runtime_error("SrtWalker::EvaluateDescriptorSources not implemented");
}
void SrtWalker::EvaluateRuntimeSources(const IrResourcePlan& program, std::span<const std::uint32_t> sources, const SrtRuntime& runtime, std::vector<DescriptorValue>& results, std::vector<std::uint32_t>& flat, std::span<const std::uint8_t> cleanFlatSlots, std::vector<std::uint8_t>& activeSources) const {
throw std::runtime_error("SrtWalker::EvaluateRuntimeSources not implemented");
}
void SrtWalker::Walk(const IrResourcePlan& program, const SrtRuntime& runtime, std::vector<std::uint32_t>& flat) const {
throw std::runtime_error("SrtWalker::Walk not implemented");
}
}

View File

@ -0,0 +1,25 @@
#include "Optimization/SrtWalker/SrtAddressArithmetic.hpp"
namespace ShaderRecompiler::Detail {
bool AddSignedAddress(std::uint64_t base, std::int64_t offset, std::uint64_t& result) {
if (base > AddressMask) {
return false;
}
if (offset < 0) {
const auto magnitude = std::uint64_t {0} - static_cast<std::uint64_t>(offset);
if (magnitude > base) {
return false;
}
result = base - magnitude;
return true;
}
const auto magnitude = static_cast<std::uint64_t>(offset);
if (magnitude > AddressMask - base) {
return false;
}
result = base + magnitude;
return true;
}
}

View File

@ -0,0 +1,99 @@
#include "Optimization/SrtWalker/SrtDescriptorEvaluation.hpp"
#include "Optimization/SrtWalker/SrtEvaluator.hpp"
#include <algorithm>
namespace ShaderRecompiler::Detail {
namespace {
const DescriptorSource* Source(const IrResourcePlan& program, std::uint32_t source) {
if (source >= program.descriptorSources.size()) {
return nullptr;
}
return &program.descriptorSources[source];
}
}
bool EvaluateRuntimeSourcesImpl(const IrResourcePlan& program, std::span<const std::uint32_t> sources, const SrtRuntime& runtime, std::vector<DescriptorValue>& results, std::vector<std::uint32_t>& flat, bool evaluateFlat, std::span<const std::uint8_t> cleanFlatSlots, std::vector<std::uint8_t>& activeSources) {
if (!program.srtPlanComplete) {
return false;
}
if (std::any_of(cleanFlatSlots.begin(), cleanFlatSlots.end(), [](std::uint8_t clean) { return clean != 0u; }) && runtime.readSpecializationMemory == nullptr) {
return false;
}
SrtRuntime cleanRuntime = runtime;
cleanRuntime.readMemory = runtime.readSpecializationMemory;
Evaluator cleanEvaluator(program, cleanRuntime);
Evaluator evaluator(program, runtime, cleanFlatSlots, &cleanEvaluator);
std::vector<std::uint8_t> active;
if (evaluateFlat) {
active.assign(program.descriptorSources.size(), 1u);
}
if (evaluateFlat && !program.controlFlow.empty()) {
for (const auto& block : program.controlFlow) {
for (const auto source : block.sources) {
active.at(source) = 0u;
}
}
std::vector<std::uint8_t> visited(program.controlFlow.size());
std::vector<std::uint32_t> pending {0};
while (!pending.empty()) {
const auto index = pending.back();
pending.pop_back();
if (visited.at(index)) {
continue;
}
visited[index] = 1u;
const auto& block = program.controlFlow[index];
for (const auto source : block.sources) {
active[source] = 1u;
}
std::uint32_t condition = 0;
const bool cleanEvaluable = block.condition != nullptr && runtime.readSpecializationMemory != nullptr && cleanEvaluator.Evaluate(block.condition, condition);
if (cleanEvaluable) {
pending.push_back(block.successors[condition != 0u ? 0u : 1u]);
} else {
pending.insert(pending.end(), block.successors.begin(), block.successors.end());
}
}
}
std::vector<DescriptorValue> evaluated;
evaluated.reserve(sources.size());
for (const auto sourceIndex : sources) {
const auto* source = Source(program, sourceIndex);
if (source == nullptr) {
return false;
}
DescriptorValue value;
value.dwordCount = source->dwordCount;
if (!evaluateFlat || active[sourceIndex]) {
for (std::uint32_t index = 0; index < source->dwordCount; index++) {
if (!evaluator.Evaluate(source->dwords[index], value.dwords[index])) {
return false;
}
}
}
evaluated.push_back(value);
}
std::vector<std::uint32_t> flattened;
if (evaluateFlat) {
flattened.resize(program.srtReads.size());
for (const auto& read : program.srtReads) {
const bool clean = read.flatOffset < cleanFlatSlots.size() && cleanFlatSlots[read.flatOffset] != 0u;
auto& selected = clean ? cleanEvaluator : evaluator;
if (read.flatOffset >= flattened.size() || !selected.Evaluate(read.value, flattened[read.flatOffset])) {
return false;
}
}
}
results = std::move(evaluated);
activeSources = std::move(active);
if (evaluateFlat) {
flat = std::move(flattened);
}
return true;
}
}

View File

@ -0,0 +1,30 @@
#include "Optimization/SrtWalker/SrtDiagnostics.hpp"
#include <cstdio>
#include <stdexcept>
namespace ShaderRecompiler::Detail {
const char* StageName(IrShaderStage stage) {
switch (stage) {
case IrShaderStage::Vertex: return "vertex";
case IrShaderStage::Pixel: return "pixel";
case IrShaderStage::Fetch: return "fetch";
case IrShaderStage::Compute: return "compute";
default: return "unknown";
}
}
std::string Diagnostic(const IrResourcePlan& program, std::uint32_t pc, const std::string& message) {
char hashBuffer[32];
std::snprintf(hashBuffer, sizeof(hashBuffer), "0x%016llx", static_cast<unsigned long long>(program.shaderHash));
char pcBuffer[16];
std::snprintf(pcBuffer, sizeof(pcBuffer), "0x%08x", pc);
return "shader SRT: hash=" + std::string(hashBuffer) + " stage=" + StageName(program.stage) + " pc=" + std::string(pcBuffer) + " " + message;
}
[[noreturn]] void Fail(const IrResourcePlan& program, std::uint32_t pc, const std::string& message) {
throw std::runtime_error(Diagnostic(program, pc, message));
}
}

View File

@ -0,0 +1,480 @@
#include "Optimization/SrtWalker/SrtEvaluator.hpp"
#include "Optimization/SrtWalker/SrtAddressArithmetic.hpp"
#include "Optimization/SrtWalker/SrtInstructionPredicates.hpp"
#include "IntermediateRepresentation/IrBuilder.hpp"
#include <algorithm>
#include <bit>
#include <cmath>
#include <cstring>
namespace ShaderRecompiler::Detail {
bool Evaluator::Evaluate(IrValue* value, std::uint32_t& result) {
std::uint64_t wide = 0;
if (!EvaluateWide(value, wide)) {
return false;
}
result = static_cast<std::uint32_t>(wide);
return true;
}
bool Evaluator::EvaluateWide(IrValue* raw, std::uint64_t& result) {
IrValue* value = raw->Resolve();
if (value->HasImmediate()) {
switch (value->Type()) {
case IrType::Bool: result = value->ImmediateBool() ? 1u : 0u; return true;
case IrType::U8: result = value->ImmediateU8(); return true;
case IrType::U16: result = value->ImmediateU16(); return true;
case IrType::U32: result = value->ImmediateU32(); return true;
case IrType::U64: result = value->ImmediateU64(); return true;
case IrType::F32: result = Float32Bits(value->ImmediateF32()); return true;
default: return false;
}
}
if (value->Opcode() == IrOpcode::Void) {
return false;
}
IrValue* inst = value;
if (_activeMask != nullptr && IsRuntimeSelect(inst->Opcode()) && inst->ArgumentCount() == 3 && inst->Argument(0)->Resolve() == _activeMask) {
return EvaluateWide(inst->Argument(1), result);
}
if (const auto found = _cache.find(inst); found != _cache.end()) {
result = found->second;
return true;
}
if (std::find(_visiting.begin(), _visiting.end(), inst) != _visiting.end()) {
return false;
}
_visiting.push_back(inst);
std::uint64_t out = 0;
const bool evaluated = EvaluateInst(*inst, out);
_visiting.pop_back();
if (!evaluated) {
return false;
}
_cache.emplace(inst, out);
result = out;
return true;
}
float Evaluator::Float32(std::uint64_t bits) { return std::bit_cast<float>(static_cast<std::uint32_t>(bits)); }
std::uint64_t Evaluator::Float32Bits(float value) { return std::bit_cast<std::uint32_t>(value); }
bool Evaluator::Arg(IrValue& inst, std::size_t index, std::uint64_t& result) { return EvaluateWide(inst.Argument(index), result); }
bool Evaluator::EvaluatePhi(IrValue& inst, std::uint64_t& result) {
IrValue* value = ResolveInvariantPhi(_program, &inst);
return value != nullptr && EvaluateWide(value, result);
}
bool Evaluator::EvaluateExtract(IrValue& inst, std::uint64_t& result) {
IrValue* index = inst.Argument(1)->Resolve();
if (!index->HasImmediate() || index->Type() != IrType::U32) {
return false;
}
const auto component = index->ImmediateU32();
if (component >= 2u) {
return false;
}
if (inst.Opcode() == IrOpcode::CompositeExtractU64) {
std::uint64_t packed = 0;
if (!Arg(inst, 0, packed)) {
return false;
}
result = static_cast<std::uint32_t>(packed >> (component * 32u));
return true;
}
IrValue* source = inst.Argument(0)->Resolve();
if (source->Opcode() == IrOpcode::Void) {
return false;
}
if (source->Opcode() == IrOpcode::CompositeConstructU32x2) {
return EvaluateWide(source->Argument(component), result);
}
if (source->Opcode() == IrOpcode::IAddCarry32) {
std::uint64_t lhs = 0;
std::uint64_t rhs = 0;
if (!Arg(*source, 0, lhs) || !Arg(*source, 1, rhs)) {
return false;
}
const auto sum = static_cast<std::uint64_t>(static_cast<std::uint32_t>(lhs)) + static_cast<std::uint32_t>(rhs);
result = component == 0u ? static_cast<std::uint32_t>(sum) : static_cast<std::uint32_t>(sum >> 32u);
return true;
}
return false;
}
bool Evaluator::EvaluateRawRead(IrValue& inst, std::uint64_t& result) {
const auto flags = inst.Flags<MemoryFlags>();
if (flags.index >= _program.memoryInfo.size()) {
return false;
}
const auto& mem = _program.memoryInfo[flags.index];
IrValue* handle = inst.Argument(0)->Resolve();
if (handle->Opcode() == IrOpcode::Void) {
return false;
}
std::uint64_t low = 0;
std::uint64_t high = 0;
std::uint64_t offset = 0;
if (!Arg(*handle, 0, low) || !Arg(*handle, 1, high) || !Arg(inst, 1, offset)) {
return false;
}
const auto base = ((high << 32u) | static_cast<std::uint32_t>(low)) & AddressMask;
const auto immediate = static_cast<std::int64_t>(static_cast<std::int32_t>(mem.offset));
std::uint64_t address = 0;
if (inst.Opcode() == IrOpcode::ReadConstBuffer) {
std::uint64_t records = 0;
std::uint64_t word3 = 0;
if (handle->ArgumentCount() != 4u || !Arg(*handle, 2, records) || !Arg(*handle, 3, word3)) {
return false;
}
if (immediate < 0) {
return false;
}
const auto byteOffset = static_cast<std::uint64_t>(immediate) + static_cast<std::uint32_t>(offset);
const auto aligned = byteOffset & ~std::uint64_t {3};
const auto stride = (static_cast<std::uint32_t>(high) >> 16u) & 0x3fffu;
const auto size = stride == 0u ? static_cast<std::uint64_t>(static_cast<std::uint32_t>(records)) : static_cast<std::uint64_t>(stride) * static_cast<std::uint32_t>(records);
if (aligned > size || size - aligned < sizeof(std::uint32_t)) {
return false;
}
address = ((base & ~std::uint64_t {3}) + byteOffset) & ~std::uint64_t {3};
} else {
const auto relative = (immediate & ~std::int64_t {3}) + static_cast<std::int64_t>(static_cast<std::uint32_t>(offset) & ~3u);
if (!AddSignedAddress(base & ~std::uint64_t {3}, relative, address)) {
return false;
}
}
std::uint32_t word = 0;
if (_runtime.readMemory != nullptr) {
if (!_runtime.readMemory(_runtime.userContext, address, &word)) {
return false;
}
} else {
std::memcpy(&word, reinterpret_cast<const void*>(address), sizeof(word));
}
result = word;
return true;
}
bool Evaluator::EvaluateInst(IrValue& inst, std::uint64_t& result) {
std::uint64_t a = 0;
std::uint64_t b = 0;
std::uint64_t c = 0;
const auto binary = [&]() { return Arg(inst, 0, a) && Arg(inst, 1, b); };
const auto ternary = [&]() { return Arg(inst, 0, a) && Arg(inst, 1, b) && Arg(inst, 2, c); };
switch (inst.Opcode()) {
case IrOpcode::GetUserData: {
const auto reg = RegIndex(static_cast<ScalarReg>(inst.Argument(0)->Register().index));
if (reg < _program.userDataBase || reg - _program.userDataBase >= _runtime.userData.size()) {
return false;
}
result = _runtime.userData[reg - _program.userDataBase];
return true;
}
case IrOpcode::GetShaderBase: result = _runtime.shaderBase; return true;
case IrOpcode::Phi: return EvaluatePhi(inst, result);
case IrOpcode::ReadFirstLane: {
Evaluator active(_program, _runtime, _cleanFlatSlots, _cleanEvaluator, inst.Argument(1));
return active.EvaluateWide(inst.Argument(0), result);
}
case IrOpcode::BitCastU32F32:
case IrOpcode::BitCastF32U32: return Arg(inst, 0, result);
case IrOpcode::CompositeExtractU64:
case IrOpcode::CompositeExtractU32x2: return EvaluateExtract(inst, result);
case IrOpcode::CompositeConstructU64:
if (!binary()) {
return false;
}
result = static_cast<std::uint32_t>(a) | (static_cast<std::uint64_t>(static_cast<std::uint32_t>(b)) << 32u);
return true;
case IrOpcode::ReadConst: {
IrValue* slot = inst.Argument(1)->Resolve();
if (!slot->HasImmediate() || slot->Type() != IrType::U32 || slot->ImmediateU32() >= _program.srtReads.size()) {
return false;
}
if (slot->ImmediateU32() < _cleanFlatSlots.size() && _cleanFlatSlots[slot->ImmediateU32()] != 0u && _cleanEvaluator != nullptr) {
return _cleanEvaluator->EvaluateWide(_program.srtReads[slot->ImmediateU32()].value, result);
}
return EvaluateWide(_program.srtReads[slot->ImmediateU32()].value, result);
}
case IrOpcode::LoadAddressU32:
case IrOpcode::ReadConstBuffer:
if (IsRawRead(_program, inst)) {
return EvaluateRawRead(inst, result);
}
break;
case IrOpcode::IAdd32:
if (binary()) {
result = static_cast<std::uint32_t>(a + b);
return true;
}
return false;
case IrOpcode::IAdd64:
if (binary()) {
result = a + b;
return true;
}
return false;
case IrOpcode::ISub32:
if (binary()) {
result = static_cast<std::uint32_t>(a - b);
return true;
}
return false;
case IrOpcode::ISub64:
if (binary()) {
result = a - b;
return true;
}
return false;
case IrOpcode::IMul32:
if (binary()) {
result = static_cast<std::uint32_t>(a * b);
return true;
}
return false;
case IrOpcode::IMul64:
if (binary()) {
result = a * b;
return true;
}
return false;
case IrOpcode::UMin32:
if (binary()) {
result = std::min(static_cast<std::uint32_t>(a), static_cast<std::uint32_t>(b));
return true;
}
return false;
case IrOpcode::ConvertF32U32:
if (Arg(inst, 0, a)) {
result = Float32Bits(static_cast<float>(static_cast<std::uint32_t>(a)));
return true;
}
return false;
case IrOpcode::ConvertU32F32:
if (Arg(inst, 0, a)) {
const auto value = Float32(a);
if (!std::isfinite(value) || value < 0.0f || static_cast<double>(value) > 4294967295.0) {
return false;
}
result = static_cast<std::uint32_t>(value);
return true;
}
return false;
case IrOpcode::FPMul32:
if (binary()) {
result = Float32Bits(Float32(a) * Float32(b));
return true;
}
return false;
case IrOpcode::FPTrunc32:
if (Arg(inst, 0, a)) {
result = Float32Bits(std::trunc(Float32(a)));
return true;
}
return false;
case IrOpcode::FPIsNan32:
if (Arg(inst, 0, a)) {
result = std::isnan(Float32(a)) ? 1u : 0u;
return true;
}
return false;
case IrOpcode::FPOrdLessThanEqual32:
if (binary()) {
result = Float32(a) <= Float32(b) ? 1u : 0u;
return true;
}
return false;
case IrOpcode::FPOrdGreaterThanEqual32:
if (binary()) {
result = Float32(a) >= Float32(b) ? 1u : 0u;
return true;
}
return false;
case IrOpcode::BitwiseAnd32:
if (binary()) {
result = static_cast<std::uint32_t>(a & b);
return true;
}
return false;
case IrOpcode::BitwiseAnd64:
if (binary()) {
result = a & b;
return true;
}
return false;
case IrOpcode::BitwiseOr32:
if (binary()) {
result = static_cast<std::uint32_t>(a | b);
return true;
}
return false;
case IrOpcode::BitwiseXor32:
if (binary()) {
result = static_cast<std::uint32_t>(a ^ b);
return true;
}
return false;
case IrOpcode::BitwiseNot32:
if (Arg(inst, 0, a)) {
result = ~static_cast<std::uint32_t>(a);
return true;
}
return false;
case IrOpcode::ShiftLeftLogical32:
if (binary()) {
result = static_cast<std::uint32_t>(a) << (b & 31u);
return true;
}
return false;
case IrOpcode::ShiftLeftLogical64:
if (binary()) {
result = a << (b & 63u);
return true;
}
return false;
case IrOpcode::ShiftRightLogical32:
if (binary()) {
result = static_cast<std::uint32_t>(a) >> (b & 31u);
return true;
}
return false;
case IrOpcode::ShiftRightLogical64:
if (binary()) {
result = a >> (b & 63u);
return true;
}
return false;
case IrOpcode::ShiftRightArithmetic32:
if (binary()) {
result = static_cast<std::uint32_t>(std::bit_cast<std::int32_t>(static_cast<std::uint32_t>(a)) >> (b & 31u));
return true;
}
return false;
case IrOpcode::ShiftRightArithmetic64:
if (binary()) {
result = static_cast<std::uint64_t>(std::bit_cast<std::int64_t>(a) >> (b & 63u));
return true;
}
return false;
case IrOpcode::BitFieldUExtract:
if (ternary()) {
const auto offset = static_cast<std::uint32_t>(b);
const auto width = static_cast<std::uint32_t>(c);
if (offset > 32u || width > 32u - offset) {
return false;
}
const auto mask = width == 32u ? 0xffffffffu : width == 0u ? 0u : (std::uint32_t {1} << width) - 1u;
result = width == 0u ? 0u : (static_cast<std::uint32_t>(a) >> offset) & mask;
return true;
}
return false;
case IrOpcode::BitFieldSExtract:
if (ternary()) {
const auto offset = static_cast<std::uint32_t>(b);
const auto width = static_cast<std::uint32_t>(c);
if (offset > 32u || width > 32u - offset) {
return false;
}
if (width == 0u) {
result = 0;
return true;
}
const auto mask = width == 32u ? 0xffffffffu : (std::uint32_t {1} << width) - 1u;
auto bits = (static_cast<std::uint32_t>(a) >> offset) & mask;
if (width < 32u && (bits & (std::uint32_t {1} << (width - 1u))) != 0u) {
bits |= ~mask;
}
result = bits;
return true;
}
return false;
case IrOpcode::BitFieldInsert: {
std::uint64_t d = 0;
if (!ternary() || !Arg(inst, 3, d)) {
return false;
}
const auto offset = static_cast<std::uint32_t>(c);
const auto width = static_cast<std::uint32_t>(d);
if (offset > 32u || width > 32u - offset) {
return false;
}
if (width == 0u) {
result = static_cast<std::uint32_t>(a);
return true;
}
const auto mask = width == 32u ? 0xffffffffu : ((std::uint32_t {1} << width) - 1u) << offset;
result = (static_cast<std::uint32_t>(a) & ~mask) | ((static_cast<std::uint32_t>(b) << offset) & mask);
return true;
}
case IrOpcode::SelectU32:
case IrOpcode::SelectU1:
case IrOpcode::SelectF32:
if (ternary()) {
result = a != 0u ? b : c;
return true;
}
return false;
case IrOpcode::IEqual32:
if (binary()) {
result = static_cast<std::uint32_t>(a) == static_cast<std::uint32_t>(b) ? 1u : 0u;
return true;
}
return false;
case IrOpcode::INotEqual32:
if (binary()) {
result = static_cast<std::uint32_t>(a) != static_cast<std::uint32_t>(b) ? 1u : 0u;
return true;
}
return false;
case IrOpcode::ULessThan32:
if (binary()) {
result = static_cast<std::uint32_t>(a) < static_cast<std::uint32_t>(b) ? 1u : 0u;
return true;
}
return false;
case IrOpcode::UGreaterThan32:
if (binary()) {
result = static_cast<std::uint32_t>(a) > static_cast<std::uint32_t>(b) ? 1u : 0u;
return true;
}
return false;
case IrOpcode::LogicalAnd:
if (binary()) {
result = (a != 0u) && (b != 0u) ? 1u : 0u;
return true;
}
return false;
case IrOpcode::LogicalOr:
if (binary()) {
result = (a != 0u) || (b != 0u) ? 1u : 0u;
return true;
}
return false;
case IrOpcode::LogicalXor:
if (binary()) {
result = (a != 0u) != (b != 0u) ? 1u : 0u;
return true;
}
return false;
case IrOpcode::LogicalNot:
if (Arg(inst, 0, a)) {
result = a == 0u ? 1u : 0u;
return true;
}
return false;
case IrOpcode::UndefU1:
case IrOpcode::UndefU8:
case IrOpcode::UndefU16:
case IrOpcode::UndefU32:
case IrOpcode::UndefU64: return false;
default: break;
}
return false;
}
}

View File

@ -0,0 +1,84 @@
#include "Optimization/SrtWalker/SrtInstructionPredicates.hpp"
namespace ShaderRecompiler::Detail {
bool IsRawRead(const IrResourcePlan& program, const IrValue& inst) {
const auto op = inst.Opcode();
if (op != IrOpcode::LoadAddressU32 && op != IrOpcode::ReadConstBuffer) {
return false;
}
const auto index = inst.Flags<MemoryFlags>().index;
if (index >= program.memoryInfo.size()) {
return false;
}
const auto kind = program.memoryInfo[index].kind;
return (op == IrOpcode::LoadAddressU32 && kind == ResourceKind::ScalarAddress) || (op == IrOpcode::ReadConstBuffer && kind == ResourceKind::ScalarBuffer);
}
bool IsDescriptorHandle(IrOpcode opcode) {
switch (opcode) {
case IrOpcode::GetBufferResource:
case IrOpcode::GetAddressResource:
case IrOpcode::GetImageResource:
case IrOpcode::GetSamplerResource: return true;
default: return false;
}
}
bool IsRuntimeSelect(IrOpcode opcode) {
return opcode == IrOpcode::SelectU1 || opcode == IrOpcode::SelectU32 || opcode == IrOpcode::SelectF32;
}
bool IsRuntimeUniformOp(IrOpcode opcode) {
switch (opcode) {
case IrOpcode::BitCastU32F32:
case IrOpcode::BitCastF32U32:
case IrOpcode::ConvertU32F32:
case IrOpcode::ConvertF32U32:
case IrOpcode::CompositeConstructU64:
case IrOpcode::CompositeExtractU64:
case IrOpcode::CompositeConstructU32x2:
case IrOpcode::CompositeExtractU32x2:
case IrOpcode::BitFieldInsert:
case IrOpcode::BitFieldUExtract:
case IrOpcode::BitFieldSExtract:
case IrOpcode::IAdd32:
case IrOpcode::IAdd64:
case IrOpcode::IAddCarry32:
case IrOpcode::ISub32:
case IrOpcode::ISub64:
case IrOpcode::IMul32:
case IrOpcode::IMul64:
case IrOpcode::UMin32:
case IrOpcode::ShiftLeftLogical32:
case IrOpcode::ShiftLeftLogical64:
case IrOpcode::ShiftRightLogical32:
case IrOpcode::ShiftRightLogical64:
case IrOpcode::ShiftRightArithmetic32:
case IrOpcode::ShiftRightArithmetic64:
case IrOpcode::BitwiseAnd32:
case IrOpcode::BitwiseAnd64:
case IrOpcode::BitwiseOr32:
case IrOpcode::BitwiseXor32:
case IrOpcode::BitwiseNot32:
case IrOpcode::SelectU1:
case IrOpcode::SelectU32:
case IrOpcode::SelectF32:
case IrOpcode::ULessThan32:
case IrOpcode::IEqual32:
case IrOpcode::UGreaterThan32:
case IrOpcode::INotEqual32:
case IrOpcode::LogicalOr:
case IrOpcode::LogicalAnd:
case IrOpcode::LogicalXor:
case IrOpcode::LogicalNot:
case IrOpcode::FPOrdLessThanEqual32:
case IrOpcode::FPOrdGreaterThanEqual32:
case IrOpcode::FPIsNan32:
case IrOpcode::FPMul32:
case IrOpcode::FPTrunc32: return true;
default: return false;
}
}
}

View File

@ -0,0 +1,129 @@
#include "Optimization/SrtWalker/SrtPlanBuilder.hpp"
#include "Optimization/SrtWalker/SrtDiagnostics.hpp"
#include "Optimization/SrtWalker/SrtInstructionPredicates.hpp"
#include "Optimization/SrtWalker/SrtRuntimeValidator.hpp"
#include "IntermediateRepresentation/IrBuilder.hpp"
#include <algorithm>
#include <stdexcept>
namespace ShaderRecompiler::Detail {
void PlanBuilder::Run() {
_program.Resources().srtReads.clear();
_program.Metadata().dynamicReads.clear();
for (auto& block : _program.Blocks()) {
for (IrValue* inst : block->Instructions()) {
const auto op = inst->Opcode();
if (op == IrOpcode::LoadAddressU32 || op == IrOpcode::ReadConstBuffer) {
const auto flags = inst->Flags<MemoryFlags>();
if (flags.index < _program.Resources().memoryInfo.size()) {
const auto kind = _program.Resources().memoryInfo[flags.index].kind;
const bool crosswired = (op == IrOpcode::LoadAddressU32 && kind == ResourceKind::ScalarBuffer) || (op == IrOpcode::ReadConstBuffer && kind == ResourceKind::ScalarAddress);
if (crosswired) {
Fail(_program.Resources(), flags.pc, std::string(IrOpcodeName(op)) + " has incompatible scalar memory metadata");
}
}
}
if (IsDescriptorHandle(inst->Opcode())) {
for (std::size_t index = 0; index < inst->ArgumentCount(); index++) {
Collect(inst->Argument(index), 0);
}
}
}
}
for (auto& block : _program.Blocks()) {
for (IrValue* inst : block->Instructions()) {
if (inst->Opcode() == IrOpcode::LoadAddressU32 && IsRawRead(_program.Resources(), *inst) && inst->Argument(1)->Resolve()->HasImmediate() && RuntimeValidator(_program.Resources(), RuntimeValueType::Any).Run(inst)) {
Collect(inst, inst->Flags<MemoryFlags>().pc);
}
}
}
PatchReads();
}
void PlanBuilder::Collect(IrValue* raw, std::uint32_t usePc) {
IrValue* value = raw->Resolve();
if (value->HasImmediate()) {
return;
}
if (value->Opcode() == IrOpcode::Void) {
Fail(_program.Resources(), usePc, "invalid typed planning value");
}
IrValue* inst = value;
const auto cycle = std::find(_visiting.begin(), _visiting.end(), inst);
if (cycle != _visiting.end()) {
const auto containsPhi = std::any_of(cycle, _visiting.end(), [](IrValue* candidate) { return candidate->Opcode() == IrOpcode::Phi; });
if (containsPhi) {
return;
}
Fail(_program.Resources(), usePc, "cyclic typed planning value " + std::string(IrOpcodeName(inst->Opcode())) + " without a phi");
}
if (std::find(_visited.begin(), _visited.end(), inst) != _visited.end()) {
return;
}
_visiting.push_back(inst);
for (std::size_t index = 0; index < inst->ArgumentCount(); index++) {
Collect(inst->Argument(index), usePc);
}
_visiting.pop_back();
_visited.push_back(inst);
if (!IsRawRead(_program.Resources(), *inst)) {
return;
}
IrValue* offset = inst->Argument(1)->Resolve();
if (!offset->HasImmediate() || offset->Type() != IrType::U32) {
if (std::find(_program.Metadata().dynamicReads.begin(), _program.Metadata().dynamicReads.end(), value) == _program.Metadata().dynamicReads.end()) {
_program.Metadata().dynamicReads.push_back(value);
}
return;
}
for (std::uint32_t slot = 0; slot < _program.Resources().srtReads.size(); slot++) {
if (EquivalentValue(_program.Resources(), value, _program.Resources().srtReads[slot].value)) {
_patches.push_back({inst, slot, false});
return;
}
}
const auto slot = static_cast<std::uint32_t>(_program.Resources().srtReads.size());
_program.Resources().srtReads.push_back({value, slot});
_patches.push_back({inst, slot, true});
}
void PlanBuilder::PatchReads() {
IrBuilder builder(_program);
for (const auto& patch : _patches) {
IrBlock* block = patch.inst->Parent();
if (block == nullptr) {
throw std::runtime_error("SrtWalker::BuildPlan patched value has no parent block");
}
builder.SetInsertionPoint(*block);
IrValue& resource = builder.Emit(IrOpcode::GetSrtResource, IrType::SrtResource, {});
block->RemoveInstruction(&resource);
block->InsertInstructionBefore(patch.inst, &resource);
IrValue& slotConstant = builder.Constant(patch.slot);
IrValue& flat = builder.Emit(IrOpcode::ReadConst, IrType::U32, {&resource, &slotConstant});
block->RemoveInstruction(&flat);
block->InsertInstructionBefore(patch.inst, &flat);
for (const auto& use : std::vector<IrUse>(patch.inst->OperandUses())) {
use.user->ReplaceArgument(use.operand, &flat);
}
for (auto& info : _program.Metadata().blockInfo) {
if (info.condition != nullptr && info.condition->Resolve() == patch.inst) {
info.condition = &flat;
}
if (info.indirectTarget != nullptr && info.indirectTarget->Resolve() == patch.inst) {
info.indirectTarget = &flat;
}
}
if (patch.keep) {
const auto memory = patch.inst->Flags<MemoryFlags>().index;
if (memory < _program.Resources().memoryInfo.size()) {
_program.Resources().memoryInfo[memory].planningOnly = true;
}
builder.Emit(IrOpcode::ReferenceU32, IrType::Void, {patch.inst});
}
}
}
}

View File

@ -0,0 +1,145 @@
#include "Optimization/SrtWalker/SrtRuntimeValidator.hpp"
#include "Optimization/SrtWalker/SrtInstructionPredicates.hpp"
#include "IntermediateRepresentation/IrBuilder.hpp"
namespace ShaderRecompiler::Detail {
bool RuntimeValidator::ValidateArguments(IrValue& inst, bool requireUniform) {
for (std::size_t index = 0; index < inst.ArgumentCount(); index++) {
if (!Validate(inst.Argument(index), requireUniform)) return false;
}
return true;
}
bool RuntimeValidator::Validate(IrValue* raw, bool requireUniform) {
IrValue* value = raw->Resolve();
if (_type == RuntimeValueType::Integer && TypesOverlap(value->Type(), IrType::F16 | IrType::F32 | IrType::Vec2F32)) {
return false;
}
if (value->Opcode() == IrOpcode::Void) {
if (!requireUniform) return true;
switch (value->Type()) {
case IrType::Bool:
case IrType::U8:
case IrType::U16:
case IrType::U32:
case IrType::U64:
case IrType::F32: return true;
default: return false;
}
}
IrValue& inst = *value;
if (!requireUniform && _validatedDependencies.contains(&inst)) return true;
if (!_visiting.insert(&inst).second) {
return !requireUniform;
}
const auto finish = [&](bool valid) {
_visiting.erase(&inst);
if (valid && !requireUniform) _validatedDependencies.insert(&inst);
return valid;
};
const auto op = inst.Opcode();
if (op == IrOpcode::ReadConst) {
IrValue* slot = inst.ArgumentCount() == 2 ? inst.Argument(1)->Resolve() : nullptr;
if (inst.ArgumentCount() != 2 || inst.Argument(0)->Resolve()->Opcode() == IrOpcode::Void || inst.Argument(0)->Resolve()->Opcode() != IrOpcode::GetSrtResource || slot == nullptr || !slot->HasImmediate() || slot->Type() != IrType::U32 || slot->ImmediateU32() >= _program.srtReads.size()) {
return finish(false);
}
if (_type == RuntimeValueType::Integer) {
const auto activeMask = _activeMask;
_activeMask = nullptr;
const bool valid = Validate(_program.srtReads[slot->ImmediateU32()].value);
_activeMask = activeMask;
if (!valid) return finish(false);
}
}
if (!requireUniform) return finish(ValidateArguments(inst, false));
if (_activeMask != nullptr && IsRuntimeSelect(op) && inst.ArgumentCount() == 3 && inst.Argument(0)->Resolve() == _activeMask) {
if (_type == RuntimeValueType::Integer && !Validate(inst.Argument(2), false)) {
return finish(false);
}
return finish(Validate(inst.Argument(1)));
}
if (op == IrOpcode::UndefU1 || op == IrOpcode::UndefU8 || op == IrOpcode::UndefU16 || op == IrOpcode::UndefU32 || op == IrOpcode::UndefU64 || op == IrOpcode::Void) {
return finish(false);
}
if (op == IrOpcode::GetUserData) {
if (inst.ArgumentCount() != 1 || inst.Argument(0)->Type() != IrType::ScalarReg) {
return finish(false);
}
const auto reg = RegIndex(static_cast<ScalarReg>(inst.Argument(0)->Register().index));
if (reg < _program.userDataBase || reg - _program.userDataBase >= _program.userDataCount) {
return finish(false);
}
return finish(true);
}
if (op == IrOpcode::GetShaderBase) {
if (inst.ArgumentCount() != 0) {
return finish(false);
}
return finish(true);
}
if (op == IrOpcode::Phi) {
if (_type == RuntimeValueType::Integer && !ValidateArguments(inst, false)) {
return finish(false);
}
IrValue* invariant = ResolveInvariantPhi(_program, &inst);
if (invariant == nullptr) {
return finish(false);
}
return finish(Validate(invariant));
}
if (op == IrOpcode::ReadFirstLane) {
if (inst.ArgumentCount() != 2 || inst.Argument(0)->Type() != IrType::U32 || inst.Argument(1)->Type() != IrType::Bool) {
return finish(false);
}
if (_type == RuntimeValueType::Integer && !Validate(inst.Argument(1), false)) {
return finish(false);
}
const auto activeMask = _activeMask;
_activeMask = inst.Argument(1)->Resolve();
const bool valid = Validate(inst.Argument(0));
_activeMask = activeMask;
return finish(valid);
}
if (op == IrOpcode::GetSrtResource) {
if (inst.ArgumentCount() != 0) {
return finish(false);
}
return finish(true);
}
if (op == IrOpcode::LoadAddressU32 || op == IrOpcode::ReadConstBuffer) {
const auto expected = op == IrOpcode::LoadAddressU32 ? IrOpcode::GetAddressResource : IrOpcode::GetBufferResource;
IrValue* handle = inst.ArgumentCount() != 0 ? inst.Argument(0)->Resolve() : nullptr;
if (!IsRawRead(_program, inst) || handle == nullptr || handle->Opcode() != expected) {
return finish(false);
}
} else if (op == IrOpcode::CompositeExtractU64) {
IrValue* index = inst.ArgumentCount() == 2 ? inst.Argument(1)->Resolve() : nullptr;
if (index == nullptr || !index->HasImmediate() || index->Type() != IrType::U32 || index->ImmediateU32() >= 2u) {
return finish(false);
}
} else if (op == IrOpcode::CompositeExtractU32x2) {
IrValue* source = inst.ArgumentCount() == 2 ? inst.Argument(0)->Resolve() : nullptr;
IrValue* index = inst.ArgumentCount() == 2 ? inst.Argument(1)->Resolve() : nullptr;
if (source == nullptr || index == nullptr || !index->HasImmediate() || index->Type() != IrType::U32 || index->ImmediateU32() >= 2u || (source->Opcode() != IrOpcode::CompositeConstructU32x2 && source->Opcode() != IrOpcode::IAddCarry32)) {
return finish(false);
}
}
if (IsDescriptorHandle(op)) {
std::size_t expected = 4u;
if (op == IrOpcode::GetImageResource) {
expected = 8u;
} else if (op == IrOpcode::GetAddressResource) {
expected = 2u;
}
if (inst.ArgumentCount() != expected) {
return finish(false);
}
} else if (op != IrOpcode::ReadConst && op != IrOpcode::ReadConstBuffer && op != IrOpcode::LoadAddressU32 && !IsRuntimeUniformOp(op)) {
return finish(false);
}
return finish(ValidateArguments(inst, true));
}
}

View File

@ -0,0 +1,64 @@
#include "Optimization/SrtWalker.hpp"
#include "Optimization/SrtWalker/SrtDescriptorEvaluation.hpp"
#include "Optimization/SrtWalker/SrtEvaluator.hpp"
#include "Optimization/SrtWalker/SrtPlanBuilder.hpp"
#include "Optimization/SrtWalker/SrtRuntimeValidator.hpp"
#include <stdexcept>
namespace ShaderRecompiler {
void SrtWalker::BuildPlan(IrProgram& program) const {
if (program.Resources().resourceTrackingComplete) {
throw std::runtime_error("shader SRT planning failed: cannot rebuild SRT after resource tracking");
}
program.Resources().srtPlanComplete = false;
Detail::PlanBuilder(program).Run();
program.Resources().srtPlanComplete = true;
}
bool SrtWalker::ValidateRuntimeValue(const IrResourcePlan& program, const IrValue* value, RuntimeValueType type) const {
return Detail::RuntimeValidator(program, type).Run(const_cast<IrValue*>(value));
}
void SrtWalker::EvaluateUniformValues(const IrResourcePlan& program, std::span<IrValue* const> values, const SrtRuntime& runtime, std::span<std::uint32_t> results) const {
if (values.size() != results.size()) {
throw std::runtime_error("SrtWalker::EvaluateUniformValues value and result counts differ");
}
auto clean = runtime;
clean.readMemory = runtime.readSpecializationMemory != nullptr ? runtime.readSpecializationMemory : +[](void*, std::uint64_t, std::uint32_t*) { return false; };
Detail::Evaluator evaluator(program, clean);
for (std::size_t i = 0; i < values.size(); ++i) {
if (!evaluator.Evaluate(values[i], results[i])) {
throw std::runtime_error("SrtWalker::EvaluateUniformValues failed to evaluate a uniform value");
}
}
}
void SrtWalker::EvaluateDescriptorSource(const IrResourcePlan& program, std::uint32_t source, const SrtRuntime& runtime, DescriptorValue& result) const {
std::vector<DescriptorValue> results;
EvaluateDescriptorSources(program, std::span {&source, 1}, runtime, results);
result = results.front();
}
void SrtWalker::EvaluateDescriptorSources(const IrResourcePlan& program, std::span<const std::uint32_t> sources, const SrtRuntime& runtime, std::vector<DescriptorValue>& results) const {
std::vector<std::uint32_t> ignored;
std::vector<std::uint8_t> active;
if (!Detail::EvaluateRuntimeSourcesImpl(program, sources, runtime, results, ignored, false, {}, active)) {
throw std::runtime_error("SrtWalker::EvaluateDescriptorSources failed to evaluate descriptor sources");
}
}
void SrtWalker::EvaluateRuntimeSources(const IrResourcePlan& program, std::span<const std::uint32_t> sources, const SrtRuntime& runtime, std::vector<DescriptorValue>& results, std::vector<std::uint32_t>& flat, std::span<const std::uint8_t> cleanFlatSlots, std::vector<std::uint8_t>& activeSources) const {
if (!Detail::EvaluateRuntimeSourcesImpl(program, sources, runtime, results, flat, true, cleanFlatSlots, activeSources)) {
throw std::runtime_error("SrtWalker::EvaluateRuntimeSources failed to evaluate runtime sources");
}
}
void SrtWalker::Walk(const IrResourcePlan& program, const SrtRuntime& runtime, std::vector<std::uint32_t>& flat) const {
std::vector<DescriptorValue> ignored;
std::vector<std::uint8_t> active;
EvaluateRuntimeSources(program, {}, runtime, ignored, flat, {}, active);
}
}