Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
1ecc8fbc3b | |||
888402ecaf | |||
971d24aef0 |
@ -20,7 +20,7 @@
|
||||
<PackageVersion Include="LibHac" Version="0.19.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
|
||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.5.2" />
|
||||
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.0" />
|
||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||
|
@ -237,7 +237,7 @@ namespace ARMeilleure.CodeGen.Arm64
|
||||
long originalPosition = _stream.Position;
|
||||
|
||||
_stream.Seek(0, SeekOrigin.Begin);
|
||||
_stream.Read(code, 0, code.Length);
|
||||
_stream.ReadExactly(code, 0, code.Length);
|
||||
_stream.Seek(originalPosition, SeekOrigin.Begin);
|
||||
|
||||
RelocInfo relocInfo;
|
||||
|
@ -1444,7 +1444,7 @@ namespace ARMeilleure.CodeGen.X86
|
||||
|
||||
Span<byte> buffer = new byte[jump.JumpPosition - _stream.Position];
|
||||
|
||||
_stream.Read(buffer);
|
||||
_stream.ReadExactly(buffer);
|
||||
_stream.Seek(ReservedBytesForJump, SeekOrigin.Current);
|
||||
|
||||
codeStream.Write(buffer);
|
||||
|
140
src/Ryujinx.Common/Memory/MemoryOwner.cs
Normal file
140
src/Ryujinx.Common/Memory/MemoryOwner.cs
Normal file
@ -0,0 +1,140 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Common.Memory
|
||||
{
|
||||
/// <summary>
|
||||
/// An <see cref="IMemoryOwner{T}"/> implementation with an embedded length and fast <see cref="Span{T}"/>
|
||||
/// accessor, with memory allocated from <seealso cref="ArrayPool{T}.Shared"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of item to store.</typeparam>
|
||||
public sealed class MemoryOwner<T> : IMemoryOwner<T>
|
||||
{
|
||||
private readonly int _length;
|
||||
private T[]? _array;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MemoryOwner{T}"/> class with the specified parameters.
|
||||
/// </summary>
|
||||
/// <param name="length">The length of the new memory buffer to use</param>
|
||||
private MemoryOwner(int length)
|
||||
{
|
||||
_length = length;
|
||||
_array = ArrayPool<T>.Shared.Rent(length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="MemoryOwner{T}"/> instance with the specified length.
|
||||
/// </summary>
|
||||
/// <param name="length">The length of the new memory buffer to use</param>
|
||||
/// <returns>A <see cref="MemoryOwner{T}"/> instance of the requested length</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static MemoryOwner<T> Rent(int length) => new(length);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="MemoryOwner{T}"/> instance with the specified length and the content cleared.
|
||||
/// </summary>
|
||||
/// <param name="length">The length of the new memory buffer to use</param>
|
||||
/// <returns>A <see cref="MemoryOwner{T}"/> instance of the requested length and the content cleared</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static MemoryOwner<T> RentCleared(int length)
|
||||
{
|
||||
MemoryOwner<T> result = new(length);
|
||||
|
||||
result._array.AsSpan(0, length).Clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="MemoryOwner{T}"/> instance with the content copied from the specified buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to copy</param>
|
||||
/// <returns>A <see cref="MemoryOwner{T}"/> instance with the same length and content as <paramref name="buffer"/></returns>
|
||||
public static MemoryOwner<T> RentCopy(ReadOnlySpan<T> buffer)
|
||||
{
|
||||
MemoryOwner<T> result = new(buffer.Length);
|
||||
|
||||
buffer.CopyTo(result._array);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of items in the current instance.
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _length;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Memory<T> Memory
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
T[]? array = _array;
|
||||
|
||||
if (array is null)
|
||||
{
|
||||
ThrowObjectDisposedException();
|
||||
}
|
||||
|
||||
return new(array, 0, _length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Span{T}"/> wrapping the memory belonging to the current instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Uses a trick made possible by the .NET 6+ runtime array layout.
|
||||
/// </remarks>
|
||||
public Span<T> Span
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
T[]? array = _array;
|
||||
|
||||
if (array is null)
|
||||
{
|
||||
ThrowObjectDisposedException();
|
||||
}
|
||||
|
||||
ref T firstElementRef = ref MemoryMarshal.GetArrayDataReference(array);
|
||||
|
||||
return MemoryMarshal.CreateSpan(ref firstElementRef, _length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
T[]? array = Interlocked.Exchange(ref _array, null);
|
||||
|
||||
if (array is not null)
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(array);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an <see cref="ObjectDisposedException"/> when <see cref="_array"/> is <see langword="null"/>.
|
||||
/// </summary>
|
||||
[DoesNotReturn]
|
||||
private static void ThrowObjectDisposedException()
|
||||
{
|
||||
throw new ObjectDisposedException(nameof(MemoryOwner<T>), "The buffer has already been disposed.");
|
||||
}
|
||||
}
|
||||
}
|
114
src/Ryujinx.Common/Memory/SpanOwner.cs
Normal file
114
src/Ryujinx.Common/Memory/SpanOwner.cs
Normal file
@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Common.Memory
|
||||
{
|
||||
/// <summary>
|
||||
/// A stack-only type that rents a buffer of a specified length from <seealso cref="ArrayPool{T}.Shared"/>.
|
||||
/// It does not implement <see cref="IDisposable"/> to avoid being boxed, but should still be disposed. This
|
||||
/// is easy since C# 8, which allows use of C# `using` constructs on any type that has a public Dispose() method.
|
||||
/// To keep this type simple, fast, and read-only, it does not check or guard against multiple disposals.
|
||||
/// For all these reasons, all usage should be with a `using` block or statement.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of item to store.</typeparam>
|
||||
public readonly ref struct SpanOwner<T>
|
||||
{
|
||||
private readonly int _length;
|
||||
private readonly T[] _array;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SpanOwner{T}"/> struct with the specified parameters.
|
||||
/// </summary>
|
||||
/// <param name="length">The length of the new memory buffer to use</param>
|
||||
private SpanOwner(int length)
|
||||
{
|
||||
_length = length;
|
||||
_array = ArrayPool<T>.Shared.Rent(length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an empty <see cref="SpanOwner{T}"/> instance.
|
||||
/// </summary>
|
||||
public static SpanOwner<T> Empty
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SpanOwner{T}"/> instance with the specified length.
|
||||
/// </summary>
|
||||
/// <param name="length">The length of the new memory buffer to use</param>
|
||||
/// <returns>A <see cref="SpanOwner{T}"/> instance of the requested length</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static SpanOwner<T> Rent(int length) => new(length);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SpanOwner{T}"/> instance with the length and the content cleared.
|
||||
/// </summary>
|
||||
/// <param name="length">The length of the new memory buffer to use</param>
|
||||
/// <returns>A <see cref="SpanOwner{T}"/> instance of the requested length and the content cleared</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when <paramref name="length"/> is not valid</exception>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static SpanOwner<T> RentCleared(int length)
|
||||
{
|
||||
SpanOwner<T> result = new(length);
|
||||
|
||||
result._array.AsSpan(0, length).Clear();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="SpanOwner{T}"/> instance with the content copied from the specified buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to copy</param>
|
||||
/// <returns>A <see cref="SpanOwner{T}"/> instance with the same length and content as <paramref name="buffer"/></returns>
|
||||
public static SpanOwner<T> RentCopy(ReadOnlySpan<T> buffer)
|
||||
{
|
||||
SpanOwner<T> result = new(buffer.Length);
|
||||
|
||||
buffer.CopyTo(result._array);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of items in the current instance
|
||||
/// </summary>
|
||||
public int Length
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a <see cref="Span{T}"/> wrapping the memory belonging to the current instance.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Uses a trick made possible by the .NET 6+ runtime array layout.
|
||||
/// </remarks>
|
||||
public Span<T> Span
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
ref T firstElementRef = ref MemoryMarshal.GetArrayDataReference(_array);
|
||||
|
||||
return MemoryMarshal.CreateSpan(ref firstElementRef, _length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implements the duck-typed <see cref="IDisposable.Dispose"/> method.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose()
|
||||
{
|
||||
ArrayPool<T>.Shared.Return(_array);
|
||||
}
|
||||
}
|
||||
}
|
@ -195,7 +195,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
switch (algorithm)
|
||||
{
|
||||
case CompressionAlgorithm.None:
|
||||
stream.Read(data);
|
||||
stream.ReadExactly(data);
|
||||
break;
|
||||
case CompressionAlgorithm.Deflate:
|
||||
stream = new DeflateStream(stream, CompressionMode.Decompress, true);
|
||||
|
@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
}
|
||||
|
||||
dataFileStream.Seek((long)entry.Offset, SeekOrigin.Begin);
|
||||
dataFileStream.Read(cb1Data);
|
||||
dataFileStream.ReadExactly(cb1Data);
|
||||
BinarySerializer.ReadCompressed(dataFileStream, guestCode);
|
||||
|
||||
_cache[index] = (guestCode, cb1Data);
|
||||
@ -279,7 +279,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
dataFileStream.Seek((long)entry.Offset, SeekOrigin.Begin);
|
||||
byte[] cachedCode = new byte[entry.CodeSize];
|
||||
byte[] cachedCb1Data = new byte[entry.Cb1DataSize];
|
||||
dataFileStream.Read(cachedCb1Data);
|
||||
dataFileStream.ReadExactly(cachedCb1Data);
|
||||
BinarySerializer.ReadCompressed(dataFileStream, cachedCode);
|
||||
|
||||
if (data.SequenceEqual(cachedCode) && cb1Data.SequenceEqual(cachedCb1Data))
|
||||
|
@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
|
||||
@ -165,14 +166,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
/// <returns>True if all fences were signaled before the timeout expired, false otherwise</returns>
|
||||
private bool WaitForFencesImpl(Vk api, Device device, int offset, int size, bool hasTimeout, ulong timeout)
|
||||
{
|
||||
Span<FenceHolder> fenceHolders = new FenceHolder[CommandBufferPool.MaxCommandBuffers];
|
||||
using SpanOwner<FenceHolder> fenceHoldersOwner = SpanOwner<FenceHolder>.Rent(CommandBufferPool.MaxCommandBuffers);
|
||||
Span<FenceHolder> fenceHolders = fenceHoldersOwner.Span;
|
||||
|
||||
int count = size != 0 ? GetOverlappingFences(fenceHolders, offset, size) : GetFences(fenceHolders);
|
||||
Span<Fence> fences = stackalloc Fence[count];
|
||||
|
||||
int fenceCount = 0;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
for (int i = 0; i < fences.Length; i++)
|
||||
{
|
||||
if (fenceHolders[i].TryGet(out Fence fence))
|
||||
{
|
||||
|
@ -233,7 +233,7 @@ namespace Ryujinx.UI.Windows
|
||||
reader.ReadInt64(); // Padding
|
||||
|
||||
byte[] input = new byte[stream.Length - stream.Position];
|
||||
stream.Read(input, 0, input.Length);
|
||||
stream.ReadExactly(input, 0, input.Length);
|
||||
|
||||
long inputOffset = 0;
|
||||
|
||||
|
@ -65,7 +65,7 @@ namespace Ryujinx.UI.App.Common
|
||||
Stream resourceStream = Assembly.GetCallingAssembly().GetManifestResourceStream(resourceName);
|
||||
byte[] resourceByteArray = new byte[resourceStream.Length];
|
||||
|
||||
resourceStream.Read(resourceByteArray);
|
||||
resourceStream.ReadExactly(resourceByteArray);
|
||||
|
||||
return resourceByteArray;
|
||||
}
|
||||
|
@ -151,7 +151,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
reader.ReadInt64(); // Padding
|
||||
|
||||
byte[] input = new byte[stream.Length - stream.Position];
|
||||
stream.Read(input, 0, input.Length);
|
||||
stream.ReadExactly(input, 0, input.Length);
|
||||
|
||||
uint inputOffset = 0;
|
||||
|
||||
|
Reference in New Issue
Block a user