From 625a4af73afec1a45f5a8004b0933f5a3d414103 Mon Sep 17 00:00:00 2001
From: Billy Laws <blaws05@gmail.com>
Date: Sat, 31 Dec 2022 23:19:10 +0000
Subject: shader_recompiler: Add support for lowering geometry passthrough

Reuses most of the existing code for generating the gl_Layer passthrough. Fixes geometry in Nier: Automata on GPUs without HW passthrough support.
---
 .../frontend/maxwell/translate_program.cpp         | 106 +++++++++++++--------
 1 file changed, 66 insertions(+), 40 deletions(-)

(limited to 'src/shader_recompiler/frontend/maxwell/translate_program.cpp')

diff --git a/src/shader_recompiler/frontend/maxwell/translate_program.cpp b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
index 89fced84c8..4a0ccceb7d 100644
--- a/src/shader_recompiler/frontend/maxwell/translate_program.cpp
+++ b/src/shader_recompiler/frontend/maxwell/translate_program.cpp
@@ -171,6 +171,64 @@ std::map<IR::Attribute, IR::Attribute> GenerateLegacyToGenericMappings(
     }
     return mapping;
 }
+
+void EmitGeometryPassthrough(IR::IREmitter& ir, const IR::Program& program, const Shader::VaryingState &passthrough_mask, bool passthrough_position, std::optional<IR::Attribute> passthrough_layer_attr) {
+    for (u32 i = 0; i < program.output_vertices; i++) {
+        // Assign generics from input
+        for (u32 j = 0; j < 32; j++) {
+            if (!passthrough_mask.Generic(j)) {
+                continue;
+            }
+
+            const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4);
+            ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
+            ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
+            ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
+            ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
+        }
+
+        if (passthrough_position) {
+            // Assign position from input
+            const IR::Attribute attr = IR::Attribute::PositionX;
+            ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
+            ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
+            ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
+            ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
+        }
+
+        if (passthrough_layer_attr) {
+            // Assign layer
+            ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(*passthrough_layer_attr), ir.Imm32(0));
+        }
+
+        // Emit vertex
+        ir.EmitVertex(ir.Imm32(0));
+    }
+    ir.EndPrimitive(ir.Imm32(0));
+}
+
+u32 GetOutputTopologyVertices(OutputTopology output_topology) {
+    switch (output_topology) {
+        case OutputTopology::PointList:
+            return 1;
+        case OutputTopology::LineStrip:
+            return 2;
+        default:
+            return 3;
+    }
+}
+
+void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInfo& host_info) {
+    for (IR::Block *const block : program.blocks) {
+        for (IR::Inst &inst : block->Instructions()) {
+            if (inst.GetOpcode() == IR::Opcode::Epilogue) {
+                IR::IREmitter ir{*block, IR::Block::InstructionList::s_iterator_to(inst)};
+                EmitGeometryPassthrough(ir, program, program.info.passthrough, program.info.passthrough.AnyComponent(IR::Attribute::PositionX), {});
+            }
+        }
+    }
+}
+
 } // Anonymous namespace
 
 IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
@@ -198,6 +256,11 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
             for (size_t i = 0; i < program.info.passthrough.mask.size(); ++i) {
                 program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0;
             }
+
+            if (!host_info.support_geometry_shader_passthrough) {
+                program.output_vertices = GetOutputTopologyVertices(program.output_topology);
+                LowerGeometryPassthrough(program, host_info);
+            }
         }
         break;
     }
@@ -342,17 +405,8 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
     IR::Program program;
     program.stage = Stage::Geometry;
     program.output_topology = output_topology;
-    switch (output_topology) {
-    case OutputTopology::PointList:
-        program.output_vertices = 1;
-        break;
-    case OutputTopology::LineStrip:
-        program.output_vertices = 2;
-        break;
-    default:
-        program.output_vertices = 3;
-        break;
-    }
+    program.output_vertices = GetOutputTopologyVertices(output_topology);
+
 
     program.is_geometry_passthrough = false;
     program.info.loads.mask = source_program.info.stores.mask;
@@ -366,35 +420,7 @@ IR::Program GenerateGeometryPassthrough(ObjectPool<IR::Inst>& inst_pool,
     node.data.block = current_block;
 
     IR::IREmitter ir{*current_block};
-    for (u32 i = 0; i < program.output_vertices; i++) {
-        // Assign generics from input
-        for (u32 j = 0; j < 32; j++) {
-            if (!program.info.stores.Generic(j)) {
-                continue;
-            }
-
-            const IR::Attribute attr = IR::Attribute::Generic0X + (j * 4);
-            ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
-            ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
-            ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
-            ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
-        }
-
-        // Assign position from input
-        const IR::Attribute attr = IR::Attribute::PositionX;
-        ir.SetAttribute(attr + 0, ir.GetAttribute(attr + 0, ir.Imm32(i)), ir.Imm32(0));
-        ir.SetAttribute(attr + 1, ir.GetAttribute(attr + 1, ir.Imm32(i)), ir.Imm32(0));
-        ir.SetAttribute(attr + 2, ir.GetAttribute(attr + 2, ir.Imm32(i)), ir.Imm32(0));
-        ir.SetAttribute(attr + 3, ir.GetAttribute(attr + 3, ir.Imm32(i)), ir.Imm32(0));
-
-        // Assign layer
-        ir.SetAttribute(IR::Attribute::Layer, ir.GetAttribute(source_program.info.emulated_layer),
-                        ir.Imm32(0));
-
-        // Emit vertex
-        ir.EmitVertex(ir.Imm32(0));
-    }
-    ir.EndPrimitive(ir.Imm32(0));
+    EmitGeometryPassthrough(ir, program, program.info.stores, true, source_program.info.emulated_layer);
 
     IR::Block* return_block{block_pool.Create(inst_pool)};
     IR::IREmitter{*return_block}.Epilogue();
-- 
cgit v1.2.3-70-g09d2