From ef0155636a79046ab2711196cd716ca908186f2e Mon Sep 17 00:00:00 2001 From: nmzik Date: Tue, 8 Sep 2026 14:43:07 +0400 Subject: [PATCH] shader: support single and split NGG shaders for PPSA11142 --- .../shader/recompiler/ShaderRecompiler.cpp | 2 +- .../frontend/translate/Translate.cpp | 2 +- src/graphics/shader/shader.cpp | 30 +++++---- src/graphics/shader/shader.h | 10 ++- src/libs/agc.cpp | 1 + tests/shaderCfgTests.cpp | 61 ++++++++++++++++--- 6 files changed, 79 insertions(+), 27 deletions(-) diff --git a/src/graphics/shader/recompiler/ShaderRecompiler.cpp b/src/graphics/shader/recompiler/ShaderRecompiler.cpp index 63b62dc60..05684975a 100644 --- a/src/graphics/shader/recompiler/ShaderRecompiler.cpp +++ b/src/graphics/shader/recompiler/ShaderRecompiler.cpp @@ -535,7 +535,7 @@ TranslateResult TranslateProgram(std::span code, const CompileOp Decoder::Program decoded; std::vector joined_code; - if (options.stage == ShaderType::Mesh) { + if (!options.back_code.empty()) { decoded = DecodeFusedProgram(code, options.back_code, joined_code); } else { Decoder::DecodeProgram(code, decoded); diff --git a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp index d2208e51b..d57d3ad30 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp @@ -1109,7 +1109,7 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& : u32(0); const auto vertex = entry_ir.IMul(local, step); const auto first = entry_ir.IAdd(vertex, parity); - const auto second = mesh.InputPrimitiveSize() == 3u + const auto second = mesh.InputPrimitiveSize() >= 2u ? entry_ir.ISub(entry_ir.IAdd(vertex, u32(1)), parity) : u32(0); const auto third = mesh.InputPrimitiveSize() == 3u diff --git a/src/graphics/shader/shader.cpp b/src/graphics/shader/shader.cpp index 7278038dc..ca7b6c4bf 100644 --- a/src/graphics/shader/shader.cpp +++ b/src/graphics/shader/shader.cpp @@ -755,19 +755,9 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context& } return params; } - EXIT_IF(regs.gs_regs.data_addr == 0); - const auto back = ShaderGetMappedData(regs.gs_regs.data_addr, "ShaderGetInputInfoGS():"); - const auto back_params = - GetShaderParams(regs.gs_regs.data_addr, "ShaderRecompiler GS", - GetDeclaredShaderHash(regs.gs_regs.data_addr), {}, back); - params.back_code = back_params.code; - // Merged shaders receive the GS-back user-data pointer in s0:s1, followed - // by the ordinary user SGPRs at s8. Keep both in the runtime register snapshot. + // NGG user SGPRs start at s8; a separately compiled GS back half also receives + // its user-data pointer in s0:s1. params.user_data.insert(params.user_data.begin(), 8u, 0u); - params.user_data[0] = static_cast(regs.gs_regs.user_data_addr); - params.user_data[1] = static_cast(regs.gs_regs.user_data_addr >> 32u); - const uint64_t hashes[] = {params.hash, back_params.hash}; - params.hash = XXH3_64bits(hashes, sizeof(hashes)); info = {}; info.pa_cl_vs_out_cntl = sh.m_paClVsOutCntl; auto& mesh = info.mesh; @@ -776,11 +766,25 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context& mesh.max_vertices = sh.m_geMaxOutputPerSubgroup; mesh.provoking_vertex = context.GetModeControl().provoking_vtx_last ? 2u : 0u; mesh.lds_size_dwords = static_cast(regs.gs_regs.rsrc2.lds_size) * 128u; - mesh.scratch_size_dwords = std::max(data.scratch_size_dwords, back.scratch_size_dwords); + mesh.scratch_size_dwords = data.scratch_size_dwords; + if (data.type == Prospero::ShaderBinaryType::kGsFront) { + EXIT_IF(regs.gs_regs.data_addr == 0); + const auto back = ShaderGetMappedData(regs.gs_regs.data_addr, "ShaderGetInputInfoGS():"); + const auto back_params = + GetShaderParams(regs.gs_regs.data_addr, "ShaderRecompiler GS", + GetDeclaredShaderHash(regs.gs_regs.data_addr), {}, back); + params.back_code = back_params.code; + params.user_data[0] = static_cast(regs.gs_regs.user_data_addr); + params.user_data[1] = static_cast(regs.gs_regs.user_data_addr >> 32u); + const uint64_t hashes[] = {params.hash, back_params.hash}; + params.hash = XXH3_64bits(hashes, sizeof(hashes)); + mesh.scratch_size_dwords = std::max(mesh.scratch_size_dwords, back.scratch_size_dwords); + } EXIT_NOT_IMPLEMENTED(regs.gs_regs.rsrc1.gs_vgpr_component_count != 3u || regs.gs_regs.rsrc2.es_vgpr_component_count != 3u); const auto& group = user_config.GetGeControl(); if ((user_config.GetPrimType() != Prospero::PrimitiveType::kPointList && + user_config.GetPrimType() != Prospero::PrimitiveType::kLineList && user_config.GetPrimType() != Prospero::PrimitiveType::kTriStrip && user_config.GetPrimType() != Prospero::PrimitiveType::kTriList) || sh.m_vgtGsOutPrimType != 2u || sh.m_vgtGsMaxVertOut < 3u || diff --git a/src/graphics/shader/shader.h b/src/graphics/shader/shader.h index 37c462125..7b02eebf0 100644 --- a/src/graphics/shader/shader.h +++ b/src/graphics/shader/shader.h @@ -80,10 +80,15 @@ struct ShaderMeshInputInfo: ShaderWorkgroupInputInfo { uint32_t provoking_vertex = 0; [[nodiscard]] constexpr uint32_t InputPrimitiveSize() const { - return input_primitive == static_cast(Prospero::PrimitiveType::kPointList) ? 1u : 3u; + switch (static_cast(input_primitive)) { + case Prospero::PrimitiveType::kPointList: return 1u; + case Prospero::PrimitiveType::kLineList: return 2u; + default: return 3u; + } } [[nodiscard]] constexpr uint32_t InputPrimitiveStep() const { - return input_primitive == static_cast(Prospero::PrimitiveType::kTriList) ? 3u : 1u; + return input_primitive == static_cast(Prospero::PrimitiveType::kTriStrip) + ? 1u : InputPrimitiveSize(); } [[nodiscard]] constexpr uint32_t InputPrimitiveCount(uint32_t vertices) const { const auto size = InputPrimitiveSize(); @@ -257,6 +262,7 @@ struct Shader { }; struct ShaderMappedData { + Prospero::ShaderBinaryType type {}; ShaderUserData* user_data = nullptr; ShaderSemantic* input_semantics = nullptr; uint32_t num_input_semantics = 0; diff --git a/src/libs/agc.cpp b/src/libs/agc.cpp index 67d616406..0111f02ea 100644 --- a/src/libs/agc.cpp +++ b/src/libs/agc.cpp @@ -662,6 +662,7 @@ int KYTY_SYSV_ABI AgcCreateShader(Shader** dst, void* header, const volatile voi LOGF("\t base = 0x%016" PRIx64 "\n", base); ShaderMappedData map; + map.type = static_cast(h->type); map.user_data = h->user_data; map.input_semantics = h->input_semantics; map.num_input_semantics = h->num_input_semantics; diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 5f9435da3..2a0927043 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -9117,6 +9117,8 @@ void TestMeshExportStorage() { EncodeExp0(0x14, 0x1), EncodeExp1(0, 0, 0, 0), // primitive EncodeSopp(0x01), }; + std::vector monolithic(std::begin(front), std::begin(front) + 2); + monolithic.insert(monolithic.end(), std::begin(back), std::end(back)); ShaderVertexInputInfo input{}; input.pa_cl_vs_out_cntl = (1u << 21u) | (1u << 18u); auto &mesh = input.mesh; @@ -9136,16 +9138,23 @@ void TestMeshExportStorage() { ShaderRecompiler::CompileOptions options{}; options.stage = ShaderType::Mesh; options.input_info.vertex = &input; - options.back_code = back; options.user_data = user_data; - for (const auto [subgroup_size, push_data_start] : - {std::pair{32u, PushData::MeshDrawDwordCount}, - std::pair{64u, PushData::MeshDrawDwordCount}, - std::pair{32u, PushData::DwordCount}, - std::pair{64u, PushData::DwordCount}}) { + struct Case { + uint32_t subgroup_size, push_data_start; + bool split; + }; + const Case cases[] = { + {32u, PushData::MeshDrawDwordCount, true}, {64u, PushData::MeshDrawDwordCount, true}, + {32u, PushData::DwordCount, true}, {64u, PushData::DwordCount, true}, + {32u, PushData::MeshDrawDwordCount, false}, {64u, PushData::MeshDrawDwordCount, false}, + {32u, PushData::DwordCount, false}, {64u, PushData::DwordCount, false}, + }; + for (const auto [subgroup_size, push_data_start, split] : cases) { mesh.host_subgroup_size = subgroup_size; - const auto result = - RecompileForTest(front, options, nullptr, nullptr, push_data_start); + options.back_code = split ? std::span{back} : std::span{}; + const auto result = RecompileForTest( + split ? std::span{front} : std::span{monolithic}, + options, nullptr, nullptr, push_data_start); CheckSpirvBinaryValidates(result.spirv); const auto &layout = result.program.bindings; Check(layout.UsesPushData() == (push_data_start == PushData::MeshDrawDwordCount) && @@ -9232,9 +9241,13 @@ void TestMergedShaderUserDataSnapshot() { const std::array front_data = {regs.gs_user_sgpr.value[0], regs.gs_user_sgpr.value[1], regs.gs_user_sgpr.value[2], regs.gs_user_sgpr.value[3]}; ShaderMappedData mapped{}; + mapped.type = Prospero::ShaderBinaryType::kGsFront; mapped.code_size_bytes = sizeof(front); + mapped.scratch_size_dwords = 3; ShaderMapUserData(regs.es_regs.data_addr, mapped); + mapped.type = Prospero::ShaderBinaryType::kGsBack; mapped.code_size_bytes = sizeof(back); + mapped.scratch_size_dwords = 7; ShaderMapUserData(regs.gs_regs.data_addr, mapped); HW::Context context; context.SetShaderStages(0x20); @@ -9255,8 +9268,9 @@ void TestMergedShaderUserDataSnapshot() { second.user_data[8] == front_data[0] + 1, "merged shader parameters did not snapshot ordinary user SGPRs at s8"); Check(first.hash == second.hash && - MakeStageStaticKey(first_input) == MakeStageStaticKey(second_input), - "a dynamic merged-shader user-data pointer changed shader identity"); + MakeStageStaticKey(first_input) == MakeStageStaticKey(second_input) && + first_input.mesh.scratch_size_dwords == 7, + "merged shader identity or maximum scratch size changed with dynamic user data"); CompileOptions options{}; options.stage = ShaderType::Mesh; options.user_data_base = 0; @@ -9281,6 +9295,25 @@ void TestMergedShaderUserDataSnapshot() { std::equal(table.begin(), table.end(), back_descriptor.dwords.begin()), "merged shader resource plan did not follow the current s0:s1 pointer and s8 data"); } + const uint32_t monolithic[] = { + EncodeMubuf0(0x1c), EncodeMubuf1(0, 2, 1), EncodeSopp(0x01), + }; + regs.es_regs.data_addr = reinterpret_cast(monolithic); + mapped.type = Prospero::ShaderBinaryType::kGs; + mapped.code_size_bytes = sizeof(monolithic); + mapped.scratch_size_dwords = 3; + ShaderMapUserData(regs.es_regs.data_addr, mapped); + for (const auto back_address : {regs.gs_regs.data_addr, uint64_t{0}}) { + regs.gs_regs.data_addr = back_address; + ShaderVertexInputInfo input{}; + const auto params = PrepareProgram(regs, context, user_config, input); + Check(params.back_code.empty() && params.hash == XXH3_64bits(monolithic, sizeof(monolithic)) && + input.mesh.scratch_size_dwords == 3 && params.user_data.size() == 12 && + params.user_data[0] == 0 && params.user_data[1] == 0 && + std::equal(second.user_data.begin() + 8, second.user_data.end(), + params.user_data.begin() + 8), + "monolithic NGG shader used stale GS-back state or lost its s8 user data"); + } } void TestEmbeddedVertexFormatSwizzle() { @@ -9418,6 +9451,14 @@ void TestMeshInputAssembly() { 0x40000305, 1, 2, 3, 0, 15, false}, {Prospero::PrimitiveType::kTriStrip, 5, 8, 0, 1, 0, 0, 11, 0x40000305, 2, 1, 3, 0, 12, false}, + {Prospero::PrimitiveType::kLineList, 5, 17, 3, 0, 0, 0, 11, + 0x40000204, 0, 1, 0, 0, 23, false}, + {Prospero::PrimitiveType::kLineList, 6, 17, 2, 4, 2, 0x1002, 11, + 0x40000205, 8, 9, 0, 32, 0xabd8, true}, + {Prospero::PrimitiveType::kLineList, 6, 17, 2, 5, 2, 0x1002, 0, + 0x40000205, 10, 11, 0, 36, 0, false}, + {Prospero::PrimitiveType::kLineList, 8, 180, 1, 64, 4, 0x1000, 0, + 0x41000000, 128, 129, 0, 288, 0, false}, {Prospero::PrimitiveType::kPointList, 12, 265, 22, 0, 2, 0x1002, 0, 0x40000101, 0, 0, 0, 528, 0xabcd, true}, {Prospero::PrimitiveType::kPointList, 12, 265, 22, 1, 2, 0x1002, 0,