Explorar el Código

Texture loading: reduce memory allocations (#6623)

* rebase

* add methods Ryyjinx.Common EmbeddedResources and SteamUtils

* GAL changes - change SetData() methods and ThreadedTexture commands to use IMemoryOwner<byte> instead of SpanOrArray<byte>

* Ryujinx.Graphics.Texture: change texture conversion methods to return IMemoryOwner<byte> and allocate from ByteMemoryPool

* Ryujinx.Graphics.OpenGL: update ITexture and Texture-like types with SetData() methods to take IMemoryOwner<byte> instead of SpanOrArray<byte>

* Ryujinx.Graphics.Vulkan: update ITexture and Texture-like types with SetData() methods to take IMemoryOwner<byte> instead of SpanOrArray<byte>

* Ryujinx.Graphics.Gpu: update ITexture and Texture-like types with SetData() methods to take IMemoryOwner<byte> instead of SpanOrArray<byte>

* Remove now-unused SpanOrArray<T>

* post-rebase cleanup

* PixelConverter: remove unsafe modifier on safe methods, and remove one unnecessary cast

* use ByteMemoryPool.Rent() in GetWritableRegion() impls

* fix formatting, rename `ReadRentedMemory()` to `ReadFileToRentedMemory()``

* Texture.ConvertToHostCompatibleFormat(): dispose of `result` in Astc decode branch
jhorv hace 2 años
padre
commit
268c9aecf8
Se han modificado 29 ficheros con 434 adiciones y 321 borrados
  1. 0 89
      src/Ryujinx.Common/Memory/SpanOrArray.cs
  2. 17 0
      src/Ryujinx.Common/Utilities/EmbeddedResources.cs
  3. 64 3
      src/Ryujinx.Common/Utilities/StreamUtils.cs
  4. 5 3
      src/Ryujinx.Graphics.Device/DeviceMemoryManager.cs
  5. 28 4
      src/Ryujinx.Graphics.GAL/ITexture.cs
  6. 4 4
      src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs
  7. 4 4
      src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs
  8. 4 4
      src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs
  9. 10 7
      src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs
  10. 2 1
      src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs
  11. 3 1
      src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs
  12. 109 60
      src/Ryujinx.Graphics.Gpu/Image/Texture.cs
  13. 2 2
      src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs
  14. 5 3
      src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
  15. 6 2
      src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs
  16. 4 3
      src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs
  17. 7 3
      src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs
  18. 10 5
      src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs
  19. 48 46
      src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
  20. 5 5
      src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs
  21. 25 21
      src/Ryujinx.Graphics.Texture/BCnDecoder.cs
  22. 7 4
      src/Ryujinx.Graphics.Texture/BCnEncoder.cs
  23. 11 9
      src/Ryujinx.Graphics.Texture/ETC2Decoder.cs
  24. 9 6
      src/Ryujinx.Graphics.Texture/LayoutConverter.cs
  25. 19 17
      src/Ryujinx.Graphics.Texture/PixelConverter.cs
  26. 2 1
      src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
  27. 2 2
      src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
  28. 9 5
      src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs
  29. 13 7
      src/Ryujinx.Graphics.Vulkan/TextureView.cs

+ 0 - 89
src/Ryujinx.Common/Memory/SpanOrArray.cs

@@ -1,89 +0,0 @@
-using System;
-
-namespace Ryujinx.Common.Memory
-{
-    /// <summary>
-    /// A struct that can represent both a Span and Array.
-    /// This is useful to keep the Array representation when possible to avoid copies.
-    /// </summary>
-    /// <typeparam name="T">Element Type</typeparam>
-    public readonly ref struct SpanOrArray<T> where T : unmanaged
-    {
-        public readonly T[] Array;
-        public readonly ReadOnlySpan<T> Span;
-
-        /// <summary>
-        /// Create a new SpanOrArray from an array.
-        /// </summary>
-        /// <param name="array">Array to store</param>
-        public SpanOrArray(T[] array)
-        {
-            Array = array;
-            Span = ReadOnlySpan<T>.Empty;
-        }
-
-        /// <summary>
-        /// Create a new SpanOrArray from a readonly span.
-        /// </summary>
-        /// <param name="array">Span to store</param>
-        public SpanOrArray(ReadOnlySpan<T> span)
-        {
-            Array = null;
-            Span = span;
-        }
-
-        /// <summary>
-        /// Return the contained array, or convert the span if necessary.
-        /// </summary>
-        /// <returns>An array containing the data</returns>
-        public T[] ToArray()
-        {
-            return Array ?? Span.ToArray();
-        }
-
-        /// <summary>
-        /// Return a ReadOnlySpan from either the array or ReadOnlySpan.
-        /// </summary>
-        /// <returns>A ReadOnlySpan containing the data</returns>
-        public ReadOnlySpan<T> AsSpan()
-        {
-            return Array ?? Span;
-        }
-
-        /// <summary>
-        /// Cast an array to a SpanOrArray.
-        /// </summary>
-        /// <param name="array">Source array</param>
-        public static implicit operator SpanOrArray<T>(T[] array)
-        {
-            return new SpanOrArray<T>(array);
-        }
-
-        /// <summary>
-        /// Cast a ReadOnlySpan to a SpanOrArray.
-        /// </summary>
-        /// <param name="span">Source ReadOnlySpan</param>
-        public static implicit operator SpanOrArray<T>(ReadOnlySpan<T> span)
-        {
-            return new SpanOrArray<T>(span);
-        }
-
-        /// <summary>
-        /// Cast a Span to a SpanOrArray.
-        /// </summary>
-        /// <param name="span">Source Span</param>
-        public static implicit operator SpanOrArray<T>(Span<T> span)
-        {
-            return new SpanOrArray<T>(span);
-        }
-
-        /// <summary>
-        /// Cast a SpanOrArray to a ReadOnlySpan
-        /// </summary>
-        /// <param name="spanOrArray">Source SpanOrArray</param>
-        public static implicit operator ReadOnlySpan<T>(SpanOrArray<T> spanOrArray)
-        {
-            return spanOrArray.AsSpan();
-        }
-    }
-}

+ 17 - 0
src/Ryujinx.Common/Utilities/EmbeddedResources.cs

@@ -1,5 +1,6 @@
 using Ryujinx.Common.Utilities;
 using Ryujinx.Common.Utilities;
 using System;
 using System;
+using System.Buffers;
 using System.IO;
 using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
@@ -41,6 +42,22 @@ namespace Ryujinx.Common
             return StreamUtils.StreamToBytes(stream);
             return StreamUtils.StreamToBytes(stream);
         }
         }
 
 
+        public static IMemoryOwner<byte> ReadFileToRentedMemory(string filename)
+        {
+            var (assembly, path) = ResolveManifestPath(filename);
+
+            return ReadFileToRentedMemory(assembly, path);
+        }
+
+        public static IMemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
+        {
+            using var stream = GetStream(assembly, filename);
+
+            return stream is null
+                ? null
+                : StreamUtils.StreamToRentedMemory(stream);
+        }
+
         public async static Task<byte[]> ReadAsync(Assembly assembly, string filename)
         public async static Task<byte[]> ReadAsync(Assembly assembly, string filename)
         {
         {
             using var stream = GetStream(assembly, filename);
             using var stream = GetStream(assembly, filename);

+ 64 - 3
src/Ryujinx.Common/Utilities/StreamUtils.cs

@@ -1,4 +1,6 @@
+using Microsoft.IO;
 using Ryujinx.Common.Memory;
 using Ryujinx.Common.Memory;
+using System.Buffers;
 using System.IO;
 using System.IO;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
@@ -9,12 +11,50 @@ namespace Ryujinx.Common.Utilities
     {
     {
         public static byte[] StreamToBytes(Stream input)
         public static byte[] StreamToBytes(Stream input)
         {
         {
-            using MemoryStream stream = MemoryStreamManager.Shared.GetStream();
+            using RecyclableMemoryStream output = StreamToRecyclableMemoryStream(input);
 
 
+            return output.ToArray();
+        }
 
 
-            input.CopyTo(stream);
+        public static IMemoryOwner<byte> StreamToRentedMemory(Stream input)
+        {
+            if (input is MemoryStream inputMemoryStream)
+            {
+                return MemoryStreamToRentedMemory(inputMemoryStream);
+            }
+            else if (input.CanSeek)
+            {
+                long bytesExpected = input.Length;
 
 
-            return stream.ToArray();
+                IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(bytesExpected);
+
+                var destSpan = ownedMemory.Memory.Span;
+
+                int totalBytesRead = 0;
+
+                while (totalBytesRead < bytesExpected)
+                {
+                    int bytesRead = input.Read(destSpan[totalBytesRead..]);
+
+                    if (bytesRead == 0)
+                    {
+                        ownedMemory.Dispose();
+
+                        throw new IOException($"Tried reading {bytesExpected} but the stream closed after reading {totalBytesRead}.");
+                    }
+
+                    totalBytesRead += bytesRead;
+                }
+
+                return ownedMemory;
+            }
+            else
+            {
+                // If input is (non-seekable) then copy twice: first into a RecyclableMemoryStream, then to a rented IMemoryOwner<byte>.
+                using RecyclableMemoryStream output = StreamToRecyclableMemoryStream(input);
+
+                return MemoryStreamToRentedMemory(output);
+            }
         }
         }
 
 
         public static async Task<byte[]> StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default)
         public static async Task<byte[]> StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default)
@@ -25,5 +65,26 @@ namespace Ryujinx.Common.Utilities
 
 
             return stream.ToArray();
             return stream.ToArray();
         }
         }
+
+        private static IMemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
+        {
+            input.Position = 0;
+
+            IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(input.Length);
+
+            // Discard the return value because we assume reading a MemoryStream always succeeds completely.
+            _ = input.Read(ownedMemory.Memory.Span);
+
+            return ownedMemory;
+        }
+
+        private static RecyclableMemoryStream StreamToRecyclableMemoryStream(Stream input)
+        {
+            RecyclableMemoryStream stream = MemoryStreamManager.Shared.GetStream();
+
+            input.CopyTo(stream);
+
+            return stream;
+        }
     }
     }
 }
 }

+ 5 - 3
src/Ryujinx.Graphics.Device/DeviceMemoryManager.cs

@@ -1,5 +1,7 @@
+using Ryujinx.Common.Memory;
 using Ryujinx.Memory;
 using Ryujinx.Memory;
 using System;
 using System;
+using System.Buffers;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 
 
@@ -143,11 +145,11 @@ namespace Ryujinx.Graphics.Device
             }
             }
             else
             else
             {
             {
-                Memory<byte> memory = new byte[size];
+                IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
 
 
-                GetSpan(va, size).CopyTo(memory.Span);
+                GetSpan(va, size).CopyTo(memoryOwner.Memory.Span);
 
 
-                return new WritableRegion(this, va, memory, tracked: true);
+                return new WritableRegion(this, va, memoryOwner, tracked: true);
             }
             }
         }
         }
 
 

+ 28 - 4
src/Ryujinx.Graphics.GAL/ITexture.cs

@@ -1,4 +1,4 @@
-using Ryujinx.Common.Memory;
+using System.Buffers;
 
 
 namespace Ryujinx.Graphics.GAL
 namespace Ryujinx.Graphics.GAL
 {
 {
@@ -17,10 +17,34 @@ namespace Ryujinx.Graphics.GAL
         PinnedSpan<byte> GetData();
         PinnedSpan<byte> GetData();
         PinnedSpan<byte> GetData(int layer, int level);
         PinnedSpan<byte> GetData(int layer, int level);
 
 
-        void SetData(SpanOrArray<byte> data);
-        void SetData(SpanOrArray<byte> data, int layer, int level);
-        void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region);
+        /// <summary>
+        /// Sets the texture data. The data passed as a <see cref="IMemoryOwner{Byte}" /> will be disposed when
+        /// the operation completes.
+        /// </summary>
+        /// <param name="data">Texture data bytes</param>
+        void SetData(IMemoryOwner<byte> data);
+
+        /// <summary>
+        /// Sets the texture data. The data passed as a <see cref="IMemoryOwner{Byte}" /> will be disposed when
+        /// the operation completes.
+        /// </summary>
+        /// <param name="data">Texture data bytes</param>
+        /// <param name="layer">Target layer</param>
+        /// <param name="level">Target level</param>
+        void SetData(IMemoryOwner<byte> data, int layer, int level);
+
+        /// <summary>
+        /// Sets the texture data. The data passed as a <see cref="IMemoryOwner{Byte}" /> will be disposed when
+        /// the operation completes.
+        /// </summary>
+        /// <param name="data">Texture data bytes</param>
+        /// <param name="layer">Target layer</param>
+        /// <param name="level">Target level</param>
+        /// <param name="region">Target sub-region of the texture to update</param>
+        void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region);
+
         void SetStorage(BufferRange buffer);
         void SetStorage(BufferRange buffer);
+
         void Release();
         void Release();
     }
     }
 }
 }

+ 4 - 4
src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs

@@ -1,6 +1,6 @@
 using Ryujinx.Graphics.GAL.Multithreading.Model;
 using Ryujinx.Graphics.GAL.Multithreading.Model;
 using Ryujinx.Graphics.GAL.Multithreading.Resources;
 using Ryujinx.Graphics.GAL.Multithreading.Resources;
-using System;
+using System.Buffers;
 
 
 namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 {
 {
@@ -8,9 +8,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
     {
     {
         public readonly CommandType CommandType => CommandType.TextureSetData;
         public readonly CommandType CommandType => CommandType.TextureSetData;
         private TableRef<ThreadedTexture> _texture;
         private TableRef<ThreadedTexture> _texture;
-        private TableRef<byte[]> _data;
+        private TableRef<IMemoryOwner<byte>> _data;
 
 
-        public void Set(TableRef<ThreadedTexture> texture, TableRef<byte[]> data)
+        public void Set(TableRef<ThreadedTexture> texture, TableRef<IMemoryOwner<byte>> data)
         {
         {
             _texture = texture;
             _texture = texture;
             _data = data;
             _data = data;
@@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
         public static void Run(ref TextureSetDataCommand command, ThreadedRenderer threaded, IRenderer renderer)
         public static void Run(ref TextureSetDataCommand command, ThreadedRenderer threaded, IRenderer renderer)
         {
         {
             ThreadedTexture texture = command._texture.Get(threaded);
             ThreadedTexture texture = command._texture.Get(threaded);
-            texture.Base.SetData(new ReadOnlySpan<byte>(command._data.Get(threaded)));
+            texture.Base.SetData(command._data.Get(threaded));
         }
         }
     }
     }
 }
 }

+ 4 - 4
src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs

@@ -1,6 +1,6 @@
 using Ryujinx.Graphics.GAL.Multithreading.Model;
 using Ryujinx.Graphics.GAL.Multithreading.Model;
 using Ryujinx.Graphics.GAL.Multithreading.Resources;
 using Ryujinx.Graphics.GAL.Multithreading.Resources;
-using System;
+using System.Buffers;
 
 
 namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 {
 {
@@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
     {
     {
         public readonly CommandType CommandType => CommandType.TextureSetDataSlice;
         public readonly CommandType CommandType => CommandType.TextureSetDataSlice;
         private TableRef<ThreadedTexture> _texture;
         private TableRef<ThreadedTexture> _texture;
-        private TableRef<byte[]> _data;
+        private TableRef<IMemoryOwner<byte>> _data;
         private int _layer;
         private int _layer;
         private int _level;
         private int _level;
 
 
-        public void Set(TableRef<ThreadedTexture> texture, TableRef<byte[]> data, int layer, int level)
+        public void Set(TableRef<ThreadedTexture> texture, TableRef<IMemoryOwner<byte>> data, int layer, int level)
         {
         {
             _texture = texture;
             _texture = texture;
             _data = data;
             _data = data;
@@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
         public static void Run(ref TextureSetDataSliceCommand command, ThreadedRenderer threaded, IRenderer renderer)
         public static void Run(ref TextureSetDataSliceCommand command, ThreadedRenderer threaded, IRenderer renderer)
         {
         {
             ThreadedTexture texture = command._texture.Get(threaded);
             ThreadedTexture texture = command._texture.Get(threaded);
-            texture.Base.SetData(new ReadOnlySpan<byte>(command._data.Get(threaded)), command._layer, command._level);
+            texture.Base.SetData(command._data.Get(threaded), command._layer, command._level);
         }
         }
     }
     }
 }
 }

+ 4 - 4
src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs

@@ -1,6 +1,6 @@
 using Ryujinx.Graphics.GAL.Multithreading.Model;
 using Ryujinx.Graphics.GAL.Multithreading.Model;
 using Ryujinx.Graphics.GAL.Multithreading.Resources;
 using Ryujinx.Graphics.GAL.Multithreading.Resources;
-using System;
+using System.Buffers;
 
 
 namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 {
 {
@@ -8,12 +8,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
     {
     {
         public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion;
         public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion;
         private TableRef<ThreadedTexture> _texture;
         private TableRef<ThreadedTexture> _texture;
-        private TableRef<byte[]> _data;
+        private TableRef<IMemoryOwner<byte>> _data;
         private int _layer;
         private int _layer;
         private int _level;
         private int _level;
         private Rectangle<int> _region;
         private Rectangle<int> _region;
 
 
-        public void Set(TableRef<ThreadedTexture> texture, TableRef<byte[]> data, int layer, int level, Rectangle<int> region)
+        public void Set(TableRef<ThreadedTexture> texture, TableRef<IMemoryOwner<byte>> data, int layer, int level, Rectangle<int> region)
         {
         {
             _texture = texture;
             _texture = texture;
             _data = data;
             _data = data;
@@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
         public static void Run(ref TextureSetDataSliceRegionCommand command, ThreadedRenderer threaded, IRenderer renderer)
         public static void Run(ref TextureSetDataSliceRegionCommand command, ThreadedRenderer threaded, IRenderer renderer)
         {
         {
             ThreadedTexture texture = command._texture.Get(threaded);
             ThreadedTexture texture = command._texture.Get(threaded);
-            texture.Base.SetData(new ReadOnlySpan<byte>(command._data.Get(threaded)), command._layer, command._level, command._region);
+            texture.Base.SetData(command._data.Get(threaded), command._layer, command._level, command._region);
         }
         }
     }
     }
 }
 }

+ 10 - 7
src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs

@@ -1,6 +1,6 @@
-using Ryujinx.Common.Memory;
 using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
 using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
 using Ryujinx.Graphics.GAL.Multithreading.Model;
 using Ryujinx.Graphics.GAL.Multithreading.Model;
+using System.Buffers;
 
 
 namespace Ryujinx.Graphics.GAL.Multithreading.Resources
 namespace Ryujinx.Graphics.GAL.Multithreading.Resources
 {
 {
@@ -110,21 +110,24 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
             _renderer.QueueCommand();
             _renderer.QueueCommand();
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data)
+        /// <inheritdoc/>
+        public void SetData(IMemoryOwner<byte> data)
         {
         {
-            _renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data.ToArray()));
+            _renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data));
             _renderer.QueueCommand();
             _renderer.QueueCommand();
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data, int layer, int level)
+        /// <inheritdoc/>
+        public void SetData(IMemoryOwner<byte> data, int layer, int level)
         {
         {
-            _renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level);
+            _renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data), layer, level);
             _renderer.QueueCommand();
             _renderer.QueueCommand();
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
+        /// <inheritdoc/>
+        public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
         {
         {
-            _renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level, region);
+            _renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data), layer, level, region);
             _renderer.QueueCommand();
             _renderer.QueueCommand();
         }
         }
 
 

+ 2 - 1
src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs

@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Gpu.Engine.Threed;
 using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.Graphics.Texture;
 using Ryujinx.Graphics.Texture;
 using System;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
@@ -308,7 +309,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
 
 
                     if (target != null)
                     if (target != null)
                     {
                     {
-                        byte[] data;
+                        IMemoryOwner<byte> data;
                         if (srcLinear)
                         if (srcLinear)
                         {
                         {
                             data = LayoutConverter.ConvertLinearStridedToLinear(
                             data = LayoutConverter.ConvertLinearStridedToLinear(

+ 3 - 1
src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs

@@ -1,4 +1,5 @@
 using Ryujinx.Common;
 using Ryujinx.Common;
+using Ryujinx.Common.Memory;
 using Ryujinx.Graphics.Device;
 using Ryujinx.Graphics.Device;
 using Ryujinx.Graphics.Texture;
 using Ryujinx.Graphics.Texture;
 using System;
 using System;
@@ -198,7 +199,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
                     if (target != null)
                     if (target != null)
                     {
                     {
                         target.SynchronizeMemory();
                         target.SynchronizeMemory();
-                        target.SetData(data, 0, 0, new GAL.Rectangle<int>(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount));
+                        var dataCopy = ByteMemoryPool.RentCopy(data);
+                        target.SetData(dataCopy, 0, 0, new GAL.Rectangle<int>(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount));
                         target.SignalModified();
                         target.SignalModified();
 
 
                         return;
                         return;

+ 109 - 60
src/Ryujinx.Graphics.Gpu/Image/Texture.cs

@@ -1,5 +1,4 @@
 using Ryujinx.Common.Logging;
 using Ryujinx.Common.Logging;
-using Ryujinx.Common.Memory;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.Graphics.Texture;
 using Ryujinx.Graphics.Texture;
@@ -7,6 +6,7 @@ using Ryujinx.Graphics.Texture.Astc;
 using Ryujinx.Memory;
 using Ryujinx.Memory;
 using Ryujinx.Memory.Range;
 using Ryujinx.Memory.Range;
 using System;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Linq;
 using System.Linq;
@@ -661,7 +661,7 @@ namespace Ryujinx.Graphics.Gpu.Image
                 }
                 }
             }
             }
 
 
-            SpanOrArray<byte> result = ConvertToHostCompatibleFormat(data);
+            IMemoryOwner<byte> result = ConvertToHostCompatibleFormat(data);
 
 
             if (ScaleFactor != 1f && AllowScaledSetData())
             if (ScaleFactor != 1f && AllowScaledSetData())
             {
             {
@@ -684,7 +684,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// Uploads new texture data to the host GPU.
         /// Uploads new texture data to the host GPU.
         /// </summary>
         /// </summary>
         /// <param name="data">New data</param>
         /// <param name="data">New data</param>
-        public void SetData(SpanOrArray<byte> data)
+        public void SetData(IMemoryOwner<byte> data)
         {
         {
             BlacklistScale();
             BlacklistScale();
 
 
@@ -703,7 +703,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// <param name="data">New data</param>
         /// <param name="data">New data</param>
         /// <param name="layer">Target layer</param>
         /// <param name="layer">Target layer</param>
         /// <param name="level">Target level</param>
         /// <param name="level">Target level</param>
-        public void SetData(SpanOrArray<byte> data, int layer, int level)
+        public void SetData(IMemoryOwner<byte> data, int layer, int level)
         {
         {
             BlacklistScale();
             BlacklistScale();
 
 
@@ -721,7 +721,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// <param name="layer">Target layer</param>
         /// <param name="layer">Target layer</param>
         /// <param name="level">Target level</param>
         /// <param name="level">Target level</param>
         /// <param name="region">Target sub-region of the texture to update</param>
         /// <param name="region">Target sub-region of the texture to update</param>
-        public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
+        public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
         {
         {
             BlacklistScale();
             BlacklistScale();
 
 
@@ -739,7 +739,7 @@ namespace Ryujinx.Graphics.Gpu.Image
         /// <param name="level">Mip level to convert</param>
         /// <param name="level">Mip level to convert</param>
         /// <param name="single">True to convert a single slice</param>
         /// <param name="single">True to convert a single slice</param>
         /// <returns>Converted data</returns>
         /// <returns>Converted data</returns>
-        public SpanOrArray<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
+        public IMemoryOwner<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
         {
         {
             int width = Info.Width;
             int width = Info.Width;
             int height = Info.Height;
             int height = Info.Height;
@@ -754,11 +754,11 @@ namespace Ryujinx.Graphics.Gpu.Image
 
 
             int sliceDepth = single ? 1 : depth;
             int sliceDepth = single ? 1 : depth;
 
 
-            SpanOrArray<byte> result;
+            IMemoryOwner<byte> linear;
 
 
             if (Info.IsLinear)
             if (Info.IsLinear)
             {
             {
-                result = LayoutConverter.ConvertLinearStridedToLinear(
+                linear = LayoutConverter.ConvertLinearStridedToLinear(
                     width,
                     width,
                     height,
                     height,
                     Info.FormatInfo.BlockWidth,
                     Info.FormatInfo.BlockWidth,
@@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Gpu.Image
             }
             }
             else
             else
             {
             {
-                result = LayoutConverter.ConvertBlockLinearToLinear(
+                linear = LayoutConverter.ConvertBlockLinearToLinear(
                     width,
                     width,
                     height,
                     height,
                     depth,
                     depth,
@@ -787,33 +787,41 @@ namespace Ryujinx.Graphics.Gpu.Image
                     data);
                     data);
             }
             }
 
 
+            IMemoryOwner<byte> result = linear;
+
             // Handle compressed cases not supported by the host:
             // Handle compressed cases not supported by the host:
             // - ASTC is usually not supported on desktop cards.
             // - ASTC is usually not supported on desktop cards.
             // - BC4/BC5 is not supported on 3D textures.
             // - BC4/BC5 is not supported on 3D textures.
             if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc())
             if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc())
             {
             {
-                if (!AstcDecoder.TryDecodeToRgba8P(
-                    result.ToArray(),
-                    Info.FormatInfo.BlockWidth,
-                    Info.FormatInfo.BlockHeight,
-                    width,
-                    height,
-                    sliceDepth,
-                    levels,
-                    layers,
-                    out byte[] decoded))
+                using (result)
                 {
                 {
-                    string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";
+                    if (!AstcDecoder.TryDecodeToRgba8P(
+                        result.Memory,
+                        Info.FormatInfo.BlockWidth,
+                        Info.FormatInfo.BlockHeight,
+                        width,
+                        height,
+                        sliceDepth,
+                        levels,
+                        layers,
+                        out IMemoryOwner<byte> decoded))
+                    {
+                        string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";
 
 
-                    Logger.Debug?.Print(LogClass.Gpu, $"Invalid ASTC texture at 0x{Info.GpuAddress:X} ({texInfo}).");
-                }
+                        Logger.Debug?.Print(LogClass.Gpu, $"Invalid ASTC texture at 0x{Info.GpuAddress:X} ({texInfo}).");
+                    }
 
 
-                if (GraphicsConfig.EnableTextureRecompression)
-                {
-                    decoded = BCnEncoder.EncodeBC7(decoded, width, height, sliceDepth, levels, layers);
-                }
+                    if (GraphicsConfig.EnableTextureRecompression)
+                    {
+                        using (decoded)
+                        {
+                            return BCnEncoder.EncodeBC7(decoded.Memory, width, height, sliceDepth, levels, layers);
+                        }
+                    }
 
 
-                result = decoded;
+                    return decoded;
+                }
             }
             }
             else if (!_context.Capabilities.SupportsEtc2Compression && Format.IsEtc2())
             else if (!_context.Capabilities.SupportsEtc2Compression && Format.IsEtc2())
             {
             {
@@ -821,16 +829,22 @@ namespace Ryujinx.Graphics.Gpu.Image
                 {
                 {
                     case Format.Etc2RgbaSrgb:
                     case Format.Etc2RgbaSrgb:
                     case Format.Etc2RgbaUnorm:
                     case Format.Etc2RgbaUnorm:
-                        result = ETC2Decoder.DecodeRgba(result, width, height, sliceDepth, levels, layers);
-                        break;
+                        using (result)
+                        {
+                            return ETC2Decoder.DecodeRgba(result.Memory.Span, width, height, sliceDepth, levels, layers);
+                        }
                     case Format.Etc2RgbPtaSrgb:
                     case Format.Etc2RgbPtaSrgb:
                     case Format.Etc2RgbPtaUnorm:
                     case Format.Etc2RgbPtaUnorm:
-                        result = ETC2Decoder.DecodePta(result, width, height, sliceDepth, levels, layers);
-                        break;
+                        using (result)
+                        {
+                            return ETC2Decoder.DecodePta(result.Memory.Span, width, height, sliceDepth, levels, layers);
+                        }
                     case Format.Etc2RgbSrgb:
                     case Format.Etc2RgbSrgb:
                     case Format.Etc2RgbUnorm:
                     case Format.Etc2RgbUnorm:
-                        result = ETC2Decoder.DecodeRgb(result, width, height, sliceDepth, levels, layers);
-                        break;
+                        using (result)
+                        {
+                            return ETC2Decoder.DecodeRgb(result.Memory.Span, width, height, sliceDepth, levels, layers);
+                        }
                 }
                 }
             }
             }
             else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
             else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
@@ -839,48 +853,75 @@ namespace Ryujinx.Graphics.Gpu.Image
                 {
                 {
                     case Format.Bc1RgbaSrgb:
                     case Format.Bc1RgbaSrgb:
                     case Format.Bc1RgbaUnorm:
                     case Format.Bc1RgbaUnorm:
-                        result = BCnDecoder.DecodeBC1(result, width, height, sliceDepth, levels, layers);
-                        break;
+                        using (result)
+                        {
+                            return BCnDecoder.DecodeBC1(result.Memory.Span, width, height, sliceDepth, levels, layers);
+                        }
                     case Format.Bc2Srgb:
                     case Format.Bc2Srgb:
                     case Format.Bc2Unorm:
                     case Format.Bc2Unorm:
-                        result = BCnDecoder.DecodeBC2(result, width, height, sliceDepth, levels, layers);
-                        break;
+                        using (result)
+                        {
+                            return BCnDecoder.DecodeBC2(result.Memory.Span, width, height, sliceDepth, levels, layers);
+                        }
                     case Format.Bc3Srgb:
                     case Format.Bc3Srgb:
                     case Format.Bc3Unorm:
                     case Format.Bc3Unorm:
-                        result = BCnDecoder.DecodeBC3(result, width, height, sliceDepth, levels, layers);
-                        break;
+                        using (result)
+                        {
+                            return BCnDecoder.DecodeBC3(result.Memory.Span, width, height, sliceDepth, levels, layers);
+                        }
                     case Format.Bc4Snorm:
                     case Format.Bc4Snorm:
                     case Format.Bc4Unorm:
                     case Format.Bc4Unorm:
-                        result = BCnDecoder.DecodeBC4(result, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm);
-                        break;
+                        using (result)
+                        {
+                            return BCnDecoder.DecodeBC4(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc4Snorm);
+                        }
                     case Format.Bc5Snorm:
                     case Format.Bc5Snorm:
                     case Format.Bc5Unorm:
                     case Format.Bc5Unorm:
-                        result = BCnDecoder.DecodeBC5(result, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm);
-                        break;
+                        using (result)
+                        {
+                            return BCnDecoder.DecodeBC5(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc5Snorm);
+                        }
                     case Format.Bc6HSfloat:
                     case Format.Bc6HSfloat:
                     case Format.Bc6HUfloat:
                     case Format.Bc6HUfloat:
-                        result = BCnDecoder.DecodeBC6(result, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat);
-                        break;
+                        using (result)
+                        {
+                            return BCnDecoder.DecodeBC6(result.Memory.Span, width, height, sliceDepth, levels, layers, Format == Format.Bc6HSfloat);
+                        }
                     case Format.Bc7Srgb:
                     case Format.Bc7Srgb:
                     case Format.Bc7Unorm:
                     case Format.Bc7Unorm:
-                        result = BCnDecoder.DecodeBC7(result, width, height, sliceDepth, levels, layers);
-                        break;
+                        using (result)
+                        {
+                            return BCnDecoder.DecodeBC7(result.Memory.Span, width, height, sliceDepth, levels, layers);
+                        }
                 }
                 }
             }
             }
             else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
             else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
             {
             {
-                result = PixelConverter.ConvertR4G4ToR4G4B4A4(result, width);
-
-                if (!_context.Capabilities.SupportsR4G4B4A4Format)
+                using (result)
                 {
                 {
-                    result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
+                    var converted = PixelConverter.ConvertR4G4ToR4G4B4A4(result.Memory.Span, width);
+
+                    if (_context.Capabilities.SupportsR4G4B4A4Format)
+                    {
+                        return converted;
+                    }
+                    else
+                    {
+                        using (converted)
+                        {
+                            return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(converted.Memory.Span, width);
+                        }
+                    }
                 }
                 }
             }
             }
             else if (Format == Format.R4G4B4A4Unorm)
             else if (Format == Format.R4G4B4A4Unorm)
             {
             {
                 if (!_context.Capabilities.SupportsR4G4B4A4Format)
                 if (!_context.Capabilities.SupportsR4G4B4A4Format)
                 {
                 {
-                    result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
+                    using (result)
+                    {
+                        return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Memory.Span, width);
+                    }
                 }
                 }
             }
             }
             else if (!_context.Capabilities.Supports5BitComponentFormat && Format.Is16BitPacked())
             else if (!_context.Capabilities.Supports5BitComponentFormat && Format.Is16BitPacked())
@@ -889,19 +930,27 @@ namespace Ryujinx.Graphics.Gpu.Image
                 {
                 {
                     case Format.B5G6R5Unorm:
                     case Format.B5G6R5Unorm:
                     case Format.R5G6B5Unorm:
                     case Format.R5G6B5Unorm:
-                        result = PixelConverter.ConvertR5G6B5ToR8G8B8A8(result, width);
-                        break;
+                        using (result)
+                        {
+                            return PixelConverter.ConvertR5G6B5ToR8G8B8A8(result.Memory.Span, width);
+                        }
                     case Format.B5G5R5A1Unorm:
                     case Format.B5G5R5A1Unorm:
                     case Format.R5G5B5X1Unorm:
                     case Format.R5G5B5X1Unorm:
                     case Format.R5G5B5A1Unorm:
                     case Format.R5G5B5A1Unorm:
-                        result = PixelConverter.ConvertR5G5B5ToR8G8B8A8(result, width, Format == Format.R5G5B5X1Unorm);
-                        break;
+                        using (result)
+                        {
+                            return PixelConverter.ConvertR5G5B5ToR8G8B8A8(result.Memory.Span, width, Format == Format.R5G5B5X1Unorm);
+                        }
                     case Format.A1B5G5R5Unorm:
                     case Format.A1B5G5R5Unorm:
-                        result = PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result, width);
-                        break;
+                        using (result)
+                        {
+                            return PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result.Memory.Span, width);
+                        }
                     case Format.R4G4B4A4Unorm:
                     case Format.R4G4B4A4Unorm:
-                        result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
-                        break;
+                        using (result)
+                        {
+                            return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Memory.Span, width);
+                        }
                 }
                 }
             }
             }
 
 

+ 2 - 2
src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs

@@ -1,4 +1,3 @@
-using Ryujinx.Common.Memory;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.Graphics.Gpu.Memory;
 using Ryujinx.Graphics.Texture;
 using Ryujinx.Graphics.Texture;
@@ -6,6 +5,7 @@ using Ryujinx.Memory;
 using Ryujinx.Memory.Range;
 using Ryujinx.Memory.Range;
 using Ryujinx.Memory.Tracking;
 using Ryujinx.Memory.Tracking;
 using System;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 
 
@@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 
 
                             ReadOnlySpan<byte> data = dataSpan[(offset - spanBase)..];
                             ReadOnlySpan<byte> data = dataSpan[(offset - spanBase)..];
 
 
-                            SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
+                            IMemoryOwner<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true);
 
 
                             Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
                             Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level);
                         }
                         }

+ 5 - 3
src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs

@@ -1,6 +1,8 @@
+using Ryujinx.Common.Memory;
 using Ryujinx.Memory;
 using Ryujinx.Memory;
 using Ryujinx.Memory.Range;
 using Ryujinx.Memory.Range;
 using System;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
@@ -240,11 +242,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
             }
             }
             else
             else
             {
             {
-                Memory<byte> memory = new byte[size];
+                IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
 
 
-                GetSpan(va, size).CopyTo(memory.Span);
+                GetSpan(va, size).CopyTo(memoryOwner.Memory.Span);
 
 
-                return new WritableRegion(this, va, memory, tracked);
+                return new WritableRegion(this, va, memoryOwner, tracked);
             }
             }
         }
         }
 
 

+ 6 - 2
src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs

@@ -1,3 +1,4 @@
+using Ryujinx.Common.Memory;
 using Ryujinx.Cpu;
 using Ryujinx.Cpu;
 using Ryujinx.Graphics.Device;
 using Ryujinx.Graphics.Device;
 using Ryujinx.Graphics.Gpu.Image;
 using Ryujinx.Graphics.Gpu.Image;
@@ -6,6 +7,7 @@ using Ryujinx.Memory;
 using Ryujinx.Memory.Range;
 using Ryujinx.Memory.Range;
 using Ryujinx.Memory.Tracking;
 using Ryujinx.Memory.Tracking;
 using System;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
@@ -190,7 +192,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
             }
             }
             else
             else
             {
             {
-                Memory<byte> memory = new byte[range.GetSize()];
+                IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(range.GetSize());
+
+                Memory<byte> memory = memoryOwner.Memory;
 
 
                 int offset = 0;
                 int offset = 0;
                 for (int i = 0; i < range.Count; i++)
                 for (int i = 0; i < range.Count; i++)
@@ -204,7 +208,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
                     offset += size;
                     offset += size;
                 }
                 }
 
 
-                return new WritableRegion(new MultiRangeWritableBlock(range, this), 0, memory, tracked);
+                return new WritableRegion(new MultiRangeWritableBlock(range, this), 0, memoryOwner, tracked);
             }
             }
         }
         }
 
 

+ 4 - 3
src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs

@@ -33,7 +33,8 @@ namespace Ryujinx.Graphics.OpenGL.Effects.Smaa
 
 
         public int Quality
         public int Quality
         {
         {
-            get => _quality; set
+            get => _quality;
+            set
             {
             {
                 _quality = Math.Clamp(value, 0, _qualities.Length - 1);
                 _quality = Math.Clamp(value, 0, _qualities.Length - 1);
             }
             }
@@ -150,8 +151,8 @@ namespace Ryujinx.Graphics.OpenGL.Effects.Smaa
             _areaTexture = new TextureStorage(_renderer, areaInfo);
             _areaTexture = new TextureStorage(_renderer, areaInfo);
             _searchTexture = new TextureStorage(_renderer, searchInfo);
             _searchTexture = new TextureStorage(_renderer, searchInfo);
 
 
-            var areaTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin");
-            var searchTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin");
+            var areaTexture = EmbeddedResources.ReadFileToRentedMemory("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin");
+            var searchTexture = EmbeddedResources.ReadFileToRentedMemory("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin");
 
 
             var areaView = _areaTexture.CreateDefaultView();
             var areaView = _areaTexture.CreateDefaultView();
             var searchView = _searchTexture.CreateDefaultView();
             var searchView = _searchTexture.CreateDefaultView();

+ 7 - 3
src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs

@@ -1,4 +1,6 @@
+using Ryujinx.Common.Memory;
 using System;
 using System;
+using System.Buffers;
 using System.Numerics;
 using System.Numerics;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 using System.Runtime.Intrinsics;
 using System.Runtime.Intrinsics;
@@ -8,9 +10,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
 {
 {
     static class FormatConverter
     static class FormatConverter
     {
     {
-        public unsafe static byte[] ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
+        public unsafe static IMemoryOwner<byte> ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
         {
         {
-            byte[] output = new byte[data.Length];
+            IMemoryOwner<byte> outputMemory = ByteMemoryPool.Rent(data.Length);
+
+            Span<byte> output = outputMemory.Memory.Span;
 
 
             int start = 0;
             int start = 0;
 
 
@@ -74,7 +78,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
                 outSpan[i] = BitOperations.RotateLeft(dataSpan[i], 8);
                 outSpan[i] = BitOperations.RotateLeft(dataSpan[i], 8);
             }
             }
 
 
-            return output;
+            return outputMemory;
         }
         }
 
 
         public unsafe static byte[] ConvertD24S8ToS8D24(ReadOnlySpan<byte> data)
         public unsafe static byte[] ConvertD24S8ToS8D24(ReadOnlySpan<byte> data)

+ 10 - 5
src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs

@@ -1,7 +1,7 @@
 using OpenTK.Graphics.OpenGL;
 using OpenTK.Graphics.OpenGL;
-using Ryujinx.Common.Memory;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.GAL;
 using System;
 using System;
+using System.Buffers;
 
 
 namespace Ryujinx.Graphics.OpenGL.Image
 namespace Ryujinx.Graphics.OpenGL.Image
 {
 {
@@ -54,19 +54,24 @@ namespace Ryujinx.Graphics.OpenGL.Image
             throw new NotImplementedException();
             throw new NotImplementedException();
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data)
+        /// <inheritdoc/>
+        public void SetData(IMemoryOwner<byte> data)
         {
         {
-            var dataSpan = data.AsSpan();
+            var dataSpan = data.Memory.Span;
 
 
             Buffer.SetData(_buffer, _bufferOffset, dataSpan[..Math.Min(dataSpan.Length, _bufferSize)]);
             Buffer.SetData(_buffer, _bufferOffset, dataSpan[..Math.Min(dataSpan.Length, _bufferSize)]);
+
+            data.Dispose();
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data, int layer, int level)
+        /// <inheritdoc/>
+        public void SetData(IMemoryOwner<byte> data, int layer, int level)
         {
         {
             throw new NotSupportedException();
             throw new NotSupportedException();
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
+        /// <inheritdoc/>
+        public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
         {
         {
             throw new NotSupportedException();
             throw new NotSupportedException();
         }
         }

+ 48 - 46
src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs

@@ -1,8 +1,8 @@
 using OpenTK.Graphics.OpenGL;
 using OpenTK.Graphics.OpenGL;
 using Ryujinx.Common;
 using Ryujinx.Common;
-using Ryujinx.Common.Memory;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.GAL;
 using System;
 using System;
+using System.Buffers;
 using System.Diagnostics;
 using System.Diagnostics;
 
 
 namespace Ryujinx.Graphics.OpenGL.Image
 namespace Ryujinx.Graphics.OpenGL.Image
@@ -448,70 +448,59 @@ namespace Ryujinx.Graphics.OpenGL.Image
             }
             }
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data)
+        public void SetData(IMemoryOwner<byte> data)
         {
         {
-            var dataSpan = data.AsSpan();
-
-            if (Format == Format.S8UintD24Unorm)
-            {
-                dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
-            }
-
-            unsafe
+            using (data = EnsureDataFormat(data))
             {
             {
-                fixed (byte* ptr = dataSpan)
+                unsafe
                 {
                 {
-                    ReadFrom((IntPtr)ptr, dataSpan.Length);
+                    var dataSpan = data.Memory.Span;
+                    fixed (byte* ptr = dataSpan)
+                    {
+                        ReadFrom((IntPtr)ptr, dataSpan.Length);
+                    }
                 }
                 }
             }
             }
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data, int layer, int level)
+        public void SetData(IMemoryOwner<byte> data, int layer, int level)
         {
         {
-            var dataSpan = data.AsSpan();
-
-            if (Format == Format.S8UintD24Unorm)
-            {
-                dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
-            }
-
-            unsafe
+            using (data = EnsureDataFormat(data))
             {
             {
-                fixed (byte* ptr = dataSpan)
+                unsafe
                 {
                 {
-                    int width = Math.Max(Info.Width >> level, 1);
-                    int height = Math.Max(Info.Height >> level, 1);
+                    fixed (byte* ptr = data.Memory.Span)
+                    {
+                        int width = Math.Max(Info.Width >> level, 1);
+                        int height = Math.Max(Info.Height >> level, 1);
 
 
-                    ReadFrom2D((IntPtr)ptr, layer, level, 0, 0, width, height);
+                        ReadFrom2D((IntPtr)ptr, layer, level, 0, 0, width, height);
+                    }
                 }
                 }
             }
             }
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
+        public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
         {
         {
-            var dataSpan = data.AsSpan();
-
-            if (Format == Format.S8UintD24Unorm)
+            using (data = EnsureDataFormat(data))
             {
             {
-                dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
-            }
-
-            int wInBlocks = BitUtils.DivRoundUp(region.Width, Info.BlockWidth);
-            int hInBlocks = BitUtils.DivRoundUp(region.Height, Info.BlockHeight);
+                int wInBlocks = BitUtils.DivRoundUp(region.Width, Info.BlockWidth);
+                int hInBlocks = BitUtils.DivRoundUp(region.Height, Info.BlockHeight);
 
 
-            unsafe
-            {
-                fixed (byte* ptr = dataSpan)
+                unsafe
                 {
                 {
-                    ReadFrom2D(
-                        (IntPtr)ptr,
-                        layer,
-                        level,
-                        region.X,
-                        region.Y,
-                        region.Width,
-                        region.Height,
-                        BitUtils.AlignUp(wInBlocks * Info.BytesPerPixel, 4) * hInBlocks);
+                    fixed (byte* ptr = data.Memory.Span)
+                    {
+                        ReadFrom2D(
+                            (IntPtr)ptr,
+                            layer,
+                            level,
+                            region.X,
+                            region.Y,
+                            region.Width,
+                            region.Height,
+                            BitUtils.AlignUp(wInBlocks * Info.BytesPerPixel, 4) * hInBlocks);
+                    }
                 }
                 }
             }
             }
         }
         }
@@ -533,6 +522,19 @@ namespace Ryujinx.Graphics.OpenGL.Image
             ReadFrom2D(data, layer, level, x, y, width, height, mipSize);
             ReadFrom2D(data, layer, level, x, y, width, height, mipSize);
         }
         }
 
 
+        private IMemoryOwner<byte> EnsureDataFormat(IMemoryOwner<byte> data)
+        {
+            if (Format == Format.S8UintD24Unorm)
+            {
+                using (data)
+                {
+                    return FormatConverter.ConvertS8D24ToD24S8(data.Memory.Span);
+                }
+            }
+
+            return data;
+        }
+
         private void ReadFrom2D(IntPtr data, int layer, int level, int x, int y, int width, int height, int mipSize)
         private void ReadFrom2D(IntPtr data, int layer, int level, int x, int y, int width, int height, int mipSize)
         {
         {
             TextureTarget target = Target.Convert();
             TextureTarget target = Target.Convert();

+ 5 - 5
src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs

@@ -1,5 +1,7 @@
+using Ryujinx.Common.Memory;
 using Ryujinx.Common.Utilities;
 using Ryujinx.Common.Utilities;
 using System;
 using System;
+using System.Buffers;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Linq;
 using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
@@ -291,16 +293,14 @@ namespace Ryujinx.Graphics.Texture.Astc
             int depth,
             int depth,
             int levels,
             int levels,
             int layers,
             int layers,
-            out byte[] decoded)
+            out IMemoryOwner<byte> decoded)
         {
         {
-            byte[] output = new byte[QueryDecompressedSize(width, height, depth, levels, layers)];
+            decoded = ByteMemoryPool.Rent(QueryDecompressedSize(width, height, depth, levels, layers));
 
 
-            AstcDecoder decoder = new(data, output, blockWidth, blockHeight, width, height, depth, levels, layers);
+            AstcDecoder decoder = new(data, decoded.Memory, blockWidth, blockHeight, width, height, depth, levels, layers);
 
 
             Enumerable.Range(0, decoder.TotalBlockCount).AsParallel().ForAll(x => decoder.ProcessBlock(x));
             Enumerable.Range(0, decoder.TotalBlockCount).AsParallel().ForAll(x => decoder.ProcessBlock(x));
 
 
-            decoded = output;
-
             return decoder.Success;
             return decoder.Success;
         }
         }
 
 

+ 25 - 21
src/Ryujinx.Graphics.Texture/BCnDecoder.cs

@@ -1,5 +1,7 @@
 using Ryujinx.Common;
 using Ryujinx.Common;
+using Ryujinx.Common.Memory;
 using System;
 using System;
+using System.Buffers;
 using System.Buffers.Binary;
 using System.Buffers.Binary;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 using System.Runtime.Intrinsics;
 using System.Runtime.Intrinsics;
@@ -12,7 +14,7 @@ namespace Ryujinx.Graphics.Texture
         private const int BlockWidth = 4;
         private const int BlockWidth = 4;
         private const int BlockHeight = 4;
         private const int BlockHeight = 4;
 
 
-        public static byte[] DecodeBC1(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
+        public static IMemoryOwner<byte> DecodeBC1(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
         {
         {
             int size = 0;
             int size = 0;
 
 
@@ -21,12 +23,12 @@ namespace Ryujinx.Graphics.Texture
                 size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
                 size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
             }
             }
 
 
-            byte[] output = new byte[size];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
 
 
             Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
             Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
 
 
             Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
             Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
-            Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output);
+            Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
 
 
             Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
             Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
 
 
@@ -100,7 +102,7 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public static byte[] DecodeBC2(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
+        public static IMemoryOwner<byte> DecodeBC2(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
         {
         {
             int size = 0;
             int size = 0;
 
 
@@ -109,12 +111,12 @@ namespace Ryujinx.Graphics.Texture
                 size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
                 size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
             }
             }
 
 
-            byte[] output = new byte[size];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
 
 
             Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
             Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
 
 
             Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
             Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
-            Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output);
+            Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
 
 
             Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
             Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
 
 
@@ -195,7 +197,7 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public static byte[] DecodeBC3(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
+        public static IMemoryOwner<byte> DecodeBC3(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
         {
         {
             int size = 0;
             int size = 0;
 
 
@@ -204,13 +206,13 @@ namespace Ryujinx.Graphics.Texture
                 size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
                 size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
             }
             }
 
 
-            byte[] output = new byte[size];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
 
 
             Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
             Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
             Span<byte> rPal = stackalloc byte[8];
             Span<byte> rPal = stackalloc byte[8];
 
 
             Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
             Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
-            Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output);
+            Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
 
 
             Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
             Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
 
 
@@ -292,7 +294,7 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public static byte[] DecodeBC4(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
+        public static IMemoryOwner<byte> DecodeBC4(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
         {
         {
             int size = 0;
             int size = 0;
 
 
@@ -304,8 +306,8 @@ namespace Ryujinx.Graphics.Texture
             // Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
             // Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
             int alignedWidth = BitUtils.AlignUp(width, 4);
             int alignedWidth = BitUtils.AlignUp(width, 4);
 
 
-            byte[] output = new byte[size];
-            Span<byte> outputSpan = new(output);
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
+            Span<byte> outputSpan = output.Memory.Span;
 
 
             ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
             ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
 
 
@@ -400,7 +402,7 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public static byte[] DecodeBC5(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
+        public static IMemoryOwner<byte> DecodeBC5(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
         {
         {
             int size = 0;
             int size = 0;
 
 
@@ -412,7 +414,7 @@ namespace Ryujinx.Graphics.Texture
             // Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
             // Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
             int alignedWidth = BitUtils.AlignUp(width, 2);
             int alignedWidth = BitUtils.AlignUp(width, 2);
 
 
-            byte[] output = new byte[size];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
 
 
             ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
             ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
 
 
@@ -421,7 +423,7 @@ namespace Ryujinx.Graphics.Texture
             Span<byte> rPal = stackalloc byte[8];
             Span<byte> rPal = stackalloc byte[8];
             Span<byte> gPal = stackalloc byte[8];
             Span<byte> gPal = stackalloc byte[8];
 
 
-            Span<ushort> outputAsUshort = MemoryMarshal.Cast<byte, ushort>(output);
+            Span<ushort> outputAsUshort = MemoryMarshal.Cast<byte, ushort>(output.Memory.Span);
 
 
             Span<uint> rTileAsUint = MemoryMarshal.Cast<byte, uint>(rTile);
             Span<uint> rTileAsUint = MemoryMarshal.Cast<byte, uint>(rTile);
             Span<uint> gTileAsUint = MemoryMarshal.Cast<byte, uint>(gTile);
             Span<uint> gTileAsUint = MemoryMarshal.Cast<byte, uint>(gTile);
@@ -525,7 +527,7 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public static byte[] DecodeBC6(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
+        public static IMemoryOwner<byte> DecodeBC6(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
         {
         {
             int size = 0;
             int size = 0;
 
 
@@ -534,7 +536,8 @@ namespace Ryujinx.Graphics.Texture
                 size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 8;
                 size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 8;
             }
             }
 
 
-            byte[] output = new byte[size];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
+            Span<byte> outputSpan = output.Memory.Span;
 
 
             int inputOffset = 0;
             int inputOffset = 0;
             int outputOffset = 0;
             int outputOffset = 0;
@@ -548,7 +551,7 @@ namespace Ryujinx.Graphics.Texture
                 {
                 {
                     for (int z = 0; z < depth; z++)
                     for (int z = 0; z < depth; z++)
                     {
                     {
-                        BC6Decoder.Decode(output.AsSpan()[outputOffset..], data[inputOffset..], width, height, signed);
+                        BC6Decoder.Decode(outputSpan[outputOffset..], data[inputOffset..], width, height, signed);
 
 
                         inputOffset += w * h * 16;
                         inputOffset += w * h * 16;
                         outputOffset += width * height * 8;
                         outputOffset += width * height * 8;
@@ -563,7 +566,7 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public static byte[] DecodeBC7(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
+        public static IMemoryOwner<byte> DecodeBC7(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
         {
         {
             int size = 0;
             int size = 0;
 
 
@@ -572,7 +575,8 @@ namespace Ryujinx.Graphics.Texture
                 size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
                 size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
             }
             }
 
 
-            byte[] output = new byte[size];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
+            Span<byte> outputSpan = output.Memory.Span;
 
 
             int inputOffset = 0;
             int inputOffset = 0;
             int outputOffset = 0;
             int outputOffset = 0;
@@ -586,7 +590,7 @@ namespace Ryujinx.Graphics.Texture
                 {
                 {
                     for (int z = 0; z < depth; z++)
                     for (int z = 0; z < depth; z++)
                     {
                     {
-                        BC7Decoder.Decode(output.AsSpan()[outputOffset..], data[inputOffset..], width, height);
+                        BC7Decoder.Decode(outputSpan[outputOffset..], data[inputOffset..], width, height);
 
 
                         inputOffset += w * h * 16;
                         inputOffset += w * h * 16;
                         outputOffset += width * height * 4;
                         outputOffset += width * height * 4;

+ 7 - 4
src/Ryujinx.Graphics.Texture/BCnEncoder.cs

@@ -1,6 +1,8 @@
 using Ryujinx.Common;
 using Ryujinx.Common;
+using Ryujinx.Common.Memory;
 using Ryujinx.Graphics.Texture.Encoders;
 using Ryujinx.Graphics.Texture.Encoders;
 using System;
 using System;
+using System.Buffers;
 
 
 namespace Ryujinx.Graphics.Texture
 namespace Ryujinx.Graphics.Texture
 {
 {
@@ -9,7 +11,7 @@ namespace Ryujinx.Graphics.Texture
         private const int BlockWidth = 4;
         private const int BlockWidth = 4;
         private const int BlockHeight = 4;
         private const int BlockHeight = 4;
 
 
-        public static byte[] EncodeBC7(byte[] data, int width, int height, int depth, int levels, int layers)
+        public static IMemoryOwner<byte> EncodeBC7(Memory<byte> data, int width, int height, int depth, int levels, int layers)
         {
         {
             int size = 0;
             int size = 0;
 
 
@@ -21,7 +23,8 @@ namespace Ryujinx.Graphics.Texture
                 size += w * h * 16 * Math.Max(1, depth >> l) * layers;
                 size += w * h * 16 * Math.Max(1, depth >> l) * layers;
             }
             }
 
 
-            byte[] output = new byte[size];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
+            Memory<byte> outputMemory = output.Memory;
 
 
             int imageBaseIOffs = 0;
             int imageBaseIOffs = 0;
             int imageBaseOOffs = 0;
             int imageBaseOOffs = 0;
@@ -36,8 +39,8 @@ namespace Ryujinx.Graphics.Texture
                     for (int z = 0; z < depth; z++)
                     for (int z = 0; z < depth; z++)
                     {
                     {
                         BC7Encoder.Encode(
                         BC7Encoder.Encode(
-                            output.AsMemory()[imageBaseOOffs..],
-                            data.AsMemory()[imageBaseIOffs..],
+                            outputMemory[imageBaseOOffs..],
+                            data[imageBaseIOffs..],
                             width,
                             width,
                             height,
                             height,
                             EncodeMode.Fast | EncodeMode.Multithreaded);
                             EncodeMode.Fast | EncodeMode.Multithreaded);

+ 11 - 9
src/Ryujinx.Graphics.Texture/ETC2Decoder.cs

@@ -1,5 +1,7 @@
 using Ryujinx.Common;
 using Ryujinx.Common;
+using Ryujinx.Common.Memory;
 using System;
 using System;
+using System.Buffers;
 using System.Buffers.Binary;
 using System.Buffers.Binary;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 
 
@@ -49,15 +51,15 @@ namespace Ryujinx.Graphics.Texture
             new int[] { -3, -5, -7, -9, 2, 4, 6, 8 },
             new int[] { -3, -5, -7, -9, 2, 4, 6, 8 },
         };
         };
 
 
-        public static byte[] DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
+        public static IMemoryOwner<byte> DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
         {
         {
             ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
             ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
 
 
             int inputOffset = 0;
             int inputOffset = 0;
 
 
-            byte[] output = new byte[CalculateOutputSize(width, height, depth, levels, layers)];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers));
 
 
-            Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
+            Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
             Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
             Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
 
 
             int imageBaseOOffs = 0;
             int imageBaseOOffs = 0;
@@ -111,15 +113,15 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public static byte[] DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
+        public static IMemoryOwner<byte> DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
         {
         {
             ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
             ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
 
 
             int inputOffset = 0;
             int inputOffset = 0;
 
 
-            byte[] output = new byte[CalculateOutputSize(width, height, depth, levels, layers)];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers));
 
 
-            Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
+            Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
             Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
             Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
 
 
             int imageBaseOOffs = 0;
             int imageBaseOOffs = 0;
@@ -168,15 +170,15 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public static byte[] DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
+        public static IMemoryOwner<byte> DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
         {
         {
             ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
             ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
 
 
             int inputOffset = 0;
             int inputOffset = 0;
 
 
-            byte[] output = new byte[CalculateOutputSize(width, height, depth, levels, layers)];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers));
 
 
-            Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
+            Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
             Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
             Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
 
 
             int imageBaseOOffs = 0;
             int imageBaseOOffs = 0;

+ 9 - 6
src/Ryujinx.Graphics.Texture/LayoutConverter.cs

@@ -1,5 +1,7 @@
 using Ryujinx.Common;
 using Ryujinx.Common;
+using Ryujinx.Common.Memory;
 using System;
 using System;
+using System.Buffers;
 using System.Runtime.Intrinsics;
 using System.Runtime.Intrinsics;
 using static Ryujinx.Graphics.Texture.BlockLinearConstants;
 using static Ryujinx.Graphics.Texture.BlockLinearConstants;
 
 
@@ -93,7 +95,7 @@ namespace Ryujinx.Graphics.Texture
             };
             };
         }
         }
 
 
-        public static byte[] ConvertBlockLinearToLinear(
+        public static IMemoryOwner<byte> ConvertBlockLinearToLinear(
             int width,
             int width,
             int height,
             int height,
             int depth,
             int depth,
@@ -119,7 +121,8 @@ namespace Ryujinx.Graphics.Texture
                 blockHeight,
                 blockHeight,
                 bytesPerPixel);
                 bytesPerPixel);
 
 
-            byte[] output = new byte[outSize];
+            IMemoryOwner<byte> outputOwner = ByteMemoryPool.Rent(outSize);
+            Span<byte> output = outputOwner.Memory.Span;
 
 
             int outOffs = 0;
             int outOffs = 0;
 
 
@@ -243,10 +246,10 @@ namespace Ryujinx.Graphics.Texture
                     _ => throw new NotSupportedException($"Unable to convert ${bytesPerPixel} bpp pixel format."),
                     _ => throw new NotSupportedException($"Unable to convert ${bytesPerPixel} bpp pixel format."),
                 };
                 };
             }
             }
-            return output;
+            return outputOwner;
         }
         }
 
 
-        public static byte[] ConvertLinearStridedToLinear(
+        public static IMemoryOwner<byte> ConvertLinearStridedToLinear(
             int width,
             int width,
             int height,
             int height,
             int blockWidth,
             int blockWidth,
@@ -262,8 +265,8 @@ namespace Ryujinx.Graphics.Texture
             int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
             int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
             lineSize = Math.Min(lineSize, outStride);
             lineSize = Math.Min(lineSize, outStride);
 
 
-            byte[] output = new byte[h * outStride];
-            Span<byte> outSpan = output;
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(h * outStride);
+            Span<byte> outSpan = output.Memory.Span;
 
 
             int outOffs = 0;
             int outOffs = 0;
             int inOffs = 0;
             int inOffs = 0;

+ 19 - 17
src/Ryujinx.Graphics.Texture/PixelConverter.cs

@@ -1,5 +1,7 @@
 using Ryujinx.Common;
 using Ryujinx.Common;
+using Ryujinx.Common.Memory;
 using System;
 using System;
+using System.Buffers;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 using System.Runtime.Intrinsics;
 using System.Runtime.Intrinsics;
 using System.Runtime.Intrinsics.X86;
 using System.Runtime.Intrinsics.X86;
@@ -19,13 +21,13 @@ namespace Ryujinx.Graphics.Texture
             return (remainder, outRemainder, length / stride);
             return (remainder, outRemainder, length / stride);
         }
         }
 
 
-        public unsafe static byte[] ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
+        public unsafe static IMemoryOwner<byte> ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
         {
         {
-            byte[] output = new byte[data.Length * 2];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
 
 
             (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2);
             (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2);
 
 
-            Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output);
+            Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output.Memory.Span);
 
 
             if (remainder == 0)
             if (remainder == 0)
             {
             {
@@ -36,7 +38,7 @@ namespace Ryujinx.Graphics.Texture
                     int sizeTrunc = data.Length & ~7;
                     int sizeTrunc = data.Length & ~7;
                     start = sizeTrunc;
                     start = sizeTrunc;
 
 
-                    fixed (byte* inputPtr = data, outputPtr = output)
+                    fixed (byte* inputPtr = data, outputPtr = output.Memory.Span)
                     {
                     {
                         for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
                         for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
                         {
                         {
@@ -47,7 +49,7 @@ namespace Ryujinx.Graphics.Texture
 
 
                 for (int i = start; i < data.Length; i++)
                 for (int i = start; i < data.Length; i++)
                 {
                 {
-                    outputSpan[i] = (ushort)data[i];
+                    outputSpan[i] = data[i];
                 }
                 }
             }
             }
             else
             else
@@ -70,16 +72,16 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public unsafe static byte[] ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
+        public static IMemoryOwner<byte> ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
         {
         {
-            byte[] output = new byte[data.Length * 2];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
             int offset = 0;
             int offset = 0;
             int outOffset = 0;
             int outOffset = 0;
 
 
             (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
             (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
 
 
             ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
             ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
-            Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
+            Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
 
 
             for (int y = 0; y < height; y++)
             for (int y = 0; y < height; y++)
             {
             {
@@ -107,16 +109,16 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public unsafe static byte[] ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
+        public static IMemoryOwner<byte> ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
         {
         {
-            byte[] output = new byte[data.Length * 2];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
             int offset = 0;
             int offset = 0;
             int outOffset = 0;
             int outOffset = 0;
 
 
             (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
             (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
 
 
             ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
             ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
-            Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
+            Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
 
 
             for (int y = 0; y < height; y++)
             for (int y = 0; y < height; y++)
             {
             {
@@ -144,16 +146,16 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public unsafe static byte[] ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
+        public static IMemoryOwner<byte> ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
         {
         {
-            byte[] output = new byte[data.Length * 2];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
             int offset = 0;
             int offset = 0;
             int outOffset = 0;
             int outOffset = 0;
 
 
             (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
             (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
 
 
             ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
             ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
-            Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
+            Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
 
 
             for (int y = 0; y < height; y++)
             for (int y = 0; y < height; y++)
             {
             {
@@ -181,16 +183,16 @@ namespace Ryujinx.Graphics.Texture
             return output;
             return output;
         }
         }
 
 
-        public unsafe static byte[] ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
+        public static IMemoryOwner<byte> ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
         {
         {
-            byte[] output = new byte[data.Length * 2];
+            IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2);
             int offset = 0;
             int offset = 0;
             int outOffset = 0;
             int outOffset = 0;
 
 
             (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
             (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
 
 
             ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
             ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
-            Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
+            Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span);
 
 
             for (int y = 0; y < height; y++)
             for (int y = 0; y < height; y++)
             {
             {

+ 2 - 1
src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs

@@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.Shader;
 using Ryujinx.Graphics.Shader;
 using Silk.NET.Vulkan;
 using Silk.NET.Vulkan;
 using System;
 using System;
+using System.Buffers;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
 using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
@@ -216,7 +217,7 @@ namespace Ryujinx.Graphics.Vulkan
 
 
         public void Initialize()
         public void Initialize()
         {
         {
-            Span<byte> dummyTextureData = stackalloc byte[4];
+            IMemoryOwner<byte> dummyTextureData = ByteMemoryPool.RentCleared(4);
             _dummyTexture.SetData(dummyTextureData);
             _dummyTexture.SetData(dummyTextureData);
         }
         }
 
 

+ 2 - 2
src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs

@@ -174,8 +174,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
                 SwizzleComponent.Blue,
                 SwizzleComponent.Blue,
                 SwizzleComponent.Alpha);
                 SwizzleComponent.Alpha);
 
 
-            var areaTexture = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin");
-            var searchTexture = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin");
+            var areaTexture = EmbeddedResources.ReadFileToRentedMemory("Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin");
+            var searchTexture = EmbeddedResources.ReadFileToRentedMemory("Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin");
 
 
             _areaTexture = _renderer.CreateTexture(areaInfo) as TextureView;
             _areaTexture = _renderer.CreateTexture(areaInfo) as TextureView;
             _searchTexture = _renderer.CreateTexture(searchInfo) as TextureView;
             _searchTexture = _renderer.CreateTexture(searchInfo) as TextureView;

+ 9 - 5
src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs

@@ -1,7 +1,7 @@
-using Ryujinx.Common.Memory;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.GAL;
 using Silk.NET.Vulkan;
 using Silk.NET.Vulkan;
 using System;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using Format = Ryujinx.Graphics.GAL.Format;
 using Format = Ryujinx.Graphics.GAL.Format;
 using VkFormat = Silk.NET.Vulkan.Format;
 using VkFormat = Silk.NET.Vulkan.Format;
@@ -94,17 +94,21 @@ namespace Ryujinx.Graphics.Vulkan
             _bufferView = null;
             _bufferView = null;
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data)
+        /// <inheritdoc/>
+        public void SetData(IMemoryOwner<byte> data)
         {
         {
-            _gd.SetBufferData(_bufferHandle, _offset, data);
+            _gd.SetBufferData(_bufferHandle, _offset, data.Memory.Span);
+            data.Dispose();
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data, int layer, int level)
+        /// <inheritdoc/>
+        public void SetData(IMemoryOwner<byte> data, int layer, int level)
         {
         {
             throw new NotSupportedException();
             throw new NotSupportedException();
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
+        /// <inheritdoc/>
+        public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
         {
         {
             throw new NotSupportedException();
             throw new NotSupportedException();
         }
         }

+ 13 - 7
src/Ryujinx.Graphics.Vulkan/TextureView.cs

@@ -1,7 +1,7 @@
-using Ryujinx.Common.Memory;
 using Ryujinx.Graphics.GAL;
 using Ryujinx.Graphics.GAL;
 using Silk.NET.Vulkan;
 using Silk.NET.Vulkan;
 using System;
 using System;
+using System.Buffers;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Threading;
 using System.Threading;
@@ -702,19 +702,25 @@ namespace Ryujinx.Graphics.Vulkan
             return GetDataFromBuffer(result, size, result);
             return GetDataFromBuffer(result, size, result);
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data)
+        /// <inheritdoc/>
+        public void SetData(IMemoryOwner<byte> data)
         {
         {
-            SetData(data, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false);
+            SetData(data.Memory.Span, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false);
+            data.Dispose();
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data, int layer, int level)
+        /// <inheritdoc/>
+        public void SetData(IMemoryOwner<byte> data, int layer, int level)
         {
         {
-            SetData(data, layer, level, 1, 1, singleSlice: true);
+            SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true);
+            data.Dispose();
         }
         }
 
 
-        public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
+        /// <inheritdoc/>
+        public void SetData(IMemoryOwner<byte> data, int layer, int level, Rectangle<int> region)
         {
         {
-            SetData(data, layer, level, 1, 1, singleSlice: true, region);
+            SetData(data.Memory.Span, layer, level, 1, 1, singleSlice: true, region);
+            data.Dispose();
         }
         }
 
 
         private void SetData(ReadOnlySpan<byte> data, int layer, int level, int layers, int levels, bool singleSlice, Rectangle<int>? region = null)
         private void SetData(ReadOnlySpan<byte> data, int layer, int level, int layers, int levels, bool singleSlice, Rectangle<int>? region = null)