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:
jhorv 2024-04-14 16:06:14 -04:00 committed by GitHub
parent e916662b0f
commit 268c9aecf8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 435 additions and 322 deletions

View file

@ -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();
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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