From 5011640b3086b86b0f0b39b60fdb2aa946d4f5c8 Mon Sep 17 00:00:00 2001
From: gdkchan <gab.dark.100@gmail.com>
Date: Sat, 23 May 2020 06:46:09 -0300
Subject: [PATCH] Spanify Graphics Abstraction Layer (#1226)

* Spanify Graphics Abstraction Layer

* Be explicit about BufferHandle size
---
 Ryujinx.Graphics.GAL/BufferHandle.cs          | 22 +++++++
 Ryujinx.Graphics.GAL/BufferRange.cs           |  8 +--
 Ryujinx.Graphics.GAL/IBuffer.cs               | 15 -----
 Ryujinx.Graphics.GAL/IPipeline.cs             | 11 ++--
 Ryujinx.Graphics.GAL/IRenderer.cs             |  8 ++-
 Ryujinx.Graphics.Gpu/Constants.cs             |  5 ++
 Ryujinx.Graphics.Gpu/Engine/Methods.cs        |  8 +--
 Ryujinx.Graphics.Gpu/Memory/Buffer.cs         | 16 ++---
 Ryujinx.Graphics.Gpu/Memory/BufferManager.cs  |  7 +-
 Ryujinx.Graphics.OpenGL/Buffer.cs             | 43 +++++--------
 Ryujinx.Graphics.OpenGL/Constants.cs          | 10 +++
 Ryujinx.Graphics.OpenGL/Framebuffer.cs        |  1 +
 Ryujinx.Graphics.OpenGL/Handle.cs             | 23 +++++++
 .../{ => Image}/Sampler.cs                    |  2 +-
 .../{ => Image}/TextureBase.cs                |  5 +-
 .../{ => Image}/TextureBuffer.cs              | 14 ++--
 .../{ => Image}/TextureCopy.cs                |  2 +-
 .../{ => Image}/TextureCopyUnscaled.cs        |  2 +-
 .../{ => Image}/TextureStorage.cs             |  2 +-
 .../{ => Image}/TextureView.cs                |  2 +-
 Ryujinx.Graphics.OpenGL/Pipeline.cs           | 51 ++++++++-------
 Ryujinx.Graphics.OpenGL/Renderer.cs           | 20 +++++-
 Ryujinx.Graphics.OpenGL/VertexArray.cs        | 64 +++++++++++--------
 Ryujinx.Graphics.OpenGL/Window.cs             |  1 +
 24 files changed, 208 insertions(+), 134 deletions(-)
 create mode 100644 Ryujinx.Graphics.GAL/BufferHandle.cs
 delete mode 100644 Ryujinx.Graphics.GAL/IBuffer.cs
 create mode 100644 Ryujinx.Graphics.OpenGL/Constants.cs
 create mode 100644 Ryujinx.Graphics.OpenGL/Handle.cs
 rename Ryujinx.Graphics.OpenGL/{ => Image}/Sampler.cs (98%)
 rename Ryujinx.Graphics.OpenGL/{ => Image}/TextureBase.cs (88%)
 rename Ryujinx.Graphics.OpenGL/{ => Image}/TextureBuffer.cs (79%)
 rename Ryujinx.Graphics.OpenGL/{ => Image}/TextureCopy.cs (99%)
 rename Ryujinx.Graphics.OpenGL/{ => Image}/TextureCopyUnscaled.cs (98%)
 rename Ryujinx.Graphics.OpenGL/{ => Image}/TextureStorage.cs (99%)
 rename Ryujinx.Graphics.OpenGL/{ => Image}/TextureView.cs (99%)

diff --git a/Ryujinx.Graphics.GAL/BufferHandle.cs b/Ryujinx.Graphics.GAL/BufferHandle.cs
new file mode 100644
index 0000000000..49f834425a
--- /dev/null
+++ b/Ryujinx.Graphics.GAL/BufferHandle.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Graphics.GAL
+{
+    [StructLayout(LayoutKind.Sequential, Size = 8)]
+    public struct BufferHandle : IEquatable<BufferHandle>
+    {
+        private readonly ulong _value;
+
+        public static BufferHandle Null => new BufferHandle(0);
+
+        private BufferHandle(ulong value) => _value = value;
+
+        public override bool Equals(object obj) => obj is BufferHandle handle && Equals(handle);
+        public bool Equals([AllowNull] BufferHandle other) => other._value == _value;
+        public override int GetHashCode() => _value.GetHashCode();
+        public static bool operator ==(BufferHandle left, BufferHandle right) => left.Equals(right);
+        public static bool operator !=(BufferHandle left, BufferHandle right) => !(left == right);
+    }
+}
diff --git a/Ryujinx.Graphics.GAL/BufferRange.cs b/Ryujinx.Graphics.GAL/BufferRange.cs
index a35636aa73..34d523f9fc 100644
--- a/Ryujinx.Graphics.GAL/BufferRange.cs
+++ b/Ryujinx.Graphics.GAL/BufferRange.cs
@@ -2,18 +2,18 @@ namespace Ryujinx.Graphics.GAL
 {
     public struct BufferRange
     {
-        private static BufferRange _empty = new BufferRange(null, 0, 0);
+        private static readonly BufferRange _empty = new BufferRange(BufferHandle.Null, 0, 0);
 
         public BufferRange Empty => _empty;
 
-        public IBuffer Buffer { get; }
+        public BufferHandle Handle { get; }
 
         public int Offset { get; }
         public int Size   { get; }
 
-        public BufferRange(IBuffer buffer, int offset, int size)
+        public BufferRange(BufferHandle handle, int offset, int size)
         {
-            Buffer = buffer;
+            Handle = handle;
             Offset = offset;
             Size   = size;
         }
diff --git a/Ryujinx.Graphics.GAL/IBuffer.cs b/Ryujinx.Graphics.GAL/IBuffer.cs
deleted file mode 100644
index 43e3769186..0000000000
--- a/Ryujinx.Graphics.GAL/IBuffer.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-
-namespace Ryujinx.Graphics.GAL
-{
-    public interface IBuffer : IDisposable
-    {
-        void CopyTo(IBuffer destination, int srcOffset, int dstOffset, int size);
-
-        byte[] GetData(int offset, int size);
-
-        void SetData(ReadOnlySpan<byte> data);
-
-        void SetData(int offset, ReadOnlySpan<byte> data);
-    }
-}
\ No newline at end of file
diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs
index 3bf7ab9348..22e4e9e219 100644
--- a/Ryujinx.Graphics.GAL/IPipeline.cs
+++ b/Ryujinx.Graphics.GAL/IPipeline.cs
@@ -1,4 +1,5 @@
 using Ryujinx.Graphics.Shader;
+using System;
 
 namespace Ryujinx.Graphics.GAL
 {
@@ -14,6 +15,8 @@ namespace Ryujinx.Graphics.GAL
             int   stencilValue,
             int   stencilMask);
 
+        void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size);
+
         void DispatchCompute(int groupsX, int groupsY, int groupsZ);
 
         void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance);
@@ -49,7 +52,7 @@ namespace Ryujinx.Graphics.GAL
 
         void SetRasterizerDiscard(bool discard);
 
-        void SetRenderTargetColorMasks(uint[] componentMask);
+        void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask);
 
         void SetRenderTargets(ITexture[] colors, ITexture depthStencil);
 
