From 8feee9c005b9144fd6f58ee9ca72b63f95af176c Mon Sep 17 00:00:00 2001
From: Isaac Marovitz <isaacryu@icloud.com>
Date: Wed, 22 May 2024 17:21:44 -0400
Subject: [PATCH] Shitty Clears + Inline Buffer Improvements?

---
 src/Ryujinx.Graphics.Metal/EncoderState.cs    |  4 +-
 .../EncoderStateManager.cs                    | 13 ++--
 src/Ryujinx.Graphics.Metal/HelperShader.cs    | 68 ++++++++-----------
 src/Ryujinx.Graphics.Metal/MetalRenderer.cs   | 22 +++---
 src/Ryujinx.Graphics.Metal/Pipeline.cs        |  9 ++-
 .../Ryujinx.Graphics.Metal.csproj             |  4 +-
 .../Shaders/ColorClear.metal                  | 28 ++++++++
 .../Shaders/ColorClearF.metal                 |  0
 .../Shaders/ColorClearSI.metal                |  0
 .../Shaders/ColorClearUI.metal                |  0
 .../Shaders/DepthStencilClear.metal           | 37 ++++++++++
 11 files changed, 124 insertions(+), 61 deletions(-)
 create mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal
 delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal
 delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal
 delete mode 100644 src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal

diff --git a/src/Ryujinx.Graphics.Metal/EncoderState.cs b/src/Ryujinx.Graphics.Metal/EncoderState.cs
index a787d1424..3ebf9fbd9 100644
--- a/src/Ryujinx.Graphics.Metal/EncoderState.cs
+++ b/src/Ryujinx.Graphics.Metal/EncoderState.cs
@@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Metal
     }
 
     [SupportedOSPlatform("macos")]
