From f09bba82b9366e5912b639a610ae89cbb1cf352c Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Tue, 29 Aug 2023 21:10:34 -0300
Subject: Geometry shader emulation for macOS (#5551)

* Implement vertex and geometry shader conversion to compute

* Call InitializeReservedCounts for compute too

* PR feedback

* Set clip distance mask for geometry and tessellation shaders too

* Transform feedback emulation only for vertex
---
 src/Ryujinx.Graphics.Gpu/Memory/BufferUpdater.cs | 123 +++++++++++++++++++++++
 1 file changed, 123 insertions(+)
 create mode 100644 src/Ryujinx.Graphics.Gpu/Memory/BufferUpdater.cs

(limited to 'src/Ryujinx.Graphics.Gpu/Memory/BufferUpdater.cs')

diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferUpdater.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferUpdater.cs
new file mode 100644
index 00000000..02090c04
--- /dev/null
+++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferUpdater.cs
@@ -0,0 +1,123 @@
+using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.Shader;
+using System;
+
+namespace Ryujinx.Graphics.Gpu.Memory
+{
+    /// <summary>
+    /// Buffer data updater.
+    /// </summary>
+    class BufferUpdater : IDisposable
+    {
+        private BufferHandle _handle;
+
+        /// <summary>
+        /// Handle of the buffer.
+        /// </summary>
+        public BufferHandle Handle => _handle;
+
+        private readonly IRenderer _renderer;
+        private int _startOffset = -1;
+        private int _endOffset = -1;
+
+        /// <summary>
+        /// Creates a new instance of the buffer updater.
+        /// </summary>
+        /// <param name="renderer">Renderer that the buffer will be used with</param>
+        public BufferUpdater(IRenderer renderer)
+        {
+            _renderer = renderer;
+        }
+
+        /// <summary>
+        /// Mark a region of the buffer as modified and needing to be sent to the GPU.
+        /// </summary>
+        /// <param name="startOffset">Start offset of the region in bytes</param>
+        /// <param name="byteSize">Size of the region in bytes</param>
+        protected void MarkDirty(int startOffset, int byteSize)
+        {
+            int endOffset = startOffset + byteSize;
+
+            if (_startOffset == -1)
+            {
+                _startOffset = startOffset;
+                _endOffset = endOffset;
+            }
+            else
+            {
+                if (startOffset < _startOffset)
+                {
+                    _startOffset = startOffset;
+                }
+
+                if (endOffset > _endOffset)
+                {
+                    _endOffset = endOffset;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Submits all pending buffer updates to the GPU.
+        /// </summary>
+        /// <param name="data">All data that should be sent to the GPU. Only the modified regions will be updated</param>
+        /// <param name="binding">Optional binding to bind the buffer if a new buffer was created</param>
+        protected void Commit(ReadOnlySpan<byte> data, int binding = -1)
+        {
+            if (_startOffset != -1)
+            {
+                if (_handle == BufferHandle.Null)
+                {
+                    _handle = _renderer.CreateBuffer(data.Length, BufferAccess.Stream);
+                    _renderer.Pipeline.ClearBuffer(_handle, 0, data.Length, 0);
+
+                    if (binding >= 0)
+                    {
+                        var range = new BufferRange(_handle, 0, data.Length);
+                        _renderer.Pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, range) });
+                    }
+                };
+
+                _renderer.SetBufferData(_handle, _startOffset, data[_startOffset.._endOffset]);
+
+                _startOffset = -1;
+                _endOffset = -1;
+            }
+        }
+
+        /// <summary>
+        /// Gets a reference to a given element of a vector.
+        /// </summary>
+        /// <param name="vector">Vector to get the element reference from</param>
+        /// <param name="elementIndex">Element index</param>
+        /// <returns>Reference to the specified element</returns>
+        protected static ref T GetElementRef<T>(ref Vector4<T> vector, int elementIndex)
+        {
+            switch (elementIndex)
+            {
+                case 0:
+                    return ref vector.X;
+                case 1:
+                    return ref vector.Y;
+                case 2:
+                    return ref vector.Z;
+                case 3:
+                    return ref vector.W;
+                default:
+                    throw new ArgumentOutOfRangeException(nameof(elementIndex));
+            }
+        }
+
+        /// <summary>
+        /// Destroys the buffer.
+        /// </summary>
+        public void Dispose()
+        {
+            if (_handle != BufferHandle.Null)
+            {
+                _renderer.DeleteBuffer(_handle);
+                _handle = BufferHandle.Null;
+            }
+        }
+    }
+}
-- 
cgit v1.2.3-70-g09d2