forked from Mirror/Ryujinx
Reducing Memory Allocations 202303 (#4624)
* use ArrayPool, avoid 6000-7000 allocs/sec of runtime * use ArrayPool, avoid ~7k allocs/second during game execution * use ArrayPool, avoid ~3000 allocs/sec during game execution * use MemoryPool, reduce 0.5 MB/sec of new allocations during game execution * avoid over-allocation by setting List<> Capacity when known * remove LINQ in KTimeManager.UnscheduleFutureInvocation * KTimeManager - avoid spinning one more time when the time has arrived * KTimeManager - let SpinWait decide when to Thread.Yield(), and don't SpinOnce() immediately after Thread.Yield() * use MemoryPool, reduce ~175k bytes/sec allocation during game execution * IpcService - call commands via dynamic methods instead of reflection .Invoke(). Faster to call and with fewer allocations because parameters can be passed directly instead of as an array * Make ButtonMappingEntry a record struct to avoid allocations. Set the List<ButtonMappingEntry> capacity according to use. * add MemoryBuffer type for working with MemoryPool<byte> * update changes to use MemoryBuffer * make parameter ReadOnlySpan instead of Span * whitespace fix * Revert "IpcService - call commands via dynamic methods instead of reflection .Invoke(). Faster to call and with fewer allocations because parameters can be passed directly instead of as an array" This reverts commit f2c698bdf65f049e8481c9f2ec7138d9b9a8261d. * tweak KTimeManager spin behavior * replace MemoryBuffer with ByteMemoryPool modeled after System.Buffers.ArrayMemoryPool<T> * make ByteMemoryPoolBuffer responsible for renting memory
This commit is contained in:
parent
8d9d508dc7
commit
666e05f5cb
13 changed files with 303 additions and 109 deletions
|
@ -11,6 +11,7 @@ using Ryujinx.Audio.Renderer.Server.Voice;
|
||||||
using Ryujinx.Audio.Renderer.Utils;
|
using Ryujinx.Audio.Renderer.Utils;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
@ -149,12 +150,16 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
state.InUse = false;
|
state.InUse = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Memory<VoiceUpdateState>[] voiceUpdateStatesArray = ArrayPool<Memory<VoiceUpdateState>>.Shared.Rent(Constants.VoiceChannelCountMax);
|
||||||
|
|
||||||
|
Span<Memory<VoiceUpdateState>> voiceUpdateStates = voiceUpdateStatesArray.AsSpan(0, Constants.VoiceChannelCountMax);
|
||||||
|
|
||||||
// Start processing
|
// Start processing
|
||||||
for (int i = 0; i < context.GetCount(); i++)
|
for (int i = 0; i < context.GetCount(); i++)
|
||||||
{
|
{
|
||||||
VoiceInParameter parameter = parameters[i];
|
VoiceInParameter parameter = parameters[i];
|
||||||
|
|
||||||
Memory<VoiceUpdateState>[] voiceUpdateStates = new Memory<VoiceUpdateState>[Constants.VoiceChannelCountMax];
|
voiceUpdateStates.Fill(Memory<VoiceUpdateState>.Empty);
|
||||||
|
|
||||||
ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef<VoiceOutStatus>(ref _output)[0];
|
ref VoiceOutStatus outStatus = ref SpanIOHelper.GetWriteRef<VoiceOutStatus>(ref _output)[0];
|
||||||
|
|
||||||
|
@ -197,6 +202,8 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayPool<Memory<VoiceUpdateState>>.Shared.Return(voiceUpdateStatesArray);
|
||||||
|
|
||||||
int currentOutputSize = _output.Length;
|
int currentOutputSize = _output.Length;
|
||||||
|
|
||||||
OutputHeader.VoicesSize = (uint)(Unsafe.SizeOf<VoiceOutStatus>() * context.GetCount());
|
OutputHeader.VoicesSize = (uint)(Unsafe.SizeOf<VoiceOutStatus>() * context.GetCount());
|
||||||
|
|
|
@ -378,7 +378,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
||||||
/// <param name="outStatus">The given user output.</param>
|
/// <param name="outStatus">The given user output.</param>
|
||||||
/// <param name="parameter">The user parameter.</param>
|
/// <param name="parameter">The user parameter.</param>
|
||||||
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
|
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
|
||||||
public void WriteOutStatus(ref VoiceOutStatus outStatus, ref VoiceInParameter parameter, Memory<VoiceUpdateState>[] voiceUpdateStates)
|
public void WriteOutStatus(ref VoiceOutStatus outStatus, ref VoiceInParameter parameter, ReadOnlySpan<Memory<VoiceUpdateState>> voiceUpdateStates)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
// Sanity check in debug mode of the internal state
|
// Sanity check in debug mode of the internal state
|
||||||
|
@ -424,7 +424,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice
|
||||||
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
|
/// <param name="voiceUpdateStates">The voice states associated to the <see cref="VoiceState"/>.</param>
|
||||||
/// <param name="mapper">The mapper to use.</param>
|
/// <param name="mapper">The mapper to use.</param>
|
||||||
/// <param name="behaviourContext">The behaviour context.</param>
|
/// <param name="behaviourContext">The behaviour context.</param>
|
||||||
public void UpdateWaveBuffers(out ErrorInfo[] errorInfos, ref VoiceInParameter parameter, Memory<VoiceUpdateState>[] voiceUpdateStates, ref PoolMapper mapper, ref BehaviourContext behaviourContext)
|
public void UpdateWaveBuffers(out ErrorInfo[] errorInfos, ref VoiceInParameter parameter, ReadOnlySpan<Memory<VoiceUpdateState>> voiceUpdateStates, ref PoolMapper mapper, ref BehaviourContext behaviourContext)
|
||||||
{
|
{
|
||||||
errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2];
|
errorInfos = new ErrorInfo[Constants.VoiceWaveBufferCount * 2];
|
||||||
|
|
||||||
|
|
51
Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
Normal file
51
Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
public sealed partial class ByteMemoryPool
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a <see cref="IMemoryOwner{Byte}"/> that wraps an array rented from
|
||||||
|
/// <see cref="ArrayPool{Byte}.Shared"/> and exposes it as <see cref="Memory{Byte}"/>
|
||||||
|
/// with a length of the requested size.
|
||||||
|
/// </summary>
|
||||||
|
private sealed class ByteMemoryPoolBuffer : IMemoryOwner<byte>
|
||||||
|
{
|
||||||
|
private byte[] _array;
|
||||||
|
private readonly int _length;
|
||||||
|
|
||||||
|
public ByteMemoryPoolBuffer(int length)
|
||||||
|
{
|
||||||
|
_array = ArrayPool<byte>.Shared.Rent(length);
|
||||||
|
_length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a <see cref="Memory{Byte}"/> belonging to this owner.
|
||||||
|
/// </summary>
|
||||||
|
public Memory<byte> Memory
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
byte[] array = _array;
|
||||||
|
|
||||||
|
ObjectDisposedException.ThrowIf(array is null, this);
|
||||||
|
|
||||||
|
return new Memory<byte>(array, 0, _length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
var array = Interlocked.Exchange(ref _array, null);
|
||||||
|
|
||||||
|
if (array != null)
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
108
Ryujinx.Common/Memory/ByteMemoryPool.cs
Normal file
108
Ryujinx.Common/Memory/ByteMemoryPool.cs
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.Memory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a pool of re-usable byte array instances.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class ByteMemoryPool
|
||||||
|
{
|
||||||
|
private static readonly ByteMemoryPool _shared = new ByteMemoryPool();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a <see cref="ByteMemoryPool"/> instance. Private to force access through
|
||||||
|
/// the <see cref="ByteMemoryPool.Shared"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
private ByteMemoryPool()
|
||||||
|
{
|
||||||
|
// No implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves a shared <see cref="ByteMemoryPool"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
public static ByteMemoryPool Shared => _shared;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the maximum buffer size supported by this pool.
|
||||||
|
/// </summary>
|
||||||
|
public int MaxBufferSize => Array.MaxLength;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer may contain data from a prior use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public IMemoryOwner<byte> Rent(long length)
|
||||||
|
=> RentImpl(checked((int)length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer may contain data from a prior use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public IMemoryOwner<byte> Rent(ulong length)
|
||||||
|
=> RentImpl(checked((int)length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer may contain data from a prior use.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public IMemoryOwner<byte> Rent(int length)
|
||||||
|
=> RentImpl(length);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer's contents are cleared (set to all 0s) before returning.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public IMemoryOwner<byte> RentCleared(long length)
|
||||||
|
=> RentCleared(checked((int)length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer's contents are cleared (set to all 0s) before returning.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public IMemoryOwner<byte> RentCleared(ulong length)
|
||||||
|
=> RentCleared(checked((int)length));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
|
||||||
|
/// The buffer's contents are cleared (set to all 0s) before returning.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="length">The buffer's required length in bytes</param>
|
||||||
|
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
|
||||||
|
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||||
|
public IMemoryOwner<byte> RentCleared(int length)
|
||||||
|
{
|
||||||
|
var buffer = RentImpl(length);
|
||||||
|
|
||||||
|
buffer.Memory.Span.Clear();
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ByteMemoryPoolBuffer RentImpl(int length)
|
||||||
|
{
|
||||||
|
if ((uint)length > Array.MaxLength)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(length), length, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ByteMemoryPoolBuffer(length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,98 +27,103 @@ namespace Ryujinx.HLE.HOS.Ipc
|
||||||
|
|
||||||
public IpcMessage()
|
public IpcMessage()
|
||||||
{
|
{
|
||||||
PtrBuff = new List<IpcPtrBuffDesc>();
|
PtrBuff = new List<IpcPtrBuffDesc>(0);
|
||||||
SendBuff = new List<IpcBuffDesc>();
|
SendBuff = new List<IpcBuffDesc>(0);
|
||||||
ReceiveBuff = new List<IpcBuffDesc>();
|
ReceiveBuff = new List<IpcBuffDesc>(0);
|
||||||
ExchangeBuff = new List<IpcBuffDesc>();
|
ExchangeBuff = new List<IpcBuffDesc>(0);
|
||||||
RecvListBuff = new List<IpcRecvListBuffDesc>();
|
RecvListBuff = new List<IpcRecvListBuffDesc>(0);
|
||||||
|
|
||||||
ObjectIds = new List<int>();
|
ObjectIds = new List<int>(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) : this()
|
public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr)
|
||||||
{
|
{
|
||||||
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data))
|
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data))
|
||||||
{
|
{
|
||||||
BinaryReader reader = new BinaryReader(ms);
|
BinaryReader reader = new BinaryReader(ms);
|
||||||
|
|
||||||
Initialize(reader, cmdPtr);
|
int word0 = reader.ReadInt32();
|
||||||
}
|
int word1 = reader.ReadInt32();
|
||||||
}
|
|
||||||
|
|
||||||
private void Initialize(BinaryReader reader, long cmdPtr)
|
Type = (IpcMessageType)(word0 & 0xffff);
|
||||||
{
|
|
||||||
int word0 = reader.ReadInt32();
|
|
||||||
int word1 = reader.ReadInt32();
|
|
||||||
|
|
||||||
Type = (IpcMessageType)(word0 & 0xffff);
|
int ptrBuffCount = (word0 >> 16) & 0xf;
|
||||||
|
int sendBuffCount = (word0 >> 20) & 0xf;
|
||||||
|
int recvBuffCount = (word0 >> 24) & 0xf;
|
||||||
|
int xchgBuffCount = (word0 >> 28) & 0xf;
|
||||||
|
|
||||||
int ptrBuffCount = (word0 >> 16) & 0xf;
|
int rawDataSize = (word1 >> 0) & 0x3ff;
|
||||||
int sendBuffCount = (word0 >> 20) & 0xf;
|
int recvListFlags = (word1 >> 10) & 0xf;
|
||||||
int recvBuffCount = (word0 >> 24) & 0xf;
|
bool hndDescEnable = ((word1 >> 31) & 0x1) != 0;
|
||||||
int xchgBuffCount = (word0 >> 28) & 0xf;
|
|
||||||
|
|
||||||
int rawDataSize = (word1 >> 0) & 0x3ff;
|
if (hndDescEnable)
|
||||||
int recvListFlags = (word1 >> 10) & 0xf;
|
|
||||||
bool hndDescEnable = ((word1 >> 31) & 0x1) != 0;
|
|
||||||
|
|
||||||
if (hndDescEnable)
|
|
||||||
{
|
|
||||||
HandleDesc = new IpcHandleDesc(reader);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int index = 0; index < ptrBuffCount; index++)
|
|
||||||
{
|
|
||||||
PtrBuff.Add(new IpcPtrBuffDesc(reader));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReadBuff(List<IpcBuffDesc> buff, int count)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < count; index++)
|
|
||||||
{
|
{
|
||||||
buff.Add(new IpcBuffDesc(reader));
|
HandleDesc = new IpcHandleDesc(reader);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ReadBuff(SendBuff, sendBuffCount);
|
PtrBuff = new List<IpcPtrBuffDesc>(ptrBuffCount);
|
||||||
ReadBuff(ReceiveBuff, recvBuffCount);
|
|
||||||
ReadBuff(ExchangeBuff, xchgBuffCount);
|
|
||||||
|
|
||||||
rawDataSize *= 4;
|
for (int index = 0; index < ptrBuffCount; index++)
|
||||||
|
{
|
||||||
|
PtrBuff.Add(new IpcPtrBuffDesc(reader));
|
||||||
|
}
|
||||||
|
|
||||||
long recvListPos = reader.BaseStream.Position + rawDataSize;
|
static List<IpcBuffDesc> ReadBuff(BinaryReader reader, int count)
|
||||||
|
{
|
||||||
|
List<IpcBuffDesc> buff = new List<IpcBuffDesc>(count);
|
||||||
|
|
||||||
|
for (int index = 0; index < count; index++)
|
||||||
|
{
|
||||||
|
buff.Add(new IpcBuffDesc(reader));
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
SendBuff = ReadBuff(reader, sendBuffCount);
|
||||||
|
ReceiveBuff = ReadBuff(reader, recvBuffCount);
|
||||||
|
ExchangeBuff = ReadBuff(reader, xchgBuffCount);
|
||||||
|
|
||||||
|
rawDataSize *= 4;
|
||||||
|
|
||||||
|
long recvListPos = reader.BaseStream.Position + rawDataSize;
|
||||||
|
|
||||||
// Only CMIF has the padding requirements.
|
// Only CMIF has the padding requirements.
|
||||||
if (Type < IpcMessageType.TipcCloseSession)
|
if (Type < IpcMessageType.TipcCloseSession)
|
||||||
{
|
{
|
||||||
long pad0 = GetPadSize16(reader.BaseStream.Position + cmdPtr);
|
long pad0 = GetPadSize16(reader.BaseStream.Position + cmdPtr);
|
||||||
|
|
||||||
if (rawDataSize != 0)
|
if (rawDataSize != 0)
|
||||||
{
|
{
|
||||||
rawDataSize -= (int)pad0;
|
rawDataSize -= (int)pad0;
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.BaseStream.Seek(pad0, SeekOrigin.Current);
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.BaseStream.Seek(pad0, SeekOrigin.Current);
|
int recvListCount = recvListFlags - 2;
|
||||||
}
|
|
||||||
|
|
||||||
int recvListCount = recvListFlags - 2;
|
if (recvListCount == 0)
|
||||||
|
{
|
||||||
|
recvListCount = 1;
|
||||||
|
}
|
||||||
|
else if (recvListCount < 0)
|
||||||
|
{
|
||||||
|
recvListCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (recvListCount == 0)
|
RawData = reader.ReadBytes(rawDataSize);
|
||||||
{
|
|
||||||
recvListCount = 1;
|
|
||||||
}
|
|
||||||
else if (recvListCount < 0)
|
|
||||||
{
|
|
||||||
recvListCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
RawData = reader.ReadBytes(rawDataSize);
|
reader.BaseStream.Seek(recvListPos, SeekOrigin.Begin);
|
||||||
|
|
||||||
reader.BaseStream.Seek(recvListPos, SeekOrigin.Begin);
|
RecvListBuff = new List<IpcRecvListBuffDesc>(recvListCount);
|
||||||
|
|
||||||
for (int index = 0; index < recvListCount; index++)
|
for (int index = 0; index < recvListCount; index++)
|
||||||
{
|
{
|
||||||
RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64()));
|
RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64()));
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectIds = new List<int>(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
{
|
{
|
||||||
lock (_context.CriticalSection.Lock)
|
lock (_context.CriticalSection.Lock)
|
||||||
{
|
{
|
||||||
_waitingObjects.RemoveAll(x => x.Object == schedulerObj);
|
for (int index = _waitingObjects.Count - 1; index >= 0; index--)
|
||||||
|
{
|
||||||
|
if (_waitingObjects[index].Object == schedulerObj)
|
||||||
|
{
|
||||||
|
_waitingObjects.RemoveAt(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,16 +111,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while (Interlocked.Read(ref _enforceWakeupFromSpinWait) != 1 && PerformanceCounter.ElapsedTicks <= next.TimePoint)
|
while (Interlocked.Read(ref _enforceWakeupFromSpinWait) != 1 && PerformanceCounter.ElapsedTicks < next.TimePoint)
|
||||||
{
|
{
|
||||||
|
// Our time is close - don't let SpinWait go off and potentially Thread.Sleep().
|
||||||
if (spinWait.NextSpinWillYield)
|
if (spinWait.NextSpinWillYield)
|
||||||
{
|
{
|
||||||
Thread.Yield();
|
Thread.Yield();
|
||||||
|
|
||||||
spinWait.Reset();
|
spinWait.Reset();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
spinWait.SpinOnce();
|
{
|
||||||
|
spinWait.SpinOnce();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spinWait.Reset();
|
spinWait.Reset();
|
||||||
|
|
|
@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
@ -553,7 +554,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
|
|
||||||
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
KProcess currentProcess = KernelStatic.GetCurrentProcess();
|
||||||
|
|
||||||
KSynchronizationObject[] syncObjs = handles.Length == 0 ? Array.Empty<KSynchronizationObject>() : new KSynchronizationObject[handles.Length];
|
KSynchronizationObject[] syncObjsArray = ArrayPool<KSynchronizationObject>.Shared.Rent(handles.Length);
|
||||||
|
|
||||||
|
Span<KSynchronizationObject> syncObjs = syncObjsArray.AsSpan(0, handles.Length);
|
||||||
|
|
||||||
for (int index = 0; index < handles.Length; index++)
|
for (int index = 0; index < handles.Length; index++)
|
||||||
{
|
{
|
||||||
|
@ -606,6 +609,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayPool<KSynchronizationObject>.Shared.Return(syncObjsArray);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
|
@ -59,7 +60,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LinkedListNode<KThread>[] syncNodes = syncObjs.Length == 0 ? Array.Empty<LinkedListNode<KThread>>() : new LinkedListNode<KThread>[syncObjs.Length];
|
LinkedListNode<KThread>[] syncNodesArray = ArrayPool<LinkedListNode<KThread>>.Shared.Rent(syncObjs.Length);
|
||||||
|
|
||||||
|
Span<LinkedListNode<KThread>> syncNodes = syncNodesArray.AsSpan(0, syncObjs.Length);
|
||||||
|
|
||||||
for (int index = 0; index < syncObjs.Length; index++)
|
for (int index = 0; index < syncObjs.Length; index++)
|
||||||
{
|
{
|
||||||
|
@ -101,6 +104,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
handleIndex = index;
|
handleIndex = index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayPool<LinkedListNode<KThread>>.Shared.Return(syncNodesArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
_context.CriticalSection.Leave();
|
_context.CriticalSection.Leave();
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
|
@ -68,25 +69,29 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
|
||||||
|
|
||||||
ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray();
|
ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray();
|
||||||
|
|
||||||
Memory<byte> output = new byte[outputSize];
|
using (IMemoryOwner<byte> outputOwner = ByteMemoryPool.Shared.RentCleared(outputSize))
|
||||||
Memory<byte> performanceOutput = new byte[performanceOutputSize];
|
using (IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.Shared.RentCleared(performanceOutputSize))
|
||||||
|
|
||||||
using MemoryHandle outputHandle = output.Pin();
|
|
||||||
using MemoryHandle performanceOutputHandle = performanceOutput.Pin();
|
|
||||||
|
|
||||||
ResultCode result = _impl.RequestUpdate(output, performanceOutput, input);
|
|
||||||
|
|
||||||
if (result == ResultCode.Success)
|
|
||||||
{
|
{
|
||||||
context.Memory.Write(outputPosition, output.Span);
|
Memory<byte> output = outputOwner.Memory;
|
||||||
context.Memory.Write(performanceOutputPosition, performanceOutput.Span);
|
Memory<byte> performanceOutput = performanceOutputOwner.Memory;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
using MemoryHandle outputHandle = output.Pin();
|
||||||
|
using MemoryHandle performanceOutputHandle = performanceOutput.Pin();
|
||||||
|
|
||||||
|
ResultCode result = _impl.RequestUpdate(output, performanceOutput, input);
|
||||||
|
|
||||||
|
if (result == ResultCode.Success)
|
||||||
|
{
|
||||||
|
context.Memory.Write(outputPosition, output.Span);
|
||||||
|
context.Memory.Write(performanceOutputPosition, performanceOutput.Span);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandCmif(5)]
|
[CommandCmif(5)]
|
||||||
|
|
|
@ -76,6 +76,8 @@ namespace Ryujinx.HLE.HOS.Services
|
||||||
|
|
||||||
context.RequestData.BaseStream.Seek(0x10 + dataPayloadSize, SeekOrigin.Begin);
|
context.RequestData.BaseStream.Seek(0x10 + dataPayloadSize, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
context.Request.ObjectIds.EnsureCapacity(inputObjCount);
|
||||||
|
|
||||||
for (int index = 0; index < inputObjCount; index++)
|
for (int index = 0; index < inputObjCount; index++)
|
||||||
{
|
{
|
||||||
context.Request.ObjectIds.Add(context.RequestData.ReadInt32());
|
context.Request.ObjectIds.Add(context.RequestData.ReadInt32());
|
||||||
|
|
|
@ -217,6 +217,8 @@ namespace Ryujinx.HLE.HOS.Services
|
||||||
|
|
||||||
if (noReceive)
|
if (noReceive)
|
||||||
{
|
{
|
||||||
|
response.PtrBuff.EnsureCapacity(request.RecvListBuff.Count);
|
||||||
|
|
||||||
for (int i = 0; i < request.RecvListBuff.Count; i++)
|
for (int i = 0; i < request.RecvListBuff.Count; i++)
|
||||||
{
|
{
|
||||||
ulong size = (ulong)BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan(sizesOffset + i * 2, 2));
|
ulong size = (ulong)BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan(sizesOffset + i * 2, 2));
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
using Ryujinx.HLE.HOS.Ipc;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Ryujinx.HLE.HOS.Ipc;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
{
|
{
|
||||||
|
@ -83,16 +85,19 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
|
|
||||||
ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize);
|
ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize);
|
||||||
|
|
||||||
Span<byte> outputParcel = new Span<byte>(new byte[replySize]);
|
using (IMemoryOwner<byte> outputParcelOwner = ByteMemoryPool.Shared.RentCleared(replySize))
|
||||||
|
|
||||||
ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel);
|
|
||||||
|
|
||||||
if (result == ResultCode.Success)
|
|
||||||
{
|
{
|
||||||
context.Memory.Write(replyPos, outputParcel);
|
Span<byte> outputParcel = outputParcelOwner.Memory.Span;
|
||||||
}
|
|
||||||
|
ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel);
|
||||||
|
|
||||||
return result;
|
if (result == ResultCode.Success)
|
||||||
|
{
|
||||||
|
context.Memory.Write(replyPos, outputParcel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract ResultCode AdjustRefcount(int binderId, int addVal, int type);
|
protected abstract ResultCode AdjustRefcount(int binderId, int addVal, int type);
|
||||||
|
|
|
@ -12,17 +12,7 @@ namespace Ryujinx.Input.SDL2
|
||||||
{
|
{
|
||||||
private bool HasConfiguration => _configuration != null;
|
private bool HasConfiguration => _configuration != null;
|
||||||
|
|
||||||
private class ButtonMappingEntry
|
private record struct ButtonMappingEntry(GamepadButtonInputId To, GamepadButtonInputId From);
|
||||||
{
|
|
||||||
public readonly GamepadButtonInputId To;
|
|
||||||
public readonly GamepadButtonInputId From;
|
|
||||||
|
|
||||||
public ButtonMappingEntry(GamepadButtonInputId to, GamepadButtonInputId from)
|
|
||||||
{
|
|
||||||
To = to;
|
|
||||||
From = from;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private StandardControllerInputConfig _configuration;
|
private StandardControllerInputConfig _configuration;
|
||||||
|
|
||||||
|
@ -85,7 +75,7 @@ namespace Ryujinx.Input.SDL2
|
||||||
public SDL2Gamepad(IntPtr gamepadHandle, string driverId)
|
public SDL2Gamepad(IntPtr gamepadHandle, string driverId)
|
||||||
{
|
{
|
||||||
_gamepadHandle = gamepadHandle;
|
_gamepadHandle = gamepadHandle;
|
||||||
_buttonsUserMapping = new List<ButtonMappingEntry>();
|
_buttonsUserMapping = new List<ButtonMappingEntry>(20);
|
||||||
|
|
||||||
Name = SDL_GameControllerName(_gamepadHandle);
|
Name = SDL_GameControllerName(_gamepadHandle);
|
||||||
Id = driverId;
|
Id = driverId;
|
||||||
|
|
Reference in a new issue