From df820a72def62319fe97236a2006c64bfb7c065a Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Tue, 12 Jan 2021 18:50:54 -0300
Subject: [PATCH] Implement clear buffer (fast path) (#1902)

* Implement clear buffer (fast path)

* Remove blank line
---
 Ryujinx.Graphics.GAL/IPipeline.cs             |  2 ++
 .../Engine/MethodCopyBuffer.cs                | 20 +++++++++--
 Ryujinx.Graphics.Gpu/Memory/BufferManager.cs  | 22 ++++++++++++
 .../State/BufferSwizzleComponent.cs           | 16 +++++++++
 .../State/CopyBufferSwizzle.cs                | 36 +++++++++++++++++++
 Ryujinx.Graphics.Gpu/State/MethodOffset.cs    |  2 ++
 Ryujinx.Graphics.OpenGL/Buffer.cs             | 21 +++++++++++
 Ryujinx.Graphics.OpenGL/Pipeline.cs           | 11 ++++--
 8 files changed, 125 insertions(+), 5 deletions(-)
 create mode 100644 Ryujinx.Graphics.Gpu/State/BufferSwizzleComponent.cs

diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs
index 1da34b96..96ccfb28 100644
--- a/Ryujinx.Graphics.GAL/IPipeline.cs
+++ b/Ryujinx.Graphics.GAL/IPipeline.cs
@@ -9,6 +9,8 @@ namespace Ryujinx.Graphics.GAL
 
         void BeginTransformFeedback(PrimitiveTopology topology);
 
+        void ClearBuffer(BufferHandle destination, int offset, int size, uint value);
+
         void ClearRenderTargetColor(int index, uint componentMask, ColorF color);
 
         void ClearRenderTargetDepthStencil(
diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
index 6f03dc5d..15ebb236 100644
--- a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
@@ -200,8 +200,24 @@ namespace Ryujinx.Graphics.Gpu.Engine
             }
             else
             {
-                // Buffer to buffer copy.
-                BufferManager.CopyBuffer(cbp.SrcAddress, cbp.DstAddress, (uint)size);
+                if (remap &&
+                    swizzle.UnpackDstX() == BufferSwizzleComponent.ConstA &&
+                    swizzle.UnpackDstY() == BufferSwizzleComponent.ConstA &&
+                    swizzle.UnpackDstZ() == BufferSwizzleComponent.ConstA &&
+                    swizzle.UnpackDstW() == BufferSwizzleComponent.ConstA &&
+                    swizzle.UnpackSrcComponentsCount() == 1 &&
+                    swizzle.UnpackDstComponentsCount() == 1 &&
+                    swizzle.UnpackComponentSize() == 4)
+                {
+                    // Fast path for clears when remap is enabled.
+                    BufferManager.ClearBuffer(cbp.DstAddress, (uint)size * 4, state.Get<uint>(MethodOffset.CopyBufferConstA));
+                }
+                else
+                {
+                    // TODO: Implement remap functionality.
+                    // Buffer to buffer copy.
+                    BufferManager.CopyBuffer(cbp.SrcAddress, cbp.DstAddress, (uint)size);
+                }
             }
         }
     }
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
index 1d48b38c..0c643191 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
@@ -821,6 +821,28 @@ namespace Ryujinx.Graphics.Gpu.Memory
             dstBuffer.Flush(dstAddress, size);
         }
 
+        /// <summary>
+        /// Clears a buffer at a given address with the specified value.
+        /// </summary>
+        /// <remarks>
+        /// Both the address and size must be aligned to 4 bytes.
+        /// </remarks>
+        /// <param name="gpuVa">GPU virtual address of the region to clear</param>
+        /// <param name="size">Number of bytes to clear</param>
+        /// <param name="value">Value to be written into the buffer</param>
+        public void ClearBuffer(GpuVa gpuVa, ulong size, uint value)
+        {
+            ulong address = TranslateAndCreateBuffer(gpuVa.Pack(), size);
+
+            Buffer buffer = GetBuffer(address, size);
+
+            int offset = (int)(address - buffer.Address);
+
+            _context.Renderer.Pipeline.ClearBuffer(buffer.Handle, offset, (int)size, value);
+
+            buffer.Flush(address, size);
+        }
+
         /// <summary>
         /// Gets a buffer sub-range for a given memory range.
         /// </summary>
diff --git a/Ryujinx.Graphics.Gpu/State/BufferSwizzleComponent.cs b/Ryujinx.Graphics.Gpu/State/BufferSwizzleComponent.cs
new file mode 100644
index 00000000..5c23cb2d
--- /dev/null
+++ b/Ryujinx.Graphics.Gpu/State/BufferSwizzleComponent.cs
@@ -0,0 +1,16 @@
+namespace Ryujinx.Graphics.Gpu.State
+{
+    /// <summary>
+    /// Buffer swizzle component.
+    /// </summary>
+    enum BufferSwizzleComponent
+    {
+        SrcX,
+        SrcY,
+        SrcZ,
+        SrcW,
+        ConstA,
+        ConstB,
+        NoWrite
+    }
+}
diff --git a/Ryujinx.Graphics.Gpu/State/CopyBufferSwizzle.cs b/Ryujinx.Graphics.Gpu/State/CopyBufferSwizzle.cs
index b4a9d9c2..94b650c4 100644
--- a/Ryujinx.Graphics.Gpu/State/CopyBufferSwizzle.cs
+++ b/Ryujinx.Graphics.Gpu/State/CopyBufferSwizzle.cs
@@ -9,6 +9,42 @@ namespace Ryujinx.Graphics.Gpu.State
         public uint Swizzle;
 #pragma warning restore CS0649
 
