From 4960ab85f81f48553b7217261f4181e31d812a10 Mon Sep 17 00:00:00 2001
From: mageven <62494521+mageven@users.noreply.github.com>
Date: Fri, 17 Apr 2020 06:46:49 +0530
Subject: [PATCH] Implement Depth Clamping (#1120)

* Implement Depth Clamping and add misc enums

* Fix formatting
---
 Ryujinx.Graphics.GAL/IPipeline.cs             |  1 +
 Ryujinx.Graphics.Gpu/Engine/Methods.cs        | 23 ++++++++++++++++---
 Ryujinx.Graphics.Gpu/State/MethodOffset.cs    |  1 +
 .../State/ViewVolumeClipControl.cs            | 12 ++++++++++
 Ryujinx.Graphics.Gpu/State/YControl.cs        | 11 +++++++++
 Ryujinx.Graphics.OpenGL/Pipeline.cs           | 15 ++++++++++++
 6 files changed, 60 insertions(+), 3 deletions(-)
 create mode 100644 Ryujinx.Graphics.Gpu/State/ViewVolumeClipControl.cs
 create mode 100644 Ryujinx.Graphics.Gpu/State/YControl.cs

diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs
index 5b663823f..4c892beaa 100644
--- a/Ryujinx.Graphics.GAL/IPipeline.cs
+++ b/Ryujinx.Graphics.GAL/IPipeline.cs
@@ -29,6 +29,7 @@ namespace Ryujinx.Graphics.GAL
         void SetBlendColor(ColorF color);
 
         void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp);
+        void SetDepthClamp(bool clampNear, bool clampFar);
         void SetDepthMode(DepthMode mode);
         void SetDepthTest(DepthTestDescriptor depthTest);
 
diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
index 509138014..b7f4e1d96 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
@@ -127,6 +127,11 @@ namespace Ryujinx.Graphics.Gpu.Engine
                 UpdateScissorState(state);
             }
 
+            if (state.QueryModified(MethodOffset.ViewVolumeClipControl))
+            {
+                UpdateDepthClampState(state);
+            }
+
             if (state.QueryModified(MethodOffset.DepthTestEnable,
                                     MethodOffset.DepthWriteEnable,
                                     MethodOffset.DepthTestFunc))
@@ -134,7 +139,9 @@ namespace Ryujinx.Graphics.Gpu.Engine
                 UpdateDepthTestState(state);
             }
 
-            if (state.QueryModified(MethodOffset.DepthMode, MethodOffset.ViewportTransform, MethodOffset.ViewportExtents))
+            if (state.QueryModified(MethodOffset.DepthMode,
+                                    MethodOffset.ViewportTransform,
+                                    MethodOffset.ViewportExtents))
             {
                 UpdateViewportTransform(state);
             }
@@ -362,6 +369,17 @@ namespace Ryujinx.Graphics.Gpu.Engine
             }
         }
 
+        /// <summary>
+        /// Updates host depth clamp state based on current GPU state.
+        /// </summary>
+        /// <param name="state">Current GPU state</param>
+        private void UpdateDepthClampState(GpuState state)
+        {
+            ViewVolumeClipControl clip = state.Get<ViewVolumeClipControl>(MethodOffset.ViewVolumeClipControl);
+            _context.Renderer.Pipeline.SetDepthClamp((clip & ViewVolumeClipControl.DepthClampNear) != 0,
+                                                     (clip & ViewVolumeClipControl.DepthClampFar) != 0);
+        }
+
         /// <summary>
         /// Updates host depth test state based on current GPU state.
         /// </summary>
@@ -384,8 +402,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
 
             _context.Renderer.Pipeline.SetDepthMode(depthMode);
 
-            bool flipY = (state.Get<int>(MethodOffset.YControl) & 1) != 0;
-
+            bool flipY = (state.Get<YControl>(MethodOffset.YControl) & YControl.NegateY) != 0;
             float yFlip = flipY ? -1 : 1;
 
             Viewport[] viewports = new Viewport[Constants.TotalViewports];
diff --git a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs
index a178c2f8c..b8ee7e916 100644
--- a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs
+++ b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs
@@ -75,6 +75,7 @@ namespace Ryujinx.Graphics.Gpu.State
         VertexBufferInstanced           = 0x620,
         FaceState                       = 0x646,
         ViewportTransformEnable         = 0x64b,
+        ViewVolumeClipControl           = 0x64f,
         Clear                           = 0x674,
         RtColorMask                     = 0x680,
         ReportState                     = 0x6c0,
diff --git a/Ryujinx.Graphics.Gpu/State/ViewVolumeClipControl.cs b/Ryujinx.Graphics.Gpu/State/ViewVolumeClipControl.cs
new file mode 100644
index 000000000..4e12c4244
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/State/ViewVolumeClipControl.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Ryujinx.Graphics.Gpu.State
+{
+    [Flags]
+    enum ViewVolumeClipControl
+    {
+        ForceDepthRangeZeroToOne = 1 << 0,
+        DepthClampNear           = 1 << 3,
+        DepthClampFar            = 1 << 4,
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/State/YControl.cs b/Ryujinx.Graphics.Gpu/State/YControl.cs
new file mode 100644
index 000000000..58e000d93
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/State/YControl.cs
@@ -0,0 +1,11 @@
+using System;
+
+namespace Ryujinx.Graphics.Gpu.State
+{
+    [Flags]
+    enum YControl
+    {
+        NegateY          = 1 << 0,
+        TriangleRastFlip = 1 << 4
+    }
+}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs
index aef3383b1..e32b5b855 100644
--- a/Ryujinx.Graphics.OpenGL/Pipeline.cs
+++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs
@@ -546,6 +546,21 @@ namespace Ryujinx.Graphics.OpenGL
             // GL.PolygonOffsetClamp(factor, units, clamp);
         }
 
+        public void SetDepthClamp(bool clampNear, bool clampFar)
+        {
+            // TODO: Use GL_AMD_depth_clamp_separate or similar if available?
+            // Currently enables clamping if either is set.
+            bool clamp = clampNear || clampFar;
+
+            if (!clamp)
+            {
+                GL.Disable(EnableCap.DepthClamp);
+                return;
+            }
+
+            GL.Enable(EnableCap.DepthClamp);
+        }
+
         public void SetDepthMode(DepthMode mode)
         {
             ClipDepthMode depthMode = mode.Convert();