From 68d0ba5a818cf56d473fd3e2753bee82db26e078 Mon Sep 17 00:00:00 2001 From: nmzik Date: Wed, 21 Oct 2026 03:48:25 +0200 Subject: [PATCH] shader: bound scalar-read overlap checks by the bytes fixed-offset buffer writes reach --- .../host_gpu/renderer/pipeline/descriptors.cpp | 8 ++++++-- src/graphics/shader/recompiler/ir/ShaderIR.h | 1 + .../recompiler/ir/passes/ResourceTracking.cpp | 18 +++++++++++++----- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp index aa7870f1f..a530f1c5f 100644 --- a/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/pipeline/descriptors.cpp @@ -941,8 +941,12 @@ void RenderExecutor::CommitBindings(CommandBuffer& buffer, for (const auto& resource: program.info.buffers) { if (!resource.written || resource.descriptor_index == UINT32_MAX) continue; const auto& written = writer->buffer_sources[resource.descriptor_index]; - if (written.size != 0 && ImageRangeOverlaps(address, size, - written.address, written.size)) { + // ADD_TID and swizzle move fixed-offset writes past write_extent. + const bool linear = (resource.packed_stride & ((1u << 14u) | (1u << 20u))) == 0u; + const auto written_size = + linear ? std::min(written.size, resource.write_extent) : written.size; + if (written_size != 0 && ImageRangeOverlaps(address, size, + written.address, written_size)) { EXIT("scalar resource reads overlap a shader buffer write\n"); } } diff --git a/src/graphics/shader/recompiler/ir/ShaderIR.h b/src/graphics/shader/recompiler/ir/ShaderIR.h index 7691b6a24..e49fbc840 100644 --- a/src/graphics/shader/recompiler/ir/ShaderIR.h +++ b/src/graphics/shader/recompiler/ir/ShaderIR.h @@ -125,6 +125,7 @@ struct BufferResource { uint32_t descriptor_index = UINT32_MAX; uint32_t first_use_pc = 0; uint32_t max_byte_extent = 0; + uint32_t write_extent = 0; // UINT32_MAX once a write is runtime-addressed or formatted uint32_t packed_stride = 0; Prospero::BufferFormat descriptor_format = Prospero::BufferFormat::kInvalid; uint32_t descriptor_swizzle = DstSel(4, 5, 6, 7); diff --git a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp index de08fca8f..b887a6fd3 100644 --- a/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ResourceTracking.cpp @@ -3749,10 +3749,10 @@ private: } } - uint32_t AddBuffer(uint32_t source, const MemoryInfo& memory, ValueOpcode op, uint32_t pc) { + uint32_t AddBuffer(uint32_t source, const MemoryInfo& memory, const Inst& inst, uint32_t pc) { for (uint32_t i = 0; i < m_info.buffers.size(); i++) { if (m_info.buffers[i].source == source) { - Merge(m_info.buffers[i], memory, op, pc); + Merge(m_info.buffers[i], memory, inst, pc); return i; } } @@ -3762,13 +3762,14 @@ private: BufferResource resource; resource.source = source; resource.first_use_pc = pc; - Merge(resource, memory, op, pc); + Merge(resource, memory, inst, pc); m_info.buffers.push_back(resource); return static_cast(m_info.buffers.size() - 1); } - static void Merge(BufferResource& resource, const MemoryInfo& memory, ValueOpcode op, + static void Merge(BufferResource& resource, const MemoryInfo& memory, const Inst& inst, uint32_t pc) { + const auto op = inst.GetOpcode(); const auto access = BufferAccessOf(op); const bool atomic = access == BufferAccess::Atomic; const bool write = access == BufferAccess::Write || atomic; @@ -3776,6 +3777,13 @@ private: resource.max_byte_extent = std::max(resource.max_byte_extent, ByteExtent(memory)); resource.read = resource.read || !write || atomic; resource.written = resource.written || write; + if (write) { + // Formatted stores write the whole format; otherwise zero index, VGPR and SGPR offsets + // leave only the immediate offset. + const bool fixed = !memory.formatted && inst.Arg(1).Resolve() == Value(0u) && + inst.Arg(2).Resolve() == Value(0u) && inst.Arg(3).Resolve() == Value(0u); + resource.write_extent = std::max(resource.write_extent, fixed ? ByteExtent(memory) : UINT32_MAX); + } resource.atomic = resource.atomic || atomic; resource.formatted = resource.formatted || memory.formatted; resource.scalar = resource.scalar || op == ValueOpcode::ReadConstBuffer || @@ -3945,7 +3953,7 @@ private: m_info.uses_dma = true; return; } - resource = AddBuffer(source, memory, op, flags.pc); + resource = AddBuffer(source, memory, inst, flags.pc); if (resource == UINT32_MAX) { Fail(flags.pc, "buffer resource limit exceeded"); }