mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 19:07:01 +02:00
shader: share runtime value checks
This commit is contained in:
@@ -620,36 +620,6 @@ bool BuildSamplerPlan(const ShaderInfo& base, const Images& images, SamplerPlan&
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool UniformIntegerValue(const ResourcePlan& program, Value root) {
|
||||
if (!ValidateRuntimeValue(program, root)) {
|
||||
return false;
|
||||
}
|
||||
std::vector<Value> pending {root};
|
||||
std::unordered_set<const Inst*> visited;
|
||||
while (!pending.empty()) {
|
||||
const auto value = pending.back().Resolve();
|
||||
pending.pop_back();
|
||||
// Host floating-point evaluation does not model the shader's rounding/denormal modes.
|
||||
if (TypesOverlap(value.GetType(), Type::F16 | Type::F32 | Type::F32x2)) {
|
||||
return false;
|
||||
}
|
||||
const auto* inst = value.TryInstruction();
|
||||
if (inst != nullptr && visited.insert(inst).second) {
|
||||
if (inst->GetOpcode() == ValueOpcode::ReadConst) {
|
||||
const auto& read = program.srt_reads[inst->Arg(1).Resolve().U32()];
|
||||
if (!ValidateRuntimeValue(program, read.value)) {
|
||||
return false;
|
||||
}
|
||||
pending.push_back(read.value);
|
||||
}
|
||||
for (size_t i = 0; i < inst->NumArgs(); i++) {
|
||||
pending.push_back(inst->Arg(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::vector<ResourceBlock> ResourceControlFlow(const Program& program) {
|
||||
if (program.blocks.size() != program.block_info.size()) {
|
||||
return {};
|
||||
@@ -670,7 +640,7 @@ static std::vector<ResourceBlock> ResourceControlFlow(const Program& program) {
|
||||
case CFG::TerminatorKind::Branch: successors.push_back(terminator.true_block); break;
|
||||
case CFG::TerminatorKind::ConditionalBranch:
|
||||
successors = {terminator.true_block, terminator.false_block};
|
||||
if (UniformIntegerValue(program, info.condition)) {
|
||||
if (ValidateRuntimeValue(program, info.condition, RuntimeValueType::Integer)) {
|
||||
block.condition = info.condition;
|
||||
}
|
||||
break;
|
||||
@@ -820,8 +790,12 @@ static UniformFillPlan AnalyzeUniformFill(const Program& program) {
|
||||
if (address == nullptr || address->GetOpcode() != ValueOpcode::MakeImageAddress) return {};
|
||||
for (uint32_t axis = 0; axis < 3; ++axis) {
|
||||
const auto index = FillIndex(address->Arg(axis), axis);
|
||||
if (!index || (*index)[0] != 0 || (*index)[1] != (axis < 2 ? 1u : 0u) ||
|
||||
(*index)[2] == 0 || (axis == 2 && (*index)[2] != 1)) return {};
|
||||
if (!index || (*index)[0] != 0) return {};
|
||||
if (axis < 2) {
|
||||
if ((*index)[1] != 1 || (*index)[2] == 0) return {};
|
||||
} else if ((*index)[1] != 0 || (*index)[2] != 1) {
|
||||
return {};
|
||||
}
|
||||
result.fill.group_stride[axis] = static_cast<uint32_t>((*index)[2]);
|
||||
}
|
||||
const auto* values = store->Arg(2).ResolveInstruction();
|
||||
@@ -855,12 +829,14 @@ static UniformFillPlan AnalyzeUniformFill(const Program& program) {
|
||||
constexpr std::array composites {ValueOpcode::CompositeConstructU32x2,
|
||||
ValueOpcode::CompositeConstructU32x3,
|
||||
ValueOpcode::CompositeConstructU32x4};
|
||||
if (result.fill.words > 1 &&
|
||||
(vector == nullptr || vector->GetOpcode() != composites[result.fill.words - 2]))
|
||||
return {};
|
||||
for (uint32_t i = 0; i < result.fill.words; ++i) {
|
||||
if (result.fill.words > 1 &&
|
||||
(vector == nullptr || vector->GetOpcode() != composites[result.fill.words - 2]))
|
||||
return {};
|
||||
const auto word = result.fill.words == 1 ? data : vector->Arg(i);
|
||||
if (word.GetType() != Type::U32 || !UniformIntegerValue(program, word)) return {};
|
||||
if (word.GetType() != Type::U32 ||
|
||||
!ValidateRuntimeValue(program, word, RuntimeValueType::Integer))
|
||||
return {};
|
||||
result.values[i] = word;
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -134,16 +134,29 @@ bool IsRuntimeUniformOp(ValueOpcode op) {
|
||||
|
||||
class RuntimeValidator {
|
||||
public:
|
||||
explicit RuntimeValidator(const ResourcePlan& program, Value active_mask = {})
|
||||
: m_program(program), m_active_mask(active_mask.Resolve()) {}
|
||||
explicit RuntimeValidator(const ResourcePlan& program, RuntimeValueType type)
|
||||
: m_program(program), m_type(type) {}
|
||||
|
||||
bool Run(Value value) { return Validate(value); }
|
||||
|
||||
private:
|
||||
bool Validate(Value value) {
|
||||
value = value.Resolve();
|
||||
bool ValidateArguments(const Inst& inst, bool require_uniform) {
|
||||
for (size_t index = 0; index < inst.NumArgs(); index++) {
|
||||
if (!Validate(inst.Arg(index), require_uniform)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Validate(Value value, bool require_uniform = true) {
|
||||
value = value.Resolve();
|
||||
// Host floating-point evaluation does not model shader rounding/denormal modes.
|
||||
if (m_type == RuntimeValueType::Integer &&
|
||||
TypesOverlap(value.GetType(), Type::F16 | Type::F32 | Type::F32x2)) {
|
||||
return false;
|
||||
}
|
||||
const auto* inst = value.TryInstruction();
|
||||
if (inst == nullptr) {
|
||||
if (!require_uniform) return true;
|
||||
switch (value.GetType()) {
|
||||
case Type::U1:
|
||||
case Type::U8:
|
||||
@@ -155,15 +168,37 @@ private:
|
||||
}
|
||||
}
|
||||
if (!m_visiting.insert(inst).second) {
|
||||
return false;
|
||||
return !require_uniform;
|
||||
}
|
||||
const auto finish = [&](bool valid) {
|
||||
m_visiting.erase(inst);
|
||||
return valid;
|
||||
};
|
||||
const auto op = inst->GetOpcode();
|
||||
if (op == ValueOpcode::ReadConst) {
|
||||
const auto slot = inst->NumArgs() == 2 ? inst->Arg(1).Resolve() : Value {};
|
||||
if (inst->NumArgs() != 2 || inst->Arg(0).Resolve().TryInstruction() == nullptr ||
|
||||
inst->Arg(0).Resolve().TryInstruction()->GetOpcode() !=
|
||||
ValueOpcode::GetSrtResource ||
|
||||
!slot.IsImmediate() || slot.GetType() != Type::U32 ||
|
||||
slot.U32() >= m_program.srt_reads.size()) {
|
||||
return finish(false);
|
||||
}
|
||||
if (m_type == RuntimeValueType::Integer) {
|
||||
const auto active_mask = m_active_mask;
|
||||
m_active_mask = {};
|
||||
const bool valid = Validate(m_program.srt_reads[slot.U32()].value);
|
||||
m_active_mask = active_mask;
|
||||
if (!valid) return finish(false);
|
||||
}
|
||||
}
|
||||
if (!require_uniform) return finish(ValidateArguments(*inst, false));
|
||||
if (!m_active_mask.IsEmpty() && IsRuntimeSelect(op) && inst->NumArgs() == 3 &&
|
||||
inst->Arg(0).Resolve() == m_active_mask) {
|
||||
// Empty EXEC reads lane zero, so ignored operands still require integer types.
|
||||
if (m_type == RuntimeValueType::Integer && !Validate(inst->Arg(2), false)) {
|
||||
return finish(false);
|
||||
}
|
||||
return finish(Validate(inst->Arg(1)));
|
||||
}
|
||||
if (op == ValueOpcode::UndefU1 || op == ValueOpcode::UndefU8 ||
|
||||
@@ -189,6 +224,9 @@ private:
|
||||
return finish(true);
|
||||
}
|
||||
if (op == ValueOpcode::Phi) {
|
||||
if (m_type == RuntimeValueType::Integer && !ValidateArguments(*inst, false)) {
|
||||
return finish(false);
|
||||
}
|
||||
const auto invariant = ResolveInvariantPhi(m_program, value);
|
||||
if (invariant.IsEmpty()) {
|
||||
return finish(false);
|
||||
@@ -200,7 +238,14 @@ private:
|
||||
inst->Arg(1).GetType() != Type::U1) {
|
||||
return finish(false);
|
||||
}
|
||||
return finish(RuntimeValidator(m_program, inst->Arg(1)).Run(inst->Arg(0)));
|
||||
if (m_type == RuntimeValueType::Integer && !Validate(inst->Arg(1), false)) {
|
||||
return finish(false);
|
||||
}
|
||||
const auto active_mask = m_active_mask;
|
||||
m_active_mask = inst->Arg(1).Resolve();
|
||||
const bool valid = Validate(inst->Arg(0));
|
||||
m_active_mask = active_mask;
|
||||
return finish(valid);
|
||||
}
|
||||
if (op == ValueOpcode::GetSrtResource) {
|
||||
if (inst->NumArgs() != 0) {
|
||||
@@ -208,16 +253,7 @@ private:
|
||||
}
|
||||
return finish(true);
|
||||
}
|
||||
if (op == ValueOpcode::ReadConst) {
|
||||
const auto slot = inst->NumArgs() == 2 ? inst->Arg(1).Resolve() : Value {};
|
||||
if (inst->NumArgs() != 2 || inst->Arg(0).Resolve().TryInstruction() == nullptr ||
|
||||
inst->Arg(0).Resolve().TryInstruction()->GetOpcode() !=
|
||||
ValueOpcode::GetSrtResource ||
|
||||
!slot.IsImmediate() || slot.GetType() != Type::U32 ||
|
||||
slot.U32() >= m_program.srt_reads.size()) {
|
||||
return finish(false);
|
||||
}
|
||||
} else if (op == ValueOpcode::LoadAddressU32 || op == ValueOpcode::ReadConstBuffer) {
|
||||
if (op == ValueOpcode::LoadAddressU32 || op == ValueOpcode::ReadConstBuffer) {
|
||||
const auto expected = op == ValueOpcode::LoadAddressU32
|
||||
? ValueOpcode::GetAddressResource
|
||||
: ValueOpcode::GetBufferResource;
|
||||
@@ -243,10 +279,12 @@ private:
|
||||
}
|
||||
if (op == ValueOpcode::GetBufferResource || op == ValueOpcode::GetImageResource ||
|
||||
op == ValueOpcode::GetSamplerResource || op == ValueOpcode::GetAddressResource) {
|
||||
const size_t expected = op == ValueOpcode::GetBufferResource ? 4u
|
||||
: op == ValueOpcode::GetImageResource ? 8u
|
||||
: op == ValueOpcode::GetSamplerResource ? 4u
|
||||
: 2u;
|
||||
size_t expected = 4u;
|
||||
if (op == ValueOpcode::GetImageResource) {
|
||||
expected = 8u;
|
||||
} else if (op == ValueOpcode::GetAddressResource) {
|
||||
expected = 2u;
|
||||
}
|
||||
if (inst->NumArgs() != expected) {
|
||||
return finish(false);
|
||||
}
|
||||
@@ -254,15 +292,11 @@ private:
|
||||
op != ValueOpcode::LoadAddressU32 && !IsRuntimeUniformOp(op)) {
|
||||
return finish(false);
|
||||
}
|
||||
for (size_t index = 0; index < inst->NumArgs(); index++) {
|
||||
if (!Validate(inst->Arg(index))) {
|
||||
return finish(false);
|
||||
}
|
||||
}
|
||||
return finish(true);
|
||||
return finish(ValidateArguments(*inst, true));
|
||||
}
|
||||
|
||||
const ResourcePlan& m_program;
|
||||
RuntimeValueType m_type;
|
||||
Value m_active_mask;
|
||||
std::unordered_set<const Inst*> m_visiting;
|
||||
};
|
||||
@@ -952,7 +986,10 @@ bool EvaluateRuntimeSourcesImpl(const ResourcePlan& program, std::span<const uin
|
||||
clean_runtime.read_memory = runtime.read_specialization_memory;
|
||||
Evaluator clean_evaluator(program, clean_runtime);
|
||||
Evaluator evaluator(program, runtime, clean_flat_slots, &clean_evaluator);
|
||||
std::vector<uint8_t> active(program.descriptor_sources.size(), 1u);
|
||||
std::vector<uint8_t> active;
|
||||
if (evaluate_flat) {
|
||||
active.assign(program.descriptor_sources.size(), 1u);
|
||||
}
|
||||
if (evaluate_flat && !program.control_flow.empty()) {
|
||||
for (const auto& block: program.control_flow) {
|
||||
for (const auto source: block.sources) {
|
||||
@@ -991,9 +1028,11 @@ bool EvaluateRuntimeSourcesImpl(const ResourcePlan& program, std::span<const uin
|
||||
}
|
||||
DescriptorValue value;
|
||||
value.dword_count = source->dword_count;
|
||||
for (uint32_t index = 0; active[source_index] && index < source->dword_count; index++) {
|
||||
if (!evaluator.Evaluate(source->dwords[index], value.dwords[index])) {
|
||||
return false;
|
||||
if (!evaluate_flat || active[source_index]) {
|
||||
for (uint32_t index = 0; index < source->dword_count; index++) {
|
||||
if (!evaluator.Evaluate(source->dwords[index], value.dwords[index])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
evaluated.push_back(value);
|
||||
@@ -1021,8 +1060,8 @@ bool EvaluateRuntimeSourcesImpl(const ResourcePlan& program, std::span<const uin
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ValidateRuntimeValue(const ResourcePlan& program, Value value) {
|
||||
return RuntimeValidator(program).Run(value);
|
||||
bool ValidateRuntimeValue(const ResourcePlan& program, Value value, RuntimeValueType type) {
|
||||
return RuntimeValidator(program, type).Run(value);
|
||||
}
|
||||
|
||||
void BuildSrtPlan(Program& program) {
|
||||
|
||||
@@ -19,10 +19,13 @@ struct SrtRuntime {
|
||||
SrtMemoryReader read_specialization_memory = nullptr;
|
||||
};
|
||||
|
||||
enum class RuntimeValueType { Any, Integer };
|
||||
|
||||
// Collects reachable ReadConst values. Immediate offsets receive compact flat-buffer slots;
|
||||
// dynamic offsets remain explicit and are never assigned a fake slot.
|
||||
void BuildSrtPlan(Program& program);
|
||||
bool ValidateRuntimeValue(const ResourcePlan& program, Value value);
|
||||
bool ValidateRuntimeValue(const ResourcePlan& program, Value value,
|
||||
RuntimeValueType type = RuntimeValueType::Any);
|
||||
bool EvaluateUniformValues(const ResourcePlan& program, std::span<const Value> values,
|
||||
const SrtRuntime& runtime, std::span<uint32_t> results);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user