@@ -68,10 +71,10 @@ namespace Ryujinx.Graphics.GAL
 
         void SetUserClipDistance(int index, bool enableClip);
 
-        void SetVertexAttribs(VertexAttribDescriptor[] vertexAttribs);
-        void SetVertexBuffers(VertexBufferDescriptor[] vertexBuffers);
+        void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs);
+        void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers);
 
-        void SetViewports(int first, Viewport[] viewports);
+        void SetViewports(int first, ReadOnlySpan<Viewport> viewports);
 
         void TextureBarrier();
         void TextureBarrierTiled();
diff --git a/Ryujinx.Graphics.GAL/IRenderer.cs b/Ryujinx.Graphics.GAL/IRenderer.cs
index 4a45f5cb4c..c41b19fe50 100644
--- a/Ryujinx.Graphics.GAL/IRenderer.cs
+++ b/Ryujinx.Graphics.GAL/IRenderer.cs
@@ -11,15 +11,21 @@ namespace Ryujinx.Graphics.GAL
 
         IShader CompileShader(ShaderProgram shader);
 
-        IBuffer CreateBuffer(int size);
+        BufferHandle CreateBuffer(int size);
 
         IProgram CreateProgram(IShader[] shaders);
 
         ISampler CreateSampler(SamplerCreateInfo info);
         ITexture CreateTexture(TextureCreateInfo info);
 
+        void DeleteBuffer(BufferHandle buffer);
+
+        byte[] GetBufferData(BufferHandle buffer, int offset, int size);
+
         Capabilities GetCapabilities();
 
+        void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data);
+
         void UpdateCounters();
 
         ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler);
diff --git a/Ryujinx.Graphics.Gpu/Constants.cs b/Ryujinx.Graphics.Gpu/Constants.cs
index 1ae902731c..ac6b6139c0 100644
--- a/Ryujinx.Graphics.Gpu/Constants.cs
+++ b/Ryujinx.Graphics.Gpu/Constants.cs
@@ -45,6 +45,11 @@ namespace Ryujinx.Graphics.Gpu
         /// </summary>
         public const int ShaderStages = 5;
 
+        /// <summary>
+        /// Maximum number of vertex attributes.
+        /// </summary>
+        public const int TotalVertexAttribs = 16;
+
         /// <summary>
         /// Maximum number of vertex buffers.
         /// </summary>
diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
index 5ead87a0ed..acb5ad5c46 100644
--- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs
+++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs
@@ -570,9 +570,9 @@ namespace Ryujinx.Graphics.Gpu.Engine
         /// <param name="state">Current GPU state</param>
         private void UpdateVertexAttribState(GpuState state)
         {
-            VertexAttribDescriptor[] vertexAttribs = new VertexAttribDescriptor[16];
+            Span<VertexAttribDescriptor> vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs];
 
