mirror of
				https://github.com/ryujinx-mirror/ryujinx.git
				synced 2025-11-04 08:18:58 -06:00 
			
		
		
		
	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
This commit is contained in:
		@@ -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();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
using Ryujinx.Common.Utilities;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
@@ -41,6 +42,22 @@ namespace Ryujinx.Common
 | 
			
		||||
            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)
 | 
			
		||||
        {
 | 
			
		||||
            using var stream = GetStream(assembly, filename);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
using Microsoft.IO;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
@@ -9,12 +11,50 @@ namespace Ryujinx.Common.Utilities
 | 
			
		||||
    {
 | 
			
		||||
        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)
 | 
			
		||||
@@ -25,5 +65,26 @@ namespace Ryujinx.Common.Utilities
 | 
			
		||||
 | 
			
		||||
            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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Memory;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
@@ -143,11 +145,11 @@ namespace Ryujinx.Graphics.Device
 | 
			
		||||
            }
 | 
			
		||||
            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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.GAL
 | 
			
		||||
{
 | 
			
		||||
@@ -17,10 +17,34 @@ namespace Ryujinx.Graphics.GAL
 | 
			
		||||
        PinnedSpan<byte> GetData();
 | 
			
		||||
        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 Release();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
 | 
			
		||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 | 
			
		||||
{
 | 
			
		||||
@@ -8,9 +8,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 | 
			
		||||
    {
 | 
			
		||||
        public readonly CommandType CommandType => CommandType.TextureSetData;
 | 
			
		||||
        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;
 | 
			
		||||
            _data = data;
 | 
			
		||||
@@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 | 
			
		||||
        public static void Run(ref TextureSetDataCommand command, ThreadedRenderer threaded, IRenderer renderer)
 | 
			
		||||
        {
 | 
			
		||||
            ThreadedTexture texture = command._texture.Get(threaded);
 | 
			
		||||
            texture.Base.SetData(new ReadOnlySpan<byte>(command._data.Get(threaded)));
 | 
			
		||||
            texture.Base.SetData(command._data.Get(threaded));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
 | 
			
		||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 | 
			
		||||
{
 | 
			
		||||
@@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 | 
			
		||||
    {
 | 
			
		||||
        public readonly CommandType CommandType => CommandType.TextureSetDataSlice;
 | 
			
		||||
        private TableRef<ThreadedTexture> _texture;
 | 
			
		||||
        private TableRef<byte[]> _data;
 | 
			
		||||
        private TableRef<IMemoryOwner<byte>> _data;
 | 
			
		||||
        private int _layer;
 | 
			
		||||
        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;
 | 
			
		||||
            _data = data;
 | 
			
		||||
@@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 | 
			
		||||
        public static void Run(ref TextureSetDataSliceCommand command, ThreadedRenderer threaded, IRenderer renderer)
 | 
			
		||||
        {
 | 
			
		||||
            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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
 | 
			
		||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 | 
			
		||||
{
 | 
			
		||||
@@ -8,12 +8,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 | 
			
		||||
    {
 | 
			
		||||
        public readonly CommandType CommandType => CommandType.TextureSetDataSliceRegion;
 | 
			
		||||
        private TableRef<ThreadedTexture> _texture;
 | 
			
		||||
        private TableRef<byte[]> _data;
 | 
			
		||||
        private TableRef<IMemoryOwner<byte>> _data;
 | 
			
		||||
        private int _layer;
 | 
			
		||||
        private int _level;
 | 
			
		||||
        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;
 | 
			
		||||
            _data = data;
 | 
			
		||||
@@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Texture
 | 
			
		||||
        public static void Run(ref TextureSetDataSliceRegionCommand command, ThreadedRenderer threaded, IRenderer renderer)
 | 
			
		||||
        {
 | 
			
		||||
            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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
 | 
			
		||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.GAL.Multithreading.Resources
 | 
			
		||||
{
 | 
			
		||||
@@ -110,21 +110,24 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
 | 
			
		||||
            _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();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Gpu.Engine.Threed;
 | 
			
		||||
using Ryujinx.Graphics.Gpu.Memory;
 | 
			
		||||
using Ryujinx.Graphics.Texture;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
@@ -308,7 +309,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
 | 
			
		||||
 | 
			
		||||
                    if (target != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        byte[] data;
 | 
			
		||||
                        IMemoryOwner<byte> data;
 | 
			
		||||
                        if (srcLinear)
 | 
			
		||||
                        {
 | 
			
		||||
                            data = LayoutConverter.ConvertLinearStridedToLinear(
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
using Ryujinx.Common;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Graphics.Device;
 | 
			
		||||
using Ryujinx.Graphics.Texture;
 | 
			
		||||
using System;
 | 
			
		||||
@@ -198,7 +199,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
 | 
			
		||||
                    if (target != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        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();
 | 
			
		||||
 | 
			
		||||
                        return;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
using Ryujinx.Common.Logging;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Graphics.GAL;
 | 
			
		||||
using Ryujinx.Graphics.Gpu.Memory;
 | 
			
		||||
using Ryujinx.Graphics.Texture;
 | 
			
		||||
@@ -7,6 +6,7 @@ using Ryujinx.Graphics.Texture.Astc;
 | 
			
		||||
using Ryujinx.Memory;
 | 
			
		||||
using Ryujinx.Memory.Range;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
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())
 | 
			
		||||
            {
 | 
			
		||||
@@ -684,7 +684,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 | 
			
		||||
        /// Uploads new texture data to the host GPU.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="data">New data</param>
 | 
			
		||||
        public void SetData(SpanOrArray<byte> data)
 | 
			
		||||
        public void SetData(IMemoryOwner<byte> data)
 | 
			
		||||
        {
 | 
			
		||||
            BlacklistScale();
 | 
			
		||||
 | 
			
		||||
@@ -703,7 +703,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 | 
			
		||||
        /// <param name="data">New data</param>
 | 
			
		||||
        /// <param name="layer">Target layer</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();
 | 
			
		||||
 | 
			
		||||
@@ -721,7 +721,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 | 
			
		||||
        /// <param name="layer">Target layer</param>
 | 
			
		||||
        /// <param name="level">Target level</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();
 | 
			
		||||
 | 
			
		||||
@@ -739,7 +739,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 | 
			
		||||
        /// <param name="level">Mip level to convert</param>
 | 
			
		||||
        /// <param name="single">True to convert a single slice</param>
 | 
			
		||||
        /// <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 height = Info.Height;
 | 
			
		||||
@@ -754,11 +754,11 @@ namespace Ryujinx.Graphics.Gpu.Image
 | 
			
		||||
 | 
			
		||||
            int sliceDepth = single ? 1 : depth;
 | 
			
		||||
 | 
			
		||||
            SpanOrArray<byte> result;
 | 
			
		||||
            IMemoryOwner<byte> linear;
 | 
			
		||||
 | 
			
		||||
            if (Info.IsLinear)
 | 
			
		||||
            {
 | 
			
		||||
                result = LayoutConverter.ConvertLinearStridedToLinear(
 | 
			
		||||
                linear = LayoutConverter.ConvertLinearStridedToLinear(
 | 
			
		||||
                    width,
 | 
			
		||||
                    height,
 | 
			
		||||
                    Info.FormatInfo.BlockWidth,
 | 
			
		||||
@@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                result = LayoutConverter.ConvertBlockLinearToLinear(
 | 
			
		||||
                linear = LayoutConverter.ConvertBlockLinearToLinear(
 | 
			
		||||
                    width,
 | 
			
		||||
                    height,
 | 
			
		||||
                    depth,
 | 
			
		||||
@@ -787,33 +787,41 @@ namespace Ryujinx.Graphics.Gpu.Image
 | 
			
		||||
                    data);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            IMemoryOwner<byte> result = linear;
 | 
			
		||||
 | 
			
		||||
            // Handle compressed cases not supported by the host:
 | 
			
		||||
            // - ASTC is usually not supported on desktop cards.
 | 
			
		||||
            // - BC4/BC5 is not supported on 3D textures.
 | 
			
		||||
            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)
 | 
			
		||||
                    {
 | 
			
		||||
                        using (decoded)
 | 
			
		||||
                        {
 | 
			
		||||
                            return BCnEncoder.EncodeBC7(decoded.Memory, width, height, sliceDepth, levels, layers);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return decoded;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (GraphicsConfig.EnableTextureRecompression)
 | 
			
		||||
                {
 | 
			
		||||
                    decoded = BCnEncoder.EncodeBC7(decoded, width, height, sliceDepth, levels, layers);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                result = decoded;
 | 
			
		||||
            }
 | 
			
		||||
            else if (!_context.Capabilities.SupportsEtc2Compression && Format.IsEtc2())
 | 
			
		||||
            {
 | 
			
		||||
@@ -821,16 +829,22 @@ namespace Ryujinx.Graphics.Gpu.Image
 | 
			
		||||
                {
 | 
			
		||||
                    case Format.Etc2RgbaSrgb:
 | 
			
		||||
                    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.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.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))
 | 
			
		||||
@@ -839,48 +853,75 @@ namespace Ryujinx.Graphics.Gpu.Image
 | 
			
		||||
                {
 | 
			
		||||
                    case Format.Bc1RgbaSrgb:
 | 
			
		||||
                    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.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.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.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.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.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.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)
 | 
			
		||||
            {
 | 
			
		||||
                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)
 | 
			
		||||
            {
 | 
			
		||||
                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())
 | 
			
		||||
@@ -889,19 +930,27 @@ namespace Ryujinx.Graphics.Gpu.Image
 | 
			
		||||
                {
 | 
			
		||||
                    case Format.B5G6R5Unorm:
 | 
			
		||||
                    case Format.R5G6B5Unorm:
 | 
			
		||||
                        result = PixelConverter.ConvertR5G6B5ToR8G8B8A8(result, width);
 | 
			
		||||
                        break;
 | 
			
		||||
                        using (result)
 | 
			
		||||
                        {
 | 
			
		||||
                            return PixelConverter.ConvertR5G6B5ToR8G8B8A8(result.Memory.Span, width);
 | 
			
		||||
                        }
 | 
			
		||||
                    case Format.B5G5R5A1Unorm:
 | 
			
		||||
                    case Format.R5G5B5X1Unorm:
 | 
			
		||||
                    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:
 | 
			
		||||
                        result = PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result, width);
 | 
			
		||||
                        break;
 | 
			
		||||
                        using (result)
 | 
			
		||||
                        {
 | 
			
		||||
                            return PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result.Memory.Span, width);
 | 
			
		||||
                        }
 | 
			
		||||
                    case Format.R4G4B4A4Unorm:
 | 
			
		||||
                        result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
 | 
			
		||||
                        break;
 | 
			
		||||
                        using (result)
 | 
			
		||||
                        {
 | 
			
		||||
                            return PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result.Memory.Span, width);
 | 
			
		||||
                        }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Graphics.GAL;
 | 
			
		||||
using Ryujinx.Graphics.Gpu.Memory;
 | 
			
		||||
using Ryujinx.Graphics.Texture;
 | 
			
		||||
@@ -6,6 +5,7 @@ using Ryujinx.Memory;
 | 
			
		||||
using Ryujinx.Memory.Range;
 | 
			
		||||
using Ryujinx.Memory.Tracking;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
 | 
			
		||||
@@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Gpu.Image
 | 
			
		||||
 | 
			
		||||
                            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);
 | 
			
		||||
                        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Memory;
 | 
			
		||||
using Ryujinx.Memory.Range;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
@@ -240,11 +242,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
 | 
			
		||||
            }
 | 
			
		||||
            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);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Cpu;
 | 
			
		||||
using Ryujinx.Graphics.Device;
 | 
			
		||||
using Ryujinx.Graphics.Gpu.Image;
 | 
			
		||||
@@ -6,6 +7,7 @@ using Ryujinx.Memory;
 | 
			
		||||
using Ryujinx.Memory.Range;
 | 
			
		||||
using Ryujinx.Memory.Tracking;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
@@ -190,7 +192,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                Memory<byte> memory = new byte[range.GetSize()];
 | 
			
		||||
                IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(range.GetSize());
 | 
			
		||||
 | 
			
		||||
                Memory<byte> memory = memoryOwner.Memory;
 | 
			
		||||
 | 
			
		||||
                int offset = 0;
 | 
			
		||||
                for (int i = 0; i < range.Count; i++)
 | 
			
		||||
@@ -204,7 +208,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
 | 
			
		||||
                    offset += size;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return new WritableRegion(new MultiRangeWritableBlock(range, this), 0, memory, tracked);
 | 
			
		||||
                return new WritableRegion(new MultiRangeWritableBlock(range, this), 0, memoryOwner, tracked);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,8 @@ namespace Ryujinx.Graphics.OpenGL.Effects.Smaa
 | 
			
		||||
 | 
			
		||||
        public int Quality
 | 
			
		||||
        {
 | 
			
		||||
            get => _quality; set
 | 
			
		||||
            get => _quality;
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                _quality = Math.Clamp(value, 0, _qualities.Length - 1);
 | 
			
		||||
            }
 | 
			
		||||
@@ -150,8 +151,8 @@ namespace Ryujinx.Graphics.OpenGL.Effects.Smaa
 | 
			
		||||
            _areaTexture = new TextureStorage(_renderer, areaInfo);
 | 
			
		||||
            _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 searchView = _searchTexture.CreateDefaultView();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,6 @@
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Numerics;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using System.Runtime.Intrinsics;
 | 
			
		||||
@@ -8,9 +10,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
 | 
			
		||||
{
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
@@ -74,7 +78,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
 | 
			
		||||
                outSpan[i] = BitOperations.RotateLeft(dataSpan[i], 8);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return output;
 | 
			
		||||
            return outputMemory;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public unsafe static byte[] ConvertD24S8ToS8D24(ReadOnlySpan<byte> data)
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
using OpenTK.Graphics.OpenGL;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Graphics.GAL;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.OpenGL.Image
 | 
			
		||||
{
 | 
			
		||||
@@ -54,19 +54,24 @@ namespace Ryujinx.Graphics.OpenGL.Image
 | 
			
		||||
            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)]);
 | 
			
		||||
 | 
			
		||||
            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();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
using OpenTK.Graphics.OpenGL;
 | 
			
		||||
using Ryujinx.Common;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Graphics.GAL;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
            using (data = EnsureDataFormat(data))
 | 
			
		||||
            {
 | 
			
		||||
                dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            unsafe
 | 
			
		||||
            {
 | 
			
		||||
                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)
 | 
			
		||||
            using (data = EnsureDataFormat(data))
 | 
			
		||||
            {
 | 
			
		||||
                dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            unsafe
 | 
			
		||||
            {
 | 
			
		||||
                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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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)
 | 
			
		||||
        {
 | 
			
		||||
            TextureTarget target = Target.Convert();
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Common.Utilities;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
@@ -291,16 +293,14 @@ namespace Ryujinx.Graphics.Texture.Astc
 | 
			
		||||
            int depth,
 | 
			
		||||
            int levels,
 | 
			
		||||
            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));
 | 
			
		||||
 | 
			
		||||
            decoded = output;
 | 
			
		||||
 | 
			
		||||
            return decoder.Success;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
using Ryujinx.Common;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Buffers.Binary;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using System.Runtime.Intrinsics;
 | 
			
		||||
@@ -12,7 +14,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
        private const int BlockWidth = 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;
 | 
			
		||||
 | 
			
		||||
@@ -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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            byte[] output = new byte[size];
 | 
			
		||||
            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
 | 
			
		||||
 | 
			
		||||
            Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
@@ -100,7 +102,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
@@ -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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            byte[] output = new byte[size];
 | 
			
		||||
            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
 | 
			
		||||
 | 
			
		||||
            Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
@@ -195,7 +197,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
@@ -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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            byte[] output = new byte[size];
 | 
			
		||||
            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
 | 
			
		||||
 | 
			
		||||
            Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
 | 
			
		||||
            Span<byte> rPal = stackalloc byte[8];
 | 
			
		||||
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
@@ -292,7 +294,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
@@ -304,8 +306,8 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            // Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
@@ -400,7 +402,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
@@ -412,7 +414,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            // Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
@@ -421,7 +423,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            Span<byte> rPal = 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> gTileAsUint = MemoryMarshal.Cast<byte, uint>(gTile);
 | 
			
		||||
@@ -525,7 +527,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
@@ -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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            byte[] output = new byte[size];
 | 
			
		||||
            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
 | 
			
		||||
            Span<byte> outputSpan = output.Memory.Span;
 | 
			
		||||
 | 
			
		||||
            int inputOffset = 0;
 | 
			
		||||
            int outputOffset = 0;
 | 
			
		||||
@@ -548,7 +551,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
                {
 | 
			
		||||
                    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;
 | 
			
		||||
                        outputOffset += width * height * 8;
 | 
			
		||||
@@ -563,7 +566,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
@@ -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;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            byte[] output = new byte[size];
 | 
			
		||||
            IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
 | 
			
		||||
            Span<byte> outputSpan = output.Memory.Span;
 | 
			
		||||
 | 
			
		||||
            int inputOffset = 0;
 | 
			
		||||
            int outputOffset = 0;
 | 
			
		||||
@@ -586,7 +590,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
                {
 | 
			
		||||
                    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;
 | 
			
		||||
                        outputOffset += width * height * 4;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
using Ryujinx.Common;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Graphics.Texture.Encoders;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Graphics.Texture
 | 
			
		||||
{
 | 
			
		||||
@@ -9,7 +11,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
        private const int BlockWidth = 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;
 | 
			
		||||
 | 
			
		||||
@@ -21,7 +23,8 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
                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 imageBaseOOffs = 0;
 | 
			
		||||
@@ -36,8 +39,8 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
                    for (int z = 0; z < depth; z++)
 | 
			
		||||
                    {
 | 
			
		||||
                        BC7Encoder.Encode(
 | 
			
		||||
                            output.AsMemory()[imageBaseOOffs..],
 | 
			
		||||
                            data.AsMemory()[imageBaseIOffs..],
 | 
			
		||||
                            outputMemory[imageBaseOOffs..],
 | 
			
		||||
                            data[imageBaseIOffs..],
 | 
			
		||||
                            width,
 | 
			
		||||
                            height,
 | 
			
		||||
                            EncodeMode.Fast | EncodeMode.Multithreaded);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
using Ryujinx.Common;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Buffers.Binary;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
@@ -49,15 +51,15 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
            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];
 | 
			
		||||
 | 
			
		||||
            int imageBaseOOffs = 0;
 | 
			
		||||
@@ -111,15 +113,15 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
            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];
 | 
			
		||||
 | 
			
		||||
            int imageBaseOOffs = 0;
 | 
			
		||||
@@ -168,15 +170,15 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
            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];
 | 
			
		||||
 | 
			
		||||
            int imageBaseOOffs = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
using Ryujinx.Common;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Runtime.Intrinsics;
 | 
			
		||||
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 height,
 | 
			
		||||
            int depth,
 | 
			
		||||
@@ -119,7 +121,8 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
                blockHeight,
 | 
			
		||||
                bytesPerPixel);
 | 
			
		||||
 | 
			
		||||
            byte[] output = new byte[outSize];
 | 
			
		||||
            IMemoryOwner<byte> outputOwner = ByteMemoryPool.Rent(outSize);
 | 
			
		||||
            Span<byte> output = outputOwner.Memory.Span;
 | 
			
		||||
 | 
			
		||||
            int outOffs = 0;
 | 
			
		||||
 | 
			
		||||
@@ -243,10 +246,10 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
                    _ => 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 height,
 | 
			
		||||
            int blockWidth,
 | 
			
		||||
@@ -262,8 +265,8 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
 | 
			
		||||
            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 inOffs = 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
using Ryujinx.Common;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using System.Runtime.Intrinsics;
 | 
			
		||||
using System.Runtime.Intrinsics.X86;
 | 
			
		||||
@@ -19,13 +21,13 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
            Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output);
 | 
			
		||||
            Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output.Memory.Span);
 | 
			
		||||
 | 
			
		||||
            if (remainder == 0)
 | 
			
		||||
            {
 | 
			
		||||
@@ -36,7 +38,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
                    int sizeTrunc = data.Length & ~7;
 | 
			
		||||
                    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)
 | 
			
		||||
                        {
 | 
			
		||||
@@ -47,7 +49,7 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
 | 
			
		||||
                for (int i = start; i < data.Length; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    outputSpan[i] = (ushort)data[i];
 | 
			
		||||
                    outputSpan[i] = data[i];
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
@@ -70,16 +72,16 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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 outOffset = 0;
 | 
			
		||||
 | 
			
		||||
            (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
 | 
			
		||||
 | 
			
		||||
            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++)
 | 
			
		||||
            {
 | 
			
		||||
@@ -107,16 +109,16 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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 outOffset = 0;
 | 
			
		||||
 | 
			
		||||
            (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
 | 
			
		||||
 | 
			
		||||
            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++)
 | 
			
		||||
            {
 | 
			
		||||
@@ -144,16 +146,16 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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 outOffset = 0;
 | 
			
		||||
 | 
			
		||||
            (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
 | 
			
		||||
 | 
			
		||||
            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++)
 | 
			
		||||
            {
 | 
			
		||||
@@ -181,16 +183,16 @@ namespace Ryujinx.Graphics.Texture
 | 
			
		||||
            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 outOffset = 0;
 | 
			
		||||
 | 
			
		||||
            (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
 | 
			
		||||
 | 
			
		||||
            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++)
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
 | 
			
		||||
using Ryujinx.Graphics.Shader;
 | 
			
		||||
using Silk.NET.Vulkan;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
 | 
			
		||||
@@ -216,7 +217,7 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
 | 
			
		||||
        public void Initialize()
 | 
			
		||||
        {
 | 
			
		||||
            Span<byte> dummyTextureData = stackalloc byte[4];
 | 
			
		||||
            IMemoryOwner<byte> dummyTextureData = ByteMemoryPool.RentCleared(4);
 | 
			
		||||
            _dummyTexture.SetData(dummyTextureData);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -174,8 +174,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
 | 
			
		||||
                SwizzleComponent.Blue,
 | 
			
		||||
                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;
 | 
			
		||||
            _searchTexture = _renderer.CreateTexture(searchInfo) as TextureView;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Graphics.GAL;
 | 
			
		||||
using Silk.NET.Vulkan;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using Format = Ryujinx.Graphics.GAL.Format;
 | 
			
		||||
using VkFormat = Silk.NET.Vulkan.Format;
 | 
			
		||||
@@ -94,17 +94,21 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            _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();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Graphics.GAL;
 | 
			
		||||
using Silk.NET.Vulkan;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
@@ -702,19 +702,25 @@ namespace Ryujinx.Graphics.Vulkan
 | 
			
		||||
            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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user