-    public struct EncoderState
+    struct EncoderState
     {
         public MTLFunction? VertexFunction = null;
         public MTLFunction? FragmentFunction = null;
@@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Metal
 
         // Changes to attachments take recreation!
         public MTLTexture DepthStencil = default;
-        public MTLTexture[] RenderTargets = new MTLTexture[Constants.MaxColorAttachments];
+        public Texture[] RenderTargets = new Texture[Constants.MaxColorAttachments];
         public Dictionary<int, BlendDescriptor> BlendDescriptors = new();
         public ColorF BlendColor = new();
 
diff --git a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs
index 15a96cc45..ae4be9e34 100644
--- a/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs
+++ b/src/Ryujinx.Graphics.Metal/EncoderStateManager.cs
@@ -25,6 +25,7 @@ namespace Ryujinx.Graphics.Metal
         public readonly MTLIndexType IndexType => _currentState.IndexType;
         public readonly ulong IndexBufferOffset => _currentState.IndexBufferOffset;
         public readonly PrimitiveTopology Topology => _currentState.Topology;
+        public readonly Texture[] RenderTargets => _currentState.RenderTargets;
 
         public EncoderStateManager(MTLDevice device, Pipeline pipeline)
         {
@@ -50,10 +51,10 @@ namespace Ryujinx.Graphics.Metal
 
             for (int i = 0; i < Constants.MaxColorAttachments; i++)
             {
-                if (_currentState.RenderTargets[i] != IntPtr.Zero)
+                if (_currentState.RenderTargets[i] != null)
                 {
                     var passAttachment = renderPassDescriptor.ColorAttachments.Object((ulong)i);
-                    passAttachment.Texture = _currentState.RenderTargets[i];
+                    passAttachment.Texture = _currentState.RenderTargets[i].MTLTexture;
                     passAttachment.LoadAction = MTLLoadAction.Load;
                 }
             }
@@ -136,10 +137,10 @@ namespace Ryujinx.Graphics.Metal
 
             for (int i = 0; i < Constants.MaxColorAttachments; i++)
             {
-                if (_currentState.RenderTargets[i] != IntPtr.Zero)
+                if (_currentState.RenderTargets[i] != null)
                 {
                     var pipelineAttachment = renderPipelineDescriptor.ColorAttachments.Object((ulong)i);
-                    pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].PixelFormat;
+                    pipelineAttachment.PixelFormat = _currentState.RenderTargets[i].MTLTexture.PixelFormat;
                     pipelineAttachment.SourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha;
                     pipelineAttachment.DestinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha;
                     pipelineAttachment.SourceRGBBlendFactor = MTLBlendFactor.SourceAlpha;
@@ -247,7 +248,7 @@ namespace Ryujinx.Graphics.Metal
 
         public void UpdateRenderTargets(ITexture[] colors, ITexture depthStencil)
         {
-            _currentState.RenderTargets = new MTLTexture[Constants.MaxColorAttachments];
+            _currentState.RenderTargets = new Texture[Constants.MaxColorAttachments];
 
             for (int i = 0; i < colors.Length; i++)
             {
@@ -256,7 +257,7 @@ namespace Ryujinx.Graphics.Metal
                     continue;
                 }
 
-                _currentState.RenderTargets[i] = tex.MTLTexture;
+                _currentState.RenderTargets[i] = tex;
             }
 
             if (depthStencil is Texture depthTexture)
diff --git a/src/Ryujinx.Graphics.Metal/HelperShader.cs b/src/Ryujinx.Graphics.Metal/HelperShader.cs
index b4ddfe02c..8e95442cd 100644
--- a/src/Ryujinx.Graphics.Metal/HelperShader.cs
+++ b/src/Ryujinx.Graphics.Metal/HelperShader.cs
@@ -2,8 +2,10 @@ using Ryujinx.Common;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Shader;
 using Ryujinx.Graphics.Shader.Translation;
+using SharpMetal.Foundation;
 using SharpMetal.Metal;
 using System;
+using System.Runtime.CompilerServices;
 using System.Runtime.Versioning;
 
 namespace Ryujinx.Graphics.Metal
@@ -23,9 +25,7 @@ namespace Ryujinx.Graphics.Metal
         private MTLDevice _device;
 
         private readonly IProgram _programColorBlit;
-        private readonly IProgram _programColorClearF;
-        private readonly IProgram _programColorClearSI;
-        private readonly IProgram _programColorClearUI;
+        private readonly IProgram _programColorClear;
         private readonly IProgram _programDepthStencilClear;
 
         public HelperShader(MTLDevice device, Pipeline pipeline)
@@ -40,6 +40,13 @@ namespace Ryujinx.Graphics.Metal
                 new ShaderSource(blitSource, ShaderStage.Vertex, TargetLanguage.Msl)
             ], device);
 
+            var colorClearSource = ReadMsl("ColorClear.metal");
+            _programColorClear = new Program(
+            [
+                new ShaderSource(colorClearSource, ShaderStage.Fragment, TargetLanguage.Msl),
+                new ShaderSource(colorClearSource, ShaderStage.Vertex, TargetLanguage.Msl)
+            ], device);
+
             // var colorClearFSource = ReadMsl("ColorClearF.metal");
             // _programColorClearF = new Program(
             // [
@@ -93,45 +100,30 @@ namespace Ryujinx.Graphics.Metal
             _pipeline.Finish();
         }
 
