From d2df2cad74019117bdc03ebad0aa5109c16d5633 Mon Sep 17 00:00:00 2001 From: nmzik Date: Mon, 21 Sep 2026 14:26:29 +0400 Subject: [PATCH] Resolve draw-uniform image descriptors through scalar buffer chains --- .../recompiler/ir/passes/ResourceTracking.cpp | 10 --- tests/ResourceTrackingTests.cpp | 83 ++++++++++++++++++- 2 files changed, 82 insertions(+), 11 deletions(-) diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp index 50f493466..409ce712c 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp @@ -535,16 +535,6 @@ private: DescriptorSource descriptor; MakeSource(*handle, width, sampler, sample_adjust, descriptor, pc); uint32_t bad_dword = 0; - if (expected == ValueOpcode::GetImageResource) { - for (; bad_dword < descriptor.dword_count; bad_dword++) { - const auto* value = descriptor.dwords[bad_dword].Resolve().TryInstruction(); - if (value != nullptr && value->GetOpcode() == ValueOpcode::ReadConstBuffer) { - Fail(pc, fmt::format("{} dword {} is not a valid runtime value", - ValueOpcodeName(expected), bad_dword)); - } - } - bad_dword = 0; - } if (!ValidateSource(descriptor, bad_dword)) { if (expected == ValueOpcode::GetBufferResource && std::all_of(descriptor.dwords.begin(), descriptor.dwords.begin() + width, diff --git a/tests/ResourceTrackingTests.cpp b/tests/ResourceTrackingTests.cpp index 87cf3a922..ed8096145 100644 --- a/tests/ResourceTrackingTests.cpp +++ b/tests/ResourceTrackingTests.cpp @@ -213,8 +213,11 @@ MakeIndirectImageFixture(bool malformed, uint32_t material_immediate = 0, fixture->AddMemory(shared_buffer, 0x10d8)); fixture->Emit(ValueOpcode::ReferenceU32, {load}); } + const auto invocation = fixture->Emit( + ValueOpcode::GetBuiltin, + {Value(static_cast(StageInputKind::GlobalInvocationId)), Value(0u)}); const auto selector = fixture->Emit(ValueOpcode::ReadFirstLane, - {fixture->UserData(8), Value(true)}); + {invocation, Value(true)}); const auto record = fixture->Emit(ValueOpcode::IMul32, {selector, Value(224u)}); const auto member = fixture->Emit(ValueOpcode::IAdd32, {record, Value(4u)}); @@ -457,6 +460,83 @@ void TestInvariantIndirectImageMaterialization() { "wrapped scalar immediate entered the invariant image proof"); } +void TestUniformScalarBufferImage() { + Fixture fixture(ShaderType::Pixel); + std::array material_words; + std::array heap_words; + for (uint32_t dword = 0; dword < 4; dword++) { + material_words[dword] = fixture.UserData(dword); + heap_words[dword] = fixture.UserData(dword + 4u); + } + const auto material = fixture.Buffer(material_words); + const auto heap = fixture.Buffer(heap_words); + const auto selector = fixture.Emit(ValueOpcode::ShiftLeftLogical32, + {fixture.UserData(8), Value(2u)}); + MemoryInfo scalar; + scalar.kind = ResourceKind::ScalarBuffer; + scalar.offset = 0x40u; + const auto key = fixture.Emit(ValueOpcode::ReadConstBuffer, + {material, selector}, fixture.AddMemory(scalar, 0x100)); + const auto record = fixture.Emit(ValueOpcode::IMul32, {key, Value(48u)}); + std::array image_words; + for (uint32_t dword = 0; dword < image_words.size(); dword++) { + scalar.offset = 0x10u + dword * sizeof(uint32_t); + image_words[dword] = fixture.Emit(ValueOpcode::ReadConstBuffer, + {heap, record}, fixture.AddMemory(scalar, 0x200)); + } + const auto image = fixture.Image(image_words); + const auto sampler = fixture.Sampler({Value(0u), Value(0u), Value(0u), Value(0u)}); + MemoryInfo sample; + sample.kind = ResourceKind::Image; + sample.image_dimension = Decoder::ImageDimension::Dim2D; + fixture.Emit(ValueOpcode::ImageSampleRaw, + {image, sampler, fixture.ImageAddress()}, fixture.AddMemory(sample, 0x228)); + fixture.PlanAndTrack(); + const auto plan = ExtractResourcePlan(fixture.program); + + std::array user_data{0x1000u, 0u, 0x100u, 0u, + 0x2000u, 0u, 0x100u, 0u, 0u}; + LinearTestMemory memory; + memory.words[0x44u / 4u] = 1u; + std::array descriptor{}; + descriptor[0] = 0x20u; + descriptor[1] = static_cast( + Libs::Graphics::Prospero::BufferFormat::k32_32_32_32Float) + << 20u; + descriptor[2] = 3u | (3u << 14u); + descriptor[3] = Libs::Graphics::DstSel(4, 5, 6, 7) | + (static_cast(Libs::Graphics::Prospero::ImageType::kColor2D) + << 28u); + for (uint32_t index = 0; index < 2; index++) { + std::copy(descriptor.begin(), descriptor.end(), + memory.words.begin() + (0x1010u + index * 48u) / 4u); + memory.words[(0x1010u + index * 48u) / 4u] += index; + } + SrtRuntime runtime{.user_data = user_data, + .read_memory = ReadLinearTestMemory, + .userdata = &memory, + .read_specialization_memory = ReadLinearTestMemory}; + ResourceSnapshot snapshot; + ResourceSpecialization specialization; + const auto materializes = [&](uint32_t address) { + return MaterializeResources(plan, runtime, snapshot, specialization) && + snapshot.images.size() == 1 && snapshot.images[0].dwords[0] == address; + }; + Check(materializes(0x20u), "nested draw-uniform image descriptor did not materialize"); + user_data[8] = 1u; + Check(materializes(0x21u), "changed draw selector reused the previous image descriptor"); + memory.words[0x44u / 4u] = 0u; + memory.words[0x1010u / 4u] = 0x30u; + Check(materializes(0x30u), "changed scalar table memory reused the previous image descriptor"); + const auto prior_snapshot = snapshot; + const auto prior_specialization = specialization; + memory.fail_address = 0x1044u; + Check(!MaterializeResources(plan, runtime, snapshot, specialization) && + SameResourceSnapshot(snapshot, prior_snapshot) && + specialization == prior_specialization, + "unavailable nested image table partially changed the resource snapshot"); +} + void TestComputeBufferFill() { struct Options { bool scalar = false; @@ -2013,6 +2093,7 @@ int main() { Run("FMASK load specialization", TestFmaskLoadSpecialization); Run("dynamic storage mips", TestDynamicStorageMipTracking); Run("invariant indirect images", TestInvariantIndirectImageMaterialization); + Run("draw-uniform scalar image", TestUniformScalarBufferImage); Run("SRT runtime", TestSrtFlatteningAndRuntimeMemoization); Run("dynamic SRT", TestDynamicSrtReadRemainsExplicit); Run("phi validation", TestPhiValidation);