From 04cb4c6553c41e9cfe48135eb7ee5f86fe5106b9 Mon Sep 17 00:00:00 2001 From: nmzik Date: Wed, 14 Oct 2026 16:55:47 +0200 Subject: [PATCH] shader: separate exact descriptor row bounds from pointer offsets --- src/graphics/shader/recompiler/ir/ShaderIR.h | 9 ++------- .../recompiler/ir/passes/ResourceMaterialization.cpp | 11 +++++------ .../shader/recompiler/ir/passes/ResourceTracking.cpp | 8 +++++--- tests/ResourceTrackingTests.cpp | 6 +++--- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/graphics/shader/recompiler/ir/ShaderIR.h b/src/graphics/shader/recompiler/ir/ShaderIR.h index 425cee9f1..462aeee7a 100644 --- a/src/graphics/shader/recompiler/ir/ShaderIR.h +++ b/src/graphics/shader/recompiler/ir/ShaderIR.h @@ -525,12 +525,6 @@ struct ShaderInfo { struct DescriptorSource { struct IndirectDescriptor { - struct PointerTable { - uint32_t maximum = 0; - uint32_t offset = 0; - - bool operator==(const PointerTable&) const = default; - }; struct SelectorRead { struct CountedRows { uint32_t source = UINT32_MAX; @@ -553,7 +547,8 @@ struct DescriptorSource { bool operator==(const SelectorRead& other) const = default; }; std::optional selector; - std::optional pointer_table; + std::optional table_maximum; + std::optional pointer_offset; uint32_t table_source = 0; uint32_t table_offset = 0; uint32_t table_immediate = 0; diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp index 510cd296c..81f69588b 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceMaterialization.cpp @@ -367,13 +367,12 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p (table.Stride() & 3u) != 0u || table.Stride() < indirect.table_record_bytes))) return false; const bool affine = indirect.table_stride != 0u; - const auto* pointer = indirect.pointer_table ? &*indirect.pointer_table : nullptr; - const uint64_t step = pointer != nullptr ? indirect.table_stride + const uint64_t step = indirect.table_maximum ? indirect.table_stride : affine ? uint64_t {1} << std::countr_zero(indirect.table_stride) : 4u; - const uint64_t first = ((pointer != nullptr ? indirect.table_offset + const uint64_t first = ((indirect.table_maximum ? indirect.table_offset : indirect.table_offset % step) & ~uint64_t {3}) + (indirect.table_immediate & ~3u); - const uint64_t domain = pointer != nullptr ? uint64_t {pointer->maximum} + 1u + const uint64_t domain = indirect.table_maximum ? uint64_t {*indirect.table_maximum} + 1u : (uint64_t {1} << 32u) / step; const auto count = table_size >= first + 4u ? std::min((table_size - first - 4u) / step + 1u, domain) @@ -390,11 +389,11 @@ bool MaterializeIndirectDescriptor(const ResourcePlan& p snapshot.flattened_srt.resize(mapping_offset + 2u, 0u); for (uint32_t word = 0; word < count; ++word) { DescriptorValue candidate {.dword_count = dword_count}; - if (pointer != nullptr) { + if (indirect.pointer_offset) { std::array address {}; if (!ReadScalarTable(table_base, table_size, first + word * step, runtime, address) || !ReadScalarTable((uint64_t {address[1]} << 32u) | address[0], UINT64_MAX, - pointer->offset, runtime, + *indirect.pointer_offset, runtime, std::span(candidate.dwords).first(dword_count))) return false; } else if (affine) { if (!ReadScalarTable(table_base, table_size, first + word * step, runtime, diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp index b04dded41..118162ccc 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp @@ -400,7 +400,7 @@ public: std::vector> buffer_selections; for (const auto& plan: m_indirect_descriptors) { auto key = plan.key; - if (m_sources[plan.source].indirect_descriptor->pointer_table) { + if (m_sources[plan.source].indirect_descriptor->table_maximum) { auto* block = plan.handle->Parent(); const auto where = std::ranges::find_if(block->Instructions(), [&](const Inst& inst) { return &inst == plan.handle; }); @@ -974,7 +974,8 @@ private: if (current.indirect_descriptor.has_value()) { const auto& a = *current.indirect_descriptor; const auto& b = *descriptor.indirect_descriptor; - if (a.selector != b.selector || a.pointer_table != b.pointer_table || + if (a.selector != b.selector || a.table_maximum != b.table_maximum || + a.pointer_offset != b.pointer_offset || a.table_source != b.table_source || a.table_offset != b.table_offset || a.table_immediate != b.table_immediate || a.table_stride != b.table_stride || a.indexed != b.indexed || @@ -2779,7 +2780,8 @@ private: indirect.table_source = InternSource(table_source); indirect.table_immediate = low_memory->offset & ~3u; indirect.table_scalar = true; - indirect.pointer_table = {{maximum, descriptor_offset}}; + indirect.table_maximum = maximum; + indirect.pointer_offset = descriptor_offset; DescriptorSource source; source.dword_count = handle.NumArgs(); source.dwords.fill(Value(0u)); diff --git a/tests/ResourceTrackingTests.cpp b/tests/ResourceTrackingTests.cpp index 8c6d039a1..8658d0834 100644 --- a/tests/ResourceTrackingTests.cpp +++ b/tests/ResourceTrackingTests.cpp @@ -656,9 +656,9 @@ void TestNestedPointerImageTable() { for (const auto source : {plan.info.images[0].source, plan.info.samplers[0].source}) { const auto& descriptor = plan.descriptor_sources[source]; const auto& indirect = descriptor.indirect_descriptor; - Check(indirect && !indirect->selector && indirect->pointer_table && - indirect->pointer_table->maximum == 65535u && indirect->table_stride == 592u && - indirect->pointer_table->offset == (descriptor.dword_count == 8u ? 0x3d0u : 0x3f8u), + Check(indirect && !indirect->selector && indirect->table_maximum == 65535u && + indirect->table_stride == 592u && + indirect->pointer_offset == (descriptor.dword_count == 8u ? 0x3d0u : 0x3f8u), "nested pointer descriptors lost their row geometry or captured the GPU selector payload"); }