aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLogan Stromberg <loganstromberg@gmail.com>2024-05-20 14:38:38 -0700
committerGitHub <noreply@github.com>2024-05-20 18:38:38 -0300
commitc634eb4054c2e7f530307198d7cc6a20b3666d7d (patch)
treec2a0e1d33288ab1b2e4378944006d13c3b23a843
parenteb1ce41b00e415fe84537bc872ddbf13996055d5 (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.props4
-rw-r--r--src/Ryujinx.Horizon/Ryujinx.Horizon.csproj8
-rw-r--r--src/Ryujinx.Horizon/Sdk/Codec/Detail/HardwareOpusDecoder.cs91
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();
}
}