From a951a91703db67a61f986f4bcb1ea3d54cda1d64 Mon Sep 17 00:00:00 2001 From: nmzik Date: Thu, 22 Oct 2026 18:02:51 +0200 Subject: [PATCH] spirv: emit OpName only with debug info --- .../recompiler/backend/spirv/SpirvBuilder.cpp | 3 +++ .../recompiler/backend/spirv/SpirvBuilder.h | 2 ++ .../recompiler/backend/spirv/SpirvEmitter.cpp | 1 + tests/ShaderRecompilerComputeTests.cpp | 23 ++++++++++++++---- tests/shaderCfgTests.cpp | 24 ++++++++++++++----- 5 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp index 29b7db89b..cf65c1d90 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.cpp @@ -169,6 +169,9 @@ uint32_t Builder::AddString(const char* text) { } void Builder::AddName(uint32_t target, const char* name) { + if (!m_emit_names) { + return; + } std::vector operands = {target}; AppendString(operands, name); AppendInstruction(m_debug, spv::OpName, operands); diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.h b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.h index 55a2d01c9..1835aee49 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.h +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvBuilder.h @@ -67,6 +67,7 @@ public: } uint32_t AddString(const char* text); + void EnableNames() { m_emit_names = true; } void AddName(uint32_t target, const char* name); template void AddAnnotation(spv::Op opcode, const Args&... operands) { @@ -159,6 +160,7 @@ private: std::map m_import_ids; std::map, uint32_t> m_declaration_ids; size_t m_unpatched_phi_incomings = 0; + bool m_emit_names = false; }; } // namespace Libs::Graphics::ShaderRecompiler::Spirv diff --git a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp index 5e4fc63d8..0f6552727 100644 --- a/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp +++ b/src/graphics/shader/recompiler/backend/spirv/SpirvEmitter.cpp @@ -226,6 +226,7 @@ std::vector EmitProgram(IR::Program& program, ShaderStageInputInfo inp std::snprintf(name, sizeof(name), "rdna2-%016" PRIx64 "-block-byte-pc-plus-one", program.shader_hash); state.debug_source = state.builder.AddString(name); + state.builder.EnableNames(); } const auto* workgroup = ShaderWorkgroupInput(program.stage, input_info); state.lane_count = diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index a46283424..e7a94da06 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -1619,6 +1619,17 @@ void CheckRectListShaders() { ValidateSpirv(name, position_only.evaluation); } +// Words without OpName/OpString/OpLine, which only graphics debug dumps emit. +size_t SpirvCodeWords(const std::vector &spirv) { + size_t words = spirv.size(); + for (size_t offset = 5; offset < spirv.size(); offset += spirv[offset] >> 16u) { + const auto opcode = static_cast(spirv[offset] & 0xffffu); + if (opcode == spv::OpName || opcode == spv::OpString || opcode == spv::OpLine) + words -= spirv[offset] >> 16u; + } + return words; +} + void CheckSpirvEmission(const TestCase &test, const std::vector &spirv) { std::vector result_types(spirv[3]); for (size_t offset = 5; offset < spirv.size();) { @@ -1679,6 +1690,7 @@ CompiledShader CompileCase(const TestCase &test, u32 host_subgroup_size = 64) { options.stage = ShaderType::Compute; options.profile = test.profile; options.dump_ir = true; + options.emit_debug_info = true; // Checks find variables and functions by OpName. auto compute_info = test.compute_info; compute_info.host_subgroup_size = host_subgroup_size; options.input_info.compute = &compute_info; @@ -1750,7 +1762,7 @@ CompiledShader CompileCase(const TestCase &test, u32 host_subgroup_size = 64) { result.program.shader_info_complete = false; ShaderRecompiler::IR::CollectShaderInfo(result.program, options.input_info); result.spirv = ShaderRecompiler::Spirv::EmitProgram( - result.program, options.input_info, 0u, *options.profile); + result.program, options.input_info, 0u, *options.profile, true); const auto shader_data = result.program.bindings.descriptor_counts[ static_cast(ShaderRecompiler::IR::DescriptorBindingKind::ShaderData)]; Require(test.name, "binding layout", @@ -1764,11 +1776,12 @@ CompiledShader CompileCase(const TestCase &test, u32 host_subgroup_size = 64) { ValidateSpirv(test.name, result.spirv); CheckSpirvEmission(test, result.spirv); if (test.max_spirv_words != 0u) { - Require(test.name, "emitted shader size", result.spirv.size() <= test.max_spirv_words, - "SPIR-V words=" + std::to_string(result.spirv.size()) + + const auto words = SpirvCodeWords(result.spirv); + Require(test.name, "emitted shader size", words <= test.max_spirv_words, + "SPIR-V words=" + std::to_string(words) + ", limit=" + std::to_string(test.max_spirv_words)); std::printf("[compute] %s wave=%u SPIR-V words=%zu\n", test.name, - result.program.wave_size, result.spirv.size()); + result.program.wave_size, words); } if (test.expected_buffer_resources) { std::vector resources; @@ -40580,7 +40593,7 @@ void CheckNestedPointerImageTable(VulkanHarness &vulkan) { compiled.program.info.samplers.size() == rows + 1u && tools.Disassemble(compiled.spirv, &text) && CountText(text, "OpImageSampleExplicitLod") == halves && - compiled.spirv.size() <= 8192u * halves, + SpirvCodeWords(compiled.spirv) <= 8192u * halves, "59 sampler records hit the SGPR root limit or duplicated image sampling bodies"); } if (large_samplers) diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index ff6b85241..37c67fe2c 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -99,6 +99,7 @@ ShaderRecompiler::CompileOptions MakeCompileOptions(ShaderType stage) { ShaderRecompiler::CompileOptions options; options.stage = stage; options.user_data = user_data; + options.emit_debug_info = true; // Checks find variables and functions by OpName. switch (stage) { case ShaderType::Vertex: options.input_info.vertex = &vertex; @@ -403,6 +404,11 @@ SpirvMetrics MeasureSpirv(const std::vector &binary) { if (word_count == 0 || i + word_count > binary.size()) { break; } + if (opcode == spv::OpName || opcode == spv::OpString || opcode == spv::OpLine) { + metrics.words -= word_count; // Budgets measure the module without debug info. + i += word_count; + continue; + } metrics.instructions++; switch (opcode) { case 11u: // OpExtInstImport @@ -10354,7 +10360,7 @@ void TestNewShaderRecompilerPlanningOnlyLoads() { ir.Emit(IR::ValueOpcode::ReferenceU32, {read}); program.shader_info_complete = false; IR::CollectShaderInfo(program, options.input_info); - const auto spirv = Spirv::EmitProgram(program, options.input_info); + const auto spirv = Spirv::EmitProgram(program, options.input_info, 0, {}, true); CheckSpirvBinaryValidates(spirv); Check(spirv == result.spirv, "planning-only loads or references emitted GPU operands/instructions"); @@ -11100,6 +11106,7 @@ void TestMeshExportStorage() { options.stage = ShaderType::Mesh; options.input_info.vertex = &input; options.user_data = user_data; + options.emit_debug_info = true; // The checks find push data by its OpName. struct Case { uint32_t subgroup_size, push_data_start; bool split; @@ -14352,7 +14359,8 @@ void TestNewShaderRecompilerNativeBindingPlan() { const auto bindings = result.program.bindings; result.program.bindings = {}; result.program.info.buffers[0].descriptor_index = UINT32_MAX; - Check(ShaderRecompiler::Spirv::EmitProgram(result.program, options.input_info) == result.spirv && + Check(ShaderRecompiler::Spirv::EmitProgram(result.program, options.input_info, 0, {}, true) == + result.spirv && result.program.bindings == bindings && result.program.info.buffers[0].descriptor_index == 0, "repeated emission did not rebuild descriptor slots deterministically"); @@ -16423,11 +16431,15 @@ void TestRepeatedExportsHaveOneInterface() { void CheckSpirvBlockSourceMapping(TestCompileResult &result) { using namespace ShaderRecompiler; const auto options = MakeCompileOptions(ShaderType::Compute); - Check(SpirvInstructionOpcodeCount(result.spirv, spv::OpString) == 0 && - SpirvInstructionOpcodeCount(result.spirv, spv::OpLine) == 0, - "default emission unexpectedly contains source mapping"); + const auto plain = Spirv::EmitProgram(result.program, options.input_info); + Check(SpirvInstructionOpcodeCount(plain, spv::OpString) == 0 && + SpirvInstructionOpcodeCount(plain, spv::OpLine) == 0 && + SpirvInstructionOpcodeCount(plain, spv::OpName) == 0, + "default emission unexpectedly contains debug info"); const auto mapped = Spirv::EmitProgram(result.program, options.input_info, 0, {}, true); + Check(SpirvInstructionOpcodeCount(mapped, spv::OpName) != 0, + "debug emission omitted names"); CheckSpirvBinaryValidates(mapped); uint32_t source = 0; uint32_t label = 0; @@ -16456,7 +16468,7 @@ void CheckSpirvBlockSourceMapping(TestCompileResult &result) { } Check(source != 0 && lines >= result.program.blocks.size(), "debug emission omitted native block source mapping"); - Check(Spirv::EmitProgram(result.program, options.input_info) == result.spirv, + Check(Spirv::EmitProgram(result.program, options.input_info) == plain, "debug re-emission changed the default binary"); }