mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-29 22:36:52 +01:00
shader: capture only memory-dependent resource predicates
This commit is contained in:
@@ -1007,10 +1007,15 @@ ResourcePlan ExtractResourcePlan(const Program& program) {
|
|||||||
// A proven uniform factor can decide a branch even when its other lanes are unknown.
|
// A proven uniform factor can decide a branch even when its other lanes are unknown.
|
||||||
// Keep only that Boolean structure, never the varying shader dependency graph.
|
// Keep only that Boolean structure, never the varying shader dependency graph.
|
||||||
Value unknown;
|
Value unknown;
|
||||||
|
bool capture_control_reads = false;
|
||||||
std::function<Value(Value)> ClonePredicate = [&](Value value) -> Value {
|
std::function<Value(Value)> ClonePredicate = [&](Value value) -> Value {
|
||||||
value = value.Resolve();
|
value = value.Resolve();
|
||||||
if (value.IsEmpty()) return {};
|
if (value.IsEmpty()) return {};
|
||||||
if (ValidateRuntimeValue(program, value, RuntimeValueType::Integer)) return Clone(value);
|
if (ValidateRuntimeValue(program, value, RuntimeValueType::Integer)) {
|
||||||
|
capture_control_reads |=
|
||||||
|
!ValidateRuntimeValue(program, value, RuntimeValueType::ImmutableInteger);
|
||||||
|
return Clone(value);
|
||||||
|
}
|
||||||
const auto* inst = value.TryInstruction();
|
const auto* inst = value.TryInstruction();
|
||||||
if (inst == nullptr) return {};
|
if (inst == nullptr) return {};
|
||||||
const auto op = inst->GetOpcode();
|
const auto op = inst->GetOpcode();
|
||||||
@@ -1040,7 +1045,7 @@ ResourcePlan ExtractResourcePlan(const Program& program) {
|
|||||||
for (uint32_t i = 0; i < plan.uniform_fill.fill.words; ++i) {
|
for (uint32_t i = 0; i < plan.uniform_fill.fill.words; ++i) {
|
||||||
plan.uniform_fill.values[i] = Clone(plan.uniform_fill.values[i]);
|
plan.uniform_fill.values[i] = Clone(plan.uniform_fill.values[i]);
|
||||||
}
|
}
|
||||||
plan.capture_specialization_reads |= capture_indirect_reads || !plan.control_flow.empty();
|
plan.capture_specialization_reads |= capture_indirect_reads || capture_control_reads;
|
||||||
if (plan.capture_specialization_reads) {
|
if (plan.capture_specialization_reads) {
|
||||||
// Every former flat alias now targets its retained raw read. Mark those reads
|
// Every former flat alias now targets its retained raw read. Mark those reads
|
||||||
// on the normalized graph, so discarded Phi/EXEC dependencies stay discarded.
|
// on the normalized graph, so discarded Phi/EXEC dependencies stay discarded.
|
||||||
|
|||||||
@@ -966,7 +966,8 @@ bool SrtWalker::RefreshFlatBuffer(std::vector<uint32_t>& flat) {
|
|||||||
}
|
}
|
||||||
uint32_t condition = 0;
|
uint32_t condition = 0;
|
||||||
auto& predicate = m_clean_evaluator != nullptr ? *m_clean_evaluator : *this;
|
auto& predicate = m_clean_evaluator != nullptr ? *m_clean_evaluator : *this;
|
||||||
if (!block.condition.IsEmpty() && m_runtime.read_specialization_memory != nullptr &&
|
if (!block.condition.IsEmpty() &&
|
||||||
|
(m_runtime.read_specialization_memory != nullptr || !m_program.capture_specialization_reads) &&
|
||||||
predicate.Evaluate(block.condition, condition)) {
|
predicate.Evaluate(block.condition, condition)) {
|
||||||
pending.push_back(block.successors[condition != 0u ? 0u : 1u]);
|
pending.push_back(block.successors[condition != 0u ? 0u : 1u]);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -435,7 +435,7 @@ void TestUnbasedFlatCacheHitMaterializes() {
|
|||||||
"unbased FLAT plan produced unexpected descriptors");
|
"unbased FLAT plan produced unexpected descriptors");
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestWrittenDescriptorUsesStrictReaderOnce() {
|
void TestWrittenDescriptorPredicateReads(bool memory_condition) {
|
||||||
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
using namespace Libs::Graphics::ShaderRecompiler::IR;
|
||||||
Program program;
|
Program program;
|
||||||
program.stage = Libs::Graphics::ShaderType::Compute;
|
program.stage = Libs::Graphics::ShaderType::Compute;
|
||||||
@@ -456,10 +456,10 @@ void TestWrittenDescriptorUsesStrictReaderOnce() {
|
|||||||
source.dword_count = 4;
|
source.dword_count = 4;
|
||||||
program.descriptor_sources.push_back(source);
|
program.descriptor_sources.push_back(source);
|
||||||
program.info.buffers.push_back({.source = 0, .written = true});
|
program.info.buffers.push_back({.source = 0, .written = true});
|
||||||
// A host-evaluable branch captures resource reads and needs the writable
|
// Only memory-derived host decisions need clean, disjoint descriptor reads.
|
||||||
// descriptor's clean provenance for the renderer's disjointness proof.
|
|
||||||
auto &condition = block.AppendNewInst(ValueOpcode::IEqual32,
|
auto &condition = block.AppendNewInst(ValueOpcode::IEqual32,
|
||||||
{Value(&offset), Value(4u)});
|
{memory_condition ? Value(&read) : Value(&offset),
|
||||||
|
Value(memory_condition ? 0x8000u : 4u)});
|
||||||
auto &store_block = AddValueBlock(program);
|
auto &store_block = AddValueBlock(program);
|
||||||
AddValueBlock(program);
|
AddValueBlock(program);
|
||||||
program.blocks[0]->condition = Value(&condition);
|
program.blocks[0]->condition = Value(&condition);
|
||||||
@@ -480,8 +480,8 @@ void TestWrittenDescriptorUsesStrictReaderOnce() {
|
|||||||
{Value(&output), Value(0u), Value(0u), Value(0u), Value(1u), Value(true)})
|
{Value(&output), Value(0u), Value(0u), Value(0u), Value(1u), Value(true)})
|
||||||
.SetFlags(MemoryFlags{.index = 1});
|
.SetFlags(MemoryFlags{.index = 1});
|
||||||
auto plan = ExtractResourcePlan(program);
|
auto plan = ExtractResourcePlan(program);
|
||||||
Check(plan.capture_specialization_reads,
|
Check(plan.capture_specialization_reads == memory_condition && !plan.control_flow.empty(),
|
||||||
"conditional writable descriptor lost its alias proof");
|
"conditional writable descriptor used the wrong memory dependency policy");
|
||||||
struct Reads { uint32_t ordinary = 0; uint32_t strict = 0; bool clean = false; } reads;
|
struct Reads { uint32_t ordinary = 0; uint32_t strict = 0; bool clean = false; } reads;
|
||||||
const std::array<uint32_t, 1> user_data{4u};
|
const std::array<uint32_t, 1> user_data{4u};
|
||||||
const SrtRuntime runtime{
|
const SrtRuntime runtime{
|
||||||
@@ -501,16 +501,21 @@ void TestWrittenDescriptorUsesStrictReaderOnce() {
|
|||||||
}};
|
}};
|
||||||
ResourceSnapshot snapshot;
|
ResourceSnapshot snapshot;
|
||||||
ResourceSpecialization specialization;
|
ResourceSpecialization specialization;
|
||||||
Check(!MaterializeResources(plan, runtime, snapshot, specialization) &&
|
for (const bool clean : {false, true}) {
|
||||||
reads.ordinary == 0 && reads.strict == 1,
|
reads = {.clean = clean};
|
||||||
"GPU-dirty dynamic writable descriptor bypassed strict provenance");
|
// A failed predicate explores both edges and the writable descriptor retries its clean read.
|
||||||
reads.clean = true;
|
const bool expected = !memory_condition || clean;
|
||||||
reads.strict = 0;
|
Check(MaterializeResources(plan, runtime, snapshot, specialization) == expected &&
|
||||||
Check(MaterializeResources(plan, runtime, snapshot, specialization) &&
|
reads.ordinary == (memory_condition ? 0u : 1u) &&
|
||||||
reads.ordinary == 0 && reads.strict == 1 && snapshot.buffers[0].dwords[0] == 0x8000u &&
|
reads.strict == (memory_condition ? (clean ? 1u : 2u) : 0u),
|
||||||
snapshot.specialization_reads ==
|
"writable descriptor changed reader policy or bypassed a dirty memory predicate");
|
||||||
std::vector<std::pair<uint64_t, uint64_t>>{{0x1004u, 4u}},
|
if (!expected) continue;
|
||||||
"writable descriptor was evaluated twice or scalar EXEC suppressed its read");
|
Check(snapshot.buffers[0].dwords[0] == 0x8000u &&
|
||||||
|
snapshot.specialization_reads == (memory_condition
|
||||||
|
? std::vector<std::pair<uint64_t, uint64_t>>{{0x1004u, 4u}}
|
||||||
|
: std::vector<std::pair<uint64_t, uint64_t>>{}),
|
||||||
|
"writable descriptor lost its address or captured an immutable predicate");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestFailedMaterializationRejectsStage() {
|
void TestFailedMaterializationRejectsStage() {
|
||||||
@@ -671,7 +676,8 @@ int main() {
|
|||||||
TestUniformVectorDescriptorRead();
|
TestUniformVectorDescriptorRead();
|
||||||
TestExactReciprocalDescriptorArithmetic();
|
TestExactReciprocalDescriptorArithmetic();
|
||||||
TestUnbasedFlatCacheHitMaterializes();
|
TestUnbasedFlatCacheHitMaterializes();
|
||||||
TestWrittenDescriptorUsesStrictReaderOnce();
|
for (const bool memory_condition : {false, true})
|
||||||
|
TestWrittenDescriptorPredicateReads(memory_condition);
|
||||||
TestFailedMaterializationRejectsStage();
|
TestFailedMaterializationRejectsStage();
|
||||||
TestFiniteImageRefreshReusesScalarReads();
|
TestFiniteImageRefreshReusesScalarReads();
|
||||||
TestMixedSamplerVariantsShareRuntimeDescriptor();
|
TestMixedSamplerVariantsShareRuntimeDescriptor();
|
||||||
|
|||||||
@@ -13779,7 +13779,7 @@ void TestUniformSelectedDescriptorLoadAddress() {
|
|||||||
auto translated = ShaderRecompiler::TranslateProgram(shader, options);
|
auto translated = ShaderRecompiler::TranslateProgram(shader, options);
|
||||||
auto plan = ExtractResourcePlan(translated.program);
|
auto plan = ExtractResourcePlan(translated.program);
|
||||||
Check(plan.srt_reads.size() == 4u && plan.info.buffers.size() == 1u &&
|
Check(plan.srt_reads.size() == 4u && plan.info.buffers.size() == 1u &&
|
||||||
plan.info.buffers[0].written,
|
plan.info.buffers[0].written && !plan.capture_specialization_reads,
|
||||||
"conditional scalar address duplicated or detached descriptor reads");
|
"conditional scalar address duplicated or detached descriptor reads");
|
||||||
const auto read = +[](void *data, uint64_t address, std::span<uint32_t> words) {
|
const auto read = +[](void *data, uint64_t address, std::span<uint32_t> words) {
|
||||||
const auto flag = *static_cast<uint32_t *>(data);
|
const auto flag = *static_cast<uint32_t *>(data);
|
||||||
@@ -13792,13 +13792,14 @@ void TestUniformSelectedDescriptorLoadAddress() {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
const SrtRuntime runtime{.user_data = user_data, .read_memory = read,
|
const SrtRuntime runtime{.user_data = user_data, .read_memory = read,
|
||||||
.userdata = &user_data[0], .read_specialization_memory = read};
|
.userdata = &user_data[0]};
|
||||||
ResourceSnapshot snapshot;
|
ResourceSnapshot snapshot;
|
||||||
ResourceSpecialization specialization;
|
ResourceSpecialization specialization;
|
||||||
for (const uint32_t flag : {0u, 1u, 0u}) {
|
for (const uint32_t flag : {0u, 1u, 0u}) {
|
||||||
user_data[0] = flag;
|
user_data[0] = flag;
|
||||||
Check(MaterializeResources(plan, runtime, snapshot, specialization) &&
|
Check(MaterializeResources(plan, runtime, snapshot, specialization) &&
|
||||||
snapshot.buffers[0].dwords[0] == 0x3000u + flag * 0x1000u,
|
snapshot.buffers[0].dwords[0] == 0x3000u + flag * 0x1000u &&
|
||||||
|
snapshot.specialization_reads.empty(),
|
||||||
"conditional descriptor load read the untaken pointer");
|
"conditional descriptor load read the untaken pointer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user