From b24ce3440d498f7bcbe525e825cb7d0ef69b964a Mon Sep 17 00:00:00 2001 From: nmzik Date: Wed, 14 Oct 2026 21:11:14 +0200 Subject: [PATCH] shader: track buffer liveness and offsets by resource count Co-authored-by: fxpw --- .../recompiler/backend/spirv/SpirvEmitter.cpp | 11 ++++++----- .../backend/spirv/spirvEmitterInternal.h | 2 +- .../spirv/spirvEmitterMemoryHelpers.cpp | 5 +++-- .../backend/spirv/spirvEmitterModule.cpp | 5 ++--- src/graphics/shader/recompiler/ir/ShaderIR.h | 2 +- .../ir/passes/ShaderInfoCollection.cpp | 6 +++--- tests/ResourceTrackingTests.cpp | 18 ++++++++++++------ 7 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp index 973edce2d..e9d30cd7e 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp @@ -18,7 +18,7 @@ namespace { } void ValidateNativeProgram(const IR::Program& program, bool lds_storage) { - uint64_t live_buffers = 0; + std::vector live_buffers(program.info.buffers.size()); bool uses_lds = false; bool uses_gds = false; const auto uses_mapping = [](const auto& resource) { @@ -85,12 +85,12 @@ void ValidateNativeProgram(const IR::Program& program, bool lds_storage) { if (memory.resource >= program.info.buffers.size()) { Fail(program, "typed shader contains an invalid buffer resource"); } - live_buffers |= uint64_t{1} << memory.resource; + live_buffers[memory.resource] = true; for (const auto child: program.info.buffers[memory.resource].indirect_resources) { if (child >= program.info.buffers.size()) { Fail(program, "typed shader contains an invalid indirect buffer resource"); } - live_buffers |= uint64_t{1} << child; + live_buffers[child] = true; } } } @@ -149,8 +149,9 @@ void ValidateNativeProgram(const IR::Program& program, bool lds_storage) { std::array(Kind::Count)> expected {}; auto& buffer_count = expected[static_cast(Kind::Buffers)]; for (uint32_t index = 0; index < program.info.buffers.size(); ++index) { - const auto slot = (live_buffers & (uint64_t{1} << index)) != 0 ? buffer_count++ : UINT32_MAX; - if (program.info.buffers[index].descriptor_index != slot) { + const auto slot = live_buffers[index] ? buffer_count++ : UINT32_MAX; + if (program.info.buffers[index].live != live_buffers[index] || + program.info.buffers[index].descriptor_index != slot) { Fail(program, "native buffer descriptor index does not match shader topology"); } } diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h index f773e48fb..616dd37bf 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterInternal.h @@ -110,7 +110,7 @@ struct EmitterState { uint32_t lane_count = 1; uint32_t lane_half = 0; std::array storage_buffers {}; - std::array memory_byte_offsets {}; + std::vector memory_byte_offsets; uint32_t bda_pagetable_variable = 0; uint32_t fault_buffer_variable = 0; uint32_t bda_pointer_function = 0; diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp index 67201cb71..94c1c9121 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterMemoryHelpers.cpp @@ -129,14 +129,15 @@ bool UsesSharedIndirectBufferAccess(const EmitterState& state, uint32_t resource } void EmitMemoryOffsets(EmitterState& state) { - std::array dynamic_offsets {}; + const auto count = state.program.bindings.descriptor_counts[static_cast(IR::DescriptorBindingKind::Buffers)]; + state.memory_byte_offsets.resize(count); + std::vector dynamic_offsets(count); for (uint32_t resource = 0; resource < state.program.info.buffers.size(); ++resource) { if (!UsesSharedIndirectBufferAccess(state, resource)) continue; for (const auto candidate: state.program.info.buffers[resource].indirect_resources) { dynamic_offsets[state.program.info.buffers[candidate].descriptor_index] = true; } } - const auto count = state.program.bindings.descriptor_counts[static_cast(IR::DescriptorBindingKind::Buffers)]; for (uint32_t i = 0; i < count; i += 4u) { const auto lanes = std::min(4u, count - i); if (std::all_of(dynamic_offsets.begin() + i, dynamic_offsets.begin() + i + lanes, diff --git a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp index c18919898..524549f4f 100644 --- a/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/spirvEmitterModule.cpp @@ -212,9 +212,8 @@ void DefineDescriptors(EmitterState& state) { auto& bindings = state.program.bindings; auto& counts = bindings.descriptor_counts; auto& buffer_count = counts[static_cast(Kind::Buffers)]; - for (uint32_t index = 0; index < info.buffers.size(); ++index) { - info.buffers[index].descriptor_index = (info.live_buffers & (uint64_t{1} << index)) != 0 - ? buffer_count++ : UINT32_MAX; + for (auto& buffer: info.buffers) { + buffer.descriptor_index = buffer.live ? buffer_count++ : UINT32_MAX; } std::array image_types {}; for (auto& image: info.images) { diff --git a/src/graphics/shader/recompiler/ir/ShaderIR.h b/src/graphics/shader/recompiler/ir/ShaderIR.h index b79489055..326d501d4 100644 --- a/src/graphics/shader/recompiler/ir/ShaderIR.h +++ b/src/graphics/shader/recompiler/ir/ShaderIR.h @@ -129,6 +129,7 @@ struct BufferResource { bool atomic = false; bool formatted = false; bool scalar = false; + bool live = false; uint32_t indirect_root = NoIndirectBuffer; uint32_t indirect_mapping_offset = 0; uint32_t indirect_search_iterations = 0; @@ -493,7 +494,6 @@ struct ShaderInfo { std::vector inputs; std::vector outputs; std::vector user_data_registers; - uint64_t live_buffers = 0; std::array vertex_fetch_components {}; int32_t vertex_offset_sgpr = -1; int32_t instance_offset_sgpr = -1; diff --git a/src/graphics/shader/recompiler/ir/passes/ShaderInfoCollection.cpp b/src/graphics/shader/recompiler/ir/passes/ShaderInfoCollection.cpp index da004e5bc..5ea69d445 100644 --- a/src/graphics/shader/recompiler/ir/passes/ShaderInfoCollection.cpp +++ b/src/graphics/shader/recompiler/ir/passes/ShaderInfoCollection.cpp @@ -311,12 +311,12 @@ void Visit(Program& program, ShaderStageInputInfo input_info, InputUsage& inputs if (memory.resource >= info.buffers.size() || memory.resource >= ShaderInfo::MaxBuffers) { return Fail("buffer operation has invalid resource metadata"); } - info.live_buffers |= uint64_t{1} << memory.resource; + info.buffers[memory.resource].live = true; for (const auto child: info.buffers[memory.resource].indirect_resources) { if (child >= info.buffers.size()) { return Fail("indirect buffer operation has invalid resource metadata"); } - info.live_buffers |= uint64_t{1} << child; + info.buffers[child].live = true; } } } @@ -572,7 +572,7 @@ void CollectShaderInfo(Program& program, ShaderStageInputInfo input_info) { info.inputs.clear(); info.outputs.clear(); info.user_data_registers.clear(); - info.live_buffers = 0; + for (auto& buffer: info.buffers) buffer.live = false; info.has_bitwise_xor = info.uses_lds = info.uses_gds = info.uses_flattened_srt = info.uses_dispatch_threads = info.uses_swizzle = false; info.bvh = info.subgroup_ballot = info.subgroup_barrier = info.subgroup_shuffle = info.subgroup_arithmetic = diff --git a/tests/ResourceTrackingTests.cpp b/tests/ResourceTrackingTests.cpp index 8ebdb3928..5a0d057ad 100644 --- a/tests/ResourceTrackingTests.cpp +++ b/tests/ResourceTrackingTests.cpp @@ -4602,7 +4602,7 @@ void TestDynamicSrtReadRemainsExplicit() { ShaderComputeInputInfo compute{}; CollectShaderInfo(fixture.program, {.compute = &compute}); Check(!fixture.program.info.uses_flattened_srt && fixture.program.info.uses_dma && - fixture.program.info.live_buffers == 1u, + fixture.program.info.buffers.size() == 1u && fixture.program.info.buffers[0].live, "dynamic scalar read received the wrong resource usage"); } @@ -6969,9 +6969,9 @@ void TestShaderInfoCollection() { {fixture.UserData(3), fixture.UserData(4), Value(64u), Value(0u)}, 4); MemoryInfo buffer; buffer.kind = ResourceKind::Buffer; - fixture.Emit(ValueOpcode::LoadBufferU32, - {handle, Value(0u), Value(0u), Value(0u), Value(true)}, - fixture.AddMemory(buffer, 4)); + const auto load = fixture.Emit(ValueOpcode::LoadBufferU32, + {handle, Value(0u), Value(0u), Value(0u), Value(true)}, + fixture.AddMemory(buffer, 4)); const auto invocation = fixture.Emit( ValueOpcode::GetBuiltin, {Value(static_cast(StageInputKind::GlobalInvocationId)), @@ -6993,7 +6993,7 @@ void TestShaderInfoCollection() { StageInputKind::GlobalInvocationId, "typed shader values were not reflected in shader info"); - Check(fixture.program.info.live_buffers == 1u && + Check(fixture.program.info.buffers.size() == 1u && fixture.program.info.buffers[0].live && fixture.program.info.uses_gds && !fixture.program.info.uses_lds, "typed resources were not collected in shader info"); Check(NativeBinding(ShaderType::Compute, DescriptorBindingKind::Buffers) == @@ -7006,6 +7006,11 @@ void TestShaderInfoCollection() { "fixed stage binding ranges are inconsistent"); Check(fixture.program.info.user_data_registers == std::vector({3u, 4u}), "shader info did not collect live typed user-data values"); + load.ResolveInstruction()->Invalidate(); + fixture.program.shader_info_complete = false; + CollectShaderInfo(fixture.program, {.compute = &compute}); + Check(!fixture.program.info.buffers[0].live, + "shader info recollection retained a removed buffer use"); } void TestImageBindingAbi() { @@ -7172,7 +7177,8 @@ void TestResourceLimitIsTransactional() { "compute shader did not retain all 64 distinct buffers"); ShaderComputeInputInfo compute{}; CollectShaderInfo(accepted.program, {.compute = &compute}); - Check(accepted.program.info.live_buffers == UINT64_MAX, + Check(std::ranges::all_of(accepted.program.info.buffers, + [](const auto &buffer) { return buffer.live; }), "shader info truncated the 64 live buffers"); Fixture fixture;