From 0cd5ba03fe4cccd232cdb6aa7e8648b4a08a0ab1 Mon Sep 17 00:00:00 2001
From: BaronKiko <BaronKiko@users.noreply.github.com>
Date: Sun, 13 Jan 2019 21:26:42 +0000
Subject: [PATCH] =?UTF-8?q?Scissor=20test=20implementation.=20Partially=20?=
 =?UTF-8?q?stubbed=20until=20geometry=20shaders=E2=80=A6=20(#556)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Scissor test implementation. Partially stubbed until geometry shaders are fixed

* Apply to all viewports when geometry shaders are disabled.

* Also apply enable cap to all viewports when geometry shaders are disabled

* Added fixme as per suggestion

Co-Authored-By: BaronKiko <BaronKiko@users.noreply.github.com>

* Apparently no alignment needed here.

* Comment on new line

* Correct height calculation
---
 Ryujinx.Graphics/Gal/GalPipelineState.cs      | 13 +++++
 Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs    | 51 +++++++++++++++++++
 Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs  | 36 +++++++++++++
 .../Graphics3d/NvGpuEngine3dReg.cs            |  3 ++
 4 files changed, 103 insertions(+)

diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs
index 8630da9cc4..8deb68b48f 100644
--- a/Ryujinx.Graphics/Gal/GalPipelineState.cs
+++ b/Ryujinx.Graphics/Gal/GalPipelineState.cs
@@ -91,6 +91,13 @@
         public GalStencilOp    StencilFrontOpZPass;
         public uint            StencilFrontMask;
 
+        public int             ScissorTestCount;
+        public bool[]          ScissorTestEnabled;
+        public int[]           ScissorTestX;
+        public int[]           ScissorTestY;
+        public int[]           ScissorTestWidth;
+        public int[]           ScissorTestHeight;
+
         public bool         BlendIndependent;
         public BlendState[] Blends;
 
@@ -111,6 +118,12 @@
 
             Blends = new BlendState[RenderTargetsCount];
 
+            ScissorTestEnabled  = new bool[RenderTargetsCount];
+            ScissorTestY        = new int[RenderTargetsCount];
+            ScissorTestX        = new int[RenderTargetsCount];
+            ScissorTestWidth    = new int[RenderTargetsCount];
+            ScissorTestHeight   = new int[RenderTargetsCount];
+
             ColorMasks = new ColorMaskState[RenderTargetsCount];
         }
     }
diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
index eb7f958b92..e9143c19d5 100644
--- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
+++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs
@@ -270,6 +270,57 @@ namespace Ryujinx.Graphics.Gal.OpenGL
                 }
             }
 
+
+            // Scissor Test
+            bool forceUpdate;
+
+            for (int Index = 0; Index < New.ScissorTestCount; Index++)
+            {
+                forceUpdate = false;
+
+                if (New.ScissorTestEnabled[Index] != Old.ScissorTestEnabled[Index])
+                {
+                    if (New.ScissorTestEnabled[Index])
+                    {
+                        // If there is only 1 scissor test geometry shaders are disables so the scissor test applies to all viewports
+                        if (New.ScissorTestCount == 1)
+                        {
+                            GL.Enable(EnableCap.ScissorTest);
+                        }
+                        else
+                        {
+                            GL.Enable(IndexedEnableCap.ScissorTest, Index);
+                        }
+                        forceUpdate = true;
+                    }
+                    else
+                    {
+                        GL.Disable(IndexedEnableCap.ScissorTest, Index);
+                    }
+                }
+
+                if (New.ScissorTestEnabled[Index] &&
+                   (New.ScissorTestX[Index]      != Old.ScissorTestX[Index]      ||
+                    New.ScissorTestY[Index]      != Old.ScissorTestY[Index]      ||
+                    New.ScissorTestWidth[Index]  != Old.ScissorTestWidth[Index]  ||
+                    New.ScissorTestHeight[Index] != Old.ScissorTestHeight[Index] ||
+                    forceUpdate)) // Force update intentionally last to reduce if comparisons
+                {
+                    // If there is only 1 scissor test geometry shaders are disables so the scissor test applies to all viewports
+                    if (New.ScissorTestCount == 1)
+                    {
+                        GL.Scissor(New.ScissorTestX[Index], New.ScissorTestY[Index],
+                                   New.ScissorTestWidth[Index], New.ScissorTestHeight[Index]);
+                    }
+                    else
+                    {
+                        GL.ScissorIndexed(Index, New.ScissorTestX[Index], New.ScissorTestY[Index],
+                                                 New.ScissorTestWidth[Index], New.ScissorTestHeight[Index]);
+                    }
+                }
+            }
+
+
             if (New.BlendIndependent)
             {
                 for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++)
diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs
index dc1eec3b6b..9a22c85f42 100644
--- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs
+++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs
@@ -24,6 +24,9 @@ namespace Ryujinx.Graphics.Graphics3d
 
         private ConstBuffer[][] ConstBuffers;
 
+        // Height kept for flipping y axis
+        private int ViewportHeight = 0;
+
         private int CurrentInstance = 0;
 
         public NvGpuEngine3d(NvGpu Gpu)
@@ -97,6 +100,7 @@ namespace Ryujinx.Graphics.Graphics3d
             SetCullFace(State);
             SetDepth(State);
             SetStencil(State);
+            SetScissor(State);
             SetBlending(State);
             SetColorMask(State);
             SetPrimitiveRestart(State);
@@ -208,6 +212,8 @@ namespace Ryujinx.Graphics.Graphics3d
 
             Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image);
 
+            ViewportHeight = VpH;
+
             Gpu.Renderer.RenderTarget.SetViewport(FbIndex, VpX, VpY, VpW, VpH);
         }
 
@@ -407,6 +413,36 @@ namespace Ryujinx.Graphics.Graphics3d
             }
         }
 
+        private void SetScissor(GalPipelineState State)
+        {
+            // FIXME: Stubbed, only the first scissor test is valid without a geometry shader loaded. At time of writing geometry shaders are also stubbed.
+            // Once geometry shaders are fixed it should be equal to GalPipelineState.RenderTargetCount when shader loaded, otherwise equal to 1
+            State.ScissorTestCount = 1;
+
+            for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++)
+            {
+                State.ScissorTestEnabled[Index] = ReadRegisterBool(NvGpuEngine3dReg.ScissorEnable + Index * 4);
+
+                if (State.ScissorTestEnabled[Index])
+                {
+                    uint ScissorHorizontal        = (uint)ReadRegister(NvGpuEngine3dReg.ScissorHorizontal + Index * 4);
+                    uint ScissorVertical          = (uint)ReadRegister(NvGpuEngine3dReg.ScissorVertical + Index * 4);
+
+                    State.ScissorTestX[Index]     = (int)((ScissorHorizontal & 0xFFFF) * State.FlipX);                          // X, lower 16 bits
+                    State.ScissorTestWidth[Index] = (int)((ScissorHorizontal >> 16) * State.FlipX) - State.ScissorTestX[Index]; // Width, right side is upper 16 bits
+
+                    State.ScissorTestY[Index]      = (int)((ScissorVertical & 0xFFFF));                                         // Y, lower 16 bits
+                    State.ScissorTestHeight[Index] = (int)((ScissorVertical >> 16)) - State.ScissorTestY[Index];                // Height, top side is upper 16 bits
+
+                    // Y coordinates may have to be flipped
+                    if ((int)State.FlipY == -1)
+                    {
+                        State.ScissorTestY[Index] = ViewportHeight - State.ScissorTestY[Index] - State.ScissorTestHeight[Index];
+                    }
+                }
+            }
+        }
+
         private void SetBlending(GalPipelineState State)
         {
             bool BlendIndependent = ReadRegisterBool(NvGpuEngine3dReg.BlendIndependent);
diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs
index 30243c02b6..026b0cd198 100644
--- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs
+++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs
@@ -22,6 +22,9 @@ namespace Ryujinx.Graphics.Graphics3d
         ClearNColor          = 0x360,
         ClearDepth           = 0x364,
         ClearStencil         = 0x368,
+        ScissorEnable        = 0x380,
+        ScissorHorizontal    = 0x381,
+        ScissorVertical      = 0x382,
         StencilBackFuncRef   = 0x3d5,
         StencilBackMask      = 0x3d6,
         StencilBackFuncMask  = 0x3d7,