+        /// <summary>
+        /// Unpacks the source for the buffer destination vector X component.
+        /// </summary>
+        /// <returns>Destination component</returns>
+        public BufferSwizzleComponent UnpackDstX()
+        {
+            return (BufferSwizzleComponent)(Swizzle & 7);
+        }
+
+        /// <summary>
+        /// Unpacks the source for the buffer destination vector Y component.
+        /// </summary>
+        /// <returns>Destination component</returns>
+        public BufferSwizzleComponent UnpackDstY()
+        {
+            return (BufferSwizzleComponent)((Swizzle >> 4) & 7);
+        }
+
+        /// <summary>
+        /// Unpacks the source for the buffer destination vector Z component.
+        /// </summary>
+        /// <returns>Destination component</returns>
+        public BufferSwizzleComponent UnpackDstZ()
+        {
+            return (BufferSwizzleComponent)((Swizzle >> 8) & 7);
+        }
+
+        /// <summary>
+        /// Unpacks the source for the buffer destination vector W component.
+        /// </summary>
+        /// <returns>Destination component</returns>
+        public BufferSwizzleComponent UnpackDstW()
+        {
+            return (BufferSwizzleComponent)((Swizzle >> 12) & 7);
+        }
+
         /// <summary>
         /// Unpacks the size of each vector component of the copy.
         /// </summary>
diff --git a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs
index 6ec94c1b..4504e6b1 100644
--- a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs
+++ b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs
@@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Gpu.State
         TfBufferState                   = 0xe0,
         CopyBufferParams                = 0x100,
         TfState                         = 0x1c0,
+        CopyBufferConstA                = 0x1c0,
+        CopyBufferConstB                = 0x1c1,
         CopyBufferSwizzle               = 0x1c2,
         CopyBufferDstTexture            = 0x1c3,
         CopyBufferSrcTexture            = 0x1ca,
diff --git a/Ryujinx.Graphics.OpenGL/Buffer.cs b/Ryujinx.Graphics.OpenGL/Buffer.cs
index 89216b83..ecb7dc90 100644
--- a/Ryujinx.Graphics.OpenGL/Buffer.cs
+++ b/Ryujinx.Graphics.OpenGL/Buffer.cs
@@ -6,6 +6,27 @@ namespace Ryujinx.Graphics.OpenGL
 {
     static class Buffer
     {
+        public static void Clear(BufferHandle destination, int offset, int size, uint value)
+        {
+            GL.BindBuffer(BufferTarget.CopyWriteBuffer, destination.ToInt32());
+
+            unsafe
+            {
+                uint* valueArr = stackalloc uint[1];
+
+                valueArr[0] = value;
+
+                GL.ClearBufferSubData(
+                    BufferTarget.CopyWriteBuffer,
+                    PixelInternalFormat.Rgba8ui,
+                    (IntPtr)offset,
+                    (IntPtr)size,
+                    PixelFormat.RgbaInteger,
+                    PixelType.UnsignedByte,
+                    (IntPtr)valueArr);
+            }
+        }
+
         public static BufferHandle Create()
         {
             return Handle.FromInt32<BufferHandle>(GL.GenBuffer());
diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs
index 8b0a86aa..b6a34e9c 100644
--- a/Ryujinx.Graphics.OpenGL/Pipeline.cs
+++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs
@@ -91,6 +91,11 @@ namespace Ryujinx.Graphics.OpenGL
             _tfEnabled = true;
         }
 
+        public void ClearBuffer(BufferHandle destination, int offset, int size, uint value)
+        {
+            Buffer.Clear(destination, offset, size, value);
+        }
+
         public void ClearRenderTargetColor(int index, uint componentMask, ColorF color)
         {
             GL.ColorMask(
@@ -102,7 +107,7 @@ namespace Ryujinx.Graphics.OpenGL
 
             float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha };
 
-            GL.ClearBuffer(ClearBuffer.Color, index, colors);
+            GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Color, index, colors);
 
             RestoreComponentMask(index);
 
@@ -133,11 +138,11 @@ namespace Ryujinx.Graphics.OpenGL
             }
             else if (depthMask)
             {
-                GL.ClearBuffer(ClearBuffer.Depth, 0, ref depthValue);
+                GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Depth, 0, ref depthValue);
             }
             else if (stencilMask != 0)
             {
-                GL.ClearBuffer(ClearBuffer.Stencil, 0, ref stencilValue);
+                GL.ClearBuffer(OpenTK.Graphics.OpenGL.ClearBuffer.Stencil, 0, ref stencilValue);
             }
 
             if (stencilMaskChanged)