shader: support single and split NGG shaders for PPSA11142

This commit is contained in:
nmzik
2026-09-08 14:43:07 +04:00
parent 9d550fbffa
commit ef0155636a
6 changed files with 79 additions and 27 deletions
@@ -535,7 +535,7 @@ TranslateResult TranslateProgram(std::span<const uint32_t> code, const CompileOp
Decoder::Program decoded; Decoder::Program decoded;
std::vector<uint32_t> joined_code; std::vector<uint32_t> joined_code;
if (options.stage == ShaderType::Mesh) { if (!options.back_code.empty()) {
decoded = DecodeFusedProgram(code, options.back_code, joined_code); decoded = DecodeFusedProgram(code, options.back_code, joined_code);
} else { } else {
Decoder::DecodeProgram(code, decoded); Decoder::DecodeProgram(code, decoded);
@@ -1109,7 +1109,7 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
: u32(0); : u32(0);
const auto vertex = entry_ir.IMul(local, step); const auto vertex = entry_ir.IMul(local, step);
const auto first = entry_ir.IAdd(vertex, parity); 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) ? entry_ir.ISub(entry_ir.IAdd(vertex, u32(1)), parity)
: u32(0); : u32(0);
const auto third = mesh.InputPrimitiveSize() == 3u const auto third = mesh.InputPrimitiveSize() == 3u
+17 -13
View File
@@ -755,19 +755,9 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context&
} }
return params; return params;
} }
EXIT_IF(regs.gs_regs.data_addr == 0); // NGG user SGPRs start at s8; a separately compiled GS back half also receives
const auto back = ShaderGetMappedData(regs.gs_regs.data_addr, "ShaderGetInputInfoGS():"); // its user-data pointer in s0:s1.
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.
params.user_data.insert(params.user_data.begin(), 8u, 0u); params.user_data.insert(params.user_data.begin(), 8u, 0u);
params.user_data[0] = static_cast<uint32_t>(regs.gs_regs.user_data_addr);
params.user_data[1] = static_cast<uint32_t>(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 = {};
info.pa_cl_vs_out_cntl = sh.m_paClVsOutCntl; info.pa_cl_vs_out_cntl = sh.m_paClVsOutCntl;
auto& mesh = info.mesh; auto& mesh = info.mesh;
@@ -776,11 +766,25 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context&
mesh.max_vertices = sh.m_geMaxOutputPerSubgroup; mesh.max_vertices = sh.m_geMaxOutputPerSubgroup;
mesh.provoking_vertex = context.GetModeControl().provoking_vtx_last ? 2u : 0u; mesh.provoking_vertex = context.GetModeControl().provoking_vtx_last ? 2u : 0u;
mesh.lds_size_dwords = static_cast<uint32_t>(regs.gs_regs.rsrc2.lds_size) * 128u; mesh.lds_size_dwords = static_cast<uint32_t>(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<uint32_t>(regs.gs_regs.user_data_addr);
params.user_data[1] = static_cast<uint32_t>(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 || EXIT_NOT_IMPLEMENTED(regs.gs_regs.rsrc1.gs_vgpr_component_count != 3u ||
regs.gs_regs.rsrc2.es_vgpr_component_count != 3u); regs.gs_regs.rsrc2.es_vgpr_component_count != 3u);
const auto& group = user_config.GetGeControl(); const auto& group = user_config.GetGeControl();
if ((user_config.GetPrimType() != Prospero::PrimitiveType::kPointList && if ((user_config.GetPrimType() != Prospero::PrimitiveType::kPointList &&
user_config.GetPrimType() != Prospero::PrimitiveType::kLineList &&
user_config.GetPrimType() != Prospero::PrimitiveType::kTriStrip && user_config.GetPrimType() != Prospero::PrimitiveType::kTriStrip &&
user_config.GetPrimType() != Prospero::PrimitiveType::kTriList) || user_config.GetPrimType() != Prospero::PrimitiveType::kTriList) ||
sh.m_vgtGsOutPrimType != 2u || sh.m_vgtGsMaxVertOut < 3u || sh.m_vgtGsOutPrimType != 2u || sh.m_vgtGsMaxVertOut < 3u ||
+8 -2
View File
@@ -80,10 +80,15 @@ struct ShaderMeshInputInfo: ShaderWorkgroupInputInfo {
uint32_t provoking_vertex = 0; uint32_t provoking_vertex = 0;
[[nodiscard]] constexpr uint32_t InputPrimitiveSize() const { [[nodiscard]] constexpr uint32_t InputPrimitiveSize() const {
return input_primitive == static_cast<uint32_t>(Prospero::PrimitiveType::kPointList) ? 1u : 3u; switch (static_cast<Prospero::PrimitiveType>(input_primitive)) {
case Prospero::PrimitiveType::kPointList: return 1u;
case Prospero::PrimitiveType::kLineList: return 2u;
default: return 3u;
}
} }
[[nodiscard]] constexpr uint32_t InputPrimitiveStep() const { [[nodiscard]] constexpr uint32_t InputPrimitiveStep() const {
return input_primitive == static_cast<uint32_t>(Prospero::PrimitiveType::kTriList) ? 3u : 1u; return input_primitive == static_cast<uint32_t>(Prospero::PrimitiveType::kTriStrip)
? 1u : InputPrimitiveSize();
} }
[[nodiscard]] constexpr uint32_t InputPrimitiveCount(uint32_t vertices) const { [[nodiscard]] constexpr uint32_t InputPrimitiveCount(uint32_t vertices) const {
const auto size = InputPrimitiveSize(); const auto size = InputPrimitiveSize();
@@ -257,6 +262,7 @@ struct Shader {
}; };
struct ShaderMappedData { struct ShaderMappedData {
Prospero::ShaderBinaryType type {};
ShaderUserData* user_data = nullptr; ShaderUserData* user_data = nullptr;
ShaderSemantic* input_semantics = nullptr; ShaderSemantic* input_semantics = nullptr;
uint32_t num_input_semantics = 0; uint32_t num_input_semantics = 0;
+1
View File
@@ -662,6 +662,7 @@ int KYTY_SYSV_ABI AgcCreateShader(Shader** dst, void* header, const volatile voi
LOGF("\t base = 0x%016" PRIx64 "\n", base); LOGF("\t base = 0x%016" PRIx64 "\n", base);
ShaderMappedData map; ShaderMappedData map;
map.type = static_cast<Prospero::ShaderBinaryType>(h->type);
map.user_data = h->user_data; map.user_data = h->user_data;
map.input_semantics = h->input_semantics; map.input_semantics = h->input_semantics;
map.num_input_semantics = h->num_input_semantics; map.num_input_semantics = h->num_input_semantics;
+51 -10
View File
@@ -9117,6 +9117,8 @@ void TestMeshExportStorage() {
EncodeExp0(0x14, 0x1), EncodeExp1(0, 0, 0, 0), // primitive EncodeExp0(0x14, 0x1), EncodeExp1(0, 0, 0, 0), // primitive
EncodeSopp(0x01), EncodeSopp(0x01),
}; };
std::vector<uint32_t> monolithic(std::begin(front), std::begin(front) + 2);
monolithic.insert(monolithic.end(), std::begin(back), std::end(back));
ShaderVertexInputInfo input{}; ShaderVertexInputInfo input{};
input.pa_cl_vs_out_cntl = (1u << 21u) | (1u << 18u); input.pa_cl_vs_out_cntl = (1u << 21u) | (1u << 18u);
auto &mesh = input.mesh; auto &mesh = input.mesh;
@@ -9136,16 +9138,23 @@ void TestMeshExportStorage() {
ShaderRecompiler::CompileOptions options{}; ShaderRecompiler::CompileOptions options{};
options.stage = ShaderType::Mesh; options.stage = ShaderType::Mesh;
options.input_info.vertex = &input; options.input_info.vertex = &input;
options.back_code = back;
options.user_data = user_data; options.user_data = user_data;
for (const auto [subgroup_size, push_data_start] : struct Case {
{std::pair{32u, PushData::MeshDrawDwordCount}, uint32_t subgroup_size, push_data_start;
std::pair{64u, PushData::MeshDrawDwordCount}, bool split;
std::pair{32u, PushData::DwordCount}, };
std::pair{64u, PushData::DwordCount}}) { 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; mesh.host_subgroup_size = subgroup_size;
const auto result = options.back_code = split ? std::span{back} : std::span<const uint32_t>{};
RecompileForTest(front, options, nullptr, nullptr, push_data_start); const auto result = RecompileForTest(
split ? std::span{front} : std::span<const uint32_t>{monolithic},
options, nullptr, nullptr, push_data_start);
CheckSpirvBinaryValidates(result.spirv); CheckSpirvBinaryValidates(result.spirv);
const auto &layout = result.program.bindings; const auto &layout = result.program.bindings;
Check(layout.UsesPushData() == (push_data_start == PushData::MeshDrawDwordCount) && Check(layout.UsesPushData() == (push_data_start == PushData::MeshDrawDwordCount) &&
@@ -9232,9 +9241,13 @@ void TestMergedShaderUserDataSnapshot() {
const std::array<uint32_t, 4> front_data = {regs.gs_user_sgpr.value[0], const std::array<uint32_t, 4> 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]}; regs.gs_user_sgpr.value[1], regs.gs_user_sgpr.value[2], regs.gs_user_sgpr.value[3]};
ShaderMappedData mapped{}; ShaderMappedData mapped{};
mapped.type = Prospero::ShaderBinaryType::kGsFront;
mapped.code_size_bytes = sizeof(front); mapped.code_size_bytes = sizeof(front);
mapped.scratch_size_dwords = 3;
ShaderMapUserData(regs.es_regs.data_addr, mapped); ShaderMapUserData(regs.es_regs.data_addr, mapped);
mapped.type = Prospero::ShaderBinaryType::kGsBack;
mapped.code_size_bytes = sizeof(back); mapped.code_size_bytes = sizeof(back);
mapped.scratch_size_dwords = 7;
ShaderMapUserData(regs.gs_regs.data_addr, mapped); ShaderMapUserData(regs.gs_regs.data_addr, mapped);
HW::Context context; HW::Context context;
context.SetShaderStages(0x20); context.SetShaderStages(0x20);
@@ -9255,8 +9268,9 @@ void TestMergedShaderUserDataSnapshot() {
second.user_data[8] == front_data[0] + 1, second.user_data[8] == front_data[0] + 1,
"merged shader parameters did not snapshot ordinary user SGPRs at s8"); "merged shader parameters did not snapshot ordinary user SGPRs at s8");
Check(first.hash == second.hash && Check(first.hash == second.hash &&
MakeStageStaticKey(first_input) == MakeStageStaticKey(second_input), MakeStageStaticKey(first_input) == MakeStageStaticKey(second_input) &&
"a dynamic merged-shader user-data pointer changed shader identity"); first_input.mesh.scratch_size_dwords == 7,
"merged shader identity or maximum scratch size changed with dynamic user data");
CompileOptions options{}; CompileOptions options{};
options.stage = ShaderType::Mesh; options.stage = ShaderType::Mesh;
options.user_data_base = 0; options.user_data_base = 0;
@@ -9281,6 +9295,25 @@ void TestMergedShaderUserDataSnapshot() {
std::equal(table.begin(), table.end(), back_descriptor.dwords.begin()), 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"); "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<uint64_t>(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() { void TestEmbeddedVertexFormatSwizzle() {
@@ -9418,6 +9451,14 @@ void TestMeshInputAssembly() {
0x40000305, 1, 2, 3, 0, 15, false}, 0x40000305, 1, 2, 3, 0, 15, false},
{Prospero::PrimitiveType::kTriStrip, 5, 8, 0, 1, 0, 0, 11, {Prospero::PrimitiveType::kTriStrip, 5, 8, 0, 1, 0, 0, 11,
0x40000305, 2, 1, 3, 0, 12, false}, 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, {Prospero::PrimitiveType::kPointList, 12, 265, 22, 0, 2, 0x1002, 0,
0x40000101, 0, 0, 0, 528, 0xabcd, true}, 0x40000101, 0, 0, 0, 528, 0xabcd, true},
{Prospero::PrimitiveType::kPointList, 12, 265, 22, 1, 2, 0x1002, 0, {Prospero::PrimitiveType::kPointList, 12, 265, 22, 1, 2, 0x1002, 0,