diff options
| author | Mary <me@thog.eu> | 2021-05-25 19:01:09 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-05-25 19:01:09 +0200 |
| commit | f3b0b4831c323a20393aa0388f947317354372b7 (patch) | |
| tree | 478412fd3a8c2de6eab5e54dd47944f59bf1a836 /Ryujinx.Audio/Renderer/Server | |
| parent | 54ea2285f05ef6f59a6f1c63df4a7bdd77d7b883 (diff) | |
amadeus: Update to REV9 (#2309)
* amadeus: Update to REV9
This implements all the changes made with REV9 on 12.0.0.
* Address Ac_k's comments
Diffstat (limited to 'Ryujinx.Audio/Renderer/Server')
19 files changed, 586 insertions, 27 deletions
diff --git a/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs b/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs index 112b0e44..943a2d78 100644 --- a/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs +++ b/Ryujinx.Audio/Renderer/Server/AudioRenderSystem.cs @@ -307,7 +307,7 @@ namespace Ryujinx.Audio.Renderer.Server _upsamplerManager = new UpsamplerManager(upSamplerWorkBuffer, _upsamplerCount); - _effectContext.Initialize(parameter.EffectCount); + _effectContext.Initialize(parameter.EffectCount, _behaviourContext.IsEffectInfoVersion2Supported() ? parameter.EffectCount : 0); _sinkContext.Initialize(parameter.SinkCount); Memory<VoiceUpdateState> voiceUpdateStatesDsp = workBufferAllocator.Allocate<VoiceUpdateState>(parameter.VoiceCount, VoiceUpdateState.Align); @@ -636,6 +636,11 @@ namespace Ryujinx.Audio.Renderer.Server _voiceContext.UpdateForCommandGeneration(); + if (_behaviourContext.IsEffectInfoVersion2Supported()) + { + _effectContext.UpdateResultStateForCommandGeneration(); + } + ulong endTicks = GetSystemTicks(); _totalElapsedTicks = endTicks - startTicks; diff --git a/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs b/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs index b31f9e9f..ed1f402e 100644 --- a/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs +++ b/Ryujinx.Audio/Renderer/Server/BehaviourContext.cs @@ -90,9 +90,17 @@ namespace Ryujinx.Audio.Renderer.Server public const int Revision8 = 8 << 24; /// <summary> + /// REV9: + /// EffectInfo parameters were revisited with a new revision (version 2) allowing more data control between the client and server. + /// A new effect was added: Limiter. This effect is effectively implemented with a DRC while providing statistics on the processing on <see cref="Parameter.EffectOutStatusVersion2"/>. + /// </summary> + /// <remarks>This was added in system update 12.0.0</remarks> + public const int Revision9 = 9 << 24; + + /// <summary> /// Last revision supported by the implementation. /// </summary> - public const int LastRevision = Revision8; + public const int LastRevision = Revision9; /// <summary> /// Target revision magic supported by the implementation. @@ -331,6 +339,15 @@ namespace Ryujinx.Audio.Renderer.Server } /// <summary> + /// Check if the audio renderer should use the new effect info format. + /// </summary> + /// <returns>True if the audio renderer should use the new effect info format.</returns> + public bool IsEffectInfoVersion2Supported() + { + return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision9); + } + + /// <summary> /// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>. /// </summary> /// <returns>The version of the <see cref="ICommandProcessingTimeEstimator"/>.</returns> diff --git a/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs b/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs index ca37e090..24cc93af 100644 --- a/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs +++ b/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs @@ -372,6 +372,49 @@ namespace Ryujinx.Audio.Renderer.Server } /// <summary> + /// Generate a new <see cref="LimiterCommandVersion1"/>. + /// </summary> + /// <param name="bufferOffset">The target buffer offset.</param> + /// <param name="parameter">The limiter parameter.</param> + /// <param name="state">The limiter state.</param> + /// <param name="isEnabled">Set to true if the effect should be active.</param> + /// <param name="workBuffer">The work buffer to use for processing.</param> + /// <param name="nodeId">The node id associated to this command.</param> + public void GenerateLimiterEffectVersion1(uint bufferOffset, LimiterParameter parameter, Memory<LimiterState> state, bool isEnabled, ulong workBuffer, int nodeId) + { + if (parameter.IsChannelCountValid()) + { + LimiterCommandVersion1 command = new LimiterCommandVersion1(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId); + + command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); + + AddCommand(command); + } + } + + /// <summary> + /// Generate a new <see cref="LimiterCommandVersion2"/>. + /// </summary> + /// <param name="bufferOffset">The target buffer offset.</param> + /// <param name="parameter">The limiter parameter.</param> + /// <param name="state">The limiter state.</param> + /// <param name="effectResultState">The DSP effect result state.</param> + /// <param name="isEnabled">Set to true if the effect should be active.</param> + /// <param name="workBuffer">The work buffer to use for processing.</param> + /// <param name="nodeId">The node id associated to this command.</param> + public void GenerateLimiterEffectVersion2(uint bufferOffset, LimiterParameter parameter, Memory<LimiterState> state, Memory<EffectResultState> effectResultState, bool isEnabled, ulong workBuffer, int nodeId) + { + if (parameter.IsChannelCountValid()) + { + LimiterCommandVersion2 command = new LimiterCommandVersion2(bufferOffset, parameter, state, effectResultState, isEnabled, workBuffer, nodeId); + + command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command); + + AddCommand(command); + } + } + + /// <summary> /// Generate a new <see cref="AuxiliaryBufferCommand"/>. /// </summary> /// <param name="bufferOffset">The target buffer offset.</param> diff --git a/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs b/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs index d2499c1d..01e7c927 100644 --- a/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs +++ b/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs @@ -538,7 +538,25 @@ namespace Ryujinx.Audio.Renderer.Server } } - private void GenerateEffect(ref MixState mix, BaseEffect effect) + private void GenerateLimiterEffect(uint bufferOffset, LimiterEffect effect, int nodeId, int effectId) + { + Debug.Assert(effect.Type == EffectType.Limiter); + + ulong workBuffer = effect.GetWorkBuffer(-1); + + if (_rendererContext.BehaviourContext.IsEffectInfoVersion2Supported()) + { + Memory<EffectResultState> dspResultState = _effectContext.GetDspStateMemory(effectId); + + _commandBuffer.GenerateLimiterEffectVersion2(bufferOffset, effect.Parameter, effect.State, dspResultState, effect.IsEnabled, workBuffer, nodeId); + } + else + { + _commandBuffer.GenerateLimiterEffectVersion1(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId); + } + } + + private void GenerateEffect(ref MixState mix, int effectId, BaseEffect effect) { int nodeId = mix.NodeId; @@ -576,6 +594,9 @@ namespace Ryujinx.Audio.Renderer.Server case EffectType.BiquadFilter: GenerateBiquadFilterEffect(mix.BufferOffset, (BiquadFilterEffect)effect, nodeId); break; + case EffectType.Limiter: + GenerateLimiterEffect(mix.BufferOffset, (LimiterEffect)effect, nodeId, effectId); + break; default: throw new NotImplementedException($"Unsupported effect type {effect.Type}"); } @@ -611,7 +632,7 @@ namespace Ryujinx.Audio.Renderer.Server if (!effect.ShouldSkip()) { - GenerateEffect(ref mix, effect); + GenerateEffect(ref mix, effectOrder, effect); } } } diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs index 81d3b57b..feb3706f 100644 --- a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs +++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion1.cs @@ -176,5 +176,15 @@ namespace Ryujinx.Audio.Renderer.Server { return 0; } + + public uint Estimate(LimiterCommandVersion1 command) + { + return 0; + } + + public uint Estimate(LimiterCommandVersion2 command) + { + return 0; + } } } diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs index 0f4fe3c2..227f3c81 100644 --- a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs +++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion2.cs @@ -540,5 +540,15 @@ namespace Ryujinx.Audio.Renderer.Server { return 0; } + + public uint Estimate(LimiterCommandVersion1 command) + { + return 0; + } + + public uint Estimate(LimiterCommandVersion2 command) + { + return 0; + } } } diff --git a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs index b48ff8b5..e00fcf7b 100644 --- a/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs +++ b/Ryujinx.Audio/Renderer/Server/CommandProcessingTimeEstimatorVersion3.cs @@ -18,6 +18,7 @@ using Ryujinx.Audio.Common; using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Dsp.Command; +using Ryujinx.Audio.Renderer.Parameter.Effect; using System; using System.Diagnostics; using static Ryujinx.Audio.Renderer.Parameter.VoiceInParameter; @@ -632,5 +633,127 @@ namespace Ryujinx.Audio.Renderer.Server throw new NotImplementedException($"{format}"); } } + + private uint EstimateLimiterCommandCommon(LimiterParameter parameter, bool enabled) + { + Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + + if (_sampleCount == 160) + { + if (enabled) + { + switch (parameter.ChannelCount) + { + case 1: + return (uint)21392.0f; + case 2: + return (uint)26829.0f; + case 4: + return (uint)32405.0f; + case 6: + return (uint)52219.0f; + default: + throw new NotImplementedException($"{parameter.ChannelCount}"); + } + } + else + { + switch (parameter.ChannelCount) + { + case 1: + return (uint)897.0f; + case 2: + return (uint)931.55f; + case 4: + return (uint)975.39f; + case 6: + return (uint)1016.8f; + default: + throw new NotImplementedException($"{parameter.ChannelCount}"); + } + } + } + + if (enabled) + { + switch (parameter.ChannelCount) + { + case 1: + return (uint)30556.0f; + case 2: + return (uint)39011.0f; + case 4: + return (uint)48270.0f; + case 6: + return (uint)76712.0f; + default: + throw new NotImplementedException($"{parameter.ChannelCount}"); + } + } + else + { + switch (parameter.ChannelCount) + { + case 1: + return (uint)874.43f; + case 2: + return (uint)921.55f; + case 4: + return (uint)945.26f; + case 6: + return (uint)992.26f; + default: + throw new NotImplementedException($"{parameter.ChannelCount}"); + } + } + } + + public uint Estimate(LimiterCommandVersion1 command) + { + Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + + return EstimateLimiterCommandCommon(command.Parameter, command.IsEffectEnabled); + } + + public uint Estimate(LimiterCommandVersion2 command) + { + Debug.Assert(_sampleCount == 160 || _sampleCount == 240); + + if (!command.Parameter.StatisticsEnabled || !command.IsEffectEnabled) + { + return EstimateLimiterCommandCommon(command.Parameter, command.IsEffectEnabled); + } + + if (_sampleCount == 160) + { + switch (command.Parameter.ChannelCount) + { + case 1: + return (uint)23309.0f; + case 2: + return (uint)29954.0f; + case 4: + return (uint)35807.0f; + case 6: + return (uint)58340.0f; + default: + throw new NotImplementedException($"{command.Parameter.ChannelCount}"); + } + } + + switch (command.Parameter.ChannelCount) + { + case 1: + return (uint)33526.0f; + case 2: + return (uint)43549.0f; + case 4: + return (uint)52190.0f; + case 6: + return (uint)85527.0f; + default: + throw new NotImplementedException($"{command.Parameter.ChannelCount}"); + } + } } } diff --git a/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs index df82945b..eac1708e 100644 --- a/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs +++ b/Ryujinx.Audio/Renderer/Server/Effect/AuxiliaryBufferEffect.cs @@ -50,7 +50,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect return WorkBuffers[index].GetReference(true); } - public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper) + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter { Debug.Assert(IsTypeValid(ref parameter)); diff --git a/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs index 7529f894..c7b06e7a 100644 --- a/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs +++ b/Ryujinx.Audio/Renderer/Server/Effect/BaseEffect.cs @@ -98,7 +98,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect /// </summary> /// <param name="parameter">The user parameter.</param> /// <returns>Returns true if the <see cref="EffectType"/> sent by the user matches the internal <see cref="EffectType"/>.</returns> - public bool IsTypeValid(ref EffectInParameter parameter) + public bool IsTypeValid<T>(ref T parameter) where T: unmanaged, IEffectInParameter { return parameter.Type == TargetEffectType; } @@ -115,7 +115,7 @@ namespace Ryujinx.Audio.Renderer.Server.Effect /// Update the internal common parameters from a user parameter. /// </summary> /// <param name="parameter">The user parameter.</param> - protected void UpdateParameterBase(ref EffectInParameter parameter) + protected void UpdateParameterBase<T>(ref T parameter) where T : unmanaged, IEffectInParameter { MixId = parameter.MixId; ProcessingOrder = parameter.ProcessingOrder; @@ -154,12 +154,38 @@ namespace Ryujinx.Audio.Renderer.Server.Effect } /// <summary> - /// Update the internal state from a user parameter. + /// Initialize the given <paramref name="state"/> result state. + /// </summary> + /// <param name="state">The state to initalize</param> + public virtual void InitializeResultState(ref EffectResultState state) {} + + /// <summary> + /// Update the <paramref name="destState"/> result state with <paramref name="srcState"/>. + /// </summary> + /// <param name="destState">The destination result state</param> + /// <param name="srcState">The source result state</param> + public virtual void UpdateResultState(ref EffectResultState destState, ref EffectResultState srcState) {} + + /// <summary> + /// Update the internal state from a user version 1 parameter. + /// </summary> + /// <param name="updateErrorInfo">The possible <see cref="ErrorInfo"/> that was generated.</param> + /// <param name="parameter">The user parameter.</param> + /// <param name="mapper">The mapper to use.</param> + public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper) + { + Debug.Assert(IsTypeValid(ref parameter)); + + updateErrorInfo = new ErrorInfo(); + } + + /// <summary> + /// Update the internal state from a user version 2 parameter. /// </summary> /// <param name="updateErrorInfo">The possible <see cref="ErrorInfo"/> that was generated.</param> /// <param name="parameter">The user parameter.</param> /// <param name="mapper">The mapper to use.</param> - public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper) + public virtual void Update(out ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper) { Debug.Assert(IsTypeValid(ref parameter)); @@ -206,26 +232,26 @@ namespace Ryujinx.Audio.Renderer.Server.Effect /// </summary> /// <param name="outStatus">The given user output.</param> /// <param name="isAudioRendererActive">If set to true, the <see cref="AudioRenderSystem"/> is active.</param> - public void StoreStatus(ref EffectOutStatus outStatus, bool isAudioRendererActive) + public void StoreStatus<T>(ref T outStatus, bool isAudioRendererActive) where T: unmanaged, IEffectOutStatus { if (isAudioRendererActive) { if (UsageState == UsageState.Disabled) { - outStatus.State = EffectOutStatus.EffectState.Disabled; + outStatus.State = EffectState.Disabled; } else { - outStatus.State = EffectOutStatus.EffectState.Enabled; + outStatus.State = EffectState.Enabled; } } else if (UsageState == UsageState.New) { - outStatus.State = EffectOutStatus.EffectState.Enabled; + outStatus.State = EffectState.Enabled; } else { - outStatus.State = EffectOutStatus.EffectState.Disabled; + outStatus.State = EffectState.Disabled; } } @@ -249,6 +275,8 @@ namespace Ryujinx.Audio.Renderer.Server.Effect return PerformanceDetailType.Reverb3d; case EffectType.BufferMix: return PerformanceDetailType.Mix; + case EffectType.Limiter: + return PerformanceDetailType.Limiter; default: throw new NotImplementedException($"{Type}"); } diff --git a/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs index 35ba8a0d..6c91b607 100644 --- a/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs +++ b/Ryujinx.Audio/Renderer/Server/Effect/BiquadFilterEffect.cs @@ -52,7 +52,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect public override EffectType TargetEffectType => EffectType.BiquadFilter; - public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper) + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter { Debug.Assert(IsTypeValid(ref parameter)); diff --git a/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs index 14ed3950..05cccad9 100644 --- a/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs +++ b/Ryujinx.Audio/Renderer/Server/Effect/BufferMixEffect.cs @@ -36,7 +36,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect public override EffectType TargetEffectType => EffectType.BufferMix; - public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper) + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter { Debug.Assert(IsTypeValid(ref parameter)); diff --git a/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs index df3e5ee7..a5b3dbc5 100644 --- a/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs +++ b/Ryujinx.Audio/Renderer/Server/Effect/DelayEffect.cs @@ -54,7 +54,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect return GetSingleBuffer(); } - public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper) + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter { Debug.Assert(IsTypeValid(ref parameter)); diff --git a/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs b/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs index ff6051ae..074f4375 100644 --- a/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs +++ b/Ryujinx.Audio/Renderer/Server/Effect/EffectContext.cs @@ -15,6 +15,9 @@ // along with this program. If not, see <https://www.gnu.org/licenses/>. // +using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.Audio.Renderer.Utils; +using System; using System.Diagnostics; namespace Ryujinx.Audio.Renderer.Server.Effect @@ -34,6 +37,9 @@ namespace Ryujinx.Audio.Renderer.Server.Effect /// </summary> private uint _effectCount; + private EffectResultState[] _resultStatesCpu; + private EffectResultState[] _resultStatesDsp; + /// <summary> /// Create a new <see cref="EffectContext"/>. /// </summary> @@ -47,7 +53,8 @@ namespace Ryujinx.Audio.Renderer.Server.Effect /// Initialize the <see cref="EffectContext"/>. /// </summary> /// <param name="effectCount">The total effect count.</param> - public void Initialize(uint effectCount) + /// <param name="resultStateCount">The total result state count.</param> + public void Initialize(uint effectCount, uint resultStateCount) { _effectCount = effectCount; _effects = new BaseEffect[effectCount]; @@ -56,6 +63,9 @@ namespace Ryujinx.Audio.Renderer.Server.Effect { _effects[i] = new BaseEffect(); } + + _resultStatesCpu = new EffectResultState[resultStateCount]; + _resultStatesDsp = new EffectResultState[resultStateCount]; } /// <summary> @@ -78,5 +88,53 @@ namespace Ryujinx.Audio.Renderer.Server.Effect return ref _effects[index]; } + + /// <summary> + /// Get a reference to a <see cref="EffectResultState"/> at the given <paramref name="index"/>. + /// </summary> + /// <param name="index">The index to use.</param> + /// <returns>A reference to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.</returns> + /// <remarks>The returned <see cref="EffectResultState"/> should only be used when updating the server state.</remarks> + public ref EffectResultState GetState(int index) + { + Debug.Assert(index >= 0 && index < _resultStatesCpu.Length); + + return ref _resultStatesCpu[index]; + } + + /// <summary> + /// Get a reference to a <see cref="EffectResultState"/> at the given <paramref name="index"/>. + /// </summary> + /// <param name="index">The index to use.</param> + /// <returns>A reference to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.</returns> + /// <remarks>The returned <see cref="EffectResultState"/> should only be used in the context of processing on the <see cref="Dsp.AudioProcessor"/>.</remarks> + public ref EffectResultState GetDspState(int index) + { + Debug.Assert(index >= 0 && index < _resultStatesDsp.Length); + + return ref _resultStatesDsp[index]; + } + + /// <summary> + /// Get a memory instance to a <see cref="EffectResultState"/> at the given <paramref name="index"/>. + /// </summary> + /// <param name="index">The index to use.</param> + /// <returns>A memory instance to a <see cref="EffectResultState"/> at the given <paramref name="index"/>.</returns> + /// <remarks>The returned <see cref="Memory{EffectResultState}"/> should only be used in the context of processing on the <see cref="Dsp.AudioProcessor"/>.</remarks> + public Memory<EffectResultState> GetDspStateMemory(int index) + { + return SpanIOHelper.GetMemory(_resultStatesDsp.AsMemory(), index, (uint)_resultStatesDsp.Length); + } + + /// <summary> + /// Update internal state during command generation. + /// </summary> + public void UpdateResultStateForCommandGeneration() + { + for (int index = 0; index < _resultStatesCpu.Length; index++) + { + _effects[index].UpdateResultState(ref _resultStatesCpu[index], ref _resultStatesDsp[index]); + } + } } } diff --git a/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs new file mode 100644 index 00000000..fa04c027 --- /dev/null +++ b/Ryujinx.Audio/Renderer/Server/Effect/LimiterEffect.cs @@ -0,0 +1,112 @@ +// +// Copyright (c) 2019-2021 Ryujinx +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see <https://www.gnu.org/licenses/>. +// + +using Ryujinx.Audio.Renderer.Common; +using Ryujinx.Audio.Renderer.Dsp.State; +using Ryujinx.Audio.Renderer.Parameter; +using Ryujinx.Audio.Renderer.Parameter.Effect; +using Ryujinx.Audio.Renderer.Server.MemoryPool; +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Ryujinx.Audio.Renderer.Server.Effect +{ + /// <summary> + /// Server state for a limiter effect. + /// </summary> + public class LimiterEffect : BaseEffect + { + /// <summary> + /// The limiter parameter. + /// </summary> + public LimiterParameter Parameter; + + /// <summary> + /// The limiter state. + /// </summary> + public Memory<LimiterState> State { get; } + + /// <summary> + /// Create a new <see cref="LimiterEffect"/>. + /// </summary> + public LimiterEffect() + { + State = new LimiterState[1]; + } + + public override EffectType TargetEffectType => EffectType.Limiter; + + public override ulong GetWorkBuffer(int index) + { + return GetSingleBuffer(); + } + + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter + { + Debug.Assert(IsTypeValid(ref parameter)); + + ref LimiterParameter limiterParameter = ref MemoryMarshal.Cast<byte, LimiterParameter>(parameter.SpecificData)[0]; + + updateErrorInfo = new BehaviourParameter.ErrorInfo(); + + UpdateParameterBase(ref parameter); + + Parameter = limiterParameter; + + IsEnabled = parameter.IsEnabled; + + if (BufferUnmapped || parameter.IsNew) + { + UsageState = UsageState.New; + Parameter.Status = UsageState.Invalid; + + BufferUnmapped = !mapper.TryAttachBuffer(out updateErrorInfo, ref WorkBuffers[0], parameter.BufferBase, parameter.BufferSize); + } + } + + public override void UpdateForCommandGeneration() + { + UpdateUsageStateForCommandGeneration(); + + Parameter.Status = UsageState.Enabled; + Parameter.StatisticsReset = false; + } + + public override void InitializeResultState(ref EffectResultState state) + { + ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(state.SpecificData)[0]; + + statistics.Reset(); + } + + public override void UpdateResultState(ref EffectResultState destState, ref EffectResultState srcState) + { + destState = srcState; + } + } +} diff --git a/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs index d9f23799..7b8fd6c4 100644 --- a/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs +++ b/Ryujinx.Audio/Renderer/Server/Effect/Reverb3dEffect.cs @@ -53,7 +53,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect return GetSingleBuffer(); } - public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper) + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T: unmanaged, IEffectInParameter { Debug.Assert(IsTypeValid(ref parameter)); diff --git a/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs b/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs index 4c81f729..30908396 100644 --- a/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs +++ b/Ryujinx.Audio/Renderer/Server/Effect/ReverbEffect.cs @@ -56,7 +56,17 @@ namespace Ryujinx.Audio.Renderer.Server.Effect return GetSingleBuffer(); } - public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameter parameter, PoolMapper mapper) + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion1 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public override void Update(out BehaviourParameter.ErrorInfo updateErrorInfo, ref EffectInParameterVersion2 parameter, PoolMapper mapper) + { + Update(out updateErrorInfo, ref parameter, mapper); + } + + public void Update<T>(out BehaviourParameter.ErrorInfo updateErrorInfo, ref T parameter, PoolMapper mapper) where T : unmanaged, IEffectInParameter { Debug.Assert(IsTypeValid(ref parameter)); diff --git a/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs b/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs index 119ec907..eae48be6 100644 --- a/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs +++ b/Ryujinx.Audio/Renderer/Server/ICommandProcessingTimeEstimator.cs @@ -48,5 +48,7 @@ namespace Ryujinx.Audio.Renderer.Server uint Estimate(DeviceSinkCommand command); uint Estimate(DownMixSurroundToStereoCommand command); uint Estimate(UpsampleCommand command); + uint Estimate(LimiterCommandVersion1 command); + uint Estimate(LimiterCommandVersion2 command); } } diff --git a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs index 3a336af3..fd7c93b1 100644 --- a/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs +++ b/Ryujinx.Audio/Renderer/Server/Performance/PerformanceManager.cs @@ -18,7 +18,6 @@ using Ryujinx.Audio.Renderer.Common; using Ryujinx.Audio.Renderer.Parameter; using System; -using System.Runtime.CompilerServices; namespace Ryujinx.Audio.Renderer.Server.Performance { diff --git a/Ryujinx.Audio/Renderer/Server/StateUpdater.cs b/Ryujinx.Audio/Renderer/Server/StateUpdater.cs index 77935b75..e7a982c4 100644 --- a/Ryujinx.Audio/Renderer/Server/StateUpdater.cs +++ b/Ryujinx.Audio/Renderer/Server/StateUpdater.cs @@ -224,7 +224,7 @@ namespace Ryujinx.Audio.Renderer.Server return ResultCode.Success; } - private static void ResetEffect(ref BaseEffect effect, ref EffectInParameter parameter, PoolMapper mapper) + private static void ResetEffect<T>(ref BaseEffect effect, ref T parameter, PoolMapper mapper) where T: unmanaged, IEffectInParameter { effect.ForceUnmapBuffers(mapper); @@ -251,6 +251,9 @@ namespace Ryujinx.Audio.Renderer.Server case EffectType.BiquadFilter: effect = new BiquadFilterEffect(); break; + case EffectType.Limiter: + effect = new LimiterEffect(); + break; default: throw new NotImplementedException($"EffectType {parameter.Type} not implemented!"); } @@ -258,14 +261,82 @@ namespace Ryujinx.Audio.Renderer.Server public ResultCode UpdateEffects(EffectContext context, bool isAudioRendererActive, Memory<MemoryPoolState> memoryPools) { - if (context.GetCount() * Unsafe.SizeOf<EffectInParameter>() != _inputHeader.EffectsSize) + if (_behaviourContext.IsEffectInfoVersion2Supported()) + { + return UpdateEffectsVersion2(context, isAudioRendererActive, memoryPools); + } + else + { + return UpdateEffectsVersion1(context, isAudioRendererActive, memoryPools); + } + } + + public ResultCode UpdateEffectsVersion2(EffectContext context, bool isAudioRendererActive, Memory<MemoryPoolState> memoryPools) + { + if (context.GetCount() * Unsafe.SizeOf<EffectInParameterVersion2>() != _inputHeader.EffectsSize) + { + return ResultCode.InvalidUpdateInfo; + } + + int initialOutputSize = _output.Length; + + ReadOnlySpan<EffectInParameterVersion2> parameters = MemoryMarshal.Cast<byte, EffectInParameterVersion2>(_input.Slice(0, (int)_inputHeader.EffectsSize).Span); + + _input = _input.Slice((int)_inputHeader.EffectsSize); + + PoolMapper mapper = new PoolMapper(_processHandle, memoryPools, _behaviourContext.IsMemoryPoolForceMappingEnabled()); + + for (int i = 0; i < context.GetCount(); i++) + { + EffectInParameterVersion2 parameter = parameters[i]; + + ref EffectOutStatusVersion2 outStatus = ref SpanIOHelper.GetWriteRef<EffectOutStatusVersion2>(ref _output)[0]; + + ref BaseEffect effect = ref context.GetEffect(i); + + if (!effect.IsTypeValid(ref parameter)) + { + ResetEffect(ref effect, ref parameter, mapper); + } + + effect.Update(out ErrorInfo updateErrorInfo, ref parameter, mapper); + + if (updateErrorInfo.ErrorCode != ResultCode.Success) + { + _behaviourContext.AppendError(ref updateErrorInfo); + } + + effect.StoreStatus(ref outStatus, isAudioRendererActive); + + if (parameter.IsNew) + { + effect.InitializeResultState(ref context.GetDspState(i)); + effect.InitializeResultState(ref context.GetState(i)); + } + + effect.UpdateResultState(ref outStatus.ResultState, ref context.GetState(i)); + } + + int currentOutputSize = _output.Length; + + OutputHeader.EffectsSize = (uint)(Unsafe.SizeOf<EffectOutStatusVersion2>() * context.GetCount()); + OutputHeader.TotalSize += OutputHeader.EffectsSize; + + Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.EffectsSize); + + return ResultCode.Success; + } + + public ResultCode UpdateEffectsVersion1(EffectContext context, bool isAudioRendererActive, Memory<MemoryPoolState> memoryPools) + { + if (context.GetCount() * Unsafe.SizeOf<EffectInParameterVersion1>() != _inputHeader.EffectsSize) { return ResultCode.InvalidUpdateInfo; } int initialOutputSize = _output.Length; - ReadOnlySpan<EffectInParameter> parameters = MemoryMarshal.Cast<byte, EffectInParameter>(_input.Slice(0, (int)_inputHeader.EffectsSize).Span); + ReadOnlySpan<EffectInParameterVersion1> parameters = MemoryMarshal.Cast<byte, EffectInParameterVersion1>(_input.Slice(0, (int)_inputHeader.EffectsSize).Span); _input = _input.Slice((int)_inputHeader.EffectsSize); @@ -273,9 +344,9 @@ namespace Ryujinx.Audio.Renderer.Server for (int i = 0; i < context.GetCount(); i++) { - EffectInParameter parameter = parameters[i]; + EffectInParameterVersion1 parameter = parameters[i]; - ref EffectOutStatus outStatus = ref SpanIOHelper.GetWriteRef<EffectOutStatus>(ref _output)[0]; + ref EffectOutStatusVersion1 outStatus = ref SpanIOHelper.GetWriteRef<EffectOutStatusVersion1>(ref _output)[0]; ref BaseEffect effect = ref context.GetEffect(i); @@ -296,7 +367,7 @@ namespace Ryujinx.Audio.Renderer.Server int currentOutputSize = _output.Length; - OutputHeader.EffectsSize = (uint)(Unsafe.SizeOf<EffectOutStatus>() * context.GetCount()); + OutputHeader.EffectsSize = (uint)(Unsafe.SizeOf<EffectOutStatusVersion1>() * context.GetCount()); OutputHeader.TotalSize += OutputHeader.EffectsSize; Debug.Assert((initialOutputSize - currentOutputSize) == OutputHeader.EffectsSize); |