-            for (int index = 0; index < 16; index++)
+            for (int index = 0; index < Constants.TotalVertexAttribs; index++)
             {
                 var vertexAttrib = state.Get<VertexAttribState>(MethodOffset.VertexAttribState, index);
 
@@ -660,7 +660,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
         {
             _isAnyVbInstanced = false;
 
-            for (int index = 0; index < 16; index++)
+            for (int index = 0; index < Constants.TotalVertexBuffers; index++)
             {
                 var vertexBuffer = state.Get<VertexBufferState>(MethodOffset.VertexBufferState, index);
 
@@ -728,7 +728,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
         {
             bool rtColorMaskShared = state.Get<Boolean32>(MethodOffset.RtColorMaskShared);
 
-            uint[] componentMasks = new uint[Constants.TotalRenderTargets];
+            Span<uint> componentMasks = stackalloc uint[Constants.TotalRenderTargets];
 
             for (int index = 0; index < Constants.TotalRenderTargets; index++)
             {
diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
index 4dd96878a3..5fe85d2ea3 100644
--- a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs
@@ -11,9 +11,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
         private readonly GpuContext _context;
 
         /// <summary>
-        /// Host buffer object.
+        /// Host buffer handle.
         /// </summary>
-        public IBuffer HostBuffer { get; }
+        public BufferHandle Handle { get; }
 
         /// <summary>
         /// Start address of the buffer in guest memory.
@@ -46,7 +46,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
             Address  = address;
             Size     = size;
 
-            HostBuffer = context.Renderer.CreateBuffer((int)size);
+            Handle = context.Renderer.CreateBuffer((int)size);
 
             _modifiedRanges = new (ulong, ulong)[size / PhysicalMemory.PageSize];
 
@@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
         {
             int offset = (int)(address - Address);
 
-            return new BufferRange(HostBuffer, offset, (int)size);
+            return new BufferRange(Handle, offset, (int)size);
         }
 
         /// <summary>
@@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
 
                 int offset = (int)(mAddress - Address);
 
-                HostBuffer.SetData(offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize));
+                _context.Renderer.SetBufferData(Handle, offset, _context.PhysicalMemory.GetSpan(mAddress, (int)mSize));
             }
         }
 
@@ -136,7 +136,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// <param name="dstOffset">The offset of the destination buffer to copy into</param>
         public void CopyTo(Buffer destination, int dstOffset)
         {
-            HostBuffer.CopyTo(destination.HostBuffer, 0, dstOffset, (int)Size);
+            _context.Renderer.Pipeline.CopyBuffer(Handle, destination.Handle, 0, dstOffset, (int)Size);
         }
 
         /// <summary>
@@ -149,7 +149,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
         {
             int offset = (int)(address - Address);
 
-            byte[] data = HostBuffer.GetData(offset, (int)size);
+            byte[] data = _context.Renderer.GetBufferData(Handle, offset, (int)size);
 
             _context.PhysicalMemory.Write(address, data);
         }
@@ -159,7 +159,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
         /// </summary>
         public void Dispose()
         {
-            HostBuffer.Dispose();
+            _context.Renderer.DeleteBuffer(Handle);
         }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
index 2fe0ecbb27..39d1cd6f1c 100644
--- a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
+++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
@@ -477,7 +477,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
             {
                 _vertexBuffersDirty = false;
 
-                VertexBufferDescriptor[] vertexBuffers = new VertexBufferDescriptor[Constants.TotalVertexBuffers];
+                Span<VertexBufferDescriptor> vertexBuffers = stackalloc VertexBufferDescriptor[Constants.TotalVertexBuffers];
 
                 for (int index = 0; (vbEnableMask >> index) != 0; index++)
                 {
@@ -666,8 +666,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
             int srcOffset = (int)(srcAddress - srcBuffer.Address);
             int dstOffset = (int)(dstAddress - dstBuffer.Address);
 
-            srcBuffer.HostBuffer.CopyTo(
-                dstBuffer.HostBuffer,
+            _context.Renderer.Pipeline.CopyBuffer(
+                srcBuffer.Handle,
+                dstBuffer.Handle,
                 srcOffset,
                 dstOffset,
                 (int)size);
diff --git a/Ryujinx.Graphics.OpenGL/Buffer.cs b/Ryujinx.Graphics.OpenGL/Buffer.cs
index db3e94ba59..e8fd9a6bf0 100644
--- a/Ryujinx.Graphics.OpenGL/Buffer.cs
+++ b/Ryujinx.Graphics.OpenGL/Buffer.cs
@@ -4,22 +4,22 @@ using System;
 
 namespace Ryujinx.Graphics.OpenGL
 {
-    class Buffer : IBuffer
+    static class Buffer
     {
-        public int Handle { get; }
-
-        public Buffer(int size)
+        public static BufferHandle Create(int size)
         {
-            Handle = GL.GenBuffer();
+            int handle = GL.GenBuffer();
 
-            GL.BindBuffer(BufferTarget.CopyWriteBuffer, Handle);
+            GL.BindBuffer(BufferTarget.CopyWriteBuffer, handle);
             GL.BufferData(BufferTarget.CopyWriteBuffer, size, IntPtr.Zero, BufferUsageHint.DynamicDraw);
+
+            return Handle.FromInt32<BufferHandle>(handle);
         }
 
-        public void CopyTo(IBuffer destination, int srcOffset, int dstOffset, int size)
+        public static void Copy(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
         {
-            GL.BindBuffer(BufferTarget.CopyReadBuffer, Handle);
-            GL.BindBuffer(BufferTarget.CopyWriteBuffer, ((Buffer)destination).Handle);
+            GL.BindBuffer(BufferTarget.CopyReadBuffer, source.ToInt32());
+            GL.BindBuffer(BufferTarget.CopyWriteBuffer, destination.ToInt32());
 
             GL.CopyBufferSubData(
                 BufferTarget.CopyReadBuffer,
@@ -29,9 +29,9 @@ namespace Ryujinx.Graphics.OpenGL
                 (IntPtr)size);
         }
 
-        public byte[] GetData(int offset, int size)
+        public static byte[] GetData(BufferHandle buffer, int offset, int size)
         {
-            GL.BindBuffer(BufferTarget.CopyReadBuffer, Handle);
+            GL.BindBuffer(BufferTarget.CopyReadBuffer, buffer.ToInt32());
 
             byte[] data = new byte[size];
 
@@ -40,22 +40,9 @@ namespace Ryujinx.Graphics.OpenGL
             return data;
         }
 
-        public void SetData(ReadOnlySpan<byte> data)
+        public static void SetData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
         {
-            unsafe
-            {
-                GL.BindBuffer(BufferTarget.CopyWriteBuffer, Handle);
-
-                fixed (byte* ptr = data)
-                {
-                    GL.BufferData(BufferTarget.CopyWriteBuffer, data.Length, (IntPtr)ptr, BufferUsageHint.DynamicDraw);
-                }
-            }
-        }
-
-        public void SetData(int offset, ReadOnlySpan<byte> data)
-        {
-            GL.BindBuffer(BufferTarget.CopyWriteBuffer, Handle);
+            GL.BindBuffer(BufferTarget.CopyWriteBuffer, buffer.ToInt32());
 
             unsafe
             {
@@ -66,9 +53,9 @@ namespace Ryujinx.Graphics.OpenGL
             }
         }
 
-        public void Dispose()
+        public static void Delete(BufferHandle buffer)
         {
-            GL.DeleteBuffer(Handle);
+            GL.DeleteBuffer(buffer.ToInt32());
         }
     }
 }
diff --git a/Ryujinx.Graphics.OpenGL/Constants.cs b/Ryujinx.Graphics.OpenGL/Constants.cs
new file mode 100644
index 0000000000..9775b240d9
--- /dev/null
+++ b/Ryujinx.Graphics.OpenGL/Constants.cs
@@ -0,0 +1,10 @@
+namespace Ryujinx.Graphics.OpenGL
+{
+    static class Constants
+    {
+        public const int MaxRenderTargets = 8;
+        public const int MaxViewports = 16;
+        public const int MaxVertexAttribs = 16;
+        public const int MaxVertexBuffers = 16;
+    }
+}
diff --git a/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/Ryujinx.Graphics.OpenGL/Framebuffer.cs
index 23f015b183..e66dcaca8c 100644
--- a/Ryujinx.Graphics.OpenGL/Framebuffer.cs
+++ b/Ryujinx.Graphics.OpenGL/Framebuffer.cs
@@ -1,5 +1,6 @@
 using OpenTK.Graphics.OpenGL;
 using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.OpenGL.Image;
 using System;
 
 namespace Ryujinx.Graphics.OpenGL
diff --git a/Ryujinx.Graphics.OpenGL/Handle.cs b/Ryujinx.Graphics.OpenGL/Handle.cs
new file mode 100644
index 0000000000..4b2f05e672
--- /dev/null
+++ b/Ryujinx.Graphics.OpenGL/Handle.cs
@@ -0,0 +1,23 @@
+using Ryujinx.Graphics.GAL;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace Ryujinx.Graphics.OpenGL
+{
+    static class Handle
+    {
+        public static T FromInt32<T>(int handle) where T : unmanaged
+        {
+            Debug.Assert(Unsafe.SizeOf<T>() == sizeof(ulong));
+
+            ulong handle64 = (uint)handle;
+
+            return Unsafe.As<ulong, T>(ref handle64);
+        }
+
+        public static int ToInt32(this BufferHandle handle)
+        {
+            return (int)Unsafe.As<BufferHandle, ulong>(ref handle);
+        }
+    }
+}
diff --git a/Ryujinx.Graphics.OpenGL/Sampler.cs b/Ryujinx.Graphics.OpenGL/Image/Sampler.cs
similarity index 98%
rename from Ryujinx.Graphics.OpenGL/Sampler.cs
rename to Ryujinx.Graphics.OpenGL/Image/Sampler.cs
index 674fc7978b..e13f0da3ff 100644
--- a/Ryujinx.Graphics.OpenGL/Sampler.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/Sampler.cs
@@ -1,7 +1,7 @@
 using OpenTK.Graphics.OpenGL;
 using Ryujinx.Graphics.GAL;
 
-namespace Ryujinx.Graphics.OpenGL
+namespace Ryujinx.Graphics.OpenGL.Image
 {
     class Sampler : ISampler
     {
diff --git a/Ryujinx.Graphics.OpenGL/TextureBase.cs b/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs
similarity index 88%
rename from Ryujinx.Graphics.OpenGL/TextureBase.cs
rename to Ryujinx.Graphics.OpenGL/Image/TextureBase.cs
index f4ab0bda42..a4209ea152 100644
--- a/Ryujinx.Graphics.OpenGL/TextureBase.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs
@@ -1,10 +1,7 @@
 using OpenTK.Graphics.OpenGL;
 using Ryujinx.Graphics.GAL;
-using System;
-using System.Collections.Generic;
-using System.Text;
 
-namespace Ryujinx.Graphics.OpenGL
+namespace Ryujinx.Graphics.OpenGL.Image
 {
     class TextureBase
     {
diff --git a/Ryujinx.Graphics.OpenGL/TextureBuffer.cs b/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs
similarity index 79%
rename from Ryujinx.Graphics.OpenGL/TextureBuffer.cs
rename to Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs
index fb18c6ee75..2c69571c3a 100644
--- a/Ryujinx.Graphics.OpenGL/TextureBuffer.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs
@@ -2,14 +2,14 @@
 using Ryujinx.Graphics.GAL;
 using System;
 
-namespace Ryujinx.Graphics.OpenGL
+namespace Ryujinx.Graphics.OpenGL.Image
 {
     class TextureBuffer : TextureBase, ITexture
     {
         private int _bufferOffset;
         private int _bufferSize;
 
-        private Buffer _buffer;
+        private BufferHandle _buffer;
 
         public TextureBuffer(TextureCreateInfo info) : base(info) {}
 
@@ -30,24 +30,24 @@ namespace Ryujinx.Graphics.OpenGL
 
         public byte[] GetData()
         {
-            return _buffer?.GetData(_bufferOffset, _bufferSize);
+            return Buffer.GetData(_buffer, _bufferOffset, _bufferSize);
         }
 
         public void SetData(ReadOnlySpan<byte> data)
         {
-            _buffer?.SetData(_bufferOffset, data.Slice(0, Math.Min(data.Length, _bufferSize)));
+            Buffer.SetData(_buffer, _bufferOffset, data.Slice(0, Math.Min(data.Length, _bufferSize)));
         }
 
         public void SetStorage(BufferRange buffer)
         {
-            if (buffer.Buffer == _buffer &&
+            if (buffer.Handle == _buffer &&
                 buffer.Offset == _bufferOffset &&
                 buffer.Size == _bufferSize)
             {
                 return;
             }
 
-            _buffer = (Buffer)buffer.Buffer;
+            _buffer = buffer.Handle;
             _bufferOffset = buffer.Offset;
             _bufferSize = buffer.Size;
 
@@ -55,7 +55,7 @@ namespace Ryujinx.Graphics.OpenGL
 
             SizedInternalFormat format = (SizedInternalFormat)FormatTable.GetFormatInfo(Info.Format).PixelInternalFormat;
 
-            GL.TexBufferRange(TextureBufferTarget.TextureBuffer, format, _buffer.Handle, (IntPtr)buffer.Offset, buffer.Size);
+            GL.TexBufferRange(TextureBufferTarget.TextureBuffer, format, _buffer.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
         }
 
         public void Dispose()
diff --git a/Ryujinx.Graphics.OpenGL/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs
similarity index 99%
rename from Ryujinx.Graphics.OpenGL/TextureCopy.cs
rename to Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs
index 59db94a47c..1cef61a9e9 100644
--- a/Ryujinx.Graphics.OpenGL/TextureCopy.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs
@@ -2,7 +2,7 @@ using Ryujinx.Graphics.GAL;
 using OpenTK.Graphics.OpenGL;
 using System;
 
-namespace Ryujinx.Graphics.OpenGL
+namespace Ryujinx.Graphics.OpenGL.Image
 {
     class TextureCopy : IDisposable
     {
diff --git a/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs
similarity index 98%
rename from Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs
rename to Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs
index 5ae75d9c0f..2840113854 100644
--- a/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs
@@ -3,7 +3,7 @@ using Ryujinx.Common;
 using Ryujinx.Graphics.GAL;
 using System;
 
-namespace Ryujinx.Graphics.OpenGL
+namespace Ryujinx.Graphics.OpenGL.Image
 {
     static class TextureCopyUnscaled
     {
diff --git a/Ryujinx.Graphics.OpenGL/TextureStorage.cs b/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs
similarity index 99%
rename from Ryujinx.Graphics.OpenGL/TextureStorage.cs
rename to Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs
index b680f3a6b7..baf8e65d05 100644
--- a/Ryujinx.Graphics.OpenGL/TextureStorage.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs
@@ -2,7 +2,7 @@ using OpenTK.Graphics.OpenGL;
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.GAL;
 
-namespace Ryujinx.Graphics.OpenGL
+namespace Ryujinx.Graphics.OpenGL.Image
 {
     class TextureStorage
     {
diff --git a/Ryujinx.Graphics.OpenGL/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
similarity index 99%
rename from Ryujinx.Graphics.OpenGL/TextureView.cs
rename to Ryujinx.Graphics.OpenGL/Image/TextureView.cs
index 0ab59d4257..0b24a29628 100644
--- a/Ryujinx.Graphics.OpenGL/TextureView.cs
+++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
@@ -2,7 +2,7 @@ using OpenTK.Graphics.OpenGL;
 using Ryujinx.Graphics.GAL;
 using System;
 
-namespace Ryujinx.Graphics.OpenGL
+namespace Ryujinx.Graphics.OpenGL.Image
 {
     class TextureView : TextureBase, ITexture
     {
diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs
index 80b0710889..05383f5d73 100644
--- a/Ryujinx.Graphics.OpenGL/Pipeline.cs
+++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs
@@ -1,6 +1,7 @@
 using OpenTK.Graphics.OpenGL;
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.OpenGL.Image;
 using Ryujinx.Graphics.OpenGL.Queries;
 using Ryujinx.Graphics.Shader;
 using System;
@@ -32,7 +33,7 @@ namespace Ryujinx.Graphics.OpenGL
         private ClipOrigin _clipOrigin;
         private ClipDepthMode _clipDepthMode;
 
-        private uint[] _componentMasks;
+        private readonly uint[] _componentMasks;
 
         private bool _scissor0Enable = false;
 
@@ -43,6 +44,13 @@ namespace Ryujinx.Graphics.OpenGL
             _rasterizerDiscard = false;
             _clipOrigin = ClipOrigin.LowerLeft;
             _clipDepthMode = ClipDepthMode.NegativeOneToOne;
+
+            _componentMasks = new uint[Constants.MaxRenderTargets];
+
+            for (int index = 0; index < Constants.MaxRenderTargets; index++)
+            {
+                _componentMasks[index] = 0xf;
+            }
         }
 
         public void Barrier()
@@ -112,6 +120,11 @@ namespace Ryujinx.Graphics.OpenGL
             _framebuffer.SignalModified();
         }
 
+        public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
+        {
+            Buffer.Copy(source, destination, srcOffset, dstOffset, size);
+        }
+
         public void DispatchCompute(int groupsX, int groupsY, int groupsZ)
         {
             if (!_program.IsLinked)
@@ -631,7 +644,7 @@ namespace Ryujinx.Graphics.OpenGL
 
             EnsureVertexArray();
 
-            _vertexArray.SetIndexBuffer((Buffer)buffer.Buffer);
+            _vertexArray.SetIndexBuffer(buffer.Handle);
         }
 
         public void SetPointSize(float size)
@@ -661,7 +674,6 @@ namespace Ryujinx.Graphics.OpenGL
         public void SetProgram(IProgram program)
         {
             _program = (Program)program;
-
             _program.Bind();
         }
 
@@ -679,12 +691,12 @@ namespace Ryujinx.Graphics.OpenGL
             _rasterizerDiscard = discard;
         }
 
-        public void SetRenderTargetColorMasks(uint[] componentMasks)
+        public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks)
         {
-            _componentMasks = (uint[])componentMasks.Clone();
-
             for (int index = 0; index < componentMasks.Length; index++)
             {
+                _componentMasks[index] = componentMasks[index];
+
                 RestoreComponentMask(index);
             }
         }
@@ -823,21 +835,21 @@ namespace Ryujinx.Graphics.OpenGL
             GL.Enable(EnableCap.ClipDistance0 + index);
         }
 
-        public void SetVertexAttribs(VertexAttribDescriptor[] vertexAttribs)
+        public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
         {
             EnsureVertexArray();
 
             _vertexArray.SetVertexAttributes(vertexAttribs);
         }
 
-        public void SetVertexBuffers(VertexBufferDescriptor[] vertexBuffers)
+        public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers)
         {
             EnsureVertexArray();
 
             _vertexArray.SetVertexBuffers(vertexBuffers);
         }
 
-        public void SetViewports(int first, Viewport[] viewports)
+        public void SetViewports(int first, ReadOnlySpan<Viewport> viewports)
         {
             bool flipY = false;
 
@@ -906,18 +918,16 @@ namespace Ryujinx.Graphics.OpenGL
                 ? BufferRangeTarget.ShaderStorageBuffer
                 : BufferRangeTarget.UniformBuffer;
 
-            if (buffer.Buffer == null)
+            if (buffer.Handle == null)
             {
                 GL.BindBufferRange(target, bindingPoint, 0, IntPtr.Zero, 0);
 
                 return;
             }
 
-            int bufferHandle = ((Buffer)buffer.Buffer).Handle;
-
             IntPtr bufferOffset = (IntPtr)buffer.Offset;
 
-            GL.BindBufferRange(target, bindingPoint, bufferHandle, bufferOffset, buffer.Size);
+            GL.BindBufferRange(target, bindingPoint, buffer.Handle.ToInt32(), bufferOffset, buffer.Size);
         }
 
         private void SetOrigin(ClipOrigin origin)
@@ -997,15 +1007,12 @@ namespace Ryujinx.Graphics.OpenGL
 
         private void RestoreComponentMask(int index)
         {
-            if (_componentMasks != null)
-            {
-                GL.ColorMask(
-                    index,
-                    (_componentMasks[index] & 1u) != 0,
-                    (_componentMasks[index] & 2u) != 0,
-                    (_componentMasks[index] & 4u) != 0,
-                    (_componentMasks[index] & 8u) != 0);
-            }
+            GL.ColorMask(
+                index,
+                (_componentMasks[index] & 1u) != 0,
+                (_componentMasks[index] & 2u) != 0,
+                (_componentMasks[index] & 4u) != 0,
+                (_componentMasks[index] & 8u) != 0);
         }
 
         public void RestoreScissor0Enable()
diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs
index b3ae8c33d6..15b223f592 100644
--- a/Ryujinx.Graphics.OpenGL/Renderer.cs
+++ b/Ryujinx.Graphics.OpenGL/Renderer.cs
@@ -1,6 +1,7 @@
 using OpenTK.Graphics.OpenGL;
 using Ryujinx.Common.Logging;
 using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.OpenGL.Image;
 using Ryujinx.Graphics.OpenGL.Queries;
 using Ryujinx.Graphics.Shader;
 using System;
@@ -38,9 +39,9 @@ namespace Ryujinx.Graphics.OpenGL
             return new Shader(shader);
         }
 
-        public IBuffer CreateBuffer(int size)
+        public BufferHandle CreateBuffer(int size)
         {
-            return new Buffer(size);
+            return Buffer.Create(size);
         }
 
         public IProgram CreateProgram(IShader[] shaders)
@@ -58,6 +59,16 @@ namespace Ryujinx.Graphics.OpenGL
             return info.Target == Target.TextureBuffer ? new TextureBuffer(info) : new TextureStorage(this, info).CreateDefaultView();
         }
 
+        public void DeleteBuffer(BufferHandle buffer)
+        {
+            Buffer.Delete(buffer);
+        }
+
+        public byte[] GetBufferData(BufferHandle buffer, int offset, int size)
+        {
+            return Buffer.GetData(buffer, offset, size);
+        }
+
         public Capabilities GetCapabilities()
         {
             return new Capabilities(
@@ -68,6 +79,11 @@ namespace Ryujinx.Graphics.OpenGL
                 HwCapabilities.MaxSupportedAnisotropy);
         }
 
+        public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
+        {
+            Buffer.SetData(buffer, offset, data);
+        }
+
         public void UpdateCounters()
         {
             _counters.Update();
diff --git a/Ryujinx.Graphics.OpenGL/VertexArray.cs b/Ryujinx.Graphics.OpenGL/VertexArray.cs
index 43d200a400..cc352761d2 100644
--- a/Ryujinx.Graphics.OpenGL/VertexArray.cs
+++ b/Ryujinx.Graphics.OpenGL/VertexArray.cs
@@ -10,12 +10,18 @@ namespace Ryujinx.Graphics.OpenGL
 
         private bool _needsAttribsUpdate;
 
-        private VertexBufferDescriptor[] _vertexBuffers;
-        private VertexAttribDescriptor[] _vertexAttribs;
+        private readonly VertexAttribDescriptor[] _vertexAttribs;
+        private readonly VertexBufferDescriptor[] _vertexBuffers;
+
+        private int _vertexAttribsCount;
+        private int _vertexBuffersCount;
 
         public VertexArray()
         {
             Handle = GL.GenVertexArray();
+
+            _vertexAttribs = new VertexAttribDescriptor[Constants.MaxVertexAttribs];
+            _vertexBuffers = new VertexBufferDescriptor[Constants.MaxVertexBuffers];
         }
 
         public void Bind()
@@ -23,17 +29,17 @@ namespace Ryujinx.Graphics.OpenGL
             GL.BindVertexArray(Handle);
         }
 
-        public void SetVertexBuffers(VertexBufferDescriptor[] vertexBuffers)
+        public void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers)
         {
             int bindingIndex = 0;
 
-            foreach (VertexBufferDescriptor vb in vertexBuffers)
+            for (int index = 0; index < vertexBuffers.Length; index++)
             {
-                if (vb.Buffer.Buffer != null)
-                {
-                    int bufferHandle = ((Buffer)vb.Buffer.Buffer).Handle;
+                VertexBufferDescriptor vb = vertexBuffers[index];
 
-                    GL.BindVertexBuffer(bindingIndex, bufferHandle, (IntPtr)vb.Buffer.Offset, vb.Stride);
+                if (vb.Buffer.Handle != null)
+                {
+                    GL.BindVertexBuffer(bindingIndex, vb.Buffer.Handle.ToInt32(), (IntPtr)vb.Buffer.Offset, vb.Stride);
 
                     GL.VertexBindingDivisor(bindingIndex, vb.Divisor);
                 }
@@ -42,31 +48,35 @@ namespace Ryujinx.Graphics.OpenGL
                     GL.BindVertexBuffer(bindingIndex, 0, IntPtr.Zero, 0);
                 }
 
+                _vertexBuffers[index] = vb;
+
                 bindingIndex++;
             }
 
-            _vertexBuffers = vertexBuffers;
+            _vertexBuffersCount = bindingIndex;
 
             _needsAttribsUpdate = true;
         }
 
-        public void SetVertexAttributes(VertexAttribDescriptor[] vertexAttribs)
+        public void SetVertexAttributes(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
         {
-            int attribIndex = 0;
+            int index = 0;
 
-            foreach (VertexAttribDescriptor attrib in vertexAttribs)
+            for (; index < vertexAttribs.Length; index++)
             {
+                VertexAttribDescriptor attrib = vertexAttribs[index];
+
                 FormatInfo fmtInfo = FormatTable.GetFormatInfo(attrib.Format);
 
                 if (attrib.IsZero)
                 {
                     // Disabling the attribute causes the shader to read a constant value.
                     // The value is configurable, but by default is a vector of (0, 0, 0, 1).
-                    GL.DisableVertexAttribArray(attribIndex);
+                    GL.DisableVertexAttribArray(index);
                 }
                 else
                 {
-                    GL.EnableVertexAttribArray(attribIndex);
+                    GL.EnableVertexAttribArray(index);
                 }
                 
                 int offset = attrib.Offset;
@@ -79,47 +89,47 @@ namespace Ryujinx.Graphics.OpenGL
                 {
                     VertexAttribType type = (VertexAttribType)fmtInfo.PixelType;
 
-                    GL.VertexAttribFormat(attribIndex, size, type, fmtInfo.Normalized, offset);
+                    GL.VertexAttribFormat(index, size, type, fmtInfo.Normalized, offset);
                 }
                 else
                 {
                     VertexAttribIntegerType type = (VertexAttribIntegerType)fmtInfo.PixelType;
 
-                    GL.VertexAttribIFormat(attribIndex, size, type, offset);
+                    GL.VertexAttribIFormat(index, size, type, offset);
                 }
 
-                GL.VertexAttribBinding(attribIndex, attrib.BufferIndex);
+                GL.VertexAttribBinding(index, attrib.BufferIndex);
 
-                attribIndex++;
+                _vertexAttribs[index] = attrib;
             }
 
-            for (; attribIndex < 16; attribIndex++)
+            _vertexAttribsCount = index;
+
+            for (; index < Constants.MaxVertexAttribs; index++)
             {
-                GL.DisableVertexAttribArray(attribIndex);
+                GL.DisableVertexAttribArray(index);
             }
-
-            _vertexAttribs = vertexAttribs;
         }
 
-        public void SetIndexBuffer(Buffer indexBuffer)
+        public void SetIndexBuffer(BufferHandle buffer)
         {
-            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer?.Handle ?? 0);
+            GL.BindBuffer(BufferTarget.ElementArrayBuffer, buffer.ToInt32());
         }
 
         public void Validate()
         {
-            for (int attribIndex = 0; attribIndex < _vertexAttribs.Length; attribIndex++)
+            for (int attribIndex = 0; attribIndex < _vertexAttribsCount; attribIndex++)
             {
                 VertexAttribDescriptor attrib = _vertexAttribs[attribIndex];
 
-                if ((uint)attrib.BufferIndex >= _vertexBuffers.Length)
+                if ((uint)attrib.BufferIndex >= _vertexBuffersCount)
                 {
                     GL.DisableVertexAttribArray(attribIndex);
 
                     continue;
                 }
 
-                if (_vertexBuffers[attrib.BufferIndex].Buffer.Buffer == null)
+                if (_vertexBuffers[attrib.BufferIndex].Buffer.Handle == null)
                 {
                     GL.DisableVertexAttribArray(attribIndex);
 
diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs
index 9f0007c4d8..6e7ddbacb5 100644
--- a/Ryujinx.Graphics.OpenGL/Window.cs
+++ b/Ryujinx.Graphics.OpenGL/Window.cs
@@ -1,5 +1,6 @@
 using OpenTK.Graphics.OpenGL;
 using Ryujinx.Graphics.GAL;
+using Ryujinx.Graphics.OpenGL.Image;
 using System;
 
 namespace Ryujinx.Graphics.OpenGL