From b3853403b7ab2524bb0798ae85bc1c349a8957f6 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 23 Nov 2018 01:57:49 -0300
Subject: [PATCH] gl_shader_decompiler: Implement clip distances

---
 src/video_core/engines/shader_bytecode.h      |  2 +
 src/video_core/engines/shader_header.h        | 11 +++-
 .../renderer_opengl/gl_shader_decompiler.cpp  | 66 +++++++++++++------
 3 files changed, 58 insertions(+), 21 deletions(-)

diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 83a6fd8757..c0275afb56 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -82,6 +82,8 @@ union Attribute {
         Position = 7,
         Attribute_0 = 8,
         Attribute_31 = 39,
+        ClipDistances0123 = 44,
+        ClipDistances4567 = 45,
         PointCoord = 46,
         // This attribute contains a tuple of (~, ~, InstanceId, VertexId) when inside a vertex
         // shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h
index a0e015c4bc..99c34649fc 100644
--- a/src/video_core/engines/shader_header.h
+++ b/src/video_core/engines/shader_header.h
@@ -62,7 +62,16 @@ struct Header {
             INSERT_PADDING_BYTES(1);  // ImapSystemValuesB
             INSERT_PADDING_BYTES(16); // ImapGenericVector[32]
             INSERT_PADDING_BYTES(2);  // ImapColor
-            INSERT_PADDING_BYTES(2);  // ImapSystemValuesC
+            union {
+                BitField<0, 8, u16> clip_distances;
+                BitField<8, 1, u16> point_sprite_s;
+                BitField<9, 1, u16> point_sprite_t;
+                BitField<10, 1, u16> fog_coordinate;
+                BitField<12, 1, u16> tessellation_eval_point_u;
+                BitField<13, 1, u16> tessellation_eval_point_v;
+                BitField<14, 1, u16> instance_id;
+                BitField<15, 1, u16> vertex_id;
+            };
             INSERT_PADDING_BYTES(5);  // ImapFixedFncTexture[10]
             INSERT_PADDING_BYTES(1);  // ImapReserved
             INSERT_PADDING_BYTES(3);  // OmapSystemValuesA
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 90a88b91a9..1ae25c7b33 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -485,27 +485,42 @@ public:
                                       const Register& buf_reg) {
         const std::string dest = GetOutputAttribute(attribute);
         const std::string src = GetRegisterAsFloat(val_reg);
+        if (dest.empty())
+            return;
 
-        if (!dest.empty()) {
-            // Can happen with unknown/unimplemented output attributes, in which case we ignore the
-            // instruction for now.
-            if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
-                // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
-                // shader. These instructions use a dirty register as buffer index, to avoid some
-                // drivers from complaining about out of boundary writes, guard them.
-                const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " +
-                                            std::to_string(MAX_GEOMETRY_BUFFERS) + ')'};
-                shader.AddLine("amem[" + buf_index + "][" +
-                               std::to_string(static_cast<u32>(attribute)) + ']' +
-                               GetSwizzle(elem) + " = " + src + ';');
-            } else {
-                if (attribute == Attribute::Index::PointSize) {
-                    fixed_pipeline_output_attributes_used.insert(attribute);
-                    shader.AddLine(dest + " = " + src + ';');
-                } else {
-                    shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';');
-                }
-            }
+        // Can happen with unknown/unimplemented output attributes, in which case we ignore the
+        // instruction for now.
+        if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
+            // TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
+            // shader. These instructions use a dirty register as buffer index, to avoid some
+            // drivers from complaining about out of boundary writes, guard them.
+            const std::string buf_index{"((" + GetRegisterAsInteger(buf_reg) + ") % " +
+                                        std::to_string(MAX_GEOMETRY_BUFFERS) + ')'};
+            shader.AddLine("amem[" + buf_index + "][" +
+                           std::to_string(static_cast<u32>(attribute)) + ']' + GetSwizzle(elem) +
+                           " = " + src + ';');
+            return;
+        }
+
+        switch (attribute) {
+        case Attribute::Index::ClipDistances0123:
+        case Attribute::Index::ClipDistances4567: {
+            const u64 index = attribute == Attribute::Index::ClipDistances4567 ? 4 : 0 + elem;
+            UNIMPLEMENTED_IF_MSG(
+                ((header.vtg.clip_distances >> index) & 1) == 0,
+                "Shader is setting gl_ClipDistance{} without enabling it in the header", index);
+
+            fixed_pipeline_output_attributes_used.insert(attribute);
+            shader.AddLine(dest + '[' + std::to_string(index) + "] = " + src + ';');
+            break;
+        }
+        case Attribute::Index::PointSize:
+            fixed_pipeline_output_attributes_used.insert(attribute);
+            shader.AddLine(dest + " = " + src + ';');
+            break;
+        default:
+            shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';');
+            break;
         }
     }
 
@@ -725,12 +740,19 @@ private:
     void GenerateVertex() {
         if (stage != Maxwell3D::Regs::ShaderStage::Vertex)
             return;
+        bool clip_distances_declared = false;
+
         declarations.AddLine("out gl_PerVertex {");
         ++declarations.scope;
         declarations.AddLine("vec4 gl_Position;");
         for (auto& o : fixed_pipeline_output_attributes_used) {
             if (o == Attribute::Index::PointSize)
                 declarations.AddLine("float gl_PointSize;");
+            if (!clip_distances_declared && (o == Attribute::Index::ClipDistances0123 ||
+                                             o == Attribute::Index::ClipDistances4567)) {
+                declarations.AddLine("float gl_ClipDistance[];");
+                clip_distances_declared = true;
+            }
         }
         --declarations.scope;
         declarations.AddLine("};");
@@ -901,6 +923,10 @@ private:
             return "gl_PointSize";
         case Attribute::Index::Position:
             return "position";
+        case Attribute::Index::ClipDistances0123:
+        case Attribute::Index::ClipDistances4567: {
+            return "gl_ClipDistance";
+        }
         default:
             const u32 index{static_cast<u32>(attribute) -
                             static_cast<u32>(Attribute::Index::Attribute_0)};