From 65a6f30f514ccafa3dc1cbbc414c6ffb8cdcc9b7 Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 15 Oct 2026 00:16:47 +0200 Subject: [PATCH] shader: compare real nested descriptor pointer instructions --- src/graphics/shader/recompiler/ir/Value.h | 1 + .../recompiler/ir/passes/ResourceTracking.cpp | 4 +- tests/ResourceTrackingTests.cpp | 37 ++++++++++++++++++- 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/graphics/shader/recompiler/ir/Value.h b/src/graphics/shader/recompiler/ir/Value.h index 72dc8a59f..72583eef8 100644 --- a/src/graphics/shader/recompiler/ir/Value.h +++ b/src/graphics/shader/recompiler/ir/Value.h @@ -21,6 +21,7 @@ class Value { public: Value() = default; explicit Value(Inst* value); + explicit Value(const Inst* value) = delete; explicit Value(ScalarReg value); explicit Value(VectorReg value); explicit Value(bool value); diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp index 381d3a107..0ee81191c 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp @@ -2768,7 +2768,7 @@ private: IndirectDescriptorPlan& plan) { if (handle.GetOpcode() != ValueOpcode::GetImageResource && handle.GetOpcode() != ValueOpcode::GetSamplerResource) return false; - const Inst* pointer = nullptr; + Inst* pointer = nullptr; uint32_t descriptor_offset = 0; for (uint32_t word = 0; word < handle.NumArgs(); ++word) { const auto* read = descriptor.dwords[word].Resolve().TryInstruction(); @@ -2777,7 +2777,7 @@ private: if (memory == nullptr || memory->kind != ResourceKind::ScalarAddress || memory->offset > INT32_MAX || !ImmediateU32(read->Arg(1), offset) || !AvailableBefore(*read, handle)) return false; - const auto* address = read->Arg(0).Resolve().TryInstruction(); + auto* address = read->Arg(0).Resolve().TryInstruction(); const uint64_t current_offset = uint64_t {offset & ~3u} + (memory->offset & ~3u); if (address == nullptr || address->GetOpcode() != ValueOpcode::GetAddressResource || address->NumArgs() != 2u || current_offset > UINT32_MAX || diff --git a/tests/ResourceTrackingTests.cpp b/tests/ResourceTrackingTests.cpp index 8d57b3a0d..7d15a00b2 100644 --- a/tests/ResourceTrackingTests.cpp +++ b/tests/ResourceTrackingTests.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include namespace { @@ -25,6 +26,10 @@ using Libs::Graphics::ShaderComputeInputInfo; using Libs::Graphics::ShaderType; namespace Decoder = Libs::Graphics::ShaderRecompiler::Decoder; +static_assert(std::is_constructible_v && + std::is_constructible_v && + !std::is_constructible_v); + void Check(bool condition, const char *message) { if (!condition) { throw std::runtime_error(message); @@ -692,7 +697,8 @@ void TestWaterfallImageTable() { void TestNestedPointerImageTable() { namespace CFG = Libs::Graphics::ShaderRecompiler::CFG; - const auto make = [](uint32_t variant = 0u) { + enum class Address { Shared, Equivalent, ImageMismatch, SamplerMismatch }; + const auto make = [](uint32_t variant = 0u, Address choice = Address::Shared) { auto fixture = std::make_unique(); std::array table_words, key_words; for (uint32_t word = 0; word < 4u; ++word) { @@ -749,6 +755,19 @@ void TestNestedPointerImageTable() { fixture->AddMemory(memory, 0x1f4u)); } const auto address = fixture->Address(pointer[0], pointer[1]); + auto other_address = address; + if (choice == Address::Equivalent) { + other_address = fixture->Address(pointer[0], pointer[1]); + } else if (choice != Address::Shared) { + for (uint32_t word = 0; word < 2u; ++word) { + MemoryInfo memory; + memory.kind = ResourceKind::ScalarBuffer; + memory.offset = 8u + word * 4u; + pointer[word] = fixture->Emit(ValueOpcode::ReadConstBuffer, {table, offset}, + fixture->AddMemory(memory, 0x1fcu)); + } + other_address = fixture->Address(pointer[0], pointer[1]); + } const auto load = [&](uint32_t first, uint32_t width) { if (width == 8u && variant >= 7u && variant <= 9u) first = variant == 7u ? 0x4000u : variant == 8u ? 0x3ff0u : 0x3fe0u; @@ -759,8 +778,12 @@ void TestNestedPointerImageTable() { memory.kind = ResourceKind::ScalarAddress; memory.offset = first + word * 4u; if (variant == 3u && width == 8u && word == 7u) memory.offset += 4u; + const bool alternate = word == 1u && + (choice == Address::Equivalent || + (choice == Address::ImageMismatch && width == 8u) || + (choice == Address::SamplerMismatch && width == 4u)); words[word] = fixture->Emit(ValueOpcode::LoadAddressU32, - {address, Value(0u), Value(0u), Value(true)}, + {alternate ? other_address : address, Value(0u), Value(0u), Value(true)}, fixture->AddMemory(memory, first == 0x3d0u ? 0x1110u : 0x1118u)); } return words; @@ -820,6 +843,16 @@ void TestNestedPointerImageTable() { snapshot.samplers[row + 1u].dwords[0] == row, "nested pointer table mixed rows or pointee members"); const auto populated = specialization; + auto equivalent = make(0u, Address::Equivalent); + equivalent->PlanAndTrack(); + Check(MaterializeResources(ExtractResourcePlan(equivalent->program), runtime, snapshot, specialization) && + specialization == populated, + "equivalent nested pointer address instructions changed descriptor selection"); + for (const auto choice : {Address::ImageMismatch, Address::SamplerMismatch}) { + auto mismatched = make(0u, choice); + CheckFatal([&] { mismatched->PlanAndTrack(); }, "not a valid runtime value", + "nested pointer planning combined descriptor words from different pointer pairs"); + } const auto reads = memory.reads; user_data[2] = 0u; Check(MaterializeResources(plan, runtime, snapshot, specialization) &&