From 8a3427a4c857aa08e365d1776d1f0d9f32639c9c Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 24 Jun 2021 17:40:24 -0300
Subject: [PATCH] glasm: Add passthrough geometry shader support

---
 .../backend/glasm/emit_context.cpp            |  5 ++--
 .../backend/glasm/emit_glasm.cpp              | 28 +++++++++++++++----
 src/video_core/renderer_opengl/gl_device.cpp  |  1 +
 src/video_core/renderer_opengl/gl_device.h    |  5 ++++
 .../renderer_opengl/gl_shader_cache.cpp       |  2 +-
 5 files changed, 33 insertions(+), 8 deletions(-)

diff --git a/src/shader_recompiler/backend/glasm/emit_context.cpp b/src/shader_recompiler/backend/glasm/emit_context.cpp
index 80dad9ff3..069c019ad 100644
--- a/src/shader_recompiler/backend/glasm/emit_context.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_context.cpp
@@ -83,13 +83,14 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
         break;
     }
     const std::string_view attr_stage{stage == Stage::Fragment ? "fragment" : "vertex"};
+    const VaryingState loads{info.loads.mask | info.passthrough.mask};
     for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
-        if (info.loads.Generic(index)) {
+        if (loads.Generic(index)) {
             Add("{}ATTRIB in_attr{}[]={{{}.attrib[{}..{}]}};",
                 InterpDecorator(info.interpolation[index]), index, attr_stage, index, index);
         }
     }
-    if (IsInputArray(stage) && info.loads.AnyComponent(IR::Attribute::PositionX)) {
+    if (IsInputArray(stage) && loads.AnyComponent(IR::Attribute::PositionX)) {
         Add("ATTRIB vertex_position=vertex.position;");
     }
     if (info.uses_invocation_id) {
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index 2b96977b3..64787b353 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -304,6 +304,9 @@ void SetupOptions(const IR::Program& program, const Profile& profile,
             header += "OPTION NV_viewport_array2;";
         }
     }
+    if (program.is_geometry_passthrough && profile.support_geometry_shader_passthrough) {
+        header += "OPTION NV_geometry_shader_passthrough;";
+    }
     if (info.uses_typeless_image_reads && profile.support_typeless_image_loads) {
         header += "OPTION EXT_shader_image_load_formatted;";
     }
@@ -410,11 +413,26 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I
                               runtime_info.tess_clockwise ? "CW" : "CCW");
         break;
     case Stage::Geometry:
-        header += fmt::format("PRIMITIVE_IN {};"
-                              "PRIMITIVE_OUT {};"
-                              "VERTICES_OUT {};",
-                              InputPrimitive(runtime_info.input_topology),
-                              OutputPrimitive(program.output_topology), program.output_vertices);
+        header += fmt::format("PRIMITIVE_IN {};", InputPrimitive(runtime_info.input_topology));
+        if (program.is_geometry_passthrough) {
+            if (profile.support_geometry_shader_passthrough) {
+                for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
+                    if (program.info.passthrough.Generic(index)) {
+                        header += fmt::format("PASSTHROUGH result.attrib[{}];", index);
+                    }
+                }
+                if (program.info.passthrough.AnyComponent(IR::Attribute::PositionX)) {
+                    header += "PASSTHROUGH result.position;";
+                }
+            } else {
+                LOG_WARNING(Shader_GLASM, "Passthrough geometry program used but not supported");
+            }
+        } else {
+            header +=
+                fmt::format("VERTICES_OUT {};"
+                            "PRIMITIVE_OUT {};",
+                            program.output_vertices, OutputPrimitive(program.output_topology));
+        }
         break;
     case Stage::Compute:
         header += fmt::format("GROUP_SIZE {} {} {};", program.workgroup_size[0],
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 27be347e6..6818951f2 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -160,6 +160,7 @@ Device::Device() {
     has_vertex_buffer_unified_memory = GLAD_GL_NV_vertex_buffer_unified_memory;
     has_debugging_tool_attached = IsDebugToolAttached(extensions);
     has_depth_buffer_float = HasExtension(extensions, "GL_NV_depth_buffer_float");
+    has_geometry_shader_passthrough = GLAD_GL_NV_geometry_shader_passthrough;
     has_nv_gpu_shader_5 = GLAD_GL_NV_gpu_shader5;
     has_shader_int64 = HasExtension(extensions, "GL_ARB_gpu_shader_int64");
     has_amd_shader_half_float = GLAD_GL_AMD_gpu_shader_half_float;
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index ad7b01b06..45ddf5e01 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -120,6 +120,10 @@ public:
         return has_depth_buffer_float;
     }
 
+    bool HasGeometryShaderPassthrough() const {
+        return has_geometry_shader_passthrough;
+    }
+
     bool HasNvGpuShader5() const {
         return has_nv_gpu_shader_5;
     }
@@ -174,6 +178,7 @@ private:
     bool use_asynchronous_shaders{};
     bool use_driver_cache{};
     bool has_depth_buffer_float{};
+    bool has_geometry_shader_passthrough{};
     bool has_nv_gpu_shader_5{};
     bool has_shader_int64{};
     bool has_amd_shader_half_float{};
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 06e39a503..af8e9f44d 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -187,7 +187,7 @@ ShaderCache::ShaderCache(RasterizerOpenGL& rasterizer_, Core::Frontend::EmuWindo
           .support_demote_to_helper_invocation = false,
           .support_int64_atomics = false,
           .support_derivative_control = device.HasDerivativeControl(),
-          .support_geometry_shader_passthrough = false, // TODO
+          .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
           .support_gl_nv_gpu_shader_5 = device.HasNvGpuShader5(),
           .support_gl_amd_gpu_shader_half_float = device.HasAmdShaderHalfFloat(),
           .support_gl_texture_shadow_lod = device.HasTextureShadowLod(),