diff --git a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp index e2840d064..c48170e6f 100644 --- a/src/graphics/shader/recompiler/frontend/translate/Translate.cpp +++ b/src/graphics/shader/recompiler/frontend/translate/Translate.cpp @@ -1137,7 +1137,8 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& } } else if (options.stage == ShaderType::Mesh) { const auto& mesh = options.input_info.vertex->mesh; - EXIT_NOT_IMPLEMENTED(mesh.primitives_per_group == 0u || mesh.vertices_per_group > 64u || + EXIT_NOT_IMPLEMENTED(mesh.primitives_per_group == 0u || mesh.vertices_per_group > 0x1ffu || + mesh.primitives_per_group > 0x1ffu || total_threads > 15u * options.wave_size); const auto u32 = [](uint32_t value) { return IR::U32(IR::Value(value)); }; const auto draw = [&](uint32_t index) { @@ -1173,6 +1174,10 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph& const auto wave_info = entry_ir.BitwiseOr(entry_ir.ShiftLeftLogical(wave, u32(24)), u32(((total_threads + options.wave_size - 1u) / options.wave_size) << 28u)); + entry_ir.SetScalarReg( + static_cast(2), + entry_ir.BitwiseOr(entry_ir.ShiftLeftLogical(vertices, u32(12)), + entry_ir.ShiftLeftLogical(primitives, u32(22)))); entry_ir.SetScalarReg( static_cast(3), entry_ir.BitwiseOr(wave_info, entry_ir.BitwiseOr(entry_ir.ShiftLeftLogical( diff --git a/src/graphics/shader/shader.cpp b/src/graphics/shader/shader.cpp index 58cbcb040..cd3db6e09 100644 --- a/src/graphics/shader/shader.cpp +++ b/src/graphics/shader/shader.cpp @@ -705,11 +705,14 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context& const auto& sh = context.GetShaderRegisters(); const auto [data, hash] = ShaderGetMappedData(regs.es_regs.data_addr, "ShaderGetInputInfoVS():"); const bool merged = (context.GetShaderStages() & 0x20u) != 0; + // GS_EN controls amplification, not whether an NGG shader uses a workgroup. + const bool native_ngg = !merged && data.type == Prospero::ShaderBinaryType::kGs && + regs.gs_regs.rsrc2.lds_size != 0; auto params = GetShaderParams( regs.es_regs.data_addr, hash, std::span(regs.gs_user_sgpr.value, regs.gs_regs.rsrc2.user_sgpr), data, - merged ? 8u : 0u); - if (!merged) { + merged || native_ngg ? 8u : 0u); + if (!merged && !native_ngg) { if (!ShaderGetStaticVertexInputInfo(regs.es_regs.data_addr, regs.gs_user_sgpr, regs.gs_regs.rsrc2.user_sgpr, sh, data, info)) { EXIT("failed to prepare vertex shader program\n"); @@ -754,7 +757,7 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context& group.primitive_group_size != 1u || group.vertex_group_size != 1u || user_vgpr.vgpr1 || user_vgpr.vgpr2 || user_vgpr.vgpr3 || mesh.max_vertices != sh.m_vgtGsMaxVertOut); - } else { + } else if (merged) { EXIT_NOT_IMPLEMENTED(regs.gs_regs.rsrc1.gs_vgpr_component_count != 3u || regs.gs_regs.rsrc2.es_vgpr_component_count != 3u); } @@ -763,7 +766,7 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context& user_config.GetPrimType() != Prospero::PrimitiveType::kTriFan && user_config.GetPrimType() != Prospero::PrimitiveType::kTriStrip && user_config.GetPrimType() != Prospero::PrimitiveType::kTriList) || - sh.m_vgtGsOutPrimType != 2u || sh.m_vgtGsMaxVertOut < 3u || + sh.m_vgtGsOutPrimType != 2u || (!native_ngg && sh.m_vgtGsMaxVertOut < 3u) || group.vertex_group_size < mesh.InputPrimitiveSize() || mesh.max_vertices == 0u) { EXIT("unsupported GS assembly: input=%u output=%u vertices=%u GE=%u/%u max_output=%u\n", @@ -771,14 +774,16 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context& group.primitive_group_size, group.vertex_group_size, mesh.max_vertices); } mesh.max_primitives = mesh.fast_launch ? mesh.max_vertices : + native_ngg ? mesh.InputPrimitiveCount(mesh.max_vertices) : group.primitive_group_size * (sh.m_vgtGsMaxVertOut - 2u); mesh.primitives_per_group = std::min({static_cast(group.primitive_group_size), mesh.InputPrimitiveCount(group.vertex_group_size), - mesh.max_vertices / sh.m_vgtGsMaxVertOut}); + native_ngg ? mesh.max_primitives : + mesh.max_vertices / sh.m_vgtGsMaxVertOut}); EXIT_IF(mesh.primitives_per_group == 0u); mesh.vertices_per_group = mesh.InputVertexCount(mesh.primitives_per_group); - mesh.threads_num[0] = - ((mesh.max_vertices + mesh.wave_size - 1u) / mesh.wave_size) * mesh.wave_size; + const auto threads = native_ngg && !mesh.fast_launch ? mesh.vertices_per_group : mesh.max_vertices; + mesh.threads_num[0] = ((threads + mesh.wave_size - 1u) / mesh.wave_size) * mesh.wave_size; mesh.threads_num[1] = mesh.threads_num[2] = 1u; return params; } diff --git a/tests/shaderCfgTests.cpp b/tests/shaderCfgTests.cpp index 926e0cbf5..9b7c74c30 100644 --- a/tests/shaderCfgTests.cpp +++ b/tests/shaderCfgTests.cpp @@ -10615,6 +10615,32 @@ void TestMergedShaderUserDataSnapshot() { regs.gs_user_sgpr.value[HW::UserSgprInfo::SGPRS_MAX - 1]++; Check(full.user_data.back() == 0x10001000u + HW::UserSgprInfo::SGPRS_MAX - 1, "merged shader parameters borrowed mutable register storage"); + regs.gs_regs.rsrc2.lds_size = 64; + regs.gs_regs.rsrc1.gs_vgpr_component_count = 1; + context.SetGsMaxVertOut(0); + for (const uint32_t wave_size : {32u, 64u}) { + context.SetShaderStages(wave_size == 32 ? 0x00400000u : 0u); + for (const uint16_t capacity : {64u, 128u, 256u}) { + context.SetMaxOutputPerSubgroup(capacity); + user_config.SetGeControl({capacity, capacity}); + ShaderVertexInputInfo input{}; + const auto params = PrepareProgram(regs, context, user_config, input); + Check(input.logical_stage == ShaderType::Mesh && input.mesh.wave_size == wave_size && + input.mesh.lds_size_dwords == 8192 && input.mesh.max_vertices == capacity && + input.mesh.max_primitives == capacity / 3 && + input.mesh.primitives_per_group == capacity / 3 && + input.mesh.vertices_per_group == capacity / 3 * 3 && + input.mesh.threads_num[0] == capacity && params.back_code.empty() && + params.user_data_count == 8u + HW::UserSgprInfo::SGPRS_MAX && + params.user_data[0] == 0 && params.user_data[1] == 0 && + std::equal(std::begin(regs.gs_user_sgpr.value), std::end(regs.gs_user_sgpr.value), + params.user_data.begin() + 8), + "non-amplifying NGG shader lost its workgroup, GE bounds, or native user SGPRs"); + } + } + regs.gs_regs.rsrc2.lds_size = 0; + regs.gs_regs.rsrc1.gs_vgpr_component_count = 3; + context.SetShaderStages(0x20); context.SetMaxOutputPerSubgroup(256); context.SetGsMaxVertOut(8); user_config.SetPrimitiveType(Prospero::PrimitiveType::kTriFan); @@ -10872,7 +10898,7 @@ void TestMeshInputAssembly() { struct Case { Prospero::PrimitiveType topology; uint32_t capacity, count, group, lane, width, address_low, base_vertex; - uint32_t wave_info, first, second, third, byte_offset, vertex_id; + uint32_t wave_info, group_info, first, second, third, byte_offset, vertex_id; bool fetch; uint32_t wave_size = 64; bool fast_launch = false; @@ -10880,51 +10906,63 @@ void TestMeshInputAssembly() { }; std::vector cases = { {Prospero::PrimitiveType::kTriList, 14, 177, 14, 2, 2, 0x1002, UINT32_MAX, - 0x40000309, 6, 7, 8, 340, 0xabcc, true}, + 0x40000309, 0x00c09000, 6, 7, 8, 340, 0xabcc, true}, {Prospero::PrimitiveType::kTriList, 14, 177, 14, 9, 2, 0x1002, 0, - 0x40000309, 27, 28, 29, 356, 0, false}, + 0x40000309, 0x00c09000, 27, 28, 29, 356, 0, false}, {Prospero::PrimitiveType::kTriList, 8, 180, 0, 64, 2, 0x1002, 0, - 0x41000000, 192, 193, 194, 128, 0, false}, + 0x41000000, 0x00806000, 192, 193, 194, 128, 0, false}, {Prospero::PrimitiveType::kTriList, 8, 180, 1, 1, 1, 0x1000, 5, - 0x40000206, 3, 4, 5, 4, 0xb0, true}, + 0x40000206, 0x00806000, 3, 4, 5, 4, 0xb0, true}, {Prospero::PrimitiveType::kTriList, 8, 180, 1, 1, 4, 0x1000, 5, - 0x40000206, 3, 4, 5, 28, 0xabcd0128, true}, + 0x40000206, 0x00806000, 3, 4, 5, 28, 0xabcd0128, true}, {Prospero::PrimitiveType::kTriStrip, 5, 8, 1, 1, 0, 0, 11, - 0x40000305, 1, 2, 3, 0, 15, false}, + 0x40000305, 0x00c05000, 1, 2, 3, 0, 15, false}, {Prospero::PrimitiveType::kTriStrip, 5, 8, 0, 1, 0, 0, 11, - 0x40000305, 2, 1, 3, 0, 12, false}, + 0x40000305, 0x00c05000, 2, 1, 3, 0, 12, false}, {Prospero::PrimitiveType::kTriFan, 32, 6, 0, 3, 2, 0x1002, 0, - 0x40000406, 0, 4, 5, 8, 0x0123, true}, + 0x40000406, 0x01006000, 0, 4, 5, 8, 0x0123, true}, {Prospero::PrimitiveType::kTriFan, 5, 8, 1, 0, 2, 0x1002, 0, - 0x40000305, 0, 1, 2, 0, 0xabcd, true}, + 0x40000305, 0x00c05000, 0, 1, 2, 0, 0xabcd, true}, {Prospero::PrimitiveType::kTriFan, 5, 8, 1, 1, 0, 0, 11, - 0x40000305, 0, 2, 3, 0, 15, false}, + 0x40000305, 0x00c05000, 0, 2, 3, 0, 15, false}, {Prospero::PrimitiveType::kTriFan, 5, 7, 1, 3, 2, 0x1002, 11, - 0x40000204, 0, 4, 5, 12, 0xabd8, true}, + 0x40000204, 0x00804000, 0, 4, 5, 12, 0xabd8, true}, {Prospero::PrimitiveType::kTriFan, 5, 7, 1, 4, 2, 0x1002, 0, - 0x40000204, 0, 5, 6, 16, 0, false}, + 0x40000204, 0x00804000, 0, 5, 6, 16, 0, false}, {Prospero::PrimitiveType::kTriFan, 5, 8, 1, 64, 4, 0x1000, 0, - 0x41000000, 0, 65, 66, 268, 0, false}, + 0x41000000, 0x00c05000, 0, 65, 66, 268, 0, false}, {Prospero::PrimitiveType::kLineList, 5, 17, 3, 0, 0, 0, 11, - 0x40000204, 0, 1, 0, 0, 23, false}, + 0x40000204, 0x00804000, 0, 1, 0, 0, 23, false}, {Prospero::PrimitiveType::kLineList, 6, 17, 2, 4, 2, 0x1002, 11, - 0x40000205, 8, 9, 0, 32, 0xabd8, true}, + 0x40000205, 0x00805000, 8, 9, 0, 32, 0xabd8, true}, {Prospero::PrimitiveType::kLineList, 6, 17, 2, 5, 2, 0x1002, 0, - 0x40000205, 10, 11, 0, 36, 0, false}, + 0x40000205, 0x00805000, 10, 11, 0, 36, 0, false}, {Prospero::PrimitiveType::kLineList, 8, 180, 1, 64, 4, 0x1000, 0, - 0x41000000, 128, 129, 0, 288, 0, false}, + 0x41000000, 0x01008000, 128, 129, 0, 288, 0, false}, {Prospero::PrimitiveType::kPointList, 12, 265, 22, 0, 2, 0x1002, 0, - 0x40000101, 0, 0, 0, 528, 0xabcd, true}, + 0x40000101, 0x00401000, 0, 0, 0, 528, 0xabcd, true}, {Prospero::PrimitiveType::kPointList, 12, 265, 22, 1, 2, 0x1002, 0, - 0x40000101, 1, 0, 0, 532, 0, false}, + 0x40000101, 0x00401000, 1, 0, 0, 532, 0, false}, {Prospero::PrimitiveType::kPointList, 1, 1, 0, 0, 0, 0, 11, - 0x40000101, 0, 0, 0, 0, 11, false}, + 0x40000101, 0x00401000, 0, 0, 0, 0, 11, false}, {Prospero::PrimitiveType::kPointList, 12, 265, 1, 1, 4, 0x1000, 5, - 0x40000c0c, 1, 0, 0, 52, 0xabcd0128, true}, + 0x40000c0c, 0x0300c000, 1, 0, 0, 52, 0xabcd0128, true}, {Prospero::PrimitiveType::kPointList, 12, 265, 1, 64, 4, 0x1000, 0, - 0x41000000, 64, 0, 0, 304, 0, false}, + 0x41000000, 0x0300c000, 64, 0, 0, 304, 0, false}, {Prospero::PrimitiveType::kTriStrip, 40, 40, 0, 32, 0, 0, 11, - 0x81000608, 32, 33, 34, 0, 43, false, 32}, + 0x81000608, 0x09828000, 32, 33, 34, 0, 43, false, 32}, + {Prospero::PrimitiveType::kTriList, 64, 64, 0, 0, 0, 0, 11, + 0x1000153f, 0x0543f000, 0, 1, 2, 0, 11, false, 64, false, 64}, + {Prospero::PrimitiveType::kTriList, 128, 128, 0, 64, 0, 0, 11, + 0x2100003e, 0x0a87e000, 192, 193, 194, 0, 75, false, 64, false, 128}, + {Prospero::PrimitiveType::kTriList, 256, 256, 0, 192, 0, 0, 11, + 0x4300003f, 0x154ff000, 576, 577, 578, 0, 203, false, 64, false, 256}, + {Prospero::PrimitiveType::kTriList, 64, 64, 0, 32, 0, 0, 11, + 0x2100001f, 0x0543f000, 96, 97, 98, 0, 43, false, 32, false, 64}, + {Prospero::PrimitiveType::kTriList, 128, 128, 0, 32, 0, 0, 11, + 0x41000a20, 0x0a87e000, 96, 97, 98, 0, 43, false, 32, false, 128}, + {Prospero::PrimitiveType::kTriList, 256, 256, 0, 64, 0, 0, 11, + 0x82001520, 0x154ff000, 192, 193, 194, 0, 75, false, 32, false, 256}, }; for (const uint32_t wave_size : {32u, 64u}) { for (const uint32_t threads : {wave_size, 2u * wave_size}) { @@ -10935,7 +10973,7 @@ void TestMeshInputAssembly() { (lane < wave_size ? 0x101u : 0u); const uint32_t base_vertex = group == 11u ? UINT32_MAX : 11u; cases.push_back({Prospero::PrimitiveType::kPointList, 1, 12, group, lane, 0, 0, - base_vertex, wave_info, 0, 0, 0, 0, group + base_vertex, false, + base_vertex, wave_info, 0x00401000, 0, 0, 0, 0, group + base_vertex, false, wave_size, true, threads}); } } @@ -10998,15 +11036,16 @@ void TestMeshInputAssembly() { ConstantPropagationPass(program.blocks); } std::array vgprs{}; - uint32_t sgpr3 = 0; + std::array sgprs{}; for (const auto &inst : *program.blocks.front()) { if (inst.GetOpcode() == ValueOpcode::SetVectorRegister) { vgprs[RegIndex(inst.Arg(0).VectorRegister())] = inst.Arg(1).Resolve().U32(); } else if (inst.GetOpcode() == ValueOpcode::SetScalarRegister) { - sgpr3 = inst.Arg(1).Resolve().U32(); + sgprs[RegIndex(inst.Arg(0).ScalarRegister())] = inst.Arg(1).Resolve().U32(); } } - Check(sgpr3 == test.wave_info && vgprs[0] == ((test.first << 2) | (test.second << 18)) && + Check(sgprs[2] == test.group_info && sgprs[3] == test.wave_info && + vgprs[0] == ((test.first << 2) | (test.second << 18)) && vgprs[1] == test.third * 4 && vgprs[5] == test.vertex_id && vgprs[test.fast_launch ? 6 : 8] == 9u, "mesh prolog changed input assembly, wave counts, vertex ID, or instance ID");