diff options
author | Logan Stromberg <loganstromberg@gmail.com> | 2024-05-20 14:38:38 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-20 18:38:38 -0300 |
commit | c634eb4054c2e7f530307198d7cc6a20b3666d7d (patch) | |
tree | c2a0e1d33288ab1b2e4378944006d13c3b23a843 | |
parent | eb1ce41b00e415fe84537bc872ddbf13996055d5 (diff) |
Updating Concentus dependency to speed up Opus decoding (#6757)1.1.1318
* Implementing new features in the latest Concentus library - span-in, span-out Opus decoding (so we don't have to make temporary buffer copies), returning a more precise error code from the decoder, and automatically linking the native opus library with P/invoke if supported on the current system
* Remove stub log messages and commit package upgrade to 2.1.0
* use more correct disposal pattern
* Bump to Concentus 2.1.1
* Bump to Concentus 2.1.2
* Don't bother pulling in native opus binaries from Concentus package (using ExcludeAssets).
* Fix opus MS channel count. Explicitly disable native lib probe in OpusCodecFactory.
* Bump to package 2.2.0 which has split out the native libs, as suggested.
---------
Co-authored-by: Logan Stromberg <lostromb@microsoft.com>
-rw-r--r-- | Directory.Packages.props | 4 | ||||
-rw-r--r-- | src/Ryujinx.Horizon/Ryujinx.Horizon.csproj | 8 | ||||
-rw-r--r-- | src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs | 91 |
3 files changed, 68 insertions, 35 deletions
diff --git a/Directory.Packages.props b/Directory.Packages.props index d04e237e..739e66bd 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -11,7 +11,7 @@ <PackageVersion Include="Avalonia.Svg" Version="11.0.0.18" /> <PackageVersion Include="Avalonia.Svg.Skia" Version="11.0.0.18" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" /> - <PackageVersion Include="Concentus" Version="1.1.7" /> + <PackageVersion Include="Concentus" Version="2.2.0" /> <PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" /> <PackageVersion Include="DynamicData" Version="8.4.1" /> <PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" /> @@ -49,4 +49,4 @@ <PackageVersion Include="System.Management" Version="8.0.0" /> <PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" /> </ItemGroup> -</Project> +</Project>
\ No newline at end of file diff --git a/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj b/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj index d1f572d5..bf34ddd1 100644 --- a/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj +++ b/src/Ryujinx.Horizon/Ryujinx.Horizon.csproj @@ -1,4 +1,4 @@ -<Project Sdk="Microsoft.NET.Sdk"> +<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> @@ -16,10 +16,4 @@ <PackageReference Include="Concentus" /> <PackageReference Include="LibHac" /> </ItemGroup> - - <!-- Due to Concentus. --> - <PropertyGroup> - <NoWarn>NU1605</NoWarn> - </PropertyGroup> - </Project> diff --git a/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs index 5d279858..2146362d 100644 --- a/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs +++ b/src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs @@ -14,6 +14,11 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail { partial class HardwareOpusDecoder : IHardwareOpusDecoder, IDisposable { + static HardwareOpusDecoder() + { + OpusCodecFactory.AttemptToUseNativeLibrary = false; + } + [StructLayout(LayoutKind.Sequential)] private struct OpusPacketHeader { @@ -30,60 +35,87 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail } } - private interface IDecoder + private interface IDecoder : IDisposable { int SampleRate { get; } int ChannelsCount { get; } - int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize); + int Decode(ReadOnlySpan<byte> inData, Span<short> outPcm, int frameSize); void ResetState(); } private class Decoder : IDecoder { - private readonly OpusDecoder _decoder; + private readonly IOpusDecoder _decoder; public int SampleRate => _decoder.SampleRate; public int ChannelsCount => _decoder.NumChannels; public Decoder(int sampleRate, int channelsCount) { - _decoder = new OpusDecoder(sampleRate, channelsCount); + _decoder = OpusCodecFactory.CreateDecoder(sampleRate, channelsCount); } - public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize) + public int Decode(ReadOnlySpan<byte> inData, Span<short> outPcm, int frameSize) { - return _decoder.Decode(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize); + return _decoder.Decode(inData, outPcm, frameSize); } public void ResetState() { _decoder.ResetState(); } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _decoder?.Dispose(); + } + } } private class MultiSampleDecoder : IDecoder { - private readonly OpusMSDecoder _decoder; + private readonly IOpusMultiStreamDecoder _decoder; public int SampleRate => _decoder.SampleRate; - public int ChannelsCount { get; } + public int ChannelsCount => _decoder.NumChannels; public MultiSampleDecoder(int sampleRate, int channelsCount, int streams, int coupledStreams, byte[] mapping) { - ChannelsCount = channelsCount; - _decoder = new OpusMSDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping); + _decoder = OpusCodecFactory.CreateMultiStreamDecoder(sampleRate, channelsCount, streams, coupledStreams, mapping); } - public int Decode(byte[] inData, int inDataOffset, int len, short[] outPcm, int outPcmOffset, int frameSize) + public int Decode(ReadOnlySpan<byte> inData, Span<short> outPcm, int frameSize) { - return _decoder.DecodeMultistream(inData, inDataOffset, len, outPcm, outPcmOffset, frameSize, 0); + return _decoder.DecodeMultistream(inData, outPcm, frameSize, false); } public void ResetState() { _decoder.ResetState(); } + + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _decoder?.Dispose(); + } + } } private readonly IDecoder _decoder; @@ -221,7 +253,8 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail { timeTaken = 0; - Result result = DecodeInterleaved(_decoder, reset, input, out short[] outPcmData, output.Length, out outConsumed, out outSamples); + Span<short> outPcmSpace = MemoryMarshal.Cast<byte, short>(output); + Result result = DecodeInterleaved(_decoder, reset, input, outPcmSpace, output.Length, out outConsumed, out outSamples); if (withPerf) { @@ -229,14 +262,12 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail timeTaken = 0; } - MemoryMarshal.Cast<short, byte>(outPcmData).CopyTo(output[..(outPcmData.Length * sizeof(short))]); - return result; } - private static Result GetPacketNumSamples(IDecoder decoder, out int numSamples, byte[] packet) + private static Result GetPacketNumSamples(IDecoder decoder, out int numSamples, ReadOnlySpan<byte> packet) { - int result = OpusPacketInfo.GetNumSamples(packet, 0, packet.Length, decoder.SampleRate); + int result = OpusPacketInfo.GetNumSamples(packet, decoder.SampleRate); numSamples = result; @@ -256,12 +287,11 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail IDecoder decoder, bool reset, ReadOnlySpan<byte> input, - out short[] outPcmData, + Span<short> outPcmData, int outputSize, out int outConsumed, out int outSamples) { - outPcmData = null; outConsumed = 0; outSamples = 0; @@ -281,7 +311,7 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail return CodecResult.InvalidLength; } - byte[] opusData = input.Slice(headerSize, (int)header.Length).ToArray(); + ReadOnlySpan<byte> opusData = input.Slice(headerSize, (int)header.Length); Result result = GetPacketNumSamples(decoder, out int numSamples, opusData); @@ -292,8 +322,6 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail return CodecResult.InvalidLength; } - outPcmData = new short[numSamples * decoder.ChannelsCount]; - if (reset) { decoder.ResetState(); @@ -301,13 +329,22 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail try { - outSamples = decoder.Decode(opusData, 0, opusData.Length, outPcmData, 0, outPcmData.Length / decoder.ChannelsCount); + outSamples = decoder.Decode(opusData, outPcmData, numSamples); outConsumed = (int)totalSize; } - catch (OpusException) + catch (OpusException e) { - // TODO: As OpusException doesn't return the exact error code, this is inaccurate in some cases... - return CodecResult.InvalidLength; + switch (e.OpusErrorCode) + { + case OpusError.OPUS_BUFFER_TOO_SMALL: + return CodecResult.InvalidLength; + case OpusError.OPUS_BAD_ARG: + return CodecResult.OpusBadArg; + case OpusError.OPUS_INVALID_PACKET: + return CodecResult.OpusInvalidPacket; + default: + return CodecResult.InvalidLength; + } } } @@ -324,6 +361,8 @@ namespace Ryujinx.Horizon.Sdk.Codec.Detail _workBufferHandle = 0; } + + _decoder?.Dispose(); } } |