-        public void ClearColor(
+        public unsafe void ClearColor(
             Texture dst,
-            uint componentMask,
-            int dstWidth,
-            int dstHeight,
-            ComponentType type,
-            Rectangle<int> scissor)
+            ReadOnlySpan<float> clearColor)
         {
-            Span<Viewport> viewports = stackalloc Viewport[1];
+            const int ClearColorBufferSize = 16;
 
-            viewports[0] = new Viewport(
-                new Rectangle<float>(0, 0, dstWidth, dstHeight),
-                ViewportSwizzle.PositiveX,
-                ViewportSwizzle.PositiveY,
-                ViewportSwizzle.PositiveZ,
-                ViewportSwizzle.PositiveW,
-                0f,
-                1f);
+            var buffer = _device.NewBuffer(ClearColorBufferSize, MTLResourceOptions.ResourceStorageModeManaged);
+            var span = new Span<float>(buffer.Contents.ToPointer(), ClearColorBufferSize);
+            clearColor.CopyTo(span);
 
-            IProgram program;
-
-            if (type == ComponentType.SignedInteger)
+            buffer.DidModifyRange(new NSRange
             {
-                program = _programColorClearSI;
-            }
-            else if (type == ComponentType.UnsignedInteger)
-            {
-                program = _programColorClearUI;
-            }
-            else
-            {
-                program = _programColorClearF;
-            }
+                location = 0,
+                length = ClearColorBufferSize
+            });
 
-            _pipeline.SetProgram(program);
-            // _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight);
-            _pipeline.SetRenderTargetColorMasks([componentMask]);
-            _pipeline.SetViewports(viewports);
-            _pipeline.SetScissors([scissor]);
+            var handle = buffer.NativePtr;
+            var range = new BufferRange(Unsafe.As<IntPtr, BufferHandle>(ref handle), 0, ClearColorBufferSize);
+
+            _pipeline.SetUniformBuffers([new BufferAssignment(0, range)]);
+
+            _pipeline.SetProgram(_programColorClear);
+            _pipeline.SetRenderTargets([dst], null);
+            // _pipeline.SetRenderTargetColorMasks([componentMask]);
             _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
             _pipeline.Draw(4, 1, 0, 0);
             _pipeline.Finish();
@@ -196,9 +188,7 @@ namespace Ryujinx.Graphics.Metal
         public void Dispose()
         {
             _programColorBlit.Dispose();
-            _programColorClearF.Dispose();
-            _programColorClearSI.Dispose();
-            _programColorClearUI.Dispose();
+            _programColorClear.Dispose();
             _programDepthStencilClear.Dispose();
             _pipeline.Dispose();
         }
diff --git a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs
index e7d26f72a..b5496028c 100644
--- a/src/Ryujinx.Graphics.Metal/MetalRenderer.cs
+++ b/src/Ryujinx.Graphics.Metal/MetalRenderer.cs
@@ -213,17 +213,19 @@ namespace Ryujinx.Graphics.Metal
 
         public unsafe void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
         {
-            MTLBuffer mtlBuffer = new(Unsafe.As<BufferHandle, IntPtr>(ref buffer));
-            var span = new Span<byte>(mtlBuffer.Contents.ToPointer(), (int)mtlBuffer.Length);
-            data.CopyTo(span[offset..]);
-            if (mtlBuffer.StorageMode == MTLStorageMode.Managed)
+            var blitEncoder = _pipeline.GetOrCreateBlitEncoder();
+
+            MTLBuffer src = _device.NewBuffer((ulong)data.Length, MTLResourceOptions.ResourceStorageModeManaged);
+            var span = new Span<byte>(src.Contents.ToPointer(), data.Length);
+            data.CopyTo(span);
+            src.DidModifyRange(new NSRange
             {
-                mtlBuffer.DidModifyRange(new NSRange
-                {
-                    location = (ulong)offset,
-                    length = (ulong)data.Length
-                });
-            }
+                location = 0,
+                length = (ulong)data.Length
+            });
+
+            MTLBuffer dst = new(Unsafe.As<BufferHandle, IntPtr>(ref buffer));
+            blitEncoder.CopyFromBuffer(src, 0, dst, (ulong)offset, (ulong)data.Length);
         }
 
         public void UpdateCounters()
