Reuse scalar evaluation storage

Co-authored-by: MehmetCambaz <mcambaz18@gmail.com>
Co-authored-by: Leclowndu93150 <xdr.a123454321@gmail.com>
This commit is contained in:
nmzik
2026-09-23 10:27:00 +04:00
parent 90471836f7
commit d6dac92da1
4 changed files with 83 additions and 18 deletions
@@ -14,6 +14,7 @@
#include <array>
#include <bit>
#include <deque>
#include <list>
#include <memory>
#include <optional>
@@ -509,6 +510,16 @@ struct UniformFillPlan {
// Immutable runtime resource analysis retained by the shader cache. It owns descriptor/SRT,
// uniform condition and fill values without retaining translated blocks.
struct ResourcePlan {
struct EvaluationContext {
struct Entry {
uint64_t value = 0;
uint64_t generation = 0;
};
std::vector<Entry> values;
uint64_t generation = 0;
};
ResourcePlan() = default;
~ResourcePlan();
@@ -533,6 +544,10 @@ struct ResourcePlan {
bool resource_tracking_complete = false;
ShaderInfo info;
UniformFillPlan uniform_fill;
// GPU-thread scratch. Each nested clean/EXEC evaluation has its own reusable memo.
mutable std::deque<EvaluationContext> evaluation_contexts;
mutable uint32_t evaluation_value_count = 0;
mutable uint32_t evaluation_depth = 0;
};
struct Program: ResourcePlan {
@@ -121,6 +121,13 @@ public:
[[nodiscard]] Block* PhiBlock(size_t index) const;
[[nodiscard]] Block* Parent() const;
[[nodiscard]] const std::vector<Use>& Uses() const;
// Runtime indices belong to the resource plan that owns this instruction.
[[nodiscard]] uint32_t EvaluationIndex(uint32_t& count) const {
if (evaluation_index == UINT32_MAX) {
evaluation_index = count++;
}
return evaluation_index;
}
void SetParent(Block* block);
void SetArg(size_t index, Value value);
@@ -155,6 +162,7 @@ private:
std::vector<Value> args;
std::vector<Block*> phi_blocks;
std::vector<Use> uses;
mutable uint32_t evaluation_index = UINT32_MAX;
};
} // namespace Libs::Graphics::ShaderRecompiler::IR
@@ -8,7 +8,6 @@
#include <cmath>
#include <cstring>
#include <fmt/format.h>
#include <unordered_map>
#include <unordered_set>
namespace Libs::Graphics::ShaderRecompiler::IR {
@@ -464,7 +463,12 @@ public:
std::span<const uint8_t> clean_flat_slots = {}, Evaluator* clean_evaluator = nullptr,
Value active_mask = {})
: m_program(program), m_runtime(runtime), m_clean_flat_slots(clean_flat_slots),
m_clean_evaluator(clean_evaluator), m_active_mask(active_mask.Resolve()) {}
m_clean_evaluator(clean_evaluator), m_active_mask(active_mask.Resolve()),
m_context(AcquireContext(program)) {}
~Evaluator() { --m_program.evaluation_depth; }
Evaluator(const Evaluator&) = delete;
Evaluator& operator=(const Evaluator&) = delete;
bool Evaluate(Value value, uint32_t& result) {
uint64_t wide = 0;
@@ -476,6 +480,15 @@ public:
}
private:
static ResourcePlan::EvaluationContext& AcquireContext(const ResourcePlan& program) {
if (program.evaluation_depth == program.evaluation_contexts.size()) {
program.evaluation_contexts.emplace_back();
}
auto& context = program.evaluation_contexts[program.evaluation_depth++];
context.generation += 2;
return context;
}
static float Float32(uint64_t bits) {
return std::bit_cast<float>(static_cast<uint32_t>(bits));
}
@@ -497,30 +510,33 @@ private:
if (inst == nullptr) {
return false;
}
if (!m_reserved) {
m_cache.reserve(m_program.value_storage.size());
m_visiting.reserve(m_program.value_storage.size());
m_reserved = true;
}
if (!m_active_mask.IsEmpty() && IsRuntimeSelect(inst->GetOpcode()) &&
inst->NumArgs() == 3 && inst->Arg(0).Resolve() == m_active_mask) {
return EvaluateWide(inst->Arg(1), result);
}
if (const auto found = m_cache.find(inst); found != m_cache.end()) {
result = found->second;
const auto index = inst->EvaluationIndex(m_program.evaluation_value_count);
if (index >= m_context.values.size()) {
m_context.values.resize(m_program.evaluation_value_count);
}
if (m_context.values[index].generation == m_context.generation) {
result = m_context.values[index].value;
return true;
}
if (std::ranges::find(m_visiting, inst) != m_visiting.end()) {
// The low generation bit marks an instruction that is still being evaluated.
if (m_context.values[index].generation == (m_context.generation | 1u)) {
return false;
}
m_visiting.push_back(inst);
m_context.values[index].generation = m_context.generation | 1u;
uint64_t out = 0;
const bool evaluated = EvaluateInst(*inst, out);
m_visiting.pop_back();
// Recursive evaluation may grow the dense memo vector.
auto& memo = m_context.values[index];
if (!evaluated) {
memo.generation = 0;
return false;
}
m_cache.emplace(inst, out);
memo.value = out;
memo.generation = m_context.generation;
result = out;
return true;
}
@@ -977,9 +993,7 @@ private:
std::span<const uint8_t> m_clean_flat_slots;
Evaluator* m_clean_evaluator = nullptr;
Value m_active_mask;
std::unordered_map<const Inst*, uint64_t> m_cache;
std::vector<const Inst*> m_visiting;
bool m_reserved = false;
ResourcePlan::EvaluationContext& m_context;
};
const DescriptorSource* Source(const ResourcePlan& program, uint32_t source) {