shader_recompiler: Implement vertex count lookup for Geometry stage

Add proper handling of input topologies in the Geometry stage for all three
shader backends (GLASM, GLSL, SPIRV). This implementation uses a lookup table
approach to determine vertex counts based on input topology type (Points,
Lines, LinesAdjacency, Triangles, TrianglesAdjacency) and shifts the vertex
count by 16 bits as required by the invocation info format.

Additional changes:
- Fixed TessellationControl and TessellationEval stages to properly break
  after emitting code
- Added proper header include for runtime_info.h in GLASM backend
- Improved code documentation with clear commenting patterns

This change ensures accurate geometry shader behavior across all backends,
improving compatibility with games that rely on proper vertex count reporting.

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron 2025-02-28 17:08:27 +10:00
parent 84e5fbc089
commit 9b293c3a98
3 changed files with 123 additions and 0 deletions

View file

@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string_view>
@ -7,6 +8,7 @@
#include "shader_recompiler/backend/glasm/glasm_emit_context.h"
#include "shader_recompiler/frontend/ir/value.h"
#include "shader_recompiler/profile.h"
#include "shader_recompiler/runtime_info.h"
#include "shader_recompiler/shader_info.h"
namespace Shader::Backend::GLASM {
@ -403,6 +405,8 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) {
switch (ctx.stage) {
case Stage::TessellationControl:
ctx.Add("SHL.U {}.x,primitive.vertexcount,16;", inst);
break;
case Stage::TessellationEval:
ctx.Add("SHL.U {}.x,primitive.vertexcount,16;", inst);
break;
@ -410,7 +414,47 @@ void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) {
// Return sample mask in upper 16 bits
ctx.Add("SHL.U {}.x,fragment.samplemask,16;", inst);
break;
case Stage::Geometry: {
// Return vertex count in upper 16 bits based on input topology
// Using a lookup table approach for vertex counts
const std::array<u32, 5> vertex_counts = {
1, // Points
2, // Lines
4, // LinesAdjacency
3, // Triangles
6 // TrianglesAdjacency
};
// Map the input topology to an index in our lookup table
u32 topology_index = 0;
switch (ctx.runtime_info.input_topology) {
case Shader::InputTopology::Lines:
topology_index = 1;
break;
case Shader::InputTopology::LinesAdjacency:
topology_index = 2;
break;
case Shader::InputTopology::Triangles:
topology_index = 3;
break;
case Shader::InputTopology::TrianglesAdjacency:
topology_index = 4;
break;
case Shader::InputTopology::Points:
default:
topology_index = 0;
break;
}
// Get the vertex count from the lookup table and shift it
const u32 result = vertex_counts[topology_index] << 16;
ctx.Add("MOV.S {}.x,0x{:x};", inst, result);
break;
}
case Stage::Compute:
// Return standard format (0x00ff0000)
ctx.Add("MOV.S {}.x,0x00ff0000;", inst);
break;
default:
// Return standard format (0x00ff0000)
ctx.Add("MOV.S {}.x,0x00ff0000;", inst);

View file

@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <string_view>
@ -423,6 +424,8 @@ void EmitInvocationId(EmitContext& ctx, IR::Inst& inst) {
void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) {
switch (ctx.stage) {
case Stage::TessellationControl:
ctx.AddU32("{}=uint(gl_PatchVerticesIn)<<16;", inst);
break;
case Stage::TessellationEval:
ctx.AddU32("{}=uint(gl_PatchVerticesIn)<<16;", inst);
break;
@ -430,7 +433,46 @@ void EmitInvocationInfo(EmitContext& ctx, IR::Inst& inst) {
// Return sample mask in upper 16 bits
ctx.AddU32("{}=uint(gl_SampleMaskIn[0])<<16;", inst);
break;
case Stage::Geometry: {
// Return vertex count in upper 16 bits based on input topology
// Using a lookup table approach for vertex counts
ctx.AddU32("{}=uint(", inst);
// Define vertex counts for each topology in a comment for clarity
ctx.Add("// Vertex counts: Points=1, Lines=2, LinesAdj=4, Triangles=3, TrianglesAdj=6\n");
// Use a lookup table approach in the generated GLSL code
ctx.Add("(");
// Generate a conditional expression that acts like a lookup table
switch (ctx.runtime_info.input_topology) {
case InputTopology::Points:
ctx.Add("1"); // Points
break;
case InputTopology::Lines:
ctx.Add("2"); // Lines
break;
case InputTopology::LinesAdjacency:
ctx.Add("4"); // LinesAdjacency
break;
case InputTopology::Triangles:
ctx.Add("3"); // Triangles
break;
case InputTopology::TrianglesAdjacency:
ctx.Add("6"); // TrianglesAdjacency
break;
default:
ctx.Add("1"); // Default to Points
break;
}
ctx.Add(")<<16);");
break;
}
case Stage::Compute:
// Return standard format (0x00ff0000)
ctx.AddU32("{}=0x00ff0000u;", inst);
break;
default:
// Return standard format (0x00ff0000)
ctx.AddU32("{}=0x00ff0000u;", inst);

View file

@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <bit>
@ -553,6 +554,42 @@ Id EmitInvocationInfo(EmitContext& ctx) {
// Return sample mask in upper 16 bits
return ctx.OpShiftLeftLogical(ctx.U32[1], ctx.OpLoad(ctx.U32[1], ctx.sample_mask),
ctx.Const(16u));
case Stage::Geometry: {
// Return vertex count in upper 16 bits based on input topology
// Using a lookup table approach for vertex counts
const std::array<u32, 5> vertex_counts = {
1, // Points
2, // Lines
4, // LinesAdjacency
3, // Triangles
6 // TrianglesAdjacency
};
// Map the input topology to an index in our lookup table
u32 topology_index = 0;
switch (ctx.runtime_info.input_topology) {
case InputTopology::Lines:
topology_index = 1;
break;
case InputTopology::LinesAdjacency:
topology_index = 2;
break;
case InputTopology::Triangles:
topology_index = 3;
break;
case InputTopology::TrianglesAdjacency:
topology_index = 4;
break;
case InputTopology::Points:
default:
topology_index = 0;
break;
}
// Get the vertex count from the lookup table and shift it
const u32 vertex_count = vertex_counts[topology_index];
return ctx.OpShiftLeftLogical(ctx.U32[1], ctx.Const(vertex_count), ctx.Const(16u));
}
case Stage::Compute:
// For compute shaders, return standard format since we can't access workgroup size directly
return ctx.Const(0x00ff0000u);