diff --git a/Directory.Packages.props b/Directory.Packages.props
index 455735fc46..c3af18ceec 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -13,14 +13,14 @@
-
+
-
+
diff --git a/docs/README.md b/docs/README.md
index 2213086f67..a22da3c7cf 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -33,8 +33,3 @@ Project Docs
=================
To be added. Many project files will contain basic XML docs for key functions and classes in the meantime.
-
-Other Information
-=================
-
-- N/A
diff --git a/src/ARMeilleure/Instructions/InstEmitAlu32.cs b/src/ARMeilleure/Instructions/InstEmitAlu32.cs
index 3a5e71bccf..028ffbeb13 100644
--- a/src/ARMeilleure/Instructions/InstEmitAlu32.cs
+++ b/src/ARMeilleure/Instructions/InstEmitAlu32.cs
@@ -19,6 +19,12 @@ namespace ARMeilleure.Instructions
Operand n = GetAluN(context);
Operand m = GetAluM(context, setCarry: false);
+ if (op.Rn == RegisterAlias.Aarch32Pc && op is OpCodeT32AluImm12)
+ {
+ // For ADR, PC is always 4 bytes aligned, even in Thumb mode.
+ n = context.BitwiseAnd(n, Const(~3u));
+ }
+
Operand res = context.Add(n, m);
if (ShouldSetFlags(context))
@@ -467,6 +473,12 @@ namespace ARMeilleure.Instructions
Operand n = GetAluN(context);
Operand m = GetAluM(context, setCarry: false);
+ if (op.Rn == RegisterAlias.Aarch32Pc && op is OpCodeT32AluImm12)
+ {
+ // For ADR, PC is always 4 bytes aligned, even in Thumb mode.
+ n = context.BitwiseAnd(n, Const(~3u));
+ }
+
Operand res = context.Subtract(n, m);
if (ShouldSetFlags(context))
diff --git a/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
index 543aab0236..13d9fac683 100644
--- a/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
+++ b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic.cs
@@ -2426,7 +2426,11 @@ namespace ARMeilleure.Instructions
}
else if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0)
{
- Operand res = EmitSse41Round32Exp8OpF(context, context.AddIntrinsic(Intrinsic.X86Rsqrtss, GetVec(op.Rn)), scalar: true);
+ // RSQRTSS handles subnormals as zero, which differs from Arm, so we can't use it here.
+
+ Operand res = context.AddIntrinsic(Intrinsic.X86Sqrtss, GetVec(op.Rn));
+ res = context.AddIntrinsic(Intrinsic.X86Rcpss, res);
+ res = EmitSse41Round32Exp8OpF(context, res, scalar: true);
context.Copy(GetVec(op.Rd), context.VectorZeroUpper96(res));
}
@@ -2451,7 +2455,11 @@ namespace ARMeilleure.Instructions
}
else if (Optimizations.FastFP && Optimizations.UseSse41 && sizeF == 0)
{
- Operand res = EmitSse41Round32Exp8OpF(context, context.AddIntrinsic(Intrinsic.X86Rsqrtps, GetVec(op.Rn)), scalar: false);
+ // RSQRTPS handles subnormals as zero, which differs from Arm, so we can't use it here.
+
+ Operand res = context.AddIntrinsic(Intrinsic.X86Sqrtps, GetVec(op.Rn));
+ res = context.AddIntrinsic(Intrinsic.X86Rcpps, res);
+ res = EmitSse41Round32Exp8OpF(context, res, scalar: false);
if (op.RegisterSize == RegisterSize.Simd64)
{
diff --git a/src/ARMeilleure/Translation/PTC/Ptc.cs b/src/ARMeilleure/Translation/PTC/Ptc.cs
index 6f6dfcadf3..58f065342e 100644
--- a/src/ARMeilleure/Translation/PTC/Ptc.cs
+++ b/src/ARMeilleure/Translation/PTC/Ptc.cs
@@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
- private const uint InternalVersion = 5518; //! To be incremented manually for each change to the ARMeilleure project.
+ private const uint InternalVersion = 6634; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";
diff --git a/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs
index 05dd2162a5..b95e5bed14 100644
--- a/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs
+++ b/src/Ryujinx.Audio/Backends/Common/DynamicRingBuffer.cs
@@ -1,5 +1,7 @@
using Ryujinx.Common;
+using Ryujinx.Common.Memory;
using System;
+using System.Buffers;
namespace Ryujinx.Audio.Backends.Common
{
@@ -12,7 +14,8 @@ namespace Ryujinx.Audio.Backends.Common
private readonly object _lock = new();
- private byte[] _buffer;
+ private IMemoryOwner _bufferOwner;
+ private Memory _buffer;
private int _size;
private int _headOffset;
private int _tailOffset;
@@ -21,7 +24,8 @@ namespace Ryujinx.Audio.Backends.Common
public DynamicRingBuffer(int initialCapacity = RingBufferAlignment)
{
- _buffer = new byte[initialCapacity];
+ _bufferOwner = ByteMemoryPool.RentCleared(initialCapacity);
+ _buffer = _bufferOwner.Memory;
}
public void Clear()
@@ -33,6 +37,11 @@ namespace Ryujinx.Audio.Backends.Common
public void Clear(int size)
{
+ if (size == 0)
+ {
+ return;
+ }
+
lock (_lock)
{
if (size > _size)
@@ -40,11 +49,6 @@ namespace Ryujinx.Audio.Backends.Common
size = _size;
}
- if (size == 0)
- {
- return;
- }
-
_headOffset = (_headOffset + size) % _buffer.Length;
_size -= size;
@@ -58,28 +62,31 @@ namespace Ryujinx.Audio.Backends.Common
private void SetCapacityLocked(int capacity)
{
- byte[] buffer = new byte[capacity];
+ IMemoryOwner newBufferOwner = ByteMemoryPool.RentCleared(capacity);
+ Memory newBuffer = newBufferOwner.Memory;
if (_size > 0)
{
if (_headOffset < _tailOffset)
{
- Buffer.BlockCopy(_buffer, _headOffset, buffer, 0, _size);
+ _buffer.Slice(_headOffset, _size).CopyTo(newBuffer);
}
else
{
- Buffer.BlockCopy(_buffer, _headOffset, buffer, 0, _buffer.Length - _headOffset);
- Buffer.BlockCopy(_buffer, 0, buffer, _buffer.Length - _headOffset, _tailOffset);
+ _buffer[_headOffset..].CopyTo(newBuffer);
+ _buffer[.._tailOffset].CopyTo(newBuffer[(_buffer.Length - _headOffset)..]);
}
}
- _buffer = buffer;
+ _bufferOwner.Dispose();
+
+ _bufferOwner = newBufferOwner;
+ _buffer = newBuffer;
_headOffset = 0;
_tailOffset = _size;
}
-
- public void Write(T[] buffer, int index, int count)
+ public void Write(ReadOnlySpan buffer, int index, int count)
{
if (count == 0)
{
@@ -99,17 +106,17 @@ namespace Ryujinx.Audio.Backends.Common
if (tailLength >= count)
{
- Buffer.BlockCopy(buffer, index, _buffer, _tailOffset, count);
+ buffer.Slice(index, count).CopyTo(_buffer.Span[_tailOffset..]);
}
else
{
- Buffer.BlockCopy(buffer, index, _buffer, _tailOffset, tailLength);
- Buffer.BlockCopy(buffer, index + tailLength, _buffer, 0, count - tailLength);
+ buffer.Slice(index, tailLength).CopyTo(_buffer.Span[_tailOffset..]);
+ buffer.Slice(index + tailLength, count - tailLength).CopyTo(_buffer.Span);
}
}
else
{
- Buffer.BlockCopy(buffer, index, _buffer, _tailOffset, count);
+ buffer.Slice(index, count).CopyTo(_buffer.Span[_tailOffset..]);
}
_size += count;
@@ -117,8 +124,13 @@ namespace Ryujinx.Audio.Backends.Common
}
}
- public int Read(T[] buffer, int index, int count)
+ public int Read(Span buffer, int index, int count)
{
+ if (count == 0)
+ {
+ return 0;
+ }
+
lock (_lock)
{
if (count > _size)
@@ -126,14 +138,9 @@ namespace Ryujinx.Audio.Backends.Common
count = _size;
}
- if (count == 0)
- {
- return 0;
- }
-
if (_headOffset < _tailOffset)
{
- Buffer.BlockCopy(_buffer, _headOffset, buffer, index, count);
+ _buffer.Span.Slice(_headOffset, count).CopyTo(buffer[index..]);
}
else
{
@@ -141,12 +148,12 @@ namespace Ryujinx.Audio.Backends.Common
if (tailLength >= count)
{
- Buffer.BlockCopy(_buffer, _headOffset, buffer, index, count);
+ _buffer.Span.Slice(_headOffset, count).CopyTo(buffer[index..]);
}
else
{
- Buffer.BlockCopy(_buffer, _headOffset, buffer, index, tailLength);
- Buffer.BlockCopy(_buffer, 0, buffer, index + tailLength, count - tailLength);
+ _buffer.Span.Slice(_headOffset, tailLength).CopyTo(buffer[index..]);
+ _buffer.Span[..(count - tailLength)].CopyTo(buffer[(index + tailLength)..]);
}
}
diff --git a/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs b/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs
index b0963c9350..3b8d15dc53 100644
--- a/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs
+++ b/src/Ryujinx.Audio/Renderer/Common/BehaviourParameter.cs
@@ -25,7 +25,7 @@ namespace Ryujinx.Audio.Renderer.Common
public ulong Flags;
///
- /// Represents an error during .
+ /// Represents an error during .
///
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ErrorInfo
diff --git a/src/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs b/src/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs
index 7efe3b02b4..98b224ebfb 100644
--- a/src/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs
+++ b/src/Ryujinx.Audio/Renderer/Common/UpdateDataHeader.cs
@@ -4,7 +4,7 @@ using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Renderer.Common
{
///
- /// Update data header used for input and output of .
+ /// Update data header used for input and output of .
///
public struct UpdateDataHeader
{
diff --git a/src/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs b/src/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs
index 5a0565dc61..72438be0e4 100644
--- a/src/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs
+++ b/src/Ryujinx.Audio/Renderer/Parameter/BehaviourErrorInfoOutStatus.cs
@@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
///
/// Output information for behaviour.
///
- /// This is used to report errors to the user during processing.
+ /// This is used to report errors to the user during processing.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BehaviourErrorInfoOutStatus
{
diff --git a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs
index 7bb8ae5ba7..9b56f5cbdf 100644
--- a/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs
@@ -386,7 +386,7 @@ namespace Ryujinx.Audio.Renderer.Server
}
}
- public ResultCode Update(Memory output, Memory performanceOutput, ReadOnlyMemory input)
+ public ResultCode Update(Memory output, Memory performanceOutput, ReadOnlySequence input)
{
lock (_lock)
{
@@ -419,14 +419,16 @@ namespace Ryujinx.Audio.Renderer.Server
return result;
}
- result = stateUpdater.UpdateVoices(_voiceContext, _memoryPools);
+ PoolMapper poolMapper = new PoolMapper(_processHandle, _memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled());
+
+ result = stateUpdater.UpdateVoices(_voiceContext, poolMapper);
if (result != ResultCode.Success)
{
return result;
}
- result = stateUpdater.UpdateEffects(_effectContext, _isActive, _memoryPools);
+ result = stateUpdater.UpdateEffects(_effectContext, _isActive, poolMapper);
if (result != ResultCode.Success)
{
@@ -450,7 +452,7 @@ namespace Ryujinx.Audio.Renderer.Server
return result;
}
- result = stateUpdater.UpdateSinks(_sinkContext, _memoryPools);
+ result = stateUpdater.UpdateSinks(_sinkContext, poolMapper);
if (result != ResultCode.Success)
{
diff --git a/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs b/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs
index 3297b5d9fa..099d8f5619 100644
--- a/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs
@@ -1,4 +1,5 @@
using System;
+using System.Buffers;
using System.Diagnostics;
using static Ryujinx.Audio.Renderer.Common.BehaviourParameter;
@@ -273,7 +274,7 @@ namespace Ryujinx.Audio.Renderer.Server
}
///
- /// Check if the audio renderer should trust the user destination count in .
+ /// Check if the audio renderer should trust the user destination count in .
///
/// True if the audio renderer should trust the user destination count.
public bool IsSplitterBugFixed()
diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs
index 57ca266f4d..74a9baff2a 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs
@@ -33,21 +33,21 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return WorkBuffers[index].GetReference(true);
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
+ public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
- UpdateParameterBase(ref parameter);
+ UpdateParameterBase(in parameter);
Parameter = MemoryMarshal.Cast(parameter.SpecificData)[0];
IsEnabled = parameter.IsEnabled;
diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs
index a9716db2a5..77d9b5c295 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs
@@ -81,7 +81,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
///
/// The user parameter.
/// Returns true if the sent by the user matches the internal .
- public bool IsTypeValid(ref T parameter) where T : unmanaged, IEffectInParameter
+ public bool IsTypeValid(in T parameter) where T : unmanaged, IEffectInParameter
{
return parameter.Type == TargetEffectType;
}
@@ -98,7 +98,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
/// Update the internal common parameters from a user parameter.
///
/// The user parameter.
- protected void UpdateParameterBase(ref T parameter) where T : unmanaged, IEffectInParameter
+ protected void UpdateParameterBase(in T parameter) where T : unmanaged, IEffectInParameter
{
MixId = parameter.MixId;
ProcessingOrder = parameter.ProcessingOrder;
@@ -139,7 +139,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
///
/// Initialize the given result state.
///
- /// The state to initalize
+ /// The state to initialize
public virtual void InitializeResultState(ref EffectResultState state) { }
///
@@ -155,9 +155,9 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
/// The possible that was generated.
/// The user parameter.
/// The mapper to use.
- public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ public virtual void Update(out ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
updateErrorInfo = new ErrorInfo();
}
@@ -168,9 +168,9 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
/// The possible that was generated.
/// The user parameter.
/// The mapper to use.
- public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ public virtual void Update(out ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
updateErrorInfo = new ErrorInfo();
}
diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs
index b987f7c85e..3b3e1021c4 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs
@@ -35,21 +35,21 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
public override EffectType TargetEffectType => EffectType.BiquadFilter;
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
+ public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
- UpdateParameterBase(ref parameter);
+ UpdateParameterBase(in parameter);
Parameter = MemoryMarshal.Cast(parameter.SpecificData)[0];
IsEnabled = parameter.IsEnabled;
diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs
index d6cb9cfa39..5d82b5ae87 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs
@@ -19,21 +19,21 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
public override EffectType TargetEffectType => EffectType.BufferMix;
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
+ public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
- UpdateParameterBase(ref parameter);
+ UpdateParameterBase(in parameter);
Parameter = MemoryMarshal.Cast(parameter.SpecificData)[0];
IsEnabled = parameter.IsEnabled;
diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs
index 5be4b4ed51..6917222f0a 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Effect/CaptureBufferEffect.cs
@@ -32,21 +32,21 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return WorkBuffers[index].GetReference(true);
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
+ public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
- UpdateParameterBase(ref parameter);
+ UpdateParameterBase(in parameter);
Parameter = MemoryMarshal.Cast(parameter.SpecificData)[0];
IsEnabled = parameter.IsEnabled;
diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs
index 826c32cb07..eff60e7da8 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Effect/CompressorEffect.cs
@@ -39,17 +39,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return GetSingleBuffer();
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
{
// Nintendo doesn't do anything here but we still require updateErrorInfo to be initialised.
updateErrorInfo = new BehaviourParameter.ErrorInfo();
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
- UpdateParameterBase(ref parameter);
+ UpdateParameterBase(in parameter);
Parameter = MemoryMarshal.Cast(parameter.SpecificData)[0];
IsEnabled = parameter.IsEnabled;
diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs
index 43cabb7db9..9db1ce465d 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs
@@ -37,19 +37,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return GetSingleBuffer();
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
+ public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
ref DelayParameter delayParameter = ref MemoryMarshal.Cast(parameter.SpecificData)[0];
@@ -57,7 +57,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
if (delayParameter.IsChannelCountMaxValid())
{
- UpdateParameterBase(ref parameter);
+ UpdateParameterBase(in parameter);
UsageState oldParameterStatus = Parameter.Status;
diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs
index 3e2f7326d0..d9b3d5666e 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs
@@ -39,25 +39,25 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return GetSingleBuffer();
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
+ public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
ref LimiterParameter limiterParameter = ref MemoryMarshal.Cast(parameter.SpecificData)[0];
updateErrorInfo = new BehaviourParameter.ErrorInfo();
- UpdateParameterBase(ref parameter);
+ UpdateParameterBase(in parameter);
Parameter = limiterParameter;
diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs
index f9d7f4943c..4b13cfec62 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs
@@ -36,19 +36,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return GetSingleBuffer();
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
+ public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
ref Reverb3dParameter reverbParameter = ref MemoryMarshal.Cast(parameter.SpecificData)[0];
@@ -56,7 +56,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
if (reverbParameter.IsChannelCountMaxValid())
{
- UpdateParameterBase(ref parameter);
+ UpdateParameterBase(in parameter);
UsageState oldParameterStatus = Parameter.ParameterStatus;
diff --git a/src/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs b/src/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs
index 6fdf8fc23d..aa6e674481 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs
@@ -39,19 +39,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
return GetSingleBuffer();
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion1 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in EffectInParameterVersion2 parameter, PoolMapper mapper)
{
- Update(out updateErrorInfo, ref parameter, mapper);
+ Update(out updateErrorInfo, in parameter, mapper);
}
- public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
+ public void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, in T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
ref ReverbParameter reverbParameter = ref MemoryMarshal.Cast(parameter.SpecificData)[0];
@@ -59,7 +59,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
if (reverbParameter.IsChannelCountMaxValid())
{
- UpdateParameterBase(ref parameter);
+ UpdateParameterBase(in parameter);
UsageState oldParameterStatus = Parameter.Status;
diff --git a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs
index 391b80f8db..f67d0c1249 100644
--- a/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/MemoryPool/PoolMapper.cs
@@ -249,7 +249,7 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
/// Input user parameter.
/// Output user parameter.
/// Returns the of the operations performed.
- public UpdateResult Update(ref MemoryPoolState memoryPool, ref MemoryPoolInParameter inParameter, ref MemoryPoolOutStatus outStatus)
+ public UpdateResult Update(ref MemoryPoolState memoryPool, in MemoryPoolInParameter inParameter, ref MemoryPoolOutStatus outStatus)
{
MemoryPoolUserState inputState = inParameter.State;
diff --git a/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs b/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs
index 88ae448314..b90574da92 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Mix/MixState.cs
@@ -195,7 +195,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
/// The input parameter of the mix.
/// The splitter context.
/// Return true, new connections were done on the adjacency matrix.
- private bool UpdateConnection(EdgeMatrix edgeMatrix, ref MixParameter parameter, ref SplitterContext splitterContext)
+ private bool UpdateConnection(EdgeMatrix edgeMatrix, in MixParameter parameter, ref SplitterContext splitterContext)
{
bool hasNewConnections;
@@ -259,7 +259,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
/// The splitter context.
/// The behaviour context.
/// Return true if the mix was changed.
- public bool Update(EdgeMatrix edgeMatrix, ref MixParameter parameter, EffectContext effectContext, SplitterContext splitterContext, BehaviourContext behaviourContext)
+ public bool Update(EdgeMatrix edgeMatrix, in MixParameter parameter, EffectContext effectContext, SplitterContext splitterContext, BehaviourContext behaviourContext)
{
bool isDirty;
@@ -273,7 +273,7 @@ namespace Ryujinx.Audio.Renderer.Server.Mix
if (behaviourContext.IsSplitterSupported())
{
- isDirty = UpdateConnection(edgeMatrix, ref parameter, ref splitterContext);
+ isDirty = UpdateConnection(edgeMatrix, in parameter, ref splitterContext);
}
else
{
diff --git a/src/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs b/src/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs
index d36c5e260e..8c65e09bc3 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Sink/BaseSink.cs
@@ -59,7 +59,7 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
///
/// The user parameter.
/// Return true, if the sent by the user match the internal .
- public bool IsTypeValid(ref SinkInParameter parameter)
+ public bool IsTypeValid(in SinkInParameter parameter)
{
return parameter.Type == TargetSinkType;
}
@@ -76,7 +76,7 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
/// Update the internal common parameters from user parameter.
///
/// The user parameter.
- protected void UpdateStandardParameter(ref SinkInParameter parameter)
+ protected void UpdateStandardParameter(in SinkInParameter parameter)
{
if (IsUsed != parameter.IsUsed)
{
@@ -92,9 +92,9 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
/// The user parameter.
/// The user output status.
/// The mapper to use.
- public virtual void Update(out ErrorInfo errorInfo, ref SinkInParameter parameter, ref SinkOutStatus outStatus, PoolMapper mapper)
+ public virtual void Update(out ErrorInfo errorInfo, in SinkInParameter parameter, ref SinkOutStatus outStatus, PoolMapper mapper)
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
errorInfo = new ErrorInfo();
}
diff --git a/src/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs b/src/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs
index 097757988d..f2751cf29b 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Sink/CircularBufferSink.cs
@@ -44,18 +44,18 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
public override SinkType TargetSinkType => SinkType.CircularBuffer;
- public override void Update(out BehaviourParameter.ErrorInfo errorInfo, ref SinkInParameter parameter, ref SinkOutStatus outStatus, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo errorInfo, in SinkInParameter parameter, ref SinkOutStatus outStatus, PoolMapper mapper)
{
errorInfo = new BehaviourParameter.ErrorInfo();
outStatus = new SinkOutStatus();
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
ref CircularBufferParameter inputDeviceParameter = ref MemoryMarshal.Cast(parameter.SpecificData)[0];
if (parameter.IsUsed != IsUsed || ShouldSkip)
{
- UpdateStandardParameter(ref parameter);
+ UpdateStandardParameter(in parameter);
if (parameter.IsUsed)
{
diff --git a/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs b/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs
index e03fe11d49..afe2d4b1b7 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Sink/DeviceSink.cs
@@ -49,15 +49,15 @@ namespace Ryujinx.Audio.Renderer.Server.Sink
public override SinkType TargetSinkType => SinkType.Device;
- public override void Update(out BehaviourParameter.ErrorInfo errorInfo, ref SinkInParameter parameter, ref SinkOutStatus outStatus, PoolMapper mapper)
+ public override void Update(out BehaviourParameter.ErrorInfo errorInfo, in SinkInParameter parameter, ref SinkOutStatus outStatus, PoolMapper mapper)
{
- Debug.Assert(IsTypeValid(ref parameter));
+ Debug.Assert(IsTypeValid(in parameter));
ref DeviceParameter inputDeviceParameter = ref MemoryMarshal.Cast(parameter.SpecificData)[0];
if (parameter.IsUsed != IsUsed)
{
- UpdateStandardParameter(ref parameter);
+ UpdateStandardParameter(in parameter);
Parameter = inputDeviceParameter;
}
else
diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs
index e408692ab9..3efa783c37 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterContext.cs
@@ -2,10 +2,11 @@ using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Utils;
using Ryujinx.Common;
+using Ryujinx.Common.Extensions;
using System;
+using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
namespace Ryujinx.Audio.Renderer.Server.Splitter
{
@@ -25,7 +26,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
private Memory _splitterDestinations;
///
- /// If set to true, trust the user destination count in .
+ /// If set to true, trust the user destination count in .
///
public bool IsBugFixed { get; private set; }
@@ -110,7 +111,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
///
/// The storage.
/// The storage.
- /// If set to true, trust the user destination count in .
+ /// If set to true, trust the user destination count in .
private void Setup(Memory splitters, Memory splitterDestinations, bool isBugFixed)
{
_splitters = splitters;
@@ -148,11 +149,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
///
/// The splitter header.
/// The raw data after the splitter header.
- private void UpdateState(scoped ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan input)
+ private void UpdateState(in SplitterInParameterHeader inputHeader, ref SequenceReader input)
{
for (int i = 0; i < inputHeader.SplitterCount; i++)
{
- SplitterInParameter parameter = MemoryMarshal.Read(input);
+ ref readonly SplitterInParameter parameter = ref input.GetRefOrRefToCopy(out _);
Debug.Assert(parameter.IsMagicValid());
@@ -162,10 +163,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
{
ref SplitterState splitter = ref GetState(parameter.Id);
- splitter.Update(this, ref parameter, input[Unsafe.SizeOf()..]);
+ splitter.Update(this, in parameter, ref input);
}
- input = input[(0x1C + parameter.DestinationCount * 4)..];
+ // NOTE: there are 12 bytes of unused/unknown data after the destination IDs array.
+ input.Advance(0xC);
+ }
+ else
+ {
+ input.Rewind(Unsafe.SizeOf());
+ break;
}
}
}
@@ -175,11 +182,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
///
/// The splitter header.
/// The raw data after the splitter header.
- private void UpdateData(scoped ref SplitterInParameterHeader inputHeader, ref ReadOnlySpan input)
+ private void UpdateData(in SplitterInParameterHeader inputHeader, ref SequenceReader input)
{
for (int i = 0; i < inputHeader.SplitterDestinationCount; i++)
{
- SplitterDestinationInParameter parameter = MemoryMarshal.Read(input);
+ ref readonly SplitterDestinationInParameter parameter = ref input.GetRefOrRefToCopy(out _);
Debug.Assert(parameter.IsMagicValid());
@@ -191,8 +198,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
destination.Update(parameter);
}
-
- input = input[Unsafe.SizeOf()..];
+ }
+ else
+ {
+ input.Rewind(Unsafe.SizeOf());
+ break;
}
}
}
@@ -201,36 +211,33 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// Update splitter from user parameters.
///
/// The input raw user data.
- /// The total consumed size.
/// Return true if the update was successful.
- public bool Update(ReadOnlySpan input, out int consumedSize)
+ public bool Update(ref SequenceReader input)
{
if (_splitterDestinations.IsEmpty || _splitters.IsEmpty)
{
- consumedSize = 0;
-
return true;
}
- int originalSize = input.Length;
-
- SplitterInParameterHeader header = SpanIOHelper.Read(ref input);
+ ref readonly SplitterInParameterHeader header = ref input.GetRefOrRefToCopy(out _);
if (header.IsMagicValid())
{
ClearAllNewConnectionFlag();
- UpdateState(ref header, ref input);
- UpdateData(ref header, ref input);
+ UpdateState(in header, ref input);
+ UpdateData(in header, ref input);
- consumedSize = BitUtils.AlignUp(originalSize - input.Length, 0x10);
+ input.SetConsumed(BitUtils.AlignUp(input.Consumed, 0x10));
return true;
}
+ else
+ {
+ input.Rewind(Unsafe.SizeOf());
- consumedSize = 0;
-
- return false;
+ return false;
+ }
}
///
diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs
index e08ee9ea77..944f092d2c 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterState.cs
@@ -1,4 +1,5 @@
using Ryujinx.Audio.Renderer.Parameter;
+using Ryujinx.Common.Extensions;
using System;
using System.Buffers;
using System.Diagnostics;
@@ -122,7 +123,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
/// The splitter context.
/// The user parameter.
/// The raw input data after the .
- public void Update(SplitterContext context, ref SplitterInParameter parameter, ReadOnlySpan input)
+ public void Update(SplitterContext context, in SplitterInParameter parameter, ref SequenceReader input)
{
ClearLinks();
@@ -139,9 +140,9 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
if (destinationCount > 0)
{
- ReadOnlySpan destinationIds = MemoryMarshal.Cast(input);
+ input.ReadLittleEndian(out int destinationId);
- Memory destination = context.GetDestinationMemory(destinationIds[0]);
+ Memory destination = context.GetDestinationMemory(destinationId);
SetDestination(ref destination.Span[0]);
@@ -149,13 +150,20 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
for (int i = 1; i < destinationCount; i++)
{
- Memory nextDestination = context.GetDestinationMemory(destinationIds[i]);
+ input.ReadLittleEndian(out destinationId);
+
+ Memory nextDestination = context.GetDestinationMemory(destinationId);
destination.Span[0].Link(ref nextDestination.Span[0]);
destination = nextDestination;
}
}
+ if (destinationCount < parameter.DestinationCount)
+ {
+ input.Advance((parameter.DestinationCount - destinationCount) * sizeof(int));
+ }
+
Debug.Assert(parameter.Id == Id);
if (parameter.Id == Id)
diff --git a/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs b/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs
index 22eebc7ccc..f8d87f2d14 100644
--- a/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs
@@ -9,41 +9,40 @@ using Ryujinx.Audio.Renderer.Server.Sink;
using Ryujinx.Audio.Renderer.Server.Splitter;
using Ryujinx.Audio.Renderer.Server.Voice;
using Ryujinx.Audio.Renderer.Utils;
+using Ryujinx.Common.Extensions;
using Ryujinx.Common.Logging;
using System;
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
using static Ryujinx.Audio.Renderer.Common.BehaviourParameter;
namespace Ryujinx.Audio.Renderer.Server
{
- public class StateUpdater
+ public ref struct StateUpdater
{
- private readonly ReadOnlyMemory _inputOrigin;
+ private SequenceReader _inputReader;
+
private readonly ReadOnlyMemory _outputOrigin;
- private ReadOnlyMemory _input;
private Memory _output;
private readonly uint _processHandle;
private BehaviourContext _behaviourContext;
- private UpdateDataHeader _inputHeader;
+ private readonly ref readonly UpdateDataHeader _inputHeader;
private readonly Memory _outputHeader;
- private ref UpdateDataHeader OutputHeader => ref _outputHeader.Span[0];
+ private readonly ref UpdateDataHeader OutputHeader => ref _outputHeader.Span[0];
- public StateUpdater(ReadOnlyMemory input, Memory output, uint processHandle, BehaviourContext behaviourContext)
+ public StateUpdater(ReadOnlySequence input, Memory output, uint processHandle, BehaviourContext behaviourContext)
{
- _input = input;
- _inputOrigin = _input;
+ _inputReader = new SequenceReader(input);
_output = output;
_outputOrigin = _output;
_processHandle = processHandle;
_behaviourContext = behaviourContext;
- _inputHeader = SpanIOHelper.Read(ref _input);
+ _inputHeader = ref _inputReader.GetRefOrRefToCopy(out _);
_outputHeader = SpanMemoryManager.Cast(_output[..Unsafe.SizeOf()]);
OutputHeader.Initialize(_behaviourContext.UserRevision);
@@ -52,7 +51,7 @@ namespace Ryujinx.Audio.Renderer.Server
public ResultCode UpdateBehaviourContext()
{
- BehaviourParameter parameter = SpanIOHelper.Read(ref _input);
+ ref readonly BehaviourParameter parameter = ref _inputReader.GetRefOrRefToCopy(out _);
if (!BehaviourContext.CheckValidRevision(parameter.UserRevision) || parameter.UserRevision != _behaviourContext.UserRevision)
{
@@ -81,11 +80,11 @@ namespace Ryujinx.Audio.Renderer.Server
foreach (ref MemoryPoolState memoryPool in memoryPools)
{
- MemoryPoolInParameter parameter = SpanIOHelper.Read(ref _input);
+ ref readonly MemoryPoolInParameter parameter = ref _inputReader.GetRefOrRefToCopy(out _);
ref MemoryPoolOutStatus outStatus = ref SpanIOHelper.GetWriteRef(ref _output)[0];
- PoolMapper.UpdateResult updateResult = mapper.Update(ref memoryPool, ref parameter, ref outStatus);
+ PoolMapper.UpdateResult updateResult = mapper.Update(ref memoryPool, in parameter, ref outStatus);
if (updateResult != PoolMapper.UpdateResult.Success &&
updateResult != PoolMapper.UpdateResult.MapError &&
@@ -115,7 +114,7 @@ namespace Ryujinx.Audio.Renderer.Server
for (int i = 0; i < context.GetCount(); i++)
{
- VoiceChannelResourceInParameter parameter = SpanIOHelper.Read(ref _input);
+ ref readonly VoiceChannelResourceInParameter parameter = ref _inputReader.GetRefOrRefToCopy(out _);
ref VoiceChannelResource resource = ref context.GetChannelResource(i);
@@ -127,7 +126,7 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.Success;
}
- public ResultCode UpdateVoices(VoiceContext context, Memory memoryPools)
+ public ResultCode UpdateVoices(VoiceContext context, PoolMapper mapper)
{
if (context.GetCount() * Unsafe.SizeOf() != _inputHeader.VoicesSize)
{
@@ -136,11 +135,7 @@ namespace Ryujinx.Audio.Renderer.Server
int initialOutputSize = _output.Length;
- ReadOnlySpan parameters = MemoryMarshal.Cast(_input[..(int)_inputHeader.VoicesSize].Span);
-
- _input = _input[(int)_inputHeader.VoicesSize..];
-
- PoolMapper mapper = new(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled());
+ long initialInputConsumed = _inputReader.Consumed;
// First make everything not in use.
for (int i = 0; i < context.GetCount(); i++)
@@ -157,7 +152,7 @@ namespace Ryujinx.Audio.Renderer.Server
// Start processing
for (int i = 0; i < context.GetCount(); i++)
{
- VoiceInParameter parameter = parameters[i];
+ ref readonly VoiceInParameter parameter = ref _inputReader.GetRefOrRefToCopy(out _);
voiceUpdateStates.Fill(Memory.Empty);
@@ -181,14 +176,14 @@ namespace Ryujinx.Audio.Renderer.Server
currentVoiceState.Initialize();
}
- currentVoiceState.UpdateParameters(out ErrorInfo updateParameterError, ref parameter, ref mapper, ref _behaviourContext);
+ currentVoiceState.UpdateParameters(out ErrorInfo updateParameterError, in parameter, mapper, ref _behaviourContext);
if (updateParameterError.ErrorCode != ResultCode.Success)
{
_behaviourContext.AppendError(ref updateParameterError);
}
- currentVoiceState.UpdateWaveBuffers(out ErrorInfo[] waveBufferUpdateErrorInfos, ref parameter, voiceUpdateStates, ref mapper, ref _behaviourContext);
+ currentVoiceState.UpdateWaveBuffers(out ErrorInfo[] waveBufferUpdateErrorInfos, in parameter, voiceUpdateStates, mapper, ref _behaviourContext);
foreach (ref ErrorInfo errorInfo in waveBufferUpdateErrorInfos.AsSpan())
{
@@ -198,7 +193,7 @@ namespace Ryujinx.Audio.Renderer.Server
}
}
- currentVoiceState.WriteOutStatus(ref outStatus, ref parameter, voiceUpdateStates);
+ currentVoiceState.WriteOutStatus(ref outStatus, in parameter, voiceUpdateStates);
}
}
@@ -211,10 +206,12 @@ namespace Ryujinx.Audio.Renderer.Server
Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.VoicesSize);
+ _inputReader.SetConsumed(initialInputConsumed + _inputHeader.VoicesSize);
+
return ResultCode.Success;
}
- private static void ResetEffect(ref BaseEffect effect, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
+ private static void ResetEffect(ref BaseEffect effect, in T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter
{
effect.ForceUnmapBuffers(mapper);
@@ -234,17 +231,17 @@ namespace Ryujinx.Audio.Renderer.Server
};
}
- public ResultCode UpdateEffects(EffectContext context, bool isAudioRendererActive, Memory memoryPools)
+ public ResultCode UpdateEffects(EffectContext context, bool isAudioRendererActive, PoolMapper mapper)
{
if (_behaviourContext.IsEffectInfoVersion2Supported())
{
- return UpdateEffectsVersion2(context, isAudioRendererActive, memoryPools);
+ return UpdateEffectsVersion2(context, isAudioRendererActive, mapper);
}
- return UpdateEffectsVersion1(context, isAudioRendererActive, memoryPools);
+ return UpdateEffectsVersion1(context, isAudioRendererActive, mapper);
}
- public ResultCode UpdateEffectsVersion2(EffectContext context, bool isAudioRendererActive, Memory memoryPools)
+ public ResultCode UpdateEffectsVersion2(EffectContext context, bool isAudioRendererActive, PoolMapper mapper)
{
if (context.GetCount() * Unsafe.SizeOf() != _inputHeader.EffectsSize)
{
@@ -253,26 +250,22 @@ namespace Ryujinx.Audio.Renderer.Server
int initialOutputSize = _output.Length;
- ReadOnlySpan parameters = MemoryMarshal.Cast(_input[..(int)_inputHeader.EffectsSize].Span);
-
- _input = _input[(int)_inputHeader.EffectsSize..];
-
- PoolMapper mapper = new(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled());
+ long initialInputConsumed = _inputReader.Consumed;
for (int i = 0; i < context.GetCount(); i++)
{
- EffectInParameterVersion2 parameter = parameters[i];
+ ref readonly EffectInParameterVersion2 parameter = ref _inputReader.GetRefOrRefToCopy(out _);
ref EffectOutStatusVersion2 outStatus = ref SpanIOHelper.GetWriteRef(ref _output)[0];
ref BaseEffect effect = ref context.GetEffect(i);
- if (!effect.IsTypeValid(ref parameter))
+ if (!effect.IsTypeValid(in parameter))
{
- ResetEffect(ref effect, ref parameter, mapper);
+ ResetEffect(ref effect, in parameter, mapper);
}
- effect.Update(out ErrorInfo updateErrorInfo, ref parameter, mapper);
+ effect.Update(out ErrorInfo updateErrorInfo, in parameter, mapper);
if (updateErrorInfo.ErrorCode != ResultCode.Success)
{
@@ -297,10 +290,12 @@ namespace Ryujinx.Audio.Renderer.Server
Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.EffectsSize);
+ _inputReader.SetConsumed(initialInputConsumed + _inputHeader.EffectsSize);
+
return ResultCode.Success;
}
- public ResultCode UpdateEffectsVersion1(EffectContext context, bool isAudioRendererActive, Memory memoryPools)
+ public ResultCode UpdateEffectsVersion1(EffectContext context, bool isAudioRendererActive, PoolMapper mapper)
{
if (context.GetCount() * Unsafe.SizeOf() != _inputHeader.EffectsSize)
{
@@ -309,26 +304,22 @@ namespace Ryujinx.Audio.Renderer.Server
int initialOutputSize = _output.Length;
- ReadOnlySpan parameters = MemoryMarshal.Cast(_input[..(int)_inputHeader.EffectsSize].Span);
-
- _input = _input[(int)_inputHeader.EffectsSize..];
-
- PoolMapper mapper = new(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled());
+ long initialInputConsumed = _inputReader.Consumed;
for (int i = 0; i < context.GetCount(); i++)
{
- EffectInParameterVersion1 parameter = parameters[i];
+ ref readonly EffectInParameterVersion1 parameter = ref _inputReader.GetRefOrRefToCopy(out _);
ref EffectOutStatusVersion1 outStatus = ref SpanIOHelper.GetWriteRef(ref _output)[0];
ref BaseEffect effect = ref context.GetEffect(i);
- if (!effect.IsTypeValid(ref parameter))
+ if (!effect.IsTypeValid(in parameter))
{
- ResetEffect(ref effect, ref parameter, mapper);
+ ResetEffect(ref effect, in parameter, mapper);
}
- effect.Update(out ErrorInfo updateErrorInfo, ref parameter, mapper);
+ effect.Update(out ErrorInfo updateErrorInfo, in parameter, mapper);
if (updateErrorInfo.ErrorCode != ResultCode.Success)
{
@@ -345,38 +336,40 @@ namespace Ryujinx.Audio.Renderer.Server
Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.EffectsSize);
+ _inputReader.SetConsumed(initialInputConsumed + _inputHeader.EffectsSize);
+
return ResultCode.Success;
}
public ResultCode UpdateSplitter(SplitterContext context)
{
- if (context.Update(_input.Span, out int consumedSize))
+ if (context.Update(ref _inputReader))
{
- _input = _input[consumedSize..];
-
return ResultCode.Success;
}
return ResultCode.InvalidUpdateInfo;
}
- private static bool CheckMixParametersValidity(MixContext mixContext, uint mixBufferCount, uint inputMixCount, ReadOnlySpan parameters)
+ private static bool CheckMixParametersValidity(MixContext mixContext, uint mixBufferCount, uint inputMixCount, SequenceReader parameters)
{
uint maxMixStateCount = mixContext.GetCount();
uint totalRequiredMixBufferCount = 0;
for (int i = 0; i < inputMixCount; i++)
{
- if (parameters[i].IsUsed)
+ ref readonly MixParameter parameter = ref parameters.GetRefOrRefToCopy(out _);
+
+ if (parameter.IsUsed)
{
- if (parameters[i].DestinationMixId != Constants.UnusedMixId &&
- parameters[i].DestinationMixId > maxMixStateCount &&
- parameters[i].MixId != Constants.FinalMixId)
+ if (parameter.DestinationMixId != Constants.UnusedMixId &&
+ parameter.DestinationMixId > maxMixStateCount &&
+ parameter.MixId != Constants.FinalMixId)
{
return true;
}
- totalRequiredMixBufferCount += parameters[i].BufferCount;
+ totalRequiredMixBufferCount += parameter.BufferCount;
}
}
@@ -391,7 +384,7 @@ namespace Ryujinx.Audio.Renderer.Server
if (_behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported())
{
- MixInParameterDirtyOnlyUpdate parameter = MemoryMarshal.Cast(_input.Span)[0];
+ ref readonly MixInParameterDirtyOnlyUpdate parameter = ref _inputReader.GetRefOrRefToCopy(out _);
mixCount = parameter.MixCount;
@@ -411,25 +404,20 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.InvalidUpdateInfo;
}
- if (_behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported())
- {
- _input = _input[Unsafe.SizeOf()..];
- }
+ long initialInputConsumed = _inputReader.Consumed;
- ReadOnlySpan parameters = MemoryMarshal.Cast(_input.Span[..(int)inputMixSize]);
+ int parameterCount = (int)inputMixSize / Unsafe.SizeOf();
- _input = _input[(int)inputMixSize..];
-
- if (CheckMixParametersValidity(mixContext, mixBufferCount, mixCount, parameters))
+ if (CheckMixParametersValidity(mixContext, mixBufferCount, mixCount, _inputReader))
{
return ResultCode.InvalidUpdateInfo;
}
bool isMixContextDirty = false;
- for (int i = 0; i < parameters.Length; i++)
+ for (int i = 0; i < parameterCount; i++)
{
- MixParameter parameter = parameters[i];
+ ref readonly MixParameter parameter = ref _inputReader.GetRefOrRefToCopy(out _);
int mixId = i;
@@ -454,7 +442,7 @@ namespace Ryujinx.Audio.Renderer.Server
if (mix.IsUsed)
{
- isMixContextDirty |= mix.Update(mixContext.EdgeMatrix, ref parameter, effectContext, splitterContext, _behaviourContext);
+ isMixContextDirty |= mix.Update(mixContext.EdgeMatrix, in parameter, effectContext, splitterContext, _behaviourContext);
}
}
@@ -473,10 +461,12 @@ namespace Ryujinx.Audio.Renderer.Server
}
}
+ _inputReader.SetConsumed(initialInputConsumed + inputMixSize);
+
return ResultCode.Success;
}
- private static void ResetSink(ref BaseSink sink, ref SinkInParameter parameter)
+ private static void ResetSink(ref BaseSink sink, in SinkInParameter parameter)
{
sink.CleanUp();
@@ -489,10 +479,8 @@ namespace Ryujinx.Audio.Renderer.Server
};
}
- public ResultCode UpdateSinks(SinkContext context, Memory memoryPools)
+ public ResultCode UpdateSinks(SinkContext context, PoolMapper mapper)
{
- PoolMapper mapper = new(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled());
-
if (context.GetCount() * Unsafe.SizeOf() != _inputHeader.SinksSize)
{
return ResultCode.InvalidUpdateInfo;
@@ -500,22 +488,20 @@ namespace Ryujinx.Audio.Renderer.Server
int initialOutputSize = _output.Length;
- ReadOnlySpan parameters = MemoryMarshal.Cast(_input[..(int)_inputHeader.SinksSize].Span);
-
- _input = _input[(int)_inputHeader.SinksSize..];
+ long initialInputConsumed = _inputReader.Consumed;
for (int i = 0; i < context.GetCount(); i++)
{
- SinkInParameter parameter = parameters[i];
+ ref readonly SinkInParameter parameter = ref _inputReader.GetRefOrRefToCopy(out _);
ref SinkOutStatus outStatus = ref SpanIOHelper.GetWriteRef(ref _output)[0];
ref BaseSink sink = ref context.GetSink(i);
- if (!sink.IsTypeValid(ref parameter))
+ if (!sink.IsTypeValid(in parameter))
{
- ResetSink(ref sink, ref parameter);
+ ResetSink(ref sink, in parameter);
}
- sink.Update(out ErrorInfo updateErrorInfo, ref parameter, ref outStatus, mapper);
+ sink.Update(out ErrorInfo updateErrorInfo, in parameter, ref outStatus, mapper);
if (updateErrorInfo.ErrorCode != ResultCode.Success)
{
@@ -530,6 +516,8 @@ namespace Ryujinx.Audio.Renderer.Server
Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.SinksSize);
+ _inputReader.SetConsumed(initialInputConsumed + _inputHeader.SinksSize);
+
return ResultCode.Success;
}
@@ -540,7 +528,7 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.InvalidUpdateInfo;
}
- PerformanceInParameter parameter = SpanIOHelper.Read(ref _input);
+ ref readonly PerformanceInParameter parameter = ref _inputReader.GetRefOrRefToCopy(out _);
ref PerformanceOutStatus outStatus = ref SpanIOHelper.GetWriteRef(ref _output)[0];
@@ -585,9 +573,9 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.Success;
}
- public ResultCode CheckConsumedSize()
+ public readonly ResultCode CheckConsumedSize()
{
- int consumedInputSize = _inputOrigin.Length - _input.Length;
+ long consumedInputSize = _inputReader.Consumed;
int consumedOutputSize = _outputOrigin.Length - _output.Length;
if (consumedInputSize != _inputHeader.TotalSize)
diff --git a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs
index 225f7d31b0..040c70e6ce 100644
--- a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs
+++ b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs
@@ -254,7 +254,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
///
/// The user parameter.
/// Return true, if the server voice information needs to be updated.
- private readonly bool ShouldUpdateParameters(ref VoiceInParameter parameter)
+ private readonly bool ShouldUpdateParameters(in VoiceInParameter parameter)
{
if (DataSourceStateAddressInfo.CpuAddress == parameter.DataSourceStateAddress)
{
@@ -273,7 +273,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// The user parameter.
/// The mapper to use.
/// The behaviour context.
- public void UpdateParameters(out ErrorInfo outErrorInfo, ref VoiceInParameter parameter, ref PoolMapper poolMapper, ref BehaviourContext behaviourContext)
+ public void UpdateParameters(out ErrorInfo outErrorInfo, in VoiceInParameter parameter, PoolMapper poolMapper, ref BehaviourContext behaviourContext)
{
InUse = parameter.InUse;
Id = parameter.Id;
@@ -326,7 +326,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
VoiceDropFlag = false;
}
- if (ShouldUpdateParameters(ref parameter))
+ if (ShouldUpdateParameters(in parameter))
{
DataSourceStateUnmapped = !poolMapper.TryAttachBuffer(out outErrorInfo, ref DataSourceStateAddressInfo, parameter.DataSourceStateAddress, parameter.DataSourceStateSize);
}
@@ -380,7 +380,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// The given user output.
/// The user parameter.
/// The voice states associated to the .
- public void WriteOutStatus(ref VoiceOutStatus outStatus, ref VoiceInParameter parameter, ReadOnlySpan> voiceUpdateStates)
+ public void WriteOutStatus(ref VoiceOutStatus outStatus, in VoiceInParameter parameter, ReadOnlySpan> voiceUpdateStates)
{
#if DEBUG
// Sanity check in debug mode of the internal state
@@ -426,7 +426,12 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// The voice states associated to the .
/// The mapper to use.
/// The behaviour context.
- public void UpdateWaveBuffers(out ErrorInfo[] errorInfos, ref VoiceInParameter parameter, ReadOnlySpan> voiceUpdateStates, ref PoolMapper mapper, ref BehaviourContext behaviourContext)
+ public void UpdateWaveBuffers(
+ out ErrorInfo[] errorInfos,
+ in VoiceInParameter parameter,
+ ReadOnlySpan> voiceUpdateStates,
+ PoolMapper mapper,
+ ref BehaviourContext behaviourContext)
{
errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2];
@@ -444,7 +449,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
for (int i = 0; i < Constants.VoiceWaveBufferCount; i++)
{
- UpdateWaveBuffer(errorInfos.AsSpan(i * 2, 2), ref WaveBuffers[i], ref parameter.WaveBuffers[i], parameter.SampleFormat, voiceUpdateState.IsWaveBufferValid[i], ref mapper, ref behaviourContext);
+ UpdateWaveBuffer(errorInfos.AsSpan(i * 2, 2), ref WaveBuffers[i], ref parameter.WaveBuffers[i], parameter.SampleFormat, voiceUpdateState.IsWaveBufferValid[i], mapper, ref behaviourContext);
}
}
@@ -458,7 +463,14 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
/// If set to true, the server side wavebuffer is considered valid.
/// The mapper to use.
/// The behaviour context.
- private void UpdateWaveBuffer(Span errorInfos, ref WaveBuffer waveBuffer, ref WaveBufferInternal inputWaveBuffer, SampleFormat sampleFormat, bool isValid, ref PoolMapper mapper, ref BehaviourContext behaviourContext)
+ private void UpdateWaveBuffer(
+ Span errorInfos,
+ ref WaveBuffer waveBuffer,
+ ref WaveBufferInternal inputWaveBuffer,
+ SampleFormat sampleFormat,
+ bool isValid,
+ PoolMapper mapper,
+ ref BehaviourContext behaviourContext)
{
if (!isValid && waveBuffer.IsSendToAudioProcessor && waveBuffer.BufferAddressInfo.CpuAddress != 0)
{
diff --git a/src/Ryujinx.Common/Extensions/SequenceReaderExtensions.cs b/src/Ryujinx.Common/Extensions/SequenceReaderExtensions.cs
new file mode 100644
index 0000000000..5403c87c07
--- /dev/null
+++ b/src/Ryujinx.Common/Extensions/SequenceReaderExtensions.cs
@@ -0,0 +1,181 @@
+using System;
+using System.Buffers;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Common.Extensions
+{
+ public static class SequenceReaderExtensions
+ {
+ ///
+ /// Dumps the entire to a file, restoring its previous location afterward.
+ /// Useful for debugging purposes.
+ ///
+ /// The to write to a file
+ /// The path and name of the file to create and dump to
+ public static void DumpToFile(this ref SequenceReader reader, string fileFullName)
+ {
+ var initialConsumed = reader.Consumed;
+
+ reader.Rewind(initialConsumed);
+
+ using (var fileStream = System.IO.File.Create(fileFullName, 4096, System.IO.FileOptions.None))
+ {
+ while (reader.End == false)
+ {
+ var span = reader.CurrentSpan;
+ fileStream.Write(span);
+ reader.Advance(span.Length);
+ }
+ }
+
+ reader.SetConsumed(initialConsumed);
+ }
+
+ ///
+ /// Returns a reference to the desired value. This ref should always be used. The argument passed in should never be used, as this is only used for storage if the value
+ /// must be copied from multiple segments held by the .
+ ///
+ /// Type to get
+ /// The to read from
+ /// A location used as storage if (and only if) the value to be read spans multiple segments
+ /// A reference to the desired value, either directly to memory in the , or to if it has been used for copying the value in to
+ ///
+ /// DO NOT use after calling this method, as it will only
+ /// contain a value if the value couldn't be referenced directly because it spans multiple segments.
+ /// To discourage use, it is recommended to to call this method like the following:
+ ///
+ /// ref readonly MyStruct value = ref sequenceReader.GetRefOrRefToCopy{MyStruct}(out _);
+ ///
+ ///
+ /// The does not contain enough data to read a value of type
+ public static ref readonly T GetRefOrRefToCopy(this scoped ref SequenceReader reader, out T copyDestinationIfRequiredDoNotUse) where T : unmanaged
+ {
+ int lengthRequired = Unsafe.SizeOf();
+
+ ReadOnlySpan span = reader.UnreadSpan;
+ if (lengthRequired <= span.Length)
+ {
+ reader.Advance(lengthRequired);
+
+ copyDestinationIfRequiredDoNotUse = default;
+
+ ReadOnlySpan spanOfT = MemoryMarshal.Cast(span);
+
+ return ref spanOfT[0];
+ }
+ else
+ {
+ copyDestinationIfRequiredDoNotUse = default;
+
+ Span valueSpan = MemoryMarshal.CreateSpan(ref copyDestinationIfRequiredDoNotUse, 1);
+
+ Span valueBytesSpan = MemoryMarshal.AsBytes(valueSpan);
+
+ if (!reader.TryCopyTo(valueBytesSpan))
+ {
+ throw new ArgumentOutOfRangeException(nameof(reader), "The sequence is not long enough to read the desired value.");
+ }
+
+ reader.Advance(lengthRequired);
+
+ return ref valueSpan[0];
+ }
+ }
+
+ ///
+ /// Reads an as little endian.
+ ///
+ /// The to read from
+ /// A location to receive the read value
+ /// Thrown if there wasn't enough data for an
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ReadLittleEndian(this ref SequenceReader reader, out int value)
+ {
+ if (!reader.TryReadLittleEndian(out value))
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), "The sequence is not long enough to read the desired value.");
+ }
+ }
+
+ ///
+ /// Reads the desired unmanaged value by copying it to the specified .
+ ///
+ /// Type to read
+ /// The to read from
+ /// The target that will receive the read value
+ /// The does not contain enough data to read a value of type
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static void ReadUnmanaged(this ref SequenceReader reader, out T value) where T : unmanaged
+ {
+ if (!reader.TryReadUnmanaged(out value))
+ {
+ throw new ArgumentOutOfRangeException(nameof(value), "The sequence is not long enough to read the desired value.");
+ }
+ }
+
+ ///
+ /// Sets the reader's position as bytes consumed.
+ ///
+ /// The to set the position
+ /// The number of bytes consumed
+ public static void SetConsumed(ref this SequenceReader reader, long consumed)
+ {
+ reader.Rewind(reader.Consumed);
+ reader.Advance(consumed);
+ }
+
+ ///
+ /// Try to read the given type out of the buffer if possible. Warning: this is dangerous to use with arbitrary
+ /// structs - see remarks for full details.
+ ///
+ /// Type to read
+ ///
+ /// IMPORTANT: The read is a straight copy of bits. If a struct depends on specific state of it's members to
+ /// behave correctly this can lead to exceptions, etc. If reading endian specific integers, use the explicit
+ /// overloads such as
+ ///
+ ///
+ /// True if successful. will be default if failed (due to lack of space).
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe bool TryReadUnmanaged(ref this SequenceReader reader, out T value) where T : unmanaged
+ {
+ ReadOnlySpan span = reader.UnreadSpan;
+
+ if (span.Length < sizeof(T))
+ {
+ return TryReadUnmanagedMultiSegment(ref reader, out value);
+ }
+
+ value = Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(span));
+
+ reader.Advance(sizeof(T));
+
+ return true;
+ }
+
+ private static unsafe bool TryReadUnmanagedMultiSegment(ref SequenceReader reader, out T value) where T : unmanaged
+ {
+ Debug.Assert(reader.UnreadSpan.Length < sizeof(T));
+
+ // Not enough data in the current segment, try to peek for the data we need.
+ T buffer = default;
+
+ Span tempSpan = new Span(&buffer, sizeof(T));
+
+ if (!reader.TryCopyTo(tempSpan))
+ {
+ value = default;
+ return false;
+ }
+
+ value = Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(tempSpan));
+
+ reader.Advance(sizeof(T));
+
+ return true;
+ }
+ }
+}
diff --git a/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
index df3f8dc93e..05fb29ac71 100644
--- a/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
+++ b/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
@@ -4,7 +4,7 @@ using System.Threading;
namespace Ryujinx.Common.Memory
{
- public sealed partial class ByteMemoryPool
+ public partial class ByteMemoryPool
{
///
/// Represents a that wraps an array rented from
diff --git a/src/Ryujinx.Common/Memory/ByteMemoryPool.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.cs
index 071f56b136..6fd6a98aa7 100644
--- a/src/Ryujinx.Common/Memory/ByteMemoryPool.cs
+++ b/src/Ryujinx.Common/Memory/ByteMemoryPool.cs
@@ -6,24 +6,8 @@ namespace Ryujinx.Common.Memory
///
/// Provides a pool of re-usable byte array instances.
///
- public sealed partial class ByteMemoryPool
+ public static partial class ByteMemoryPool
{
- private static readonly ByteMemoryPool _shared = new();
-
- ///
- /// Constructs a instance. Private to force access through
- /// the instance.
- ///
- private ByteMemoryPool()
- {
- // No implementation
- }
-
- ///
- /// Retrieves a shared instance.
- ///
- public static ByteMemoryPool Shared => _shared;
-
///
/// Returns the maximum buffer size supported by this pool.
///
@@ -95,6 +79,20 @@ namespace Ryujinx.Common.Memory
return buffer;
}
+ ///
+ /// Copies into a newly rented byte memory buffer.
+ ///
+ /// The byte buffer to copy
+ /// A wrapping the rented memory with copied to it
+ public static IMemoryOwner RentCopy(ReadOnlySpan buffer)
+ {
+ var copy = RentImpl(buffer.Length);
+
+ buffer.CopyTo(copy.Memory.Span);
+
+ return copy;
+ }
+
private static ByteMemoryPoolBuffer RentImpl(int length)
{
if ((uint)length > Array.MaxLength)
diff --git a/src/Ryujinx.Common/Memory/SpanOrArray.cs b/src/Ryujinx.Common/Memory/SpanOrArray.cs
deleted file mode 100644
index 269ac02fd6..0000000000
--- 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 a4facc2e37..e22571c966 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 7a20c98e95..74b6af5ecf 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.Cpu/AddressSpace.cs b/src/Ryujinx.Cpu/AddressSpace.cs
index beea14beec..6664ed1345 100644
--- a/src/Ryujinx.Cpu/AddressSpace.cs
+++ b/src/Ryujinx.Cpu/AddressSpace.cs
@@ -1,5 +1,3 @@
-using Ryujinx.Common;
-using Ryujinx.Common.Collections;
using Ryujinx.Memory;
using System;
@@ -7,175 +5,23 @@ namespace Ryujinx.Cpu
{
public class AddressSpace : IDisposable
{
- private const int DefaultBlockAlignment = 1 << 20;
-
- private enum MappingType : byte
- {
- None,
- Private,
- Shared,
- }
-
- private class Mapping : IntrusiveRedBlackTreeNode, IComparable
- {
- public ulong Address { get; private set; }
- public ulong Size { get; private set; }
- public ulong EndAddress => Address + Size;
- public MappingType Type { get; private set; }
-
- public Mapping(ulong address, ulong size, MappingType type)
- {
- Address = address;
- Size = size;
- Type = type;
- }
-
- public Mapping Split(ulong splitAddress)
- {
- ulong leftSize = splitAddress - Address;
- ulong rightSize = EndAddress - splitAddress;
-
- Mapping left = new(Address, leftSize, Type);
-
- Address = splitAddress;
- Size = rightSize;
-
- return left;
- }
-
- public void UpdateState(MappingType newType)
- {
- Type = newType;
- }
-
- public void Extend(ulong sizeDelta)
- {
- Size += sizeDelta;
- }
-
- public int CompareTo(Mapping other)
- {
- if (Address < other.Address)
- {
- return -1;
- }
- else if (Address <= other.EndAddress - 1UL)
- {
- return 0;
- }
- else
- {
- return 1;
- }
- }
- }
-
- private class PrivateMapping : IntrusiveRedBlackTreeNode, IComparable
- {
- public ulong Address { get; private set; }
- public ulong Size { get; private set; }
- public ulong EndAddress => Address + Size;
- public PrivateMemoryAllocation PrivateAllocation { get; private set; }
-
- public PrivateMapping(ulong address, ulong size, PrivateMemoryAllocation privateAllocation)
- {
- Address = address;
- Size = size;
- PrivateAllocation = privateAllocation;
- }
-
- public PrivateMapping Split(ulong splitAddress)
- {
- ulong leftSize = splitAddress - Address;
- ulong rightSize = EndAddress - splitAddress;
-
- (var leftAllocation, PrivateAllocation) = PrivateAllocation.Split(leftSize);
-
- PrivateMapping left = new(Address, leftSize, leftAllocation);
-
- Address = splitAddress;
- Size = rightSize;
-
- return left;
- }
-
- public void Map(MemoryBlock baseBlock, MemoryBlock mirrorBlock, PrivateMemoryAllocation newAllocation)
- {
- baseBlock.MapView(newAllocation.Memory, newAllocation.Offset, Address, Size);
- mirrorBlock.MapView(newAllocation.Memory, newAllocation.Offset, Address, Size);
- PrivateAllocation = newAllocation;
- }
-
- public void Unmap(MemoryBlock baseBlock, MemoryBlock mirrorBlock)
- {
- if (PrivateAllocation.IsValid)
- {
- baseBlock.UnmapView(PrivateAllocation.Memory, Address, Size);
- mirrorBlock.UnmapView(PrivateAllocation.Memory, Address, Size);
- PrivateAllocation.Dispose();
- }
-
- PrivateAllocation = default;
- }
-
- public void Extend(ulong sizeDelta)
- {
- Size += sizeDelta;
- }
-
- public int CompareTo(PrivateMapping other)
- {
- if (Address < other.Address)
- {
- return -1;
- }
- else if (Address <= other.EndAddress - 1UL)
- {
- return 0;
- }
- else
- {
- return 1;
- }
- }
- }
-
private readonly MemoryBlock _backingMemory;
- private readonly PrivateMemoryAllocator _privateMemoryAllocator;
- private readonly IntrusiveRedBlackTree _mappingTree;
- private readonly IntrusiveRedBlackTree _privateTree;
-
- private readonly object _treeLock;
-
- private readonly bool _supports4KBPages;
public MemoryBlock Base { get; }
public MemoryBlock Mirror { get; }
public ulong AddressSpaceSize { get; }
- public AddressSpace(MemoryBlock backingMemory, MemoryBlock baseMemory, MemoryBlock mirrorMemory, ulong addressSpaceSize, bool supports4KBPages)
+ public AddressSpace(MemoryBlock backingMemory, MemoryBlock baseMemory, MemoryBlock mirrorMemory, ulong addressSpaceSize)
{
- if (!supports4KBPages)
- {
- _privateMemoryAllocator = new PrivateMemoryAllocator(DefaultBlockAlignment, MemoryAllocationFlags.Mirrorable | MemoryAllocationFlags.NoMap);
- _mappingTree = new IntrusiveRedBlackTree();
- _privateTree = new IntrusiveRedBlackTree();
- _treeLock = new object();
-
- _mappingTree.Add(new Mapping(0UL, addressSpaceSize, MappingType.None));
- _privateTree.Add(new PrivateMapping(0UL, addressSpaceSize, default));
- }
-
_backingMemory = backingMemory;
- _supports4KBPages = supports4KBPages;
Base = baseMemory;
Mirror = mirrorMemory;
AddressSpaceSize = addressSpaceSize;
}
- public static bool TryCreate(MemoryBlock backingMemory, ulong asSize, bool supports4KBPages, out AddressSpace addressSpace)
+ public static bool TryCreate(MemoryBlock backingMemory, ulong asSize, out AddressSpace addressSpace)
{
addressSpace = null;
@@ -193,7 +39,7 @@ namespace Ryujinx.Cpu
{
baseMemory = new MemoryBlock(addressSpaceSize, AsFlags);
mirrorMemory = new MemoryBlock(addressSpaceSize, AsFlags);
- addressSpace = new AddressSpace(backingMemory, baseMemory, mirrorMemory, addressSpaceSize, supports4KBPages);
+ addressSpace = new AddressSpace(backingMemory, baseMemory, mirrorMemory, addressSpaceSize);
break;
}
@@ -209,289 +55,20 @@ namespace Ryujinx.Cpu
public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
{
- if (_supports4KBPages)
- {
- Base.MapView(_backingMemory, pa, va, size);
- Mirror.MapView(_backingMemory, pa, va, size);
-
- return;
- }
-
- lock (_treeLock)
- {
- ulong alignment = MemoryBlock.GetPageSize();
- bool isAligned = ((va | pa | size) & (alignment - 1)) == 0;
-
- if (flags.HasFlag(MemoryMapFlags.Private) && !isAligned)
- {
- Update(va, pa, size, MappingType.Private);
- }
- else
- {
- // The update method assumes that shared mappings are already aligned.
-
- if (!flags.HasFlag(MemoryMapFlags.Private))
- {
- if ((va & (alignment - 1)) != (pa & (alignment - 1)))
- {
- throw new InvalidMemoryRegionException($"Virtual address 0x{va:X} and physical address 0x{pa:X} are misaligned and can't be aligned.");
- }
-
- ulong endAddress = va + size;
- va = BitUtils.AlignDown(va, alignment);
- pa = BitUtils.AlignDown(pa, alignment);
- size = BitUtils.AlignUp(endAddress, alignment) - va;
- }
-
- Update(va, pa, size, MappingType.Shared);
- }
- }
+ Base.MapView(_backingMemory, pa, va, size);
+ Mirror.MapView(_backingMemory, pa, va, size);
}
public void Unmap(ulong va, ulong size)
{
- if (_supports4KBPages)
- {
- Base.UnmapView(_backingMemory, va, size);
- Mirror.UnmapView(_backingMemory, va, size);
-
- return;
- }
-
- lock (_treeLock)
- {
- Update(va, 0UL, size, MappingType.None);
- }
- }
-
- private void Update(ulong va, ulong pa, ulong size, MappingType type)
- {
- Mapping map = _mappingTree.GetNode(new Mapping(va, 1UL, MappingType.None));
-
- Update(map, va, pa, size, type);
- }
-
- private Mapping Update(Mapping map, ulong va, ulong pa, ulong size, MappingType type)
- {
- ulong endAddress = va + size;
-
- for (; map != null; map = map.Successor)
- {
- if (map.Address < va)
- {
- _mappingTree.Add(map.Split(va));
- }
-
- if (map.EndAddress > endAddress)
- {
- Mapping newMap = map.Split(endAddress);
- _mappingTree.Add(newMap);
- map = newMap;
- }
-
- switch (type)
- {
- case MappingType.None:
- if (map.Type == MappingType.Shared)
- {
- ulong startOffset = map.Address - va;
- ulong mapVa = va + startOffset;
- ulong mapSize = Math.Min(size - startOffset, map.Size);
- ulong mapEndAddress = mapVa + mapSize;
- ulong alignment = MemoryBlock.GetPageSize();
-
- mapVa = BitUtils.AlignDown(mapVa, alignment);
- mapEndAddress = BitUtils.AlignUp(mapEndAddress, alignment);
-
- mapSize = mapEndAddress - mapVa;
-
- Base.UnmapView(_backingMemory, mapVa, mapSize);
- Mirror.UnmapView(_backingMemory, mapVa, mapSize);
- }
- else
- {
- UnmapPrivate(va, size);
- }
- break;
- case MappingType.Private:
- if (map.Type == MappingType.Shared)
- {
- throw new InvalidMemoryRegionException($"Private mapping request at 0x{va:X} with size 0x{size:X} overlaps shared mapping at 0x{map.Address:X} with size 0x{map.Size:X}.");
- }
- else
- {
- MapPrivate(va, size);
- }
- break;
- case MappingType.Shared:
- if (map.Type != MappingType.None)
- {
- throw new InvalidMemoryRegionException($"Shared mapping request at 0x{va:X} with size 0x{size:X} overlaps mapping at 0x{map.Address:X} with size 0x{map.Size:X}.");
- }
- else
- {
- ulong startOffset = map.Address - va;
- ulong mapPa = pa + startOffset;
- ulong mapVa = va + startOffset;
- ulong mapSize = Math.Min(size - startOffset, map.Size);
-
- Base.MapView(_backingMemory, mapPa, mapVa, mapSize);
- Mirror.MapView(_backingMemory, mapPa, mapVa, mapSize);
- }
- break;
- }
-
- map.UpdateState(type);
- map = TryCoalesce(map);
-
- if (map.EndAddress >= endAddress)
- {
- break;
- }
- }
-
- return map;
- }
-
- private Mapping TryCoalesce(Mapping map)
- {
- Mapping previousMap = map.Predecessor;
- Mapping nextMap = map.Successor;
-
- if (previousMap != null && CanCoalesce(previousMap, map))
- {
- previousMap.Extend(map.Size);
- _mappingTree.Remove(map);
- map = previousMap;
- }
-
- if (nextMap != null && CanCoalesce(map, nextMap))
- {
- map.Extend(nextMap.Size);
- _mappingTree.Remove(nextMap);
- }
-
- return map;
- }
-
- private static bool CanCoalesce(Mapping left, Mapping right)
- {
- return left.Type == right.Type;
- }
-
- private void MapPrivate(ulong va, ulong size)
- {
- ulong endAddress = va + size;
-
- ulong alignment = MemoryBlock.GetPageSize();
-
- // Expand the range outwards based on page size to ensure that at least the requested region is mapped.
- ulong vaAligned = BitUtils.AlignDown(va, alignment);
- ulong endAddressAligned = BitUtils.AlignUp(endAddress, alignment);
-
- PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default));
-
- for (; map != null; map = map.Successor)
- {
- if (!map.PrivateAllocation.IsValid)
- {
- if (map.Address < vaAligned)
- {
- _privateTree.Add(map.Split(vaAligned));
- }
-
- if (map.EndAddress > endAddressAligned)
- {
- PrivateMapping newMap = map.Split(endAddressAligned);
- _privateTree.Add(newMap);
- map = newMap;
- }
-
- map.Map(Base, Mirror, _privateMemoryAllocator.Allocate(map.Size, MemoryBlock.GetPageSize()));
- }
-
- if (map.EndAddress >= endAddressAligned)
- {
- break;
- }
- }
- }
-
- private void UnmapPrivate(ulong va, ulong size)
- {
- ulong endAddress = va + size;
-
- ulong alignment = MemoryBlock.GetPageSize();
-
- // Shrink the range inwards based on page size to ensure we won't unmap memory that might be still in use.
- ulong vaAligned = BitUtils.AlignUp(va, alignment);
- ulong endAddressAligned = BitUtils.AlignDown(endAddress, alignment);
-
- if (endAddressAligned <= vaAligned)
- {
- return;
- }
-
- PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default));
-
- for (; map != null; map = map.Successor)
- {
- if (map.PrivateAllocation.IsValid)
- {
- if (map.Address < vaAligned)
- {
- _privateTree.Add(map.Split(vaAligned));
- }
-
- if (map.EndAddress > endAddressAligned)
- {
- PrivateMapping newMap = map.Split(endAddressAligned);
- _privateTree.Add(newMap);
- map = newMap;
- }
-
- map.Unmap(Base, Mirror);
- map = TryCoalesce(map);
- }
-
- if (map.EndAddress >= endAddressAligned)
- {
- break;
- }
- }
- }
-
- private PrivateMapping TryCoalesce(PrivateMapping map)
- {
- PrivateMapping previousMap = map.Predecessor;
- PrivateMapping nextMap = map.Successor;
-
- if (previousMap != null && CanCoalesce(previousMap, map))
- {
- previousMap.Extend(map.Size);
- _privateTree.Remove(map);
- map = previousMap;
- }
-
- if (nextMap != null && CanCoalesce(map, nextMap))
- {
- map.Extend(nextMap.Size);
- _privateTree.Remove(nextMap);
- }
-
- return map;
- }
-
- private static bool CanCoalesce(PrivateMapping left, PrivateMapping right)
- {
- return !left.PrivateAllocation.IsValid && !right.PrivateAllocation.IsValid;
+ Base.UnmapView(_backingMemory, va, size);
+ Mirror.UnmapView(_backingMemory, va, size);
}
public void Dispose()
{
GC.SuppressFinalize(this);
- _privateMemoryAllocator?.Dispose();
Base.Dispose();
Mirror.Dispose();
}
diff --git a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs
index 80f7c8a1f8..abdddb31c3 100644
--- a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs
+++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs
@@ -3,10 +3,10 @@ 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.CompilerServices;
-using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Cpu.AppleHv
@@ -15,7 +15,7 @@ namespace Ryujinx.Cpu.AppleHv
/// Represents a CPU memory manager which maps guest virtual memory directly onto the Hypervisor page table.
///
[SupportedOSPlatform("macos")]
- public class HvMemoryManager : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
+ public sealed class HvMemoryManager : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
{
private readonly InvalidAccessHandler _invalidAccessHandler;
@@ -28,7 +28,7 @@ namespace Ryujinx.Cpu.AppleHv
private readonly ManagedPageFlags _pages;
- public bool Supports4KBPages => true;
+ public bool UsesPrivateAllocations => false;
public int AddressSpaceBits { get; }
@@ -96,12 +96,6 @@ namespace Ryujinx.Cpu.AppleHv
}
}
- ///
- public void MapForeign(ulong va, nuint hostPointer, ulong size)
- {
- throw new NotSupportedException();
- }
-
///
public void Unmap(ulong va, ulong size)
{
@@ -126,20 +120,11 @@ namespace Ryujinx.Cpu.AppleHv
}
}
- ///
- public T Read(ulong va) where T : unmanaged
- {
- return MemoryMarshal.Cast(GetSpan(va, Unsafe.SizeOf()))[0];
- }
-
- ///
- public T ReadTracked(ulong va) where T : unmanaged
+ public override T ReadTracked(ulong va)
{
try
{
- SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), false);
-
- return Read(va);
+ return base.ReadTracked(va);
}
catch (InvalidMemoryRegionException)
{
@@ -152,7 +137,6 @@ namespace Ryujinx.Cpu.AppleHv
}
}
- ///
public override void Read(ulong va, Span data)
{
try
@@ -168,101 +152,11 @@ namespace Ryujinx.Cpu.AppleHv
}
}
- ///
- public void Write(ulong va, T value) where T : unmanaged
- {
- Write(va, MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref value, 1)));
- }
-
- ///
- public void Write(ulong va, ReadOnlySpan data)
- {
- if (data.Length == 0)
- {
- return;
- }
-
- SignalMemoryTracking(va, (ulong)data.Length, true);
-
- WriteImpl(va, data);
- }
-
- ///
- public void WriteUntracked(ulong va, ReadOnlySpan data)
- {
- if (data.Length == 0)
- {
- return;
- }
-
- WriteImpl(va, data);
- }
-
- ///
- public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data)
- {
- if (data.Length == 0)
- {
- return false;
- }
-
- SignalMemoryTracking(va, (ulong)data.Length, false);
-
- if (IsContiguousAndMapped(va, data.Length))
- {
- var target = _backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length);
-
- bool changed = !data.SequenceEqual(target);
-
- if (changed)
- {
- data.CopyTo(target);
- }
-
- return changed;
- }
- else
- {
- WriteImpl(va, data);
-
- return true;
- }
- }
-
- private void WriteImpl(ulong va, ReadOnlySpan data)
+ public override void Write(ulong va, ReadOnlySpan data)
{
try
{
- AssertValidAddressAndSize(va, (ulong)data.Length);
-
- if (IsContiguousAndMapped(va, data.Length))
- {
- data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length));
- }
- else
- {
- int offset = 0, size;
-
- if ((va & PageMask) != 0)
- {
- ulong pa = GetPhysicalAddressChecked(va);
-
- size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
-
- data[..size].CopyTo(_backingMemory.GetSpan(pa, size));
-
- offset += size;
- }
-
- for (; offset < data.Length; offset += size)
- {
- ulong pa = GetPhysicalAddressChecked(va + (ulong)offset);
-
- size = Math.Min(data.Length - offset, PageSize);
-
- data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size));
- }
- }
+ base.Write(va, data);
}
catch (InvalidMemoryRegionException)
{
@@ -273,61 +167,38 @@ namespace Ryujinx.Cpu.AppleHv
}
}
- ///
- public ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false)
+ public override void WriteUntracked(ulong va, ReadOnlySpan data)
{
- if (size == 0)
+ try
{
- return ReadOnlySpan.Empty;
+ base.WriteUntracked(va, data);
}
-
- if (tracked)
+ catch (InvalidMemoryRegionException)
{
- SignalMemoryTracking(va, (ulong)size, false);
- }
-
- if (IsContiguousAndMapped(va, size))
- {
- return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size);
- }
- else
- {
- Span data = new byte[size];
-
- base.Read(va, data);
-
- return data;
+ if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
+ {
+ throw;
+ }
}
}
- ///
- public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
+ public override ReadOnlySequence GetReadOnlySequence(ulong va, int size, bool tracked = false)
{
- if (size == 0)
+ try
{
- return new WritableRegion(null, va, Memory.Empty);
+ return base.GetReadOnlySequence(va, size, tracked);
}
-
- if (tracked)
+ catch (InvalidMemoryRegionException)
{
- SignalMemoryTracking(va, (ulong)size, true);
- }
+ if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
+ {
+ throw;
+ }
- if (IsContiguousAndMapped(va, size))
- {
- return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size));
- }
- else
- {
- Memory memory = new byte[size];
-
- base.Read(va, memory.Span);
-
- return new WritableRegion(this, va, memory);
+ return ReadOnlySequence.Empty;
}
}
- ///
public ref T GetRef(ulong va) where T : unmanaged
{
if (!IsContiguous(va, Unsafe.SizeOf()))
@@ -340,9 +211,8 @@ namespace Ryujinx.Cpu.AppleHv
return ref _backingMemory.GetRef(GetPhysicalAddressChecked(va));
}
- ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool IsMapped(ulong va)
+ public override bool IsMapped(ulong va)
{
return ValidateAddress(va) && _pages.IsMapped(va);
}
@@ -355,39 +225,6 @@ namespace Ryujinx.Cpu.AppleHv
return _pages.IsRangeMapped(va, size);
}
- private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private bool IsContiguous(ulong va, int size)
- {
- if (!ValidateAddress(va) || !ValidateAddressAndSize(va, (ulong)size))
- {
- return false;
- }
-
- int pages = GetPagesCount(va, (uint)size, out va);
-
- for (int page = 0; page < pages - 1; page++)
- {
- if (!ValidateAddress(va + PageSize))
- {
- return false;
- }
-
- if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize))
- {
- return false;
- }
-
- va += PageSize;
- }
-
- return true;
- }
-
///
public IEnumerable GetHostRegions(ulong va, ulong size)
{
@@ -464,11 +301,10 @@ namespace Ryujinx.Cpu.AppleHv
return regions;
}
- ///
///
/// This function also validates that the given range is both valid and mapped, and will throw if it is not.
///
- public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
+ public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{
AssertValidAddressAndSize(va, size);
@@ -481,24 +317,6 @@ namespace Ryujinx.Cpu.AppleHv
_pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
}
- ///
- /// Computes the number of pages in a virtual address range.
- ///
- /// Virtual address of the range
- /// Size of the range
- /// The virtual address of the beginning of the first page
- /// This function does not differentiate between allocated and unallocated pages.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int GetPagesCount(ulong va, ulong size, out ulong startVa)
- {
- // WARNING: Always check if ulong does not overflow during the operations.
- startVa = va & ~(ulong)PageMask;
- ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
-
- return (int)(vaSpan / PageSize);
- }
-
- ///
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
{
// TODO
@@ -535,7 +353,7 @@ namespace Ryujinx.Cpu.AppleHv
return Tracking.BeginSmartGranularTracking(address, size, granularity, id);
}
- private ulong GetPhysicalAddressChecked(ulong va)
+ private nuint GetPhysicalAddressChecked(ulong va)
{
if (!IsMapped(va))
{
@@ -545,9 +363,9 @@ namespace Ryujinx.Cpu.AppleHv
return GetPhysicalAddressInternal(va);
}
- private ulong GetPhysicalAddressInternal(ulong va)
+ private nuint GetPhysicalAddressInternal(ulong va)
{
- return _pageTable.Read(va) + (va & PageMask);
+ return (nuint)(_pageTable.Read(va) + (va & PageMask));
}
///
@@ -558,10 +376,17 @@ namespace Ryujinx.Cpu.AppleHv
_addressSpace.Dispose();
}
- protected override Span GetPhysicalAddressSpan(ulong pa, int size)
+ protected override Memory GetPhysicalAddressMemory(nuint pa, int size)
+ => _backingMemory.GetMemory(pa, size);
+
+ protected override Span GetPhysicalAddressSpan(nuint pa, int size)
=> _backingMemory.GetSpan(pa, size);
- protected override ulong TranslateVirtualAddressForRead(ulong va)
+ protected override nuint TranslateVirtualAddressChecked(ulong va)
=> GetPhysicalAddressChecked(va);
+
+ protected override nuint TranslateVirtualAddressUnchecked(ulong va)
+ => GetPhysicalAddressInternal(va);
+
}
}
diff --git a/src/Ryujinx.Cpu/Jit/MemoryManager.cs b/src/Ryujinx.Cpu/Jit/MemoryManager.cs
index c87c8b8cc5..6f594ec2fd 100644
--- a/src/Ryujinx.Cpu/Jit/MemoryManager.cs
+++ b/src/Ryujinx.Cpu/Jit/MemoryManager.cs
@@ -3,6 +3,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.CompilerServices;
@@ -14,7 +15,7 @@ namespace Ryujinx.Cpu.Jit
///
/// Represents a CPU memory manager.
///
- public sealed class MemoryManager : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
+ public sealed class MemoryManager : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
{
private const int PteSize = 8;
@@ -24,7 +25,7 @@ namespace Ryujinx.Cpu.Jit
private readonly InvalidAccessHandler _invalidAccessHandler;
///
- public bool Supports4KBPages => true;
+ public bool UsesPrivateAllocations => false;
///
/// Address space width in bits.
@@ -97,12 +98,6 @@ namespace Ryujinx.Cpu.Jit
Tracking.Map(oVa, size);
}
- ///
- public void MapForeign(ulong va, nuint hostPointer, ulong size)
- {
- throw new NotSupportedException();
- }
-
///
public void Unmap(ulong va, ulong size)
{
@@ -128,20 +123,11 @@ namespace Ryujinx.Cpu.Jit
}
}
- ///
- public T Read(ulong va) where T : unmanaged
- {
- return MemoryMarshal.Cast(GetSpan(va, Unsafe.SizeOf()))[0];
- }
-
- ///
- public T ReadTracked(ulong va) where T : unmanaged
+ public override T ReadTracked(ulong va)
{
try
{
- SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), false);
-
- return Read(va);
+ return base.ReadTracked(va);
}
catch (InvalidMemoryRegionException)
{
@@ -190,117 +176,11 @@ namespace Ryujinx.Cpu.Jit
}
}
- ///
- public void Write(ulong va, T value) where T : unmanaged
- {
- Write(va, MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref value, 1)));
- }
-
- ///
- public void Write(ulong va, ReadOnlySpan data)
- {
- if (data.Length == 0)
- {
- return;
- }
-
- SignalMemoryTracking(va, (ulong)data.Length, true);
-
- WriteImpl(va, data);
- }
-
- ///
- public void WriteGuest(ulong va, T value) where T : unmanaged
- {
- Span data = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref value, 1));
-
- SignalMemoryTrackingImpl(va, (ulong)data.Length, true, true);
-
- WriteImpl(va, data);
- }
-
- ///
- public void WriteUntracked(ulong va, ReadOnlySpan data)
- {
- if (data.Length == 0)
- {
- return;
- }
-
- WriteImpl(va, data);
- }
-
- ///
- public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data)
- {
- if (data.Length == 0)
- {
- return false;
- }
-
- SignalMemoryTracking(va, (ulong)data.Length, false);
-
- if (IsContiguousAndMapped(va, data.Length))
- {
- var target = _backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length);
-
- bool changed = !data.SequenceEqual(target);
-
- if (changed)
- {
- data.CopyTo(target);
- }
-
- return changed;
- }
- else
- {
- WriteImpl(va, data);
-
- return true;
- }
- }
-
- ///
- /// Writes data to CPU mapped memory.
- ///
- /// Virtual address to write the data into
- /// Data to be written
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void WriteImpl(ulong va, ReadOnlySpan data)
+ public override void Write(ulong va, ReadOnlySpan data)
{
try
{
- AssertValidAddressAndSize(va, (ulong)data.Length);
-
- if (IsContiguousAndMapped(va, data.Length))
- {
- data.CopyTo(_backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length));
- }
- else
- {
- int offset = 0, size;
-
- if ((va & PageMask) != 0)
- {
- ulong pa = GetPhysicalAddressInternal(va);
-
- size = Math.Min(data.Length, PageSize - (int)(va & PageMask));
-
- data[..size].CopyTo(_backingMemory.GetSpan(pa, size));
-
- offset += size;
- }
-
- for (; offset < data.Length; offset += size)
- {
- ulong pa = GetPhysicalAddressInternal(va + (ulong)offset);
-
- size = Math.Min(data.Length - offset, PageSize);
-
- data.Slice(offset, size).CopyTo(_backingMemory.GetSpan(pa, size));
- }
- }
+ base.Write(va, data);
}
catch (InvalidMemoryRegionException)
{
@@ -312,60 +192,47 @@ namespace Ryujinx.Cpu.Jit
}
///
- public ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false)
+ public void WriteGuest(ulong va, T value) where T : unmanaged
{
- if (size == 0)
+ Span data = MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref value, 1));
+
+ SignalMemoryTrackingImpl(va, (ulong)data.Length, true, true);
+
+ Write(va, data);
+ }
+
+ public override void WriteUntracked(ulong va, ReadOnlySpan data)
+ {
+ try
{
- return ReadOnlySpan.Empty;
+ base.WriteUntracked(va, data);
}
-
- if (tracked)
+ catch (InvalidMemoryRegionException)
{
- SignalMemoryTracking(va, (ulong)size, false);
- }
-
- if (IsContiguousAndMapped(va, size))
- {
- return _backingMemory.GetSpan(GetPhysicalAddressInternal(va), size);
- }
- else
- {
- Span data = new byte[size];
-
- base.Read(va, data);
-
- return data;
+ if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
+ {
+ throw;
+ }
}
}
- ///
- public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
+ public override ReadOnlySequence GetReadOnlySequence(ulong va, int size, bool tracked = false)
{
- if (size == 0)
+ try
{
- return new WritableRegion(null, va, Memory.Empty);
+ return base.GetReadOnlySequence(va, size, tracked);
}
-
- if (IsContiguousAndMapped(va, size))
+ catch (InvalidMemoryRegionException)
{
- if (tracked)
+ if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
{
- SignalMemoryTracking(va, (ulong)size, true);
+ throw;
}
- return new WritableRegion(null, va, _backingMemory.GetMemory(GetPhysicalAddressInternal(va), size));
- }
- else
- {
- Memory memory = new byte[size];
-
- GetSpan(va, size).CopyTo(memory.Span);
-
- return new WritableRegion(this, va, memory, tracked);
+ return ReadOnlySequence.Empty;
}
}
- ///
public ref T GetRef(ulong va) where T : unmanaged
{
if (!IsContiguous(va, Unsafe.SizeOf()))
@@ -378,56 +245,6 @@ namespace Ryujinx.Cpu.Jit
return ref _backingMemory.GetRef(GetPhysicalAddressInternal(va));
}
- ///
- /// Computes the number of pages in a virtual address range.
- ///
- /// Virtual address of the range
- /// Size of the range
- /// The virtual address of the beginning of the first page
- /// This function does not differentiate between allocated and unallocated pages.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int GetPagesCount(ulong va, uint size, out ulong startVa)
- {
- // WARNING: Always check if ulong does not overflow during the operations.
- startVa = va & ~(ulong)PageMask;
- ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
-
- return (int)(vaSpan / PageSize);
- }
-
- private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private bool IsContiguousAndMapped(ulong va, int size) => IsContiguous(va, size) && IsMapped(va);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private bool IsContiguous(ulong va, int size)
- {
- if (!ValidateAddress(va) || !ValidateAddressAndSize(va, (ulong)size))
- {
- return false;
- }
-
- int pages = GetPagesCount(va, (uint)size, out va);
-
- for (int page = 0; page < pages - 1; page++)
- {
- if (!ValidateAddress(va + PageSize))
- {
- return false;
- }
-
- if (GetPhysicalAddressInternal(va) + PageSize != GetPhysicalAddressInternal(va + PageSize))
- {
- return false;
- }
-
- va += PageSize;
- }
-
- return true;
- }
-
///
public IEnumerable GetHostRegions(ulong va, ulong size)
{
@@ -532,9 +349,8 @@ namespace Ryujinx.Cpu.Jit
return true;
}
- ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool IsMapped(ulong va)
+ public override bool IsMapped(ulong va)
{
if (!ValidateAddress(va))
{
@@ -544,9 +360,9 @@ namespace Ryujinx.Cpu.Jit
return _pageTable.Read((va / PageSize) * PteSize) != 0;
}
- private ulong GetPhysicalAddressInternal(ulong va)
+ private nuint GetPhysicalAddressInternal(ulong va)
{
- return PteToPa(_pageTable.Read((va / PageSize) * PteSize) & ~(0xffffUL << 48)) + (va & PageMask);
+ return (nuint)(PteToPa(_pageTable.Read((va / PageSize) * PteSize) & ~(0xffffUL << 48)) + (va & PageMask));
}
///
@@ -643,9 +459,7 @@ namespace Ryujinx.Cpu.Jit
{
ref long pageRef = ref _pageTable.GetRef(pageStart * PteSize);
- long pte;
-
- pte = Volatile.Read(ref pageRef);
+ long pte = Volatile.Read(ref pageRef);
if ((pte & tag) != 0)
{
@@ -663,7 +477,7 @@ namespace Ryujinx.Cpu.Jit
}
///
- public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
+ public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{
SignalMemoryTrackingImpl(va, size, write, false, precise, exemptId);
}
@@ -683,10 +497,16 @@ namespace Ryujinx.Cpu.Jit
///
protected override void Destroy() => _pageTable.Dispose();
- protected override Span GetPhysicalAddressSpan(ulong pa, int size)
+ protected override Memory GetPhysicalAddressMemory(nuint pa, int size)
+ => _backingMemory.GetMemory(pa, size);
+
+ protected override Span GetPhysicalAddressSpan(nuint pa, int size)
=> _backingMemory.GetSpan(pa, size);
- protected override ulong TranslateVirtualAddressForRead(ulong va)
+ protected override nuint TranslateVirtualAddressChecked(ulong va)
+ => GetPhysicalAddressInternal(va);
+
+ protected override nuint TranslateVirtualAddressUnchecked(ulong va)
=> GetPhysicalAddressInternal(va);
}
}
diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs
index f410d02e96..4639ab913d 100644
--- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs
+++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs
@@ -3,6 +3,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.CompilerServices;
@@ -12,7 +13,7 @@ namespace Ryujinx.Cpu.Jit
///
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
///
- public sealed class MemoryManagerHostMapped : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked, IWritableBlock
+ public sealed class MemoryManagerHostMapped : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
{
private readonly InvalidAccessHandler _invalidAccessHandler;
private readonly bool _unsafeMode;
@@ -26,7 +27,7 @@ namespace Ryujinx.Cpu.Jit
private readonly ManagedPageFlags _pages;
///
- public bool Supports4KBPages => MemoryBlock.GetPageSize() == PageSize;
+ public bool UsesPrivateAllocations => false;
public int AddressSpaceBits { get; }
@@ -96,12 +97,6 @@ namespace Ryujinx.Cpu.Jit
Tracking.Map(va, size);
}
- ///
- public void MapForeign(ulong va, nuint hostPointer, ulong size)
- {
- throw new NotSupportedException();
- }
-
///
public void Unmap(ulong va, ulong size)
{
@@ -138,8 +133,7 @@ namespace Ryujinx.Cpu.Jit
}
}
- ///
- public T Read(ulong va) where T : unmanaged
+ public override T Read(ulong va)
{
try
{
@@ -158,14 +152,11 @@ namespace Ryujinx.Cpu.Jit
}
}
- ///
- public T ReadTracked(ulong va) where T : unmanaged
+ public override T ReadTracked(ulong va)
{
try
{
- SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), false);
-
- return Read(va);
+ return base.ReadTracked(va);
}
catch (InvalidMemoryRegionException)
{
@@ -178,7 +169,6 @@ namespace Ryujinx.Cpu.Jit
}
}
- ///
public override void Read(ulong va, Span data)
{
try
@@ -196,9 +186,7 @@ namespace Ryujinx.Cpu.Jit
}
}
-
- ///
- public void Write(ulong va, T value) where T : unmanaged
+ public override void Write(ulong va, T value)
{
try
{
@@ -215,8 +203,7 @@ namespace Ryujinx.Cpu.Jit
}
}
- ///
- public void Write(ulong va, ReadOnlySpan data)
+ public override void Write(ulong va, ReadOnlySpan data)
{
try
{
@@ -233,8 +220,7 @@ namespace Ryujinx.Cpu.Jit
}
}
- ///
- public void WriteUntracked(ulong va, ReadOnlySpan data)
+ public override void WriteUntracked(ulong va, ReadOnlySpan data)
{
try
{
@@ -251,8 +237,7 @@ namespace Ryujinx.Cpu.Jit
}
}
- ///
- public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data)
+ public override bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data)
{
try
{
@@ -279,8 +264,21 @@ namespace Ryujinx.Cpu.Jit
}
}
- ///
- public ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false)
+ public override ReadOnlySequence GetReadOnlySequence(ulong va, int size, bool tracked = false)
+ {
+ if (tracked)
+ {
+ SignalMemoryTracking(va, (ulong)size, write: false);
+ }
+ else
+ {
+ AssertMapped(va, (ulong)size);
+ }
+
+ return new ReadOnlySequence(_addressSpace.Mirror.GetMemory(va, size));
+ }
+
+ public override ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false)
{
if (tracked)
{
@@ -294,8 +292,7 @@ namespace Ryujinx.Cpu.Jit
return _addressSpace.Mirror.GetSpan(va, size);
}
- ///
- public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
+ public override WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
{
if (tracked)
{
@@ -309,7 +306,6 @@ namespace Ryujinx.Cpu.Jit
return _addressSpace.Mirror.GetWritableRegion(va, size);
}
- ///
public ref T GetRef(ulong va) where T : unmanaged
{
SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), true);
@@ -317,9 +313,8 @@ namespace Ryujinx.Cpu.Jit
return ref _addressSpace.Mirror.GetRef(va);
}
- ///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool IsMapped(ulong va)
+ public override bool IsMapped(ulong va)
{
return ValidateAddress(va) && _pages.IsMapped(va);
}
@@ -390,11 +385,10 @@ namespace Ryujinx.Cpu.Jit
return _pageTable.Read(va) + (va & PageMask);
}
- ///
///
/// This function also validates that the given range is both valid and mapped, and will throw if it is not.
///
- public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
+ public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{
AssertValidAddressAndSize(va, size);
@@ -407,23 +401,6 @@ namespace Ryujinx.Cpu.Jit
_pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
}
- ///
- /// Computes the number of pages in a virtual address range.
- ///
- /// Virtual address of the range
- /// Size of the range
- /// The virtual address of the beginning of the first page
- /// This function does not differentiate between allocated and unallocated pages.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int GetPagesCount(ulong va, ulong size, out ulong startVa)
- {
- // WARNING: Always check if ulong does not overflow during the operations.
- startVa = va & ~(ulong)PageMask;
- ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
-
- return (int)(vaSpan / PageSize);
- }
-
///
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
{
@@ -470,10 +447,16 @@ namespace Ryujinx.Cpu.Jit
_memoryEh.Dispose();
}
- protected override Span GetPhysicalAddressSpan(ulong pa, int size)
+ protected override Memory GetPhysicalAddressMemory(nuint pa, int size)
+ => _addressSpace.Mirror.GetMemory(pa, size);
+
+ protected override Span GetPhysicalAddressSpan(nuint pa, int size)
=> _addressSpace.Mirror.GetSpan(pa, size);
- protected override ulong TranslateVirtualAddressForRead(ulong va)
- => va;
+ protected override nuint TranslateVirtualAddressChecked(ulong va)
+ => (nuint)GetPhysicalAddressChecked(va);
+
+ protected override nuint TranslateVirtualAddressUnchecked(ulong va)
+ => (nuint)GetPhysicalAddressInternal(va);
}
}
diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs
index 18404bcc74..0acb57be42 100644
--- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs
+++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs
@@ -1,21 +1,22 @@
using ARMeilleure.Memory;
+using Ryujinx.Common.Memory;
using Ryujinx.Cpu.Jit.HostTracked;
using Ryujinx.Cpu.Signal;
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.CompilerServices;
-using System.Runtime.InteropServices;
namespace Ryujinx.Cpu.Jit
{
///
/// Represents a CPU memory manager which maps guest virtual memory directly onto a host virtual region.
///
- public sealed class MemoryManagerHostTracked : VirtualMemoryManagerRefCountedBase, IWritableBlock, IMemoryManager, IVirtualMemoryManagerTracked
+ public sealed class MemoryManagerHostTracked : VirtualMemoryManagerRefCountedBase, IMemoryManager, IVirtualMemoryManagerTracked
{
private readonly InvalidAccessHandler _invalidAccessHandler;
private readonly bool _unsafeMode;
@@ -34,7 +35,7 @@ namespace Ryujinx.Cpu.Jit
protected override ulong AddressSpaceSize { get; }
///
- public bool Supports4KBPages => false;
+ public bool UsesPrivateAllocations => true;
public IntPtr PageTablePointer => _nativePageTable.PageTablePointer;
@@ -100,12 +101,6 @@ namespace Ryujinx.Cpu.Jit
Tracking.Map(va, size);
}
- ///
- public void MapForeign(ulong va, nuint hostPointer, ulong size)
- {
- throw new NotSupportedException();
- }
-
///
public void Unmap(ulong va, ulong size)
{
@@ -120,18 +115,11 @@ namespace Ryujinx.Cpu.Jit
_nativePageTable.Unmap(va, size);
}
- public T Read(ulong va) where T : unmanaged
- {
- return MemoryMarshal.Cast(GetSpan(va, Unsafe.SizeOf()))[0];
- }
-
- public T ReadTracked(ulong va) where T : unmanaged
+ public override T ReadTracked(ulong va)
{
try
{
- SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), false);
-
- return Read(va);
+ return base.ReadTracked(va);
}
catch (InvalidMemoryRegionException)
{
@@ -145,38 +133,39 @@ namespace Ryujinx.Cpu.Jit
}
public override void Read(ulong va, Span data)
- {
- ReadImpl(va, data);
- }
-
- public void Write(ulong va, T value) where T : unmanaged
- {
- Write(va, MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref value, 1)));
- }
-
- public void Write(ulong va, ReadOnlySpan data)
{
if (data.Length == 0)
{
return;
}
- SignalMemoryTracking(va, (ulong)data.Length, true);
-
- WriteImpl(va, data);
- }
-
- public void WriteUntracked(ulong va, ReadOnlySpan data)
- {
- if (data.Length == 0)
+ try
{
- return;
- }
+ AssertValidAddressAndSize(va, (ulong)data.Length);
- WriteImpl(va, data);
+ ulong endVa = va + (ulong)data.Length;
+ int offset = 0;
+
+ while (va < endVa)
+ {
+ (MemoryBlock memory, ulong rangeOffset, ulong copySize) = GetMemoryOffsetAndSize(va, (ulong)(data.Length - offset));
+
+ memory.GetSpan(rangeOffset, (int)copySize).CopyTo(data.Slice(offset, (int)copySize));
+
+ va += copySize;
+ offset += (int)copySize;
+ }
+ }
+ catch (InvalidMemoryRegionException)
+ {
+ if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
+ {
+ throw;
+ }
+ }
}
- public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data)
+ public override bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data)
{
if (data.Length == 0)
{
@@ -206,35 +195,7 @@ namespace Ryujinx.Cpu.Jit
}
}
- private void WriteImpl(ulong va, ReadOnlySpan data)
- {
- try
- {
- AssertValidAddressAndSize(va, (ulong)data.Length);
-
- ulong endVa = va + (ulong)data.Length;
- int offset = 0;
-
- while (va < endVa)
- {
- (MemoryBlock memory, ulong rangeOffset, ulong copySize) = GetMemoryOffsetAndSize(va, (ulong)(data.Length - offset));
-
- data.Slice(offset, (int)copySize).CopyTo(memory.GetSpan(rangeOffset, (int)copySize));
-
- va += copySize;
- offset += (int)copySize;
- }
- }
- catch (InvalidMemoryRegionException)
- {
- if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
- {
- throw;
- }
- }
- }
-
- public ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false)
+ public override ReadOnlySpan GetSpan(ulong va, int size, bool tracked = false)
{
if (size == 0)
{
@@ -254,13 +215,13 @@ namespace Ryujinx.Cpu.Jit
{
Span data = new byte[size];
- ReadImpl(va, data);
+ Read(va, data);
return data;
}
}
- public WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
+ public override WritableRegion GetWritableRegion(ulong va, int size, bool tracked = false)
{
if (size == 0)
{
@@ -278,11 +239,11 @@ namespace Ryujinx.Cpu.Jit
}
else
{
- Memory memory = new byte[size];
+ IMemoryOwner memoryOwner = ByteMemoryPool.Rent(size);
- ReadImpl(va, memory.Span);
+ Read(va, memoryOwner.Memory.Span);
- return new WritableRegion(this, va, memory);
+ return new WritableRegion(this, va, memoryOwner);
}
}
@@ -299,7 +260,7 @@ namespace Ryujinx.Cpu.Jit
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool IsMapped(ulong va)
+ public override bool IsMapped(ulong va)
{
return ValidateAddress(va) && _pages.IsMapped(va);
}
@@ -311,8 +272,6 @@ namespace Ryujinx.Cpu.Jit
return _pages.IsRangeMapped(va, size);
}
- private static void ThrowMemoryNotContiguous() => throw new MemoryNotContiguousException();
-
private bool TryGetVirtualContiguous(ulong va, int size, out MemoryBlock memory, out ulong offset)
{
if (_addressSpace.HasAnyPrivateAllocation(va, (ulong)size, out PrivateRange range))
@@ -491,44 +450,11 @@ namespace Ryujinx.Cpu.Jit
return regions;
}
- private void ReadImpl(ulong va, Span data)
- {
- if (data.Length == 0)
- {
- return;
- }
-
- try
- {
- AssertValidAddressAndSize(va, (ulong)data.Length);
-
- ulong endVa = va + (ulong)data.Length;
- int offset = 0;
-
- while (va < endVa)
- {
- (MemoryBlock memory, ulong rangeOffset, ulong copySize) = GetMemoryOffsetAndSize(va, (ulong)(data.Length - offset));
-
- memory.GetSpan(rangeOffset, (int)copySize).CopyTo(data.Slice(offset, (int)copySize));
-
- va += copySize;
- offset += (int)copySize;
- }
- }
- catch (InvalidMemoryRegionException)
- {
- if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
- {
- throw;
- }
- }
- }
-
///
///
/// This function also validates that the given range is both valid and mapped, and will throw if it is not.
///
- public void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
+ public override void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null)
{
AssertValidAddressAndSize(va, size);
@@ -543,23 +469,6 @@ namespace Ryujinx.Cpu.Jit
_pages.SignalMemoryTracking(Tracking, va, size, write, exemptId);
}
- ///
- /// Computes the number of pages in a virtual address range.
- ///
- /// Virtual address of the range
- /// Size of the range
- /// The virtual address of the beginning of the first page
- /// This function does not differentiate between allocated and unallocated pages.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private int GetPagesCount(ulong va, ulong size, out ulong startVa)
- {
- // WARNING: Always check if ulong does not overflow during the operations.
- startVa = va & ~(ulong)PageMask;
- ulong vaSpan = (va - startVa + size + PageMask) & ~(ulong)PageMask;
-
- return (int)(vaSpan / PageSize);
- }
-
public RegionHandle BeginTracking(ulong address, ulong size, int id, RegionFlags flags = RegionFlags.None)
{
return Tracking.BeginTracking(address, size, id, flags);
@@ -618,10 +527,44 @@ namespace Ryujinx.Cpu.Jit
_nativePageTable.Dispose();
}
- protected override Span GetPhysicalAddressSpan(ulong pa, int size)
+ protected override Memory GetPhysicalAddressMemory(nuint pa, int size)
+ => _backingMemory.GetMemory(pa, size);
+
+ protected override Span GetPhysicalAddressSpan(nuint pa, int size)
=> _backingMemory.GetSpan(pa, size);
- protected override ulong TranslateVirtualAddressForRead(ulong va)
- => GetPhysicalAddressInternal(va);
+ protected override void WriteImpl(ulong va, ReadOnlySpan data)
+ {
+ try
+ {
+ AssertValidAddressAndSize(va, (ulong)data.Length);
+
+ ulong endVa = va + (ulong)data.Length;
+ int offset = 0;
+
+ while (va < endVa)
+ {
+ (MemoryBlock memory, ulong rangeOffset, ulong copySize) = GetMemoryOffsetAndSize(va, (ulong)(data.Length - offset));
+
+ data.Slice(offset, (int)copySize).CopyTo(memory.GetSpan(rangeOffset, (int)copySize));
+
+ va += copySize;
+ offset += (int)copySize;
+ }
+ }
+ catch (InvalidMemoryRegionException)
+ {
+ if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
+ {
+ throw;
+ }
+ }
+ }
+
+ protected override nuint TranslateVirtualAddressChecked(ulong va)
+ => (nuint)GetPhysicalAddressChecked(va);
+
+ protected override nuint TranslateVirtualAddressUnchecked(ulong va)
+ => (nuint)GetPhysicalAddressInternal(va);
}
}
diff --git a/src/Ryujinx.Cpu/VirtualMemoryManagerRefCountedBase.cs b/src/Ryujinx.Cpu/VirtualMemoryManagerRefCountedBase.cs
index c2d8cfb1a0..3c7b338055 100644
--- a/src/Ryujinx.Cpu/VirtualMemoryManagerRefCountedBase.cs
+++ b/src/Ryujinx.Cpu/VirtualMemoryManagerRefCountedBase.cs
@@ -1,13 +1,10 @@
using Ryujinx.Memory;
using System.Diagnostics;
-using System.Numerics;
using System.Threading;
namespace Ryujinx.Cpu
{
- public abstract class VirtualMemoryManagerRefCountedBase : VirtualMemoryManagerBase, IRefCounted
- where TVirtual : IBinaryInteger
- where TPhysical : IBinaryInteger
+ public abstract class VirtualMemoryManagerRefCountedBase : VirtualMemoryManagerBase, IRefCounted
{
private int _referenceCount;
diff --git a/src/Ryujinx.Graphics.Device/DeviceMemoryManager.cs b/src/Ryujinx.Graphics.Device/DeviceMemoryManager.cs
index d64ed30954..fc075a2643 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/IImageArray.cs b/src/Ryujinx.Graphics.GAL/IImageArray.cs
new file mode 100644
index 0000000000..30cff50b15
--- /dev/null
+++ b/src/Ryujinx.Graphics.GAL/IImageArray.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Graphics.GAL
+{
+ public interface IImageArray
+ {
+ void SetFormats(int index, Format[] imageFormats);
+ void SetImages(int index, ITexture[] images);
+ }
+}
diff --git a/src/Ryujinx.Graphics.GAL/IPipeline.cs b/src/Ryujinx.Graphics.GAL/IPipeline.cs
index 3ba084aa5b..9efb9e3e8f 100644
--- a/src/Ryujinx.Graphics.GAL/IPipeline.cs
+++ b/src/Ryujinx.Graphics.GAL/IPipeline.cs
@@ -59,6 +59,7 @@ namespace Ryujinx.Graphics.GAL
void SetIndexBuffer(BufferRange buffer, IndexType type);
void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat);
+ void SetImageArray(ShaderStage stage, int binding, IImageArray array);
void SetLineParameters(float width, bool smooth);
@@ -89,6 +90,7 @@ namespace Ryujinx.Graphics.GAL
void SetStorageBuffers(ReadOnlySpan buffers);
void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler);
+ void SetTextureArray(ShaderStage stage, int binding, ITextureArray array);
void SetTransformFeedbackBuffers(ReadOnlySpan buffers);
void SetUniformBuffers(ReadOnlySpan buffers);
diff --git a/src/Ryujinx.Graphics.GAL/IRenderer.cs b/src/Ryujinx.Graphics.GAL/IRenderer.cs
index 3bf56465eb..a3466e3966 100644
--- a/src/Ryujinx.Graphics.GAL/IRenderer.cs
+++ b/src/Ryujinx.Graphics.GAL/IRenderer.cs
@@ -21,10 +21,14 @@ namespace Ryujinx.Graphics.GAL
BufferHandle CreateBuffer(nint pointer, int size);
BufferHandle CreateBufferSparse(ReadOnlySpan storageBuffers);
+ IImageArray CreateImageArray(int size, bool isBuffer);
+
IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info);
ISampler CreateSampler(SamplerCreateInfo info);
ITexture CreateTexture(TextureCreateInfo info);
+ ITextureArray CreateTextureArray(int size, bool isBuffer);
+
bool PrepareHostMapping(nint address, ulong size);
void CreateSync(ulong id, bool strict);
diff --git a/src/Ryujinx.Graphics.GAL/ITexture.cs b/src/Ryujinx.Graphics.GAL/ITexture.cs
index 5a4623a66d..2d9c6b7990 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/ITextureArray.cs b/src/Ryujinx.Graphics.GAL/ITextureArray.cs
new file mode 100644
index 0000000000..35c2116b54
--- /dev/null
+++ b/src/Ryujinx.Graphics.GAL/ITextureArray.cs
@@ -0,0 +1,8 @@
+namespace Ryujinx.Graphics.GAL
+{
+ public interface ITextureArray
+ {
+ void SetSamplers(int index, ISampler[] samplers);
+ void SetTextures(int index, ITexture[] textures);
+ }
+}
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs
index 5bf3d32835..fd2919be4d 100644
--- a/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/CommandHelper.cs
@@ -1,10 +1,12 @@
using Ryujinx.Graphics.GAL.Multithreading.Commands;
using Ryujinx.Graphics.GAL.Multithreading.Commands.Buffer;
using Ryujinx.Graphics.GAL.Multithreading.Commands.CounterEvent;
+using Ryujinx.Graphics.GAL.Multithreading.Commands.ImageArray;
using Ryujinx.Graphics.GAL.Multithreading.Commands.Program;
using Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer;
using Ryujinx.Graphics.GAL.Multithreading.Commands.Sampler;
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
+using Ryujinx.Graphics.GAL.Multithreading.Commands.TextureArray;
using Ryujinx.Graphics.GAL.Multithreading.Commands.Window;
using System;
using System.Linq;
@@ -46,10 +48,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register(CommandType.CreateBufferAccess);
Register(CommandType.CreateBufferSparse);
Register(CommandType.CreateHostBuffer);
+ Register(CommandType.CreateImageArray);
Register(CommandType.CreateProgram);
Register(CommandType.CreateSampler);
Register(CommandType.CreateSync);
Register(CommandType.CreateTexture);
+ Register(CommandType.CreateTextureArray);
Register(CommandType.GetCapabilities);
Register(CommandType.PreFrame);
Register(CommandType.ReportCounter);
@@ -63,6 +67,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register(CommandType.CounterEventDispose);
Register(CommandType.CounterEventFlush);
+ Register(CommandType.ImageArraySetFormats);
+ Register(CommandType.ImageArraySetImages);
+
Register(CommandType.ProgramDispose);
Register(CommandType.ProgramGetBinary);
Register(CommandType.ProgramCheckLink);
@@ -82,6 +89,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register(CommandType.TextureSetDataSliceRegion);
Register(CommandType.TextureSetStorage);
+ Register(CommandType.TextureArraySetSamplers);
+ Register(CommandType.TextureArraySetTextures);
+
Register(CommandType.WindowPresent);
Register(CommandType.Barrier);
@@ -114,6 +124,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register(CommandType.SetTransformFeedbackBuffers);
Register(CommandType.SetUniformBuffers);
Register(CommandType.SetImage);
+ Register(CommandType.SetImageArray);
Register(CommandType.SetIndexBuffer);
Register(CommandType.SetLineParameters);
Register(CommandType.SetLogicOpState);
@@ -130,6 +141,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
Register(CommandType.SetScissor);
Register(CommandType.SetStencilTest);
Register(CommandType.SetTextureAndSampler);
+ Register(CommandType.SetTextureArray);
Register(CommandType.SetUserClipDistance);
Register(CommandType.SetVertexAttribs);
Register(CommandType.SetVertexBuffers);
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs
index 6be639253a..a5e7336cdf 100644
--- a/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/CommandType.cs
@@ -7,10 +7,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading
CreateBufferAccess,
CreateBufferSparse,
CreateHostBuffer,
+ CreateImageArray,
CreateProgram,
CreateSampler,
CreateSync,
CreateTexture,
+ CreateTextureArray,
GetCapabilities,
Unused,
PreFrame,
@@ -25,6 +27,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
CounterEventDispose,
CounterEventFlush,
+ ImageArraySetFormats,
+ ImageArraySetImages,
+
ProgramDispose,
ProgramGetBinary,
ProgramCheckLink,
@@ -44,6 +49,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
TextureSetDataSliceRegion,
TextureSetStorage,
+ TextureArraySetSamplers,
+ TextureArraySetTextures,
+
WindowPresent,
Barrier,
@@ -76,6 +84,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
SetTransformFeedbackBuffers,
SetUniformBuffers,
SetImage,
+ SetImageArray,
SetIndexBuffer,
SetLineParameters,
SetLogicOpState,
@@ -92,6 +101,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
SetScissor,
SetStencilTest,
SetTextureAndSampler,
+ SetTextureArray,
SetUserClipDistance,
SetVertexAttribs,
SetVertexBuffers,
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ImageArray/ImageArraySetFormatsCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ImageArray/ImageArraySetFormatsCommand.cs
new file mode 100644
index 0000000000..8e3ba88a88
--- /dev/null
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ImageArray/ImageArraySetFormatsCommand.cs
@@ -0,0 +1,26 @@
+using Ryujinx.Graphics.GAL.Multithreading.Model;
+using Ryujinx.Graphics.GAL.Multithreading.Resources;
+
+namespace Ryujinx.Graphics.GAL.Multithreading.Commands.ImageArray
+{
+ struct ImageArraySetFormatsCommand : IGALCommand, IGALCommand
+ {
+ public readonly CommandType CommandType => CommandType.ImageArraySetFormats;
+ private TableRef _imageArray;
+ private int _index;
+ private TableRef _imageFormats;
+
+ public void Set(TableRef imageArray, int index, TableRef imageFormats)
+ {
+ _imageArray = imageArray;
+ _index = index;
+ _imageFormats = imageFormats;
+ }
+
+ public static void Run(ref ImageArraySetFormatsCommand command, ThreadedRenderer threaded, IRenderer renderer)
+ {
+ ThreadedImageArray imageArray = command._imageArray.Get(threaded);
+ imageArray.Base.SetFormats(command._index, command._imageFormats.Get(threaded));
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ImageArray/ImageArraySetImagesCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ImageArray/ImageArraySetImagesCommand.cs
new file mode 100644
index 0000000000..cc28d585c4
--- /dev/null
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/ImageArray/ImageArraySetImagesCommand.cs
@@ -0,0 +1,27 @@
+using Ryujinx.Graphics.GAL.Multithreading.Model;
+using Ryujinx.Graphics.GAL.Multithreading.Resources;
+using System.Linq;
+
+namespace Ryujinx.Graphics.GAL.Multithreading.Commands.ImageArray
+{
+ struct ImageArraySetImagesCommand : IGALCommand, IGALCommand
+ {
+ public readonly CommandType CommandType => CommandType.ImageArraySetImages;
+ private TableRef _imageArray;
+ private int _index;
+ private TableRef _images;
+
+ public void Set(TableRef imageArray, int index, TableRef images)
+ {
+ _imageArray = imageArray;
+ _index = index;
+ _images = images;
+ }
+
+ public static void Run(ref ImageArraySetImagesCommand command, ThreadedRenderer threaded, IRenderer renderer)
+ {
+ ThreadedImageArray imageArray = command._imageArray.Get(threaded);
+ imageArray.Base.SetImages(command._index, command._images.Get(threaded).Select(texture => ((ThreadedTexture)texture)?.Base).ToArray());
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateImageArrayCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateImageArrayCommand.cs
new file mode 100644
index 0000000000..1c3fb81209
--- /dev/null
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateImageArrayCommand.cs
@@ -0,0 +1,25 @@
+using Ryujinx.Graphics.GAL.Multithreading.Model;
+using Ryujinx.Graphics.GAL.Multithreading.Resources;
+
+namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer
+{
+ struct CreateImageArrayCommand : IGALCommand, IGALCommand
+ {
+ public readonly CommandType CommandType => CommandType.CreateImageArray;
+ private TableRef _imageArray;
+ private int _size;
+ private bool _isBuffer;
+
+ public void Set(TableRef imageArray, int size, bool isBuffer)
+ {
+ _imageArray = imageArray;
+ _size = size;
+ _isBuffer = isBuffer;
+ }
+
+ public static void Run(ref CreateImageArrayCommand command, ThreadedRenderer threaded, IRenderer renderer)
+ {
+ command._imageArray.Get(threaded).Base = renderer.CreateImageArray(command._size, command._isBuffer);
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureArrayCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureArrayCommand.cs
new file mode 100644
index 0000000000..9bd891e68d
--- /dev/null
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Renderer/CreateTextureArrayCommand.cs
@@ -0,0 +1,25 @@
+using Ryujinx.Graphics.GAL.Multithreading.Model;
+using Ryujinx.Graphics.GAL.Multithreading.Resources;
+
+namespace Ryujinx.Graphics.GAL.Multithreading.Commands.Renderer
+{
+ struct CreateTextureArrayCommand : IGALCommand, IGALCommand
+ {
+ public readonly CommandType CommandType => CommandType.CreateTextureArray;
+ private TableRef _textureArray;
+ private int _size;
+ private bool _isBuffer;
+
+ public void Set(TableRef textureArray, int size, bool isBuffer)
+ {
+ _textureArray = textureArray;
+ _size = size;
+ _isBuffer = isBuffer;
+ }
+
+ public static void Run(ref CreateTextureArrayCommand command, ThreadedRenderer threaded, IRenderer renderer)
+ {
+ command._textureArray.Get(threaded).Base = renderer.CreateTextureArray(command._size, command._isBuffer);
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageArrayCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageArrayCommand.cs
new file mode 100644
index 0000000000..b8d3c7ac5c
--- /dev/null
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetImageArrayCommand.cs
@@ -0,0 +1,26 @@
+using Ryujinx.Graphics.GAL.Multithreading.Model;
+using Ryujinx.Graphics.GAL.Multithreading.Resources;
+using Ryujinx.Graphics.Shader;
+
+namespace Ryujinx.Graphics.GAL.Multithreading.Commands
+{
+ struct SetImageArrayCommand : IGALCommand, IGALCommand
+ {
+ public readonly CommandType CommandType => CommandType.SetImageArray;
+ private ShaderStage _stage;
+ private int _binding;
+ private TableRef _array;
+
+ public void Set(ShaderStage stage, int binding, TableRef array)
+ {
+ _stage = stage;
+ _binding = binding;
+ _array = array;
+ }
+
+ public static void Run(ref SetImageArrayCommand command, ThreadedRenderer threaded, IRenderer renderer)
+ {
+ renderer.Pipeline.SetImageArray(command._stage, command._binding, command._array.GetAs(threaded)?.Base);
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureArrayCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureArrayCommand.cs
new file mode 100644
index 0000000000..45e28aa658
--- /dev/null
+++ b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/SetTextureArrayCommand.cs
@@ -0,0 +1,26 @@
+using Ryujinx.Graphics.GAL.Multithreading.Model;
+using Ryujinx.Graphics.GAL.Multithreading.Resources;
+using Ryujinx.Graphics.Shader;
+
+namespace Ryujinx.Graphics.GAL.Multithreading.Commands
+{
+ struct SetTextureArrayCommand : IGALCommand, IGALCommand
+ {
+ public readonly CommandType CommandType => CommandType.SetTextureArray;
+ private ShaderStage _stage;
+ private int _binding;
+ private TableRef _array;
+
+ public void Set(ShaderStage stage, int binding, TableRef array)
+ {
+ _stage = stage;
+ _binding = binding;
+ _array = array;
+ }
+
+ public static void Run(ref SetTextureArrayCommand command, ThreadedRenderer threaded, IRenderer renderer)
+ {
+ renderer.Pipeline.SetTextureArray(command._stage, command._binding, command._array.GetAs(threaded)?.Base);
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Commands/Texture/TextureSetDataCommand.cs
index 36feeeba75..3aba004dff 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 c50bfe089a..7ad709a757 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