From f7352411f08c3a099b753d290540bb7c02fecac3 Mon Sep 17 00:00:00 2001
From: ameerj <52414509+ameerj@users.noreply.github.com>
Date: Thu, 24 Jun 2021 20:13:21 -0400
Subject: [PATCH] glsl: Add passthrough geometry shader support

---
 .../backend/glsl/emit_context.cpp             | 30 ++++++++++++++-----
 .../backend/glsl/emit_context.h               |  1 +
 .../backend/glsl/emit_glsl_special.cpp        |  3 ++
 3 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/src/shader_recompiler/backend/glsl/emit_context.cpp b/src/shader_recompiler/backend/glsl/emit_context.cpp
index 0d7f7bc3ba..36527bbd4b 100644
--- a/src/shader_recompiler/backend/glsl/emit_context.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_context.cpp
@@ -236,6 +236,9 @@ void SetupOutPerVertex(EmitContext& ctx, std::string& header) {
     if (!StoresPerVertexAttributes(ctx.stage)) {
         return;
     }
+    if (ctx.uses_geometry_passthrough) {
+        return;
+    }
     header += "out gl_PerVertex{vec4 gl_Position;";
     if (ctx.info.stores[IR::Attribute::PointSize]) {
         header += "float gl_PointSize;";
@@ -272,12 +275,13 @@ void SetupLegacyInPerFragment(EmitContext& ctx, std::string& header) {
 
 EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile& profile_,
                          const RuntimeInfo& runtime_info_)
-    : info{program.info}, profile{profile_}, runtime_info{runtime_info_} {
+    : info{program.info}, profile{profile_}, runtime_info{runtime_info_}, stage{program.stage},
+      uses_geometry_passthrough{program.is_geometry_passthrough &&
+                                profile.support_geometry_shader_passthrough} {
     if (profile.need_fastmath_off) {
         header += "#pragma optionNV(fastmath off)\n";
     }
     SetupExtensions();
-    stage = program.stage;
     switch (program.stage) {
     case Stage::VertexA:
     case Stage::VertexB:
@@ -295,10 +299,17 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
         break;
     case Stage::Geometry:
         stage_name = "gs";
-        header += fmt::format("layout({})in;layout({},max_vertices={})out;"
-                              "in gl_PerVertex{{vec4 gl_Position;}}gl_in[];",
-                              InputPrimitive(runtime_info.input_topology),
-                              OutputPrimitive(program.output_topology), program.output_vertices);
+        header += fmt::format("layout({})in;", InputPrimitive(runtime_info.input_topology));
+        if (uses_geometry_passthrough) {
+            header += "layout(passthrough)in gl_PerVertex{vec4 gl_Position;};";
+            break;
+        } else if (program.is_geometry_passthrough &&
+                   !profile.support_geometry_shader_passthrough) {
+            LOG_WARNING(Shader_GLSL, "Passthrough geometry program used but not supported");
+        }
+        header += fmt::format(
+            "layout({},max_vertices={})out;in gl_PerVertex{{vec4 gl_Position;}}gl_in[];",
+            OutputPrimitive(program.output_topology), program.output_vertices);
         break;
     case Stage::Fragment:
         stage_name = "fs";
@@ -329,7 +340,9 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
         if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) {
             continue;
         }
-        header += fmt::format("layout(location={}){}in vec4 in_attr{}{};", index,
+        const auto qualifier{uses_geometry_passthrough ? "passthrough"
+                                                       : fmt::format("location={}", index)};
+        header += fmt::format("layout({}){}in vec4 in_attr{}{};", qualifier,
                               InterpDecorator(info.interpolation[index]), index,
                               InputArrayDecorator(stage));
     }
@@ -412,6 +425,9 @@ void EmitContext::SetupExtensions() {
     if (info.uses_derivatives && profile.support_gl_derivative_control) {
         header += "#extension GL_ARB_derivative_control : enable\n";
     }
+    if (uses_geometry_passthrough) {
+        header += "#extension GL_NV_geometry_shader_passthrough : enable\n";
+    }
 }
 
 void EmitContext::DefineConstantBuffers(Bindings& bindings) {
diff --git a/src/shader_recompiler/backend/glsl/emit_context.h b/src/shader_recompiler/backend/glsl/emit_context.h
index ecdf6e5bc8..dd7397489a 100644
--- a/src/shader_recompiler/backend/glsl/emit_context.h
+++ b/src/shader_recompiler/backend/glsl/emit_context.h
@@ -157,6 +157,7 @@ public:
 
     bool uses_y_direction{};
     bool uses_cc_carry{};
+    bool uses_geometry_passthrough{};
 
 private:
     void SetupExtensions();
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
index 298881c7bb..9b866f8898 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_special.cpp
@@ -17,6 +17,9 @@ std::string_view OutputVertexIndex(EmitContext& ctx) {
 }
 
 void InitializeOutputVaryings(EmitContext& ctx) {
+    if (ctx.uses_geometry_passthrough) {
+        return;
+    }
     if (ctx.stage == Stage::VertexB || ctx.stage == Stage::Geometry) {
         ctx.Add("gl_Position=vec4(0,0,0,1);");
     }