diff --git a/src/graphics/shader/recompiler/ir/Serialize.h b/src/graphics/shader/recompiler/ir/Serialize.h index 29427b229..e7de23560 100644 --- a/src/graphics/shader/recompiler/ir/Serialize.h +++ b/src/graphics/shader/recompiler/ir/Serialize.h @@ -165,7 +165,7 @@ KYTY_IO_FIELDS(DescriptorSource::IndirectDescriptor, a, b, c, d, e, f, g, h, i, q, r) KYTY_IO_FIELDS(DescriptorSource, a, b, c) KYTY_IO_FIELDS(SrtRead, a, b) -KYTY_IO_FIELDS(ResourceBlock, a, b, c, d) +KYTY_IO_FIELDS(ResourceBlock, a, b, c, d, e) KYTY_IO_FIELDS(UniformFill, a, b, c, d, e, f) KYTY_IO_FIELDS(UniformFillPlan, a, b, c, d) KYTY_IO_FIELDS(ResourceSpecialization::Buffer, a, b, c, d, e, f, g, h) diff --git a/src/graphics/shader/recompiler/ir/ShaderIR.h b/src/graphics/shader/recompiler/ir/ShaderIR.h index ce4627186..fe6b21346 100644 --- a/src/graphics/shader/recompiler/ir/ShaderIR.h +++ b/src/graphics/shader/recompiler/ir/ShaderIR.h @@ -647,6 +647,7 @@ struct ResourceBlock { Value condition; std::vector successors; std::vector sources; + std::vector images; std::vector srt_reads; }; diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp index b1626c737..152400b19 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp @@ -1220,14 +1220,16 @@ static std::vector ResourceControlFlow(const Program& program, co block.sources.push_back(program.info.buffers.at(memory.resource).source); } else { block.sources.push_back(program.info.images.at(memory.resource).source); + block.images.push_back(memory.resource); if (image.needs_sampler) { block.sources.push_back(program.info.samplers.at(memory.sampler).source); } } } - std::ranges::sort(block.sources); - block.sources.erase(std::unique(block.sources.begin(), block.sources.end()), - block.sources.end()); + for (auto* list: {&block.sources, &block.images}) { + std::ranges::sort(*list); + list->erase(std::unique(list->begin(), list->end()), list->end()); + } } for (auto& block: blocks) block.condition = predicate(block.condition); if (std::ranges::none_of( @@ -1701,7 +1703,7 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime } if (source->indirect_descriptor.has_value()) { snapshot.images[i] = {.dword_count = 8u}; - if (!WalkedActive(program, image.source)) { + if (!WalkedActive(program, image.source) || !WalkedImageActive(program, i)) { continue; } const auto& indirect = *source->indirect_descriptor; @@ -1721,7 +1723,9 @@ bool MaterializeResources(const ResourcePlan& program, const SrtRuntime& runtime if (!evaluate(image.source, snapshot.images[i])) { return false; } - if (!ValidImageDescriptor(snapshot.images[i], image.r128)) { + // An image listed only by unreached blocks reads zero, not its source's descriptor. + if (!WalkedImageActive(program, i) || + !ValidImageDescriptor(snapshot.images[i], image.r128)) { snapshot.images[i].dwords.fill(0); } } diff --git a/src/graphics/shader/recompiler/ir/passes/SrtWalker.cpp b/src/graphics/shader/recompiler/ir/passes/SrtWalker.cpp index 43de7e5c3..799515e22 100644 --- a/src/graphics/shader/recompiler/ir/passes/SrtWalker.cpp +++ b/src/graphics/shader/recompiler/ir/passes/SrtWalker.cpp @@ -148,8 +148,8 @@ bool ImmediateBits(Value value, uint64_t& bits) { } // Scratch layout in bytes: per descriptor source its eight DWORDs and a known DWORD, one -// activity byte per source, then one qword per walked value (all ones when unknown). The -// walker's value slots, captured addresses, reachability and states follow. +// activity byte per source, then per image, then one qword per walked value (all ones when +// unknown). The walker's value slots, captured addresses, reachability and states follow. constexpr uint32_t DescriptorStride = 9u * sizeof(uint32_t); uint32_t ActiveOffset(const ResourcePlan& plan) { @@ -157,7 +157,8 @@ uint32_t ActiveOffset(const ResourcePlan& plan) { } uint32_t OutputOffset(const ResourcePlan& plan) { - return (ActiveOffset(plan) + static_cast(plan.descriptor_sources.size()) + 7u) & ~7u; + const auto active = plan.descriptor_sources.size() + plan.info.images.size(); + return (ActiveOffset(plan) + static_cast(active) + 7u) & ~7u; } const uint8_t* Scratch(const ResourcePlan& plan) { @@ -1290,11 +1291,14 @@ void WalkerCompiler::Compile(std::span values) { for (const auto slot: blocks[block].srt_reads) flat(slot); c.L(skip); } - // A source no block lists is always active. - for (uint32_t source = 0; source < sources.size(); ++source) { + // A source or image no block lists is always active. + for (uint32_t index = 0; index < sources.size() + m_plan.info.images.size(); ++index) { + const bool image = index >= sources.size(); + const uint32_t id = image ? index - static_cast(sources.size()) : index; bool listed = false; for (uint32_t block = 0; block < blocks.size(); ++block) { - if (std::ranges::find(blocks[block].sources, source) == blocks[block].sources.end()) continue; + const auto& list = image ? blocks[block].images : blocks[block].sources; + if (std::ranges::find(list, id) == list.end()) continue; if (listed) { c.or_(c.al, c.byte[c.rdx + m_reach + block]); } else { @@ -1302,7 +1306,7 @@ void WalkerCompiler::Compile(std::span values) { } listed = true; } - const auto active = c.byte[c.rdx + ActiveOffset(m_plan) + source]; + const auto active = c.byte[c.rdx + ActiveOffset(m_plan) + index]; if (listed) { c.mov(active, c.al); } else { @@ -1409,6 +1413,10 @@ bool WalkedActive(const ResourcePlan& plan, uint32_t source) { return Scratch(plan)[ActiveOffset(plan) + source] != 0u; } +bool WalkedImageActive(const ResourcePlan& plan, uint32_t image) { + return Scratch(plan)[ActiveOffset(plan) + plan.descriptor_sources.size() + image] != 0u; +} + bool ValidateRuntimeValue(const ResourcePlan& program, Value value, RuntimeValueType type) { return RuntimeValidator(program, type).Run(value); } diff --git a/src/graphics/shader/recompiler/ir/passes/SrtWalker.h b/src/graphics/shader/recompiler/ir/passes/SrtWalker.h index adf02dfe4..74dee70fe 100644 --- a/src/graphics/shader/recompiler/ir/passes/SrtWalker.h +++ b/src/graphics/shader/recompiler/ir/passes/SrtWalker.h @@ -78,6 +78,7 @@ bool WalkSrt(const ResourcePlan& plan, const SrtRuntime& runtime, std::vector data{uint32_t(BufferFormat::k8_8_8_8UNorm) << 20u, 0u}; + const SrtRuntime runtime{.user_data = data}; + ResourceSnapshot snapshot; + ResourceSpecialization specialization; + Check(MaterializeResources(plan, runtime, snapshot, specialization) && + snapshot.images[0].dwords[1] == data[0] && snapshot.images[2].dwords[1] == data[0] && + snapshot.images[1] == DescriptorValue{.dword_count = 8u}, + "unreached atomic image kept the shared descriptor or an unlisted image lost it"); + data[1] = 1u; + Check(!MaterializeResources(plan, runtime, snapshot, specialization), + "reached atomic image accepted a format its width rejects"); +} + void TestIndirectImageSamplerClasses(bool mixed) { using namespace Libs::Graphics::ShaderRecompiler::IR; using Libs::Graphics::Prospero::BufferFormat; @@ -1451,6 +1507,7 @@ int main() { TestEmptyScalarBufferSpecialization(); TestMixedSamplerVariantsShareRuntimeDescriptor(); TestSignedStorageSpecialization(); + for (const bool atomic64 : {false, true}) TestUnreachedImageIgnoresSharedDescriptor(atomic64); for (const bool mixed : {false, true}) TestIndirectImageSamplerClasses(mixed); TestShaderCacheRecords(); std::puts("ResourceMaterializationTests: all cases passed");