From 268c9aecf8e9181bb7114cf1dd826f00b2237714 Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Sun, 14 Apr 2024 16:06:14 -0400 Subject: [PATCH] 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 instead of SpanOrArray * Ryujinx.Graphics.Texture: change texture conversion methods to return IMemoryOwner and allocate from ByteMemoryPool * Ryujinx.Graphics.OpenGL: update ITexture and Texture-like types with SetData() methods to take IMemoryOwner instead of SpanOrArray * Ryujinx.Graphics.Vulkan: update ITexture and Texture-like types with SetData() methods to take IMemoryOwner instead of SpanOrArray * Ryujinx.Graphics.Gpu: update ITexture and Texture-like types with SetData() methods to take IMemoryOwner instead of SpanOrArray * Remove now-unused SpanOrArray * 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 --- src/Ryujinx.Common/Memory/SpanOrArray.cs | 89 --------- .../Utilities/EmbeddedResources.cs | 17 ++ src/Ryujinx.Common/Utilities/StreamUtils.cs | 67 ++++++- .../DeviceMemoryManager.cs | 8 +- src/Ryujinx.Graphics.GAL/ITexture.cs | 32 +++- .../Commands/Texture/TextureSetDataCommand.cs | 8 +- .../Texture/TextureSetDataSliceCommand.cs | 8 +- .../TextureSetDataSliceRegionCommand.cs | 8 +- .../Resources/ThreadedTexture.cs | 17 +- .../Engine/Dma/DmaClass.cs | 3 +- .../InlineToMemory/InlineToMemoryClass.cs | 4 +- src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 171 +++++++++++------- .../Image/TextureGroup.cs | 4 +- .../Memory/MemoryManager.cs | 8 +- .../Memory/PhysicalMemory.cs | 8 +- .../Effects/SmaaPostProcessingEffect.cs | 7 +- .../Image/FormatConverter.cs | 10 +- .../Image/TextureBuffer.cs | 15 +- .../Image/TextureView.cs | 94 +++++----- .../Astc/AstcDecoder.cs | 10 +- src/Ryujinx.Graphics.Texture/BCnDecoder.cs | 46 ++--- src/Ryujinx.Graphics.Texture/BCnEncoder.cs | 11 +- src/Ryujinx.Graphics.Texture/ETC2Decoder.cs | 20 +- .../LayoutConverter.cs | 15 +- .../PixelConverter.cs | 36 ++-- .../DescriptorSetUpdater.cs | 3 +- .../Effects/SmaaPostProcessingEffect.cs | 4 +- src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs | 14 +- src/Ryujinx.Graphics.Vulkan/TextureView.cs | 20 +- 29 files changed, 435 insertions(+), 322 deletions(-) delete mode 100644 src/Ryujinx.Common/Memory/SpanOrArray.cs diff --git a/src/Ryujinx.Common/Memory/SpanOrArray.cs b/src/Ryujinx.Common/Memory/SpanOrArray.cs deleted file mode 100644 index 269ac02fd..000000000 --- a/src/Ryujinx.Common/Memory/SpanOrArray.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; - -namespace Ryujinx.Common.Memory -{ - /// - /// A struct that can represent both a Span and Array. - /// This is useful to keep the Array representation when possible to avoid copies. - /// - /// Element Type - public readonly ref struct SpanOrArray where T : unmanaged - { - public readonly T[] Array; - public readonly ReadOnlySpan Span; - - /// - /// Create a new SpanOrArray from an array. - /// - /// Array to store - public SpanOrArray(T[] array) - { - Array = array; - Span = ReadOnlySpan.Empty; - } - - /// - /// Create a new SpanOrArray from a readonly span. - /// - /// Span to store - public SpanOrArray(ReadOnlySpan span) - { - Array = null; - Span = span; - } - - /// - /// Return the contained array, or convert the span if necessary. - /// - /// An array containing the data - public T[] ToArray() - { - return Array ?? Span.ToArray(); - } - - /// - /// Return a ReadOnlySpan from either the array or ReadOnlySpan. - /// - /// A ReadOnlySpan containing the data - public ReadOnlySpan AsSpan() - { - return Array ?? Span; - } - - /// - /// Cast an array to a SpanOrArray. - /// - /// Source array - public static implicit operator SpanOrArray(T[] array) - { - return new SpanOrArray(array); - } - - /// - /// Cast a ReadOnlySpan to a SpanOrArray. - /// - /// Source ReadOnlySpan - public static implicit operator SpanOrArray(ReadOnlySpan span) - { - return new SpanOrArray(span); - } - - /// - /// Cast a Span to a SpanOrArray. - /// - /// Source Span - public static implicit operator SpanOrArray(Span span) - { - return new SpanOrArray(span); - } - - /// - /// Cast a SpanOrArray to a ReadOnlySpan - /// - /// Source SpanOrArray - public static implicit operator ReadOnlySpan(SpanOrArray spanOrArray) - { - return spanOrArray.AsSpan(); - } - } -} diff --git a/src/Ryujinx.Common/Utilities/EmbeddedResources.cs b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs index a4facc2e3..e22571c96 100644 --- a/src/Ryujinx.Common/Utilities/EmbeddedResources.cs +++ b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs @@ -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 ReadFileToRentedMemory(string filename) + { + var (assembly, path) = ResolveManifestPath(filename); + + return ReadFileToRentedMemory(assembly, path); + } + + public static IMemoryOwner ReadFileToRentedMemory(Assembly assembly, string filename) + { + using var stream = GetStream(assembly, filename); + + return stream is null + ? null + : StreamUtils.StreamToRentedMemory(stream); + } + public async static Task ReadAsync(Assembly assembly, string filename) { using var stream = GetStream(assembly, filename); diff --git a/src/Ryujinx.Common/Utilities/StreamUtils.cs b/src/Ryujinx.Common/Utilities/StreamUtils.cs index 7a20c98e9..74b6af5ec 100644 --- a/src/Ryujinx.Common/Utilities/StreamUtils.cs +++ b/src/Ryujinx.Common/Utilities/StreamUtils.cs @@ -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 StreamToRentedMemory(Stream input) + { + if (input is MemoryStream inputMemoryStream) + { + return MemoryStreamToRentedMemory(inputMemoryStream); + } + else if (input.CanSeek) + { + long bytesExpected = input.Length; - return stream.ToArray(); + IMemoryOwner 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. + using RecyclableMemoryStream output = StreamToRecyclableMemoryStream(input); + + return MemoryStreamToRentedMemory(output); + } } public static async Task StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default) @@ -25,5 +65,26 @@ namespace Ryujinx.Common.Utilities return stream.ToArray(); } + + private static IMemoryOwner MemoryStreamToRentedMemory(MemoryStream input) + { + input.Position = 0; + + IMemoryOwner 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; + } } } diff --git a/src/Ryujinx.Graphics.Device/DeviceMemoryManager.cs b/src/Ryujinx.Graphics.Device/DeviceMemoryManager.cs index d64ed3095..fc075a264 100644 --- a/src/Ryujinx.Graphics.Device/DeviceMemoryManager.cs +++ b/src/Ryujinx.Graphics.Device/DeviceMemoryManager.cs @@ -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 memory = new byte[size]; + IMemoryOwner 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); } } diff --git a/src/Ryujinx.Graphics.GAL/ITexture.cs b/src/Ryujinx.Graphics.GAL/ITexture.cs index 5a4623a66..2d9c6b799 100644 --- a/src/Ryujinx.Graphics.GAL/ITexture.cs +++ b/src/Ryujinx.Graphics.GAL/ITexture.cs @@ -1,4 +1,4 @@ -using Ryujinx.Common.Memory; +using System.Buffers; namespace Ryujinx.Graphics.GAL { @@ -17,10 +17,34 @@ namespace Ryujinx.Graphics.GAL PinnedSpan GetData(); PinnedSpan GetData(int layer, int level); - void SetData(SpanOrArray data); - void SetData(SpanOrArray data, int layer, int level); - void SetData(SpanOrArray data, int layer, int level, Rectangle region); + /// + /// Sets the texture data. The data passed as a will be disposed when + /// the operation completes. + /// + /// Texture data bytes + void SetData(IMemoryOwner data); + + /// + /// Sets the texture data. The data passed as a will be disposed when + /// the operation completes. + /// + /// Texture data bytes + /// Target layer + /// Target level + void SetData(IMemoryOwner data, int layer, int level); + + /// + /// Sets the texture data. The data passed as a will be disposed when + /// the operation completes. + /// + /// Texture data bytes + /// Target layer + /// Target level + /// Target sub-region of the texture to update + void SetData(IMemoryOwner data, int layer, int level, Rectangle region); + void SetStorage(BufferRange buffer); + void Release(); } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs index 36feeeba7..3aba004df 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs @@ -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 _texture; - private TableRef _data; + private TableRef> _data; - public void Set(TableRef texture, TableRef data) + public void Set(TableRef texture, TableRef> 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(command._data.Get(threaded))); + texture.Base.SetData(command._data.Get(threaded)); } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs index c50bfe089..7ad709a75 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceCommand.cs @@ -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 _texture; - private TableRef _data; + private TableRef> _data; private int _layer; private int _level; - public void Set(TableRef texture, TableRef data, int layer, int level) + public void Set(TableRef texture, TableRef> 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(command._data.Get(threaded)), command._layer, command._level); + texture.Base.SetData(command._data.Get(threaded), command._layer, command._level); } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs index 4c80d9bc3..c211931bc 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataSliceRegionCommand.cs @@ -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 _texture; - private TableRef _data; + private TableRef> _data; private int _layer; private int _level; private Rectangle _region; - public void Set(TableRef texture, TableRef data, int layer, int level, Rectangle region) + public void Set(TableRef texture, TableRef> data, int layer, int level, Rectangle 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(command._data.Get(threaded)), command._layer, command._level, command._region); + texture.Base.SetData(command._data.Get(threaded), command._layer, command._level, command._region); } } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs index 9ad9e6454..80003b844 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs @@ -1,6 +1,6 @@ -using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture; using Ryujinx.Graphics.GAL.Multithreading.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 data) + /// + public void SetData(IMemoryOwner data) { - _renderer.New().Set(Ref(this), Ref(data.ToArray())); + _renderer.New().Set(Ref(this), Ref(data)); _renderer.QueueCommand(); } - public void SetData(SpanOrArray data, int layer, int level) + /// + public void SetData(IMemoryOwner data, int layer, int level) { - _renderer.New().Set(Ref(this), Ref(data.ToArray()), layer, level); + _renderer.New().Set(Ref(this), Ref(data), layer, level); _renderer.QueueCommand(); } - public void SetData(SpanOrArray data, int layer, int level, Rectangle region) + /// + public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) { - _renderer.New().Set(Ref(this), Ref(data.ToArray()), layer, level, region); + _renderer.New().Set(Ref(this), Ref(data), layer, level, region); _renderer.QueueCommand(); } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs index de395d574..218db15cf 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Dma/DmaClass.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.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 data; if (srcLinear) { data = LayoutConverter.ConvertLinearStridedToLinear( diff --git a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs index 37d74fdd3..93e43ce3c 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/InlineToMemory/InlineToMemoryClass.cs @@ -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(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount)); + var dataCopy = ByteMemoryPool.RentCopy(data); + target.SetData(dataCopy, 0, 0, new GAL.Rectangle(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount)); target.SignalModified(); return; diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index 7a043b2b7..e67caea81 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -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 result = ConvertToHostCompatibleFormat(data); + IMemoryOwner result = ConvertToHostCompatibleFormat(data); if (ScaleFactor != 1f && AllowScaledSetData()) { @@ -684,7 +684,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Uploads new texture data to the host GPU. /// /// New data - public void SetData(SpanOrArray data) + public void SetData(IMemoryOwner data) { BlacklistScale(); @@ -703,7 +703,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// New data /// Target layer /// Target level - public void SetData(SpanOrArray data, int layer, int level) + public void SetData(IMemoryOwner data, int layer, int level) { BlacklistScale(); @@ -721,7 +721,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Target layer /// Target level /// Target sub-region of the texture to update - public void SetData(ReadOnlySpan data, int layer, int level, Rectangle region) + public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) { BlacklistScale(); @@ -739,7 +739,7 @@ namespace Ryujinx.Graphics.Gpu.Image /// Mip level to convert /// True to convert a single slice /// Converted data - public SpanOrArray ConvertToHostCompatibleFormat(ReadOnlySpan data, int level = 0, bool single = false) + public IMemoryOwner ConvertToHostCompatibleFormat(ReadOnlySpan 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 result; + IMemoryOwner 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 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 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); + } } } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index 978583942..de9c47c97 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -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 data = dataSpan[(offset - spanBase)..]; - SpanOrArray result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true); + IMemoryOwner result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel + level, true); Storage.SetData(result, info.BaseLayer + layer, info.BaseLevel + level); } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index 30f87813f..0b6c78fac 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -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 memory = new byte[size]; + IMemoryOwner 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); } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs index ce970fab7..4d09c3aab 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/PhysicalMemory.cs @@ -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 memory = new byte[range.GetSize()]; + IMemoryOwner memoryOwner = ByteMemoryPool.Rent(range.GetSize()); + + Memory 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); } } diff --git a/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs index 46dda13f2..a6c5e4aca 100644 --- a/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs @@ -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(); diff --git a/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs b/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs index c4bbf7456..434f25900 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/FormatConverter.cs @@ -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 data) + public unsafe static IMemoryOwner ConvertS8D24ToD24S8(ReadOnlySpan data) { - byte[] output = new byte[data.Length]; + IMemoryOwner outputMemory = ByteMemoryPool.Rent(data.Length); + + Span 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 data) diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs index f140b276a..a8196541a 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureBuffer.cs @@ -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 data) + /// + public void SetData(IMemoryOwner 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 data, int layer, int level) + /// + public void SetData(IMemoryOwner data, int layer, int level) { throw new NotSupportedException(); } - public void SetData(SpanOrArray data, int layer, int level, Rectangle region) + /// + public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) { throw new NotSupportedException(); } diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 7f1b1c382..8a18e6132 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -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 data) + public void SetData(IMemoryOwner 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 data, int layer, int level) + public void SetData(IMemoryOwner 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 data, int layer, int level, Rectangle region) + public void SetData(IMemoryOwner data, int layer, int level, Rectangle 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 EnsureDataFormat(IMemoryOwner 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(); diff --git a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs index edf699dc3..3f65e1225 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs @@ -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 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; } diff --git a/src/Ryujinx.Graphics.Texture/BCnDecoder.cs b/src/Ryujinx.Graphics.Texture/BCnDecoder.cs index 2d68ca346..eb85334a2 100644 --- a/src/Ryujinx.Graphics.Texture/BCnDecoder.cs +++ b/src/Ryujinx.Graphics.Texture/BCnDecoder.cs @@ -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 data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodeBC1(ReadOnlySpan 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 output = ByteMemoryPool.Rent(size); Span tile = stackalloc byte[BlockWidth * BlockHeight * 4]; Span tileAsUint = MemoryMarshal.Cast(tile); - Span outputAsUint = MemoryMarshal.Cast(output); + Span outputAsUint = MemoryMarshal.Cast(output.Memory.Span); Span> tileAsVector128 = MemoryMarshal.Cast>(tile); @@ -100,7 +102,7 @@ namespace Ryujinx.Graphics.Texture return output; } - public static byte[] DecodeBC2(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodeBC2(ReadOnlySpan 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 output = ByteMemoryPool.Rent(size); Span tile = stackalloc byte[BlockWidth * BlockHeight * 4]; Span tileAsUint = MemoryMarshal.Cast(tile); - Span outputAsUint = MemoryMarshal.Cast(output); + Span outputAsUint = MemoryMarshal.Cast(output.Memory.Span); Span> tileAsVector128 = MemoryMarshal.Cast>(tile); @@ -195,7 +197,7 @@ namespace Ryujinx.Graphics.Texture return output; } - public static byte[] DecodeBC3(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodeBC3(ReadOnlySpan 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 output = ByteMemoryPool.Rent(size); Span tile = stackalloc byte[BlockWidth * BlockHeight * 4]; Span rPal = stackalloc byte[8]; Span tileAsUint = MemoryMarshal.Cast(tile); - Span outputAsUint = MemoryMarshal.Cast(output); + Span outputAsUint = MemoryMarshal.Cast(output.Memory.Span); Span> tileAsVector128 = MemoryMarshal.Cast>(tile); @@ -292,7 +294,7 @@ namespace Ryujinx.Graphics.Texture return output; } - public static byte[] DecodeBC4(ReadOnlySpan data, int width, int height, int depth, int levels, int layers, bool signed) + public static IMemoryOwner DecodeBC4(ReadOnlySpan 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 outputSpan = new(output); + IMemoryOwner output = ByteMemoryPool.Rent(size); + Span outputSpan = output.Memory.Span; ReadOnlySpan data64 = MemoryMarshal.Cast(data); @@ -400,7 +402,7 @@ namespace Ryujinx.Graphics.Texture return output; } - public static byte[] DecodeBC5(ReadOnlySpan data, int width, int height, int depth, int levels, int layers, bool signed) + public static IMemoryOwner DecodeBC5(ReadOnlySpan 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 output = ByteMemoryPool.Rent(size); ReadOnlySpan data64 = MemoryMarshal.Cast(data); @@ -421,7 +423,7 @@ namespace Ryujinx.Graphics.Texture Span rPal = stackalloc byte[8]; Span gPal = stackalloc byte[8]; - Span outputAsUshort = MemoryMarshal.Cast(output); + Span outputAsUshort = MemoryMarshal.Cast(output.Memory.Span); Span rTileAsUint = MemoryMarshal.Cast(rTile); Span gTileAsUint = MemoryMarshal.Cast(gTile); @@ -525,7 +527,7 @@ namespace Ryujinx.Graphics.Texture return output; } - public static byte[] DecodeBC6(ReadOnlySpan data, int width, int height, int depth, int levels, int layers, bool signed) + public static IMemoryOwner DecodeBC6(ReadOnlySpan 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 output = ByteMemoryPool.Rent(size); + Span 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 data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodeBC7(ReadOnlySpan 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 output = ByteMemoryPool.Rent(size); + Span 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; diff --git a/src/Ryujinx.Graphics.Texture/BCnEncoder.cs b/src/Ryujinx.Graphics.Texture/BCnEncoder.cs index 8103990ff..253ba305c 100644 --- a/src/Ryujinx.Graphics.Texture/BCnEncoder.cs +++ b/src/Ryujinx.Graphics.Texture/BCnEncoder.cs @@ -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 EncodeBC7(Memory 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 output = ByteMemoryPool.Rent(size); + Memory 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); diff --git a/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs b/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs index 57f2e98d0..52801ff47 100644 --- a/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs +++ b/src/Ryujinx.Graphics.Texture/ETC2Decoder.cs @@ -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 data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodeRgb(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) { ReadOnlySpan dataUlong = MemoryMarshal.Cast(data); int inputOffset = 0; - byte[] output = new byte[CalculateOutputSize(width, height, depth, levels, layers)]; + IMemoryOwner output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers)); - Span outputUint = MemoryMarshal.Cast(output); + Span outputUint = MemoryMarshal.Cast(output.Memory.Span); Span tile = stackalloc uint[BlockWidth * BlockHeight]; int imageBaseOOffs = 0; @@ -111,15 +113,15 @@ namespace Ryujinx.Graphics.Texture return output; } - public static byte[] DecodePta(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodePta(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) { ReadOnlySpan dataUlong = MemoryMarshal.Cast(data); int inputOffset = 0; - byte[] output = new byte[CalculateOutputSize(width, height, depth, levels, layers)]; + IMemoryOwner output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers)); - Span outputUint = MemoryMarshal.Cast(output); + Span outputUint = MemoryMarshal.Cast(output.Memory.Span); Span tile = stackalloc uint[BlockWidth * BlockHeight]; int imageBaseOOffs = 0; @@ -168,15 +170,15 @@ namespace Ryujinx.Graphics.Texture return output; } - public static byte[] DecodeRgba(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) + public static IMemoryOwner DecodeRgba(ReadOnlySpan data, int width, int height, int depth, int levels, int layers) { ReadOnlySpan dataUlong = MemoryMarshal.Cast(data); int inputOffset = 0; - byte[] output = new byte[CalculateOutputSize(width, height, depth, levels, layers)]; + IMemoryOwner output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers)); - Span outputUint = MemoryMarshal.Cast(output); + Span outputUint = MemoryMarshal.Cast(output.Memory.Span); Span tile = stackalloc uint[BlockWidth * BlockHeight]; int imageBaseOOffs = 0; diff --git a/src/Ryujinx.Graphics.Texture/LayoutConverter.cs b/src/Ryujinx.Graphics.Texture/LayoutConverter.cs index d9a666c3f..d6732674b 100644 --- a/src/Ryujinx.Graphics.Texture/LayoutConverter.cs +++ b/src/Ryujinx.Graphics.Texture/LayoutConverter.cs @@ -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 ConvertBlockLinearToLinear( int width, int height, int depth, @@ -119,7 +121,8 @@ namespace Ryujinx.Graphics.Texture blockHeight, bytesPerPixel); - byte[] output = new byte[outSize]; + IMemoryOwner outputOwner = ByteMemoryPool.Rent(outSize); + Span 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 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 outSpan = output; + IMemoryOwner output = ByteMemoryPool.Rent(h * outStride); + Span outSpan = output.Memory.Span; int outOffs = 0; int inOffs = 0; diff --git a/src/Ryujinx.Graphics.Texture/PixelConverter.cs b/src/Ryujinx.Graphics.Texture/PixelConverter.cs index 7955aed3f..4475cc98a 100644 --- a/src/Ryujinx.Graphics.Texture/PixelConverter.cs +++ b/src/Ryujinx.Graphics.Texture/PixelConverter.cs @@ -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 data, int width) + public unsafe static IMemoryOwner ConvertR4G4ToR4G4B4A4(ReadOnlySpan data, int width) { - byte[] output = new byte[data.Length * 2]; + IMemoryOwner output = ByteMemoryPool.Rent(data.Length * 2); (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2); - Span outputSpan = MemoryMarshal.Cast(output); + Span outputSpan = MemoryMarshal.Cast(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 data, int width) + public static IMemoryOwner ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan data, int width) { - byte[] output = new byte[data.Length * 2]; + IMemoryOwner 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 inputSpan = MemoryMarshal.Cast(data); - Span outputSpan = MemoryMarshal.Cast(output); + Span outputSpan = MemoryMarshal.Cast(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 data, int width, bool forceAlpha) + public static IMemoryOwner ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan data, int width, bool forceAlpha) { - byte[] output = new byte[data.Length * 2]; + IMemoryOwner 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 inputSpan = MemoryMarshal.Cast(data); - Span outputSpan = MemoryMarshal.Cast(output); + Span outputSpan = MemoryMarshal.Cast(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 data, int width) + public static IMemoryOwner ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan data, int width) { - byte[] output = new byte[data.Length * 2]; + IMemoryOwner 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 inputSpan = MemoryMarshal.Cast(data); - Span outputSpan = MemoryMarshal.Cast(output); + Span outputSpan = MemoryMarshal.Cast(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 data, int width) + public static IMemoryOwner ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan data, int width) { - byte[] output = new byte[data.Length * 2]; + IMemoryOwner 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 inputSpan = MemoryMarshal.Cast(data); - Span outputSpan = MemoryMarshal.Cast(output); + Span outputSpan = MemoryMarshal.Cast(output.Memory.Span); for (int y = 0; y < height; y++) { diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index a0299a372..a0010e660 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -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 dummyTextureData = stackalloc byte[4]; + IMemoryOwner dummyTextureData = ByteMemoryPool.RentCleared(4); _dummyTexture.SetData(dummyTextureData); } diff --git a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs index 259be9d64..08e07f256 100644 --- a/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs +++ b/src/Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs @@ -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; diff --git a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs index 81e478814..e0694b197 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureBuffer.cs @@ -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 data) + /// + public void SetData(IMemoryOwner data) { - _gd.SetBufferData(_bufferHandle, _offset, data); + _gd.SetBufferData(_bufferHandle, _offset, data.Memory.Span); + data.Dispose(); } - public void SetData(SpanOrArray data, int layer, int level) + /// + public void SetData(IMemoryOwner data, int layer, int level) { throw new NotSupportedException(); } - public void SetData(SpanOrArray data, int layer, int level, Rectangle region) + /// + public void SetData(IMemoryOwner data, int layer, int level, Rectangle region) { throw new NotSupportedException(); } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index d918f965f..f2aaf4693 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -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 data) + /// + public void SetData(IMemoryOwner 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 data, int layer, int level) + /// + public void SetData(IMemoryOwner 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 data, int layer, int level, Rectangle region) + /// + public void SetData(IMemoryOwner data, int layer, int level, Rectangle 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 data, int layer, int level, int layers, int levels, bool singleSlice, Rectangle? region = null)