Support triangle fans in PS5 geometry shaders

Keep the fan center across subgroups and reuse the existing index fetch.
This commit is contained in:
nmzik 2026-09-16 08:42:47 +04:00
parent 957452d6f9
commit 7c9148dd47
4 changed files with 47 additions and 16 deletions

View File

@ -1102,26 +1102,32 @@ IR::Program TranslateProgram(const Decoder::Program& decoded, const CFG::Graph&
entry_ir.BitwiseOr(wave_info, entry_ir.BitwiseOr(entry_ir.ShiftLeftLogical( entry_ir.BitwiseOr(wave_info, entry_ir.BitwiseOr(entry_ir.ShiftLeftLogical(
primitive_count, u32(8)), primitive_count, u32(8)),
vertex_count))); vertex_count)));
// GS adjacency addresses local ES records in LDS. Strip winding alternates // GS adjacency addresses local ES records in LDS. Fans retain the draw's
// with the global primitive number, including across subgroup boundaries. // center in every subgroup; strip winding follows the global primitive.
const auto parity = mesh.input_primitive ==
static_cast<uint32_t>(Prospero::PrimitiveType::kTriStrip)
? entry_ir.BitwiseAnd(entry_ir.IAdd(primitive_chunk, local), u32(1))
: 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); auto first = vertex;
const auto second = mesh.InputPrimitiveSize() >= 2u auto second = u32(0);
? entry_ir.ISub(entry_ir.IAdd(vertex, u32(1)), parity) auto third = u32(0);
: u32(0); if (mesh.InputPrimitiveSize() >= 2u) {
const auto third = mesh.InputPrimitiveSize() == 3u second = entry_ir.IAdd(vertex, u32(1));
? entry_ir.IAdd(vertex, u32(2)) }
: u32(0); if (mesh.InputPrimitiveSize() == 3u) {
third = entry_ir.IAdd(vertex, u32(2));
}
auto input_vertex = entry_ir.IAdd(chunk, local);
if (mesh.input_primitive == static_cast<uint32_t>(Prospero::PrimitiveType::kTriFan)) {
first = u32(0);
input_vertex = entry_ir.Select(entry_ir.IEqual(local, u32(0)), u32(0), input_vertex);
} else if (mesh.input_primitive == static_cast<uint32_t>(Prospero::PrimitiveType::kTriStrip)) {
const auto parity = entry_ir.BitwiseAnd(entry_ir.IAdd(primitive_chunk, local), u32(1));
first = entry_ir.IAdd(first, parity);
second = entry_ir.ISub(second, parity);
}
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(0), entry_ir.SetVectorReg(static_cast<IR::VectorReg>(0),
entry_ir.BitwiseOr(entry_ir.ShiftLeftLogical(first, u32(2)), entry_ir.BitwiseOr(entry_ir.ShiftLeftLogical(first, u32(2)),
entry_ir.ShiftLeftLogical(second, u32(18)))); entry_ir.ShiftLeftLogical(second, u32(18))));
entry_ir.SetVectorReg(static_cast<IR::VectorReg>(1), entry_ir.SetVectorReg(static_cast<IR::VectorReg>(1),
entry_ir.ShiftLeftLogical(third, u32(2))); entry_ir.ShiftLeftLogical(third, u32(2)));
const auto input_vertex = entry_ir.IAdd(chunk, local);
const auto index_bytes = draw(3); const auto index_bytes = draw(3);
const auto indexed = entry_ir.INotEqual(index_bytes, u32(0)); const auto indexed = entry_ir.INotEqual(index_bytes, u32(0));
const auto index_low = draw(4); const auto index_low = draw(4);

View File

@ -797,6 +797,7 @@ ShaderParams PrepareProgram(const HW::VertexShaderInfo& regs, const HW::Context&
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::kLineList &&
user_config.GetPrimType() != Prospero::PrimitiveType::kTriFan &&
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 ||

View File

@ -97,8 +97,11 @@ struct ShaderMeshInputInfo: ShaderWorkgroupInputInfo {
} }
} }
[[nodiscard]] constexpr uint32_t InputPrimitiveStep() const { [[nodiscard]] constexpr uint32_t InputPrimitiveStep() const {
return input_primitive == static_cast<uint32_t>(Prospero::PrimitiveType::kTriStrip) switch (static_cast<Prospero::PrimitiveType>(input_primitive)) {
? 1u : InputPrimitiveSize(); case Prospero::PrimitiveType::kTriFan:
case Prospero::PrimitiveType::kTriStrip: return 1u;
default: return 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();

View File

@ -9606,6 +9606,15 @@ void TestMergedShaderUserDataSnapshot() {
params.user_data.begin() + 8), params.user_data.begin() + 8),
"monolithic NGG shader used stale GS-back state or lost its s8 user data"); "monolithic NGG shader used stale GS-back state or lost its s8 user data");
} }
context.SetMaxOutputPerSubgroup(256);
context.SetGsMaxVertOut(8);
user_config.SetPrimitiveType(Prospero::PrimitiveType::kTriFan);
user_config.SetGeControl({32, 32});
ShaderVertexInputInfo fan_input{};
PrepareProgram(regs, context, user_config, fan_input);
Check(fan_input.mesh.primitives_per_group == 30 && fan_input.mesh.vertices_per_group == 32 &&
fan_input.mesh.max_vertices == 256 && fan_input.mesh.max_primitives == 192,
"captured triangle-fan GS configuration lost its subgroup assembly limits");
} }
void TestEmbeddedFetchPreservesSharedScalarLoad() { void TestEmbeddedFetchPreservesSharedScalarLoad() {
@ -9791,6 +9800,18 @@ 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::kTriFan, 32, 6, 0, 3, 2, 0x1002, 0,
0x40000406, 0, 4, 5, 8, 0x0123, true},
{Prospero::PrimitiveType::kTriFan, 5, 8, 1, 0, 2, 0x1002, 0,
0x40000305, 0, 1, 2, 0, 0xabcd, true},
{Prospero::PrimitiveType::kTriFan, 5, 8, 1, 1, 0, 0, 11,
0x40000305, 0, 2, 3, 0, 15, false},
{Prospero::PrimitiveType::kTriFan, 5, 7, 1, 3, 2, 0x1002, 11,
0x40000204, 0, 4, 5, 12, 0xabd8, true},
{Prospero::PrimitiveType::kTriFan, 5, 7, 1, 4, 2, 0x1002, 0,
0x40000204, 0, 5, 6, 16, 0, false},
{Prospero::PrimitiveType::kTriFan, 5, 8, 1, 64, 4, 0x1000, 0,
0x41000000, 0, 65, 66, 268, 0, false},
{Prospero::PrimitiveType::kLineList, 5, 17, 3, 0, 0, 0, 11, {Prospero::PrimitiveType::kLineList, 5, 17, 3, 0, 0, 0, 11,
0x40000204, 0, 1, 0, 0, 23, false}, 0x40000204, 0, 1, 0, 0, 23, false},
{Prospero::PrimitiveType::kLineList, 6, 17, 2, 4, 2, 0x1002, 11, {Prospero::PrimitiveType::kLineList, 6, 17, 2, 4, 2, 0x1002, 11,