diff --git a/src/Ryujinx.Graphics.Metal/Pipeline.cs b/src/Ryujinx.Graphics.Metal/Pipeline.cs
index 91afb33bf..ce9686b08 100644
--- a/src/Ryujinx.Graphics.Metal/Pipeline.cs
+++ b/src/Ryujinx.Graphics.Metal/Pipeline.cs
@@ -5,6 +5,7 @@ using SharpMetal.Foundation;
 using SharpMetal.Metal;
 using SharpMetal.QuartzCore;
 using System;
+using System.Drawing;
 using System.Runtime.CompilerServices;
 using System.Runtime.Versioning;
 
@@ -203,7 +204,13 @@ namespace Ryujinx.Graphics.Metal
 
         public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color)
         {
-            Logger.Warning?.Print(LogClass.Gpu, "Not Implemented!");
+            float[] colors = [color.Red, color.Green, color.Blue, color.Alpha];
+
+            Texture target = _encoderStateManager.RenderTargets[index];
+
+            _encoderStateManager.SwapStates();
+
+            _helperShader.ClearColor(target, colors);
         }
 
         public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask)
diff --git a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj
index d8da12834..0824accc1 100644
--- a/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj
+++ b/src/Ryujinx.Graphics.Metal/Ryujinx.Graphics.Metal.csproj
@@ -16,9 +16,7 @@
 
     <ItemGroup>
       <EmbeddedResource Include="Shaders\Blit.metal" />
-      <EmbeddedResource Include="Shaders\ColorClearF.metal" />
-      <EmbeddedResource Include="Shaders\ColorClearSI.metal" />
-      <EmbeddedResource Include="Shaders\ColorClearUI.metal" />
+      <EmbeddedResource Include="Shaders\ColorClear.metal" />
       <EmbeddedResource Include="Shaders\DepthStencilClear.metal" />
     </ItemGroup>
 
diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal
new file mode 100644
index 000000000..85ae873e6
--- /dev/null
+++ b/src/Ryujinx.Graphics.Metal/Shaders/ColorClear.metal
@@ -0,0 +1,28 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+struct VertexOut {
+    float4 position [[position]];
+};
+
+vertex VertexOut vertexMain(ushort vid [[vertex_id]])
+{
+    int low = vid & 1;
+    int high = vid >> 1;
+
+    VertexOut out;
+
+    out.position.x = (float(low) - 0.5f) * 2.0f;
+    out.position.y = (float(high) - 0.5f) * 2.0f;
+    out.position.z = 0.0f;
+    out.position.w = 1.0f;
+
+    return out;
+}
+
+fragment float4 fragmentMain(VertexOut in [[stage_in]],
+                             constant float4& clear_color [[buffer(0)]])
+{
+    return clear_color;
+}
diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClearF.metal
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClearSI.metal
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal b/src/Ryujinx.Graphics.Metal/Shaders/ColorClearUI.metal
deleted file mode 100644
index e69de29bb..000000000
diff --git a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal
index e69de29bb..0a4e10a25 100644
--- a/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal
+++ b/src/Ryujinx.Graphics.Metal/Shaders/DepthStencilClear.metal
@@ -0,0 +1,37 @@
+#include <metal_stdlib>
+
+using namespace metal;
+
+struct VertexOut {
+    float4 position [[position]];
+};
+
+struct FragmentOut {
+    float4 color [[color(0)]];
+    float depth [[depth(any)]];
+};
+
+vertex VertexOut vertexMain(ushort vid [[vertex_id]])
+{
+    int low = vid & 1;
+    int high = vid >> 1;
+
+    VertexOut out;
+
+    out.position.x = (float(low) - 0.5f) * 2.0f;
+    out.position.y = (float(high) - 0.5f) * 2.0f;
+    out.position.z = 0.0f;
+    out.position.w = 1.0f;
+
+    return out;
+}
+
+fragment float4 fragmentMain(VertexOut in [[stage_in]],
+                             constant float clear_color [[buffer(0)]])
+{
+    Fragment out;
+
+    out.depth = clear_color;
+
+    return out;
+}