666e05f5cb
* 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
217 lines
No EOL
7.4 KiB
C#
217 lines
No EOL
7.4 KiB
C#
using Ryujinx.Common.Logging;
|
|
using Ryujinx.Common.Memory;
|
|
using Ryujinx.HLE.HOS.Ipc;
|
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
|
using Ryujinx.Horizon.Common;
|
|
using System;
|
|
using System.Buffers;
|
|
|
|
namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
|
|
{
|
|
class AudioRendererServer : DisposableIpcService
|
|
{
|
|
private IAudioRenderer _impl;
|
|
|
|
public AudioRendererServer(IAudioRenderer impl)
|
|
{
|
|
_impl = impl;
|
|
}
|
|
|
|
[CommandCmif(0)]
|
|
// GetSampleRate() -> u32
|
|
public ResultCode GetSampleRate(ServiceCtx context)
|
|
{
|
|
context.ResponseData.Write(_impl.GetSampleRate());
|
|
|
|
return ResultCode.Success;
|
|
}
|
|
|
|
[CommandCmif(1)]
|
|
// GetSampleCount() -> u32
|
|
public ResultCode GetSampleCount(ServiceCtx context)
|
|
{
|
|
context.ResponseData.Write(_impl.GetSampleCount());
|
|
|
|
return ResultCode.Success;
|
|
}
|
|
|
|
[CommandCmif(2)]
|
|
// GetMixBufferCount() -> u32
|
|
public ResultCode GetMixBufferCount(ServiceCtx context)
|
|
{
|
|
context.ResponseData.Write(_impl.GetMixBufferCount());
|
|
|
|
return ResultCode.Success;
|
|
}
|
|
|
|
[CommandCmif(3)]
|
|
// GetState() -> u32
|
|
public ResultCode GetState(ServiceCtx context)
|
|
{
|
|
context.ResponseData.Write(_impl.GetState());
|
|
|
|
return ResultCode.Success;
|
|
}
|
|
|
|
[CommandCmif(4)]
|
|
// RequestUpdate(buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 5> input)
|
|
// -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6> output, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 6> performanceOutput)
|
|
public ResultCode RequestUpdate(ServiceCtx context)
|
|
{
|
|
ulong inputPosition = context.Request.SendBuff[0].Position;
|
|
ulong inputSize = context.Request.SendBuff[0].Size;
|
|
|
|
ulong outputPosition = context.Request.ReceiveBuff[0].Position;
|
|
ulong outputSize = context.Request.ReceiveBuff[0].Size;
|
|
|
|
ulong performanceOutputPosition = context.Request.ReceiveBuff[1].Position;
|
|
ulong performanceOutputSize = context.Request.ReceiveBuff[1].Size;
|
|
|
|
ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray();
|
|
|
|
using (IMemoryOwner<byte> outputOwner = ByteMemoryPool.Shared.RentCleared(outputSize))
|
|
using (IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.Shared.RentCleared(performanceOutputSize))
|
|
{
|
|
Memory<byte> output = outputOwner.Memory;
|
|
Memory<byte> performanceOutput = performanceOutputOwner.Memory;
|
|
|
|
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)]
|
|
// Start()
|
|
public ResultCode Start(ServiceCtx context)
|
|
{
|
|
return _impl.Start();
|
|
}
|
|
|
|
[CommandCmif(6)]
|
|
// Stop()
|
|
public ResultCode Stop(ServiceCtx context)
|
|
{
|
|
return _impl.Stop();
|
|
}
|
|
|
|
[CommandCmif(7)]
|
|
// QuerySystemEvent() -> handle<copy, event>
|
|
public ResultCode QuerySystemEvent(ServiceCtx context)
|
|
{
|
|
ResultCode result = _impl.QuerySystemEvent(out KEvent systemEvent);
|
|
|
|
if (result == ResultCode.Success)
|
|
{
|
|
if (context.Process.HandleTable.GenerateHandle(systemEvent.ReadableEvent, out int handle) != Result.Success)
|
|
{
|
|
throw new InvalidOperationException("Out of handles!");
|
|
}
|
|
|
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
[CommandCmif(8)]
|
|
// SetAudioRendererRenderingTimeLimit(u32 limit)
|
|
public ResultCode SetAudioRendererRenderingTimeLimit(ServiceCtx context)
|
|
{
|
|
uint limit = context.RequestData.ReadUInt32();
|
|
|
|
_impl.SetRenderingTimeLimit(limit);
|
|
|
|
return ResultCode.Success;
|
|
}
|
|
|
|
[CommandCmif(9)]
|
|
// GetAudioRendererRenderingTimeLimit() -> u32 limit
|
|
public ResultCode GetAudioRendererRenderingTimeLimit(ServiceCtx context)
|
|
{
|
|
uint limit = _impl.GetRenderingTimeLimit();
|
|
|
|
context.ResponseData.Write(limit);
|
|
|
|
return ResultCode.Success;
|
|
}
|
|
|
|
[CommandCmif(10)] // 3.0.0+
|
|
// RequestUpdateAuto(buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x21> input)
|
|
// -> (buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x22> output, buffer<nn::audio::detail::AudioRendererUpdateDataHeader, 0x22> performanceOutput)
|
|
public ResultCode RequestUpdateAuto(ServiceCtx context)
|
|
{
|
|
(ulong inputPosition, ulong inputSize) = context.Request.GetBufferType0x21();
|
|
(ulong outputPosition, ulong outputSize) = context.Request.GetBufferType0x22(0);
|
|
(ulong performanceOutputPosition, ulong performanceOutputSize) = context.Request.GetBufferType0x22(1);
|
|
|
|
ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray();
|
|
|
|
Memory<byte> output = new byte[outputSize];
|
|
Memory<byte> performanceOutput = new byte[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);
|
|
context.Memory.Write(performanceOutputPosition, performanceOutput.Span);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
[CommandCmif(11)] // 3.0.0+
|
|
// ExecuteAudioRendererRendering()
|
|
public ResultCode ExecuteAudioRendererRendering(ServiceCtx context)
|
|
{
|
|
return _impl.ExecuteAudioRendererRendering();
|
|
}
|
|
|
|
[CommandCmif(12)] // 15.0.0+
|
|
// SetVoiceDropParameter(f32 voiceDropParameter)
|
|
public ResultCode SetVoiceDropParameter(ServiceCtx context)
|
|
{
|
|
float voiceDropParameter = context.RequestData.ReadSingle();
|
|
|
|
_impl.SetVoiceDropParameter(voiceDropParameter);
|
|
|
|
return ResultCode.Success;
|
|
}
|
|
|
|
[CommandCmif(13)] // 15.0.0+
|
|
// GetVoiceDropParameter() -> f32 voiceDropParameter
|
|
public ResultCode GetVoiceDropParameter(ServiceCtx context)
|
|
{
|
|
float voiceDropParameter = _impl.GetVoiceDropParameter();
|
|
|
|
context.ResponseData.Write(voiceDropParameter);
|
|
|
|
return ResultCode.Success;
|
|
}
|
|
|
|
protected override void Dispose(bool isDisposing)
|
|
{
|
|
if (isDisposing)
|
|
{
|
|
_impl.Dispose();
|
|
}
|
|
}
|
|
}
|
|
} |