mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-12-16 12:46:33 +00:00
Compare commits
14 Commits
a6be4df55b
...
bd7e2d9c37
Author | SHA1 | Date | |
---|---|---|---|
|
bd7e2d9c37 | ||
|
a2c0035013 | ||
|
ca7cc52349 | ||
|
45ce25e847 | ||
|
8b0520d2cf | ||
|
6216566166 | ||
|
7d158acc3b | ||
|
7cc334b894 | ||
|
acbd9eb98f | ||
|
95a0344eb4 | ||
|
5dbba07e33 | ||
|
d86249cb0a | ||
|
04d68ca616 | ||
|
050f22977f |
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -23,7 +23,7 @@ body:
|
||||
attributes:
|
||||
label: Log file
|
||||
description: A log file will help our developers to better diagnose and fix the issue.
|
||||
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. You can drag and drop the log on to the text area
|
||||
placeholder: Logs files can be found under "Logs" folder in Ryujinx program folder. They can also be accessed by opening Ryujinx, then going to File > Open Logs Folder. You can drag and drop the log on to the text area (do not copy paste).
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
@ -83,4 +83,4 @@ body:
|
||||
- Additional info about your environment:
|
||||
- Any other information relevant to your issue.
|
||||
validations:
|
||||
required: false
|
||||
required: false
|
||||
|
@ -1,9 +1,11 @@
|
||||
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||
using Ryujinx.Audio.Renderer.Parameter;
|
||||
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||
using Ryujinx.Audio.Renderer.Server.Effect;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
{
|
||||
@ -21,18 +23,20 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
public CompressorParameter Parameter => _parameter;
|
||||
public Memory<CompressorState> State { get; }
|
||||
public Memory<EffectResultState> ResultState { get; }
|
||||
public ushort[] OutputBufferIndices { get; }
|
||||
public ushort[] InputBufferIndices { get; }
|
||||
public bool IsEffectEnabled { get; }
|
||||
|
||||
private CompressorParameter _parameter;
|
||||
|
||||
public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
|
||||
public CompressorCommand(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, Memory<EffectResultState> resultState, bool isEnabled, int nodeId)
|
||||
{
|
||||
Enabled = true;
|
||||
NodeId = nodeId;
|
||||
_parameter = parameter;
|
||||
State = state;
|
||||
ResultState = resultState;
|
||||
|
||||
IsEffectEnabled = isEnabled;
|
||||
|
||||
@ -71,9 +75,16 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
if (IsEffectEnabled && _parameter.IsChannelCountValid())
|
||||
{
|
||||
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||
Span<float> channelInput = stackalloc float[Parameter.ChannelCount];
|
||||
if (!ResultState.IsEmpty && _parameter.StatisticsReset)
|
||||
{
|
||||
ref CompressorStatistics statistics = ref MemoryMarshal.Cast<byte, CompressorStatistics>(ResultState.Span[0].SpecificData)[0];
|
||||
|
||||
statistics.Reset(_parameter.ChannelCount);
|
||||
}
|
||||
|
||||
Span<IntPtr> inputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
||||
Span<IntPtr> outputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
||||
Span<float> channelInput = stackalloc float[_parameter.ChannelCount];
|
||||
ExponentialMovingAverage inputMovingAverage = state.InputMovingAverage;
|
||||
float unknown4 = state.Unknown4;
|
||||
ExponentialMovingAverage compressionGainAverage = state.CompressionGainAverage;
|
||||
@ -92,7 +103,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
channelInput[channelIndex] = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
||||
}
|
||||
|
||||
float newMean = inputMovingAverage.Update(FloatingPointHelper.MeanSquare(channelInput), _parameter.InputGain);
|
||||
float mean = FloatingPointHelper.MeanSquare(channelInput);
|
||||
float newMean = inputMovingAverage.Update(mean, _parameter.InputGain);
|
||||
float y = FloatingPointHelper.Log10(newMean) * 10.0f;
|
||||
float z = 1.0f;
|
||||
|
||||
@ -111,7 +123,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
if (y >= state.Unknown14)
|
||||
{
|
||||
tmpGain = ((1.0f / Parameter.Ratio) - 1.0f) * (y - Parameter.Threshold);
|
||||
tmpGain = ((1.0f / _parameter.Ratio) - 1.0f) * (y - _parameter.Threshold);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -126,7 +138,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
if ((unknown4 - z) <= 0.08f)
|
||||
{
|
||||
compressionEmaAlpha = Parameter.ReleaseCoefficient;
|
||||
compressionEmaAlpha = _parameter.ReleaseCoefficient;
|
||||
|
||||
if ((unknown4 - z) >= -0.08f)
|
||||
{
|
||||
@ -140,18 +152,31 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
}
|
||||
else
|
||||
{
|
||||
compressionEmaAlpha = Parameter.AttackCoefficient;
|
||||
compressionEmaAlpha = _parameter.AttackCoefficient;
|
||||
}
|
||||
|
||||
float compressionGain = compressionGainAverage.Update(z, compressionEmaAlpha);
|
||||
|
||||
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
|
||||
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
|
||||
{
|
||||
*((float*)outputBuffers[channelIndex] + sampleIndex) = channelInput[channelIndex] * compressionGain * state.OutputGain;
|
||||
}
|
||||
|
||||
unknown4 = unknown4New;
|
||||
previousCompressionEmaAlpha = compressionEmaAlpha;
|
||||
|
||||
if (!ResultState.IsEmpty)
|
||||
{
|
||||
ref CompressorStatistics statistics = ref MemoryMarshal.Cast<byte, CompressorStatistics>(ResultState.Span[0].SpecificData)[0];
|
||||
|
||||
statistics.MinimumGain = MathF.Min(statistics.MinimumGain, compressionGain * state.OutputGain);
|
||||
statistics.MaximumMean = MathF.Max(statistics.MaximumMean, mean);
|
||||
|
||||
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
|
||||
{
|
||||
statistics.LastSamples[channelIndex] = MathF.Abs(channelInput[channelIndex] * (1f / 32768f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.InputMovingAverage = inputMovingAverage;
|
||||
@ -161,7 +186,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||
{
|
||||
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||
{
|
||||
|
@ -38,10 +38,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||
{
|
||||
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
||||
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
||||
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
|
||||
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,11 +51,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
if (IsEffectEnabled)
|
||||
{
|
||||
if (Parameter.Status == UsageState.Invalid)
|
||||
if (_parameter.Status == UsageState.Invalid)
|
||||
{
|
||||
state = new LimiterState(ref _parameter, WorkBuffer);
|
||||
}
|
||||
else if (Parameter.Status == UsageState.New)
|
||||
else if (_parameter.Status == UsageState.New)
|
||||
{
|
||||
LimiterState.UpdateParameter(ref _parameter);
|
||||
}
|
||||
@ -66,56 +66,56 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
private unsafe void ProcessLimiter(CommandList context, ref LimiterState state)
|
||||
{
|
||||
Debug.Assert(Parameter.IsChannelCountValid());
|
||||
Debug.Assert(_parameter.IsChannelCountValid());
|
||||
|
||||
if (IsEffectEnabled && Parameter.IsChannelCountValid())
|
||||
if (IsEffectEnabled && _parameter.IsChannelCountValid())
|
||||
{
|
||||
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||
Span<IntPtr> inputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
||||
Span<IntPtr> outputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
||||
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||
{
|
||||
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
|
||||
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]);
|
||||
}
|
||||
|
||||
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
|
||||
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
|
||||
{
|
||||
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
|
||||
{
|
||||
float rawInputSample = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
||||
|
||||
float inputSample = (rawInputSample / short.MaxValue) * Parameter.InputGain;
|
||||
float inputSample = (rawInputSample / short.MaxValue) * _parameter.InputGain;
|
||||
|
||||
float sampleInputMax = Math.Abs(inputSample);
|
||||
|
||||
float inputCoefficient = Parameter.ReleaseCoefficient;
|
||||
float inputCoefficient = _parameter.ReleaseCoefficient;
|
||||
|
||||
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
|
||||
{
|
||||
inputCoefficient = Parameter.AttackCoefficient;
|
||||
inputCoefficient = _parameter.AttackCoefficient;
|
||||
}
|
||||
|
||||
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
|
||||
float attenuation = 1.0f;
|
||||
|
||||
if (detectorValue > Parameter.Threshold)
|
||||
if (detectorValue > _parameter.Threshold)
|
||||
{
|
||||
attenuation = Parameter.Threshold / detectorValue;
|
||||
attenuation = _parameter.Threshold / detectorValue;
|
||||
}
|
||||
|
||||
float outputCoefficient = Parameter.ReleaseCoefficient;
|
||||
float outputCoefficient = _parameter.ReleaseCoefficient;
|
||||
|
||||
if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
|
||||
{
|
||||
outputCoefficient = Parameter.AttackCoefficient;
|
||||
outputCoefficient = _parameter.AttackCoefficient;
|
||||
}
|
||||
|
||||
float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
|
||||
|
||||
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
||||
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * _parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
||||
|
||||
float outputSample = delayedSample * compressionGain * Parameter.OutputGain;
|
||||
float outputSample = delayedSample * compressionGain * _parameter.OutputGain;
|
||||
|
||||
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
|
||||
|
||||
@ -123,16 +123,16 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
state.DelayedSampleBufferPosition[channelIndex]++;
|
||||
|
||||
while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin)
|
||||
while (state.DelayedSampleBufferPosition[channelIndex] >= _parameter.DelayBufferSampleCountMin)
|
||||
{
|
||||
state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin;
|
||||
state.DelayedSampleBufferPosition[channelIndex] -= _parameter.DelayBufferSampleCountMin;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||
{
|
||||
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||
{
|
||||
|
@ -49,10 +49,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
InputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||
OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax];
|
||||
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||
{
|
||||
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
||||
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
||||
InputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Input[i]);
|
||||
OutputBufferIndices[i] = (ushort)(bufferOffset + _parameter.Output[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,11 +62,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
if (IsEffectEnabled)
|
||||
{
|
||||
if (Parameter.Status == UsageState.Invalid)
|
||||
if (_parameter.Status == UsageState.Invalid)
|
||||
{
|
||||
state = new LimiterState(ref _parameter, WorkBuffer);
|
||||
}
|
||||
else if (Parameter.Status == UsageState.New)
|
||||
else if (_parameter.Status == UsageState.New)
|
||||
{
|
||||
LimiterState.UpdateParameter(ref _parameter);
|
||||
}
|
||||
@ -77,63 +77,63 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
private unsafe void ProcessLimiter(CommandList context, ref LimiterState state)
|
||||
{
|
||||
Debug.Assert(Parameter.IsChannelCountValid());
|
||||
Debug.Assert(_parameter.IsChannelCountValid());
|
||||
|
||||
if (IsEffectEnabled && Parameter.IsChannelCountValid())
|
||||
if (IsEffectEnabled && _parameter.IsChannelCountValid())
|
||||
{
|
||||
if (!ResultState.IsEmpty && Parameter.StatisticsReset)
|
||||
if (!ResultState.IsEmpty && _parameter.StatisticsReset)
|
||||
{
|
||||
ref LimiterStatistics statistics = ref MemoryMarshal.Cast<byte, LimiterStatistics>(ResultState.Span[0].SpecificData)[0];
|
||||
|
||||
statistics.Reset();
|
||||
}
|
||||
|
||||
Span<IntPtr> inputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||
Span<IntPtr> outputBuffers = stackalloc IntPtr[Parameter.ChannelCount];
|
||||
Span<IntPtr> inputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
||||
Span<IntPtr> outputBuffers = stackalloc IntPtr[_parameter.ChannelCount];
|
||||
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||
{
|
||||
inputBuffers[i] = context.GetBufferPointer(InputBufferIndices[i]);
|
||||
outputBuffers[i] = context.GetBufferPointer(OutputBufferIndices[i]);
|
||||
}
|
||||
|
||||
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
|
||||
for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++)
|
||||
{
|
||||
for (int sampleIndex = 0; sampleIndex < context.SampleCount; sampleIndex++)
|
||||
{
|
||||
float rawInputSample = *((float*)inputBuffers[channelIndex] + sampleIndex);
|
||||
|
||||
float inputSample = (rawInputSample / short.MaxValue) * Parameter.InputGain;
|
||||
float inputSample = (rawInputSample / short.MaxValue) * _parameter.InputGain;
|
||||
|
||||
float sampleInputMax = Math.Abs(inputSample);
|
||||
|
||||
float inputCoefficient = Parameter.ReleaseCoefficient;
|
||||
float inputCoefficient = _parameter.ReleaseCoefficient;
|
||||
|
||||
if (sampleInputMax > state.DetectorAverage[channelIndex].Read())
|
||||
{
|
||||
inputCoefficient = Parameter.AttackCoefficient;
|
||||
inputCoefficient = _parameter.AttackCoefficient;
|
||||
}
|
||||
|
||||
float detectorValue = state.DetectorAverage[channelIndex].Update(sampleInputMax, inputCoefficient);
|
||||
float attenuation = 1.0f;
|
||||
|
||||
if (detectorValue > Parameter.Threshold)
|
||||
if (detectorValue > _parameter.Threshold)
|
||||
{
|
||||
attenuation = Parameter.Threshold / detectorValue;
|
||||
attenuation = _parameter.Threshold / detectorValue;
|
||||
}
|
||||
|
||||
float outputCoefficient = Parameter.ReleaseCoefficient;
|
||||
float outputCoefficient = _parameter.ReleaseCoefficient;
|
||||
|
||||
if (state.CompressionGainAverage[channelIndex].Read() > attenuation)
|
||||
{
|
||||
outputCoefficient = Parameter.AttackCoefficient;
|
||||
outputCoefficient = _parameter.AttackCoefficient;
|
||||
}
|
||||
|
||||
float compressionGain = state.CompressionGainAverage[channelIndex].Update(attenuation, outputCoefficient);
|
||||
|
||||
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * Parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
||||
ref float delayedSample = ref state.DelayedSampleBuffer[channelIndex * _parameter.DelayBufferSampleCountMax + state.DelayedSampleBufferPosition[channelIndex]];
|
||||
|
||||
float outputSample = delayedSample * compressionGain * Parameter.OutputGain;
|
||||
float outputSample = delayedSample * compressionGain * _parameter.OutputGain;
|
||||
|
||||
*((float*)outputBuffers[channelIndex] + sampleIndex) = outputSample * short.MaxValue;
|
||||
|
||||
@ -141,9 +141,9 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
|
||||
state.DelayedSampleBufferPosition[channelIndex]++;
|
||||
|
||||
while (state.DelayedSampleBufferPosition[channelIndex] >= Parameter.DelayBufferSampleCountMin)
|
||||
while (state.DelayedSampleBufferPosition[channelIndex] >= _parameter.DelayBufferSampleCountMin)
|
||||
{
|
||||
state.DelayedSampleBufferPosition[channelIndex] -= Parameter.DelayBufferSampleCountMin;
|
||||
state.DelayedSampleBufferPosition[channelIndex] -= _parameter.DelayBufferSampleCountMin;
|
||||
}
|
||||
|
||||
if (!ResultState.IsEmpty)
|
||||
@ -158,7 +158,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Parameter.ChannelCount; i++)
|
||||
for (int i = 0; i < _parameter.ChannelCount; i++)
|
||||
{
|
||||
if (InputBufferIndices[i] != OutputBufferIndices[i])
|
||||
{
|
||||
|
@ -90,9 +90,16 @@ namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
||||
public bool MakeupGainEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved/padding.
|
||||
/// Indicate if the compressor effect should output statistics.
|
||||
/// </summary>
|
||||
private Array2<byte> _reserved;
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool StatisticsEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Indicate to the DSP that the user did a statistics reset.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool StatisticsReset;
|
||||
|
||||
/// <summary>
|
||||
/// Check if the <see cref="ChannelCount"/> is valid.
|
||||
|
@ -0,0 +1,38 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Audio.Renderer.Parameter.Effect
|
||||
{
|
||||
/// <summary>
|
||||
/// Effect result state for <seealso cref="Common.EffectType.Compressor"/>.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct CompressorStatistics
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum input mean value since last reset.
|
||||
/// </summary>
|
||||
public float MaximumMean;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum output gain since last reset.
|
||||
/// </summary>
|
||||
public float MinimumGain;
|
||||
|
||||
/// <summary>
|
||||
/// Last processed input sample, per channel.
|
||||
/// </summary>
|
||||
public Array6<float> LastSamples;
|
||||
|
||||
/// <summary>
|
||||
/// Reset the statistics.
|
||||
/// </summary>
|
||||
/// <param name="channelCount">Number of channels to reset.</param>
|
||||
public void Reset(ushort channelCount)
|
||||
{
|
||||
MaximumMean = 0.0f;
|
||||
MinimumGain = 1.0f;
|
||||
LastSamples.AsSpan()[..channelCount].Clear();
|
||||
}
|
||||
}
|
||||
}
|
@ -28,6 +28,11 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||
/// </summary>
|
||||
bool IsUsed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true to force resetting the previous mix volumes.
|
||||
/// </summary>
|
||||
bool ResetPrevVolume { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Mix buffer volumes.
|
||||
/// </summary>
|
||||
|
@ -37,10 +37,16 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool IsUsed;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true to force resetting the previous mix volumes.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool ResetPrevVolume;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved/padding.
|
||||
/// </summary>
|
||||
private unsafe fixed byte _reserved[3];
|
||||
private unsafe fixed byte _reserved[2];
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
|
||||
private struct MixArray { }
|
||||
@ -58,6 +64,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => default;
|
||||
|
||||
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
|
||||
readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume;
|
||||
|
||||
/// <summary>
|
||||
/// The expected constant of any input header.
|
||||
|
@ -42,10 +42,16 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool IsUsed;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true to force resetting the previous mix volumes.
|
||||
/// </summary>
|
||||
[MarshalAs(UnmanagedType.I1)]
|
||||
public bool ResetPrevVolume;
|
||||
|
||||
/// <summary>
|
||||
/// Reserved/padding.
|
||||
/// </summary>
|
||||
private unsafe fixed byte _reserved[11];
|
||||
private unsafe fixed byte _reserved[10];
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Size = sizeof(float) * Constants.MixBufferCountMax, Pack = 1)]
|
||||
private struct MixArray { }
|
||||
@ -63,6 +69,7 @@ namespace Ryujinx.Audio.Renderer.Parameter
|
||||
readonly Array2<BiquadFilterParameter> ISplitterDestinationInParameter.BiquadFilters => BiquadFilters;
|
||||
|
||||
readonly bool ISplitterDestinationInParameter.IsUsed => IsUsed;
|
||||
readonly bool ISplitterDestinationInParameter.ResetPrevVolume => ResetPrevVolume;
|
||||
|
||||
/// <summary>
|
||||
/// The expected constant of any input header.
|
||||
|
@ -108,10 +108,18 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// <remarks>This was added in system update 17.0.0</remarks>
|
||||
public const int Revision12 = 12 << 24;
|
||||
|
||||
/// <summary>
|
||||
/// REV13:
|
||||
/// The compressor effect can now output statistics.
|
||||
/// Splitter destinations now explicitly reset the previous mix volume, instead of doing so on first use.
|
||||
/// </summary>
|
||||
/// <remarks>This was added in system update 18.0.0</remarks>
|
||||
public const int Revision13 = 13 << 24;
|
||||
|
||||
/// <summary>
|
||||
/// Last revision supported by the implementation.
|
||||
/// </summary>
|
||||
public const int LastRevision = Revision12;
|
||||
public const int LastRevision = Revision13;
|
||||
|
||||
/// <summary>
|
||||
/// Target revision magic supported by the implementation.
|
||||
@ -384,6 +392,15 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision12);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the audio renderer should support explicit previous mix volume reset on splitter.
|
||||
/// </summary>
|
||||
/// <returns>True if the audio renderer support explicit previous mix volume reset on splitter</returns>
|
||||
public bool IsSplitterPrevVolumeResetSupported()
|
||||
{
|
||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision13);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
|
||||
/// </summary>
|
||||
|
@ -583,11 +583,20 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
}
|
||||
}
|
||||
|
||||
public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, bool isEnabled, int nodeId)
|
||||
/// <summary>
|
||||
/// Generate a new <see cref="CompressorCommand"/>.
|
||||
/// </summary>
|
||||
/// <param name="bufferOffset">The target buffer offset.</param>
|
||||
/// <param name="parameter">The compressor parameter.</param>
|
||||
/// <param name="state">The compressor 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="nodeId">The node id associated to this command.</param>
|
||||
public void GenerateCompressorEffect(uint bufferOffset, CompressorParameter parameter, Memory<CompressorState> state, Memory<EffectResultState> effectResultState, bool isEnabled, int nodeId)
|
||||
{
|
||||
if (parameter.IsChannelCountValid())
|
||||
{
|
||||
CompressorCommand command = new(bufferOffset, parameter, state, isEnabled, nodeId);
|
||||
CompressorCommand command = new(bufferOffset, parameter, state, effectResultState, isEnabled, nodeId);
|
||||
|
||||
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||
|
||||
|
@ -735,14 +735,26 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
}
|
||||
}
|
||||
|
||||
private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId)
|
||||
private void GenerateCompressorEffect(uint bufferOffset, CompressorEffect effect, int nodeId, int effectId)
|
||||
{
|
||||
Debug.Assert(effect.Type == EffectType.Compressor);
|
||||
|
||||
Memory<EffectResultState> dspResultState;
|
||||
|
||||
if (effect.Parameter.StatisticsEnabled)
|
||||
{
|
||||
dspResultState = _effectContext.GetDspStateMemory(effectId);
|
||||
}
|
||||
else
|
||||
{
|
||||
dspResultState = Memory<EffectResultState>.Empty;
|
||||
}
|
||||
|
||||
_commandBuffer.GenerateCompressorEffect(
|
||||
bufferOffset,
|
||||
effect.Parameter,
|
||||
effect.State,
|
||||
dspResultState,
|
||||
effect.IsEnabled,
|
||||
nodeId);
|
||||
}
|
||||
@ -795,7 +807,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
GenerateCaptureEffect(mix.BufferOffset, (CaptureBufferEffect)effect, nodeId);
|
||||
break;
|
||||
case EffectType.Compressor:
|
||||
GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId);
|
||||
GenerateCompressorEffect(mix.BufferOffset, (CompressorEffect)effect, nodeId, effectId);
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException($"Unsupported effect type {effect.Type}");
|
||||
|
@ -169,14 +169,28 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
{
|
||||
if (command.Enabled)
|
||||
{
|
||||
return command.Parameter.ChannelCount switch
|
||||
if (command.Parameter.StatisticsEnabled)
|
||||
{
|
||||
1 => 34431,
|
||||
2 => 44253,
|
||||
4 => 63827,
|
||||
6 => 83361,
|
||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
||||
};
|
||||
return command.Parameter.ChannelCount switch
|
||||
{
|
||||
1 => 22100,
|
||||
2 => 33211,
|
||||
4 => 41587,
|
||||
6 => 58819,
|
||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return command.Parameter.ChannelCount switch
|
||||
{
|
||||
1 => 19052,
|
||||
2 => 29852,
|
||||
4 => 37904,
|
||||
6 => 55020,
|
||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return command.Parameter.ChannelCount switch
|
||||
@ -191,14 +205,28 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
|
||||
if (command.Enabled)
|
||||
{
|
||||
return command.Parameter.ChannelCount switch
|
||||
if (command.Parameter.StatisticsEnabled)
|
||||
{
|
||||
1 => 51095,
|
||||
2 => 65693,
|
||||
4 => 95383,
|
||||
6 => 124510,
|
||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
||||
};
|
||||
return command.Parameter.ChannelCount switch
|
||||
{
|
||||
1 => 32518,
|
||||
2 => 49102,
|
||||
4 => 61685,
|
||||
6 => 87250,
|
||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return command.Parameter.ChannelCount switch
|
||||
{
|
||||
1 => 27963,
|
||||
2 => 44016,
|
||||
4 => 56183,
|
||||
6 => 81862,
|
||||
_ => throw new NotImplementedException($"{command.Parameter.ChannelCount}"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return command.Parameter.ChannelCount switch
|
||||
|
@ -62,6 +62,19 @@ namespace Ryujinx.Audio.Renderer.Server.Effect
|
||||
UpdateUsageStateForCommandGeneration();
|
||||
|
||||
Parameter.Status = UsageState.Enabled;
|
||||
Parameter.StatisticsReset = false;
|
||||
}
|
||||
|
||||
public override void InitializeResultState(ref EffectResultState state)
|
||||
{
|
||||
ref CompressorStatistics statistics = ref MemoryMarshal.Cast<byte, CompressorStatistics>(state.SpecificData)[0];
|
||||
|
||||
statistics.Reset(Parameter.ChannelCount);
|
||||
}
|
||||
|
||||
public override void UpdateResultState(ref EffectResultState destState, ref EffectResultState srcState)
|
||||
{
|
||||
destState = srcState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,11 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// </summary>
|
||||
public bool IsBugFixed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If set to true, the previous mix volume is explicitly resetted using the input parameter, instead of implicitly on first use.
|
||||
/// </summary>
|
||||
public bool IsSplitterPrevVolumeResetSupported { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize <see cref="SplitterContext"/>.
|
||||
/// </summary>
|
||||
@ -139,6 +144,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
}
|
||||
}
|
||||
|
||||
IsSplitterPrevVolumeResetSupported = behaviourContext.IsSplitterPrevVolumeResetSupported();
|
||||
|
||||
SplitterState.InitializeSplitters(splitters.Span);
|
||||
|
||||
Setup(splitters, splitterDestinationsV1, splitterDestinationsV2, behaviourContext.IsSplitterBugFixed());
|
||||
@ -277,7 +284,7 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
{
|
||||
SplitterDestination destination = GetDestination(parameter.Id);
|
||||
|
||||
destination.Update(parameter);
|
||||
destination.Update(parameter, IsSplitterPrevVolumeResetSupported);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -184,15 +184,16 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// Update the splitter destination data from user parameter.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
||||
/// <param name="isPrevVolumeResetSupported">Indicates that the audio renderer revision in use supports explicitly resetting the volume.</param>
|
||||
public void Update<T>(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter
|
||||
{
|
||||
if (Unsafe.IsNullRef(ref _v2))
|
||||
{
|
||||
_v1.Update(parameter);
|
||||
_v1.Update(parameter, isPrevVolumeResetSupported);
|
||||
}
|
||||
else
|
||||
{
|
||||
_v2.Update(parameter);
|
||||
_v2.Update(parameter, isPrevVolumeResetSupported);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// Update the <see cref="SplitterDestinationVersion1"/> from user parameter.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
||||
/// <param name="isPrevVolumeResetSupported">Indicates that the audio renderer revision in use supports explicitly resetting the volume.</param>
|
||||
public void Update<T>(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter
|
||||
{
|
||||
Debug.Assert(Id == parameter.Id);
|
||||
|
||||
@ -103,7 +104,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
|
||||
parameter.MixBufferVolume.CopyTo(MixBufferVolume);
|
||||
|
||||
if (!IsUsed && parameter.IsUsed)
|
||||
bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed;
|
||||
if (resetPrevVolume)
|
||||
{
|
||||
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||
|
||||
|
@ -98,7 +98,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
/// Update the <see cref="SplitterDestinationVersion2"/> from user parameter.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The user parameter.</param>
|
||||
public void Update<T>(in T parameter) where T : ISplitterDestinationInParameter
|
||||
/// <param name="isPrevVolumeResetSupported">Indicates that the audio renderer revision in use supports explicitly resetting the volume.</param>
|
||||
public void Update<T>(in T parameter, bool isPrevVolumeResetSupported) where T : ISplitterDestinationInParameter
|
||||
{
|
||||
Debug.Assert(Id == parameter.Id);
|
||||
|
||||
@ -110,7 +111,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter
|
||||
|
||||
_biquadFilters = parameter.BiquadFilters;
|
||||
|
||||
if (!IsUsed && parameter.IsUsed)
|
||||
bool resetPrevVolume = isPrevVolumeResetSupported ? parameter.ResetPrevVolume : !IsUsed && parameter.IsUsed;
|
||||
if (resetPrevVolume)
|
||||
{
|
||||
MixBufferVolume.CopyTo(PreviousMixBufferVolume);
|
||||
|
||||
|
@ -50,6 +50,10 @@ namespace Ryujinx.Graphics.GAL
|
||||
public readonly bool SupportsViewportSwizzle;
|
||||
public readonly bool SupportsIndirectParameters;
|
||||
public readonly bool SupportsDepthClipControl;
|
||||
public readonly bool SupportsExtendedDynamicState;
|
||||
public readonly bool SupportsExtendedDynamicState2;
|
||||
public readonly bool SupportsLogicOpDynamicState;
|
||||
public readonly bool SupportsPatchControlPointsDynamicState;
|
||||
|
||||
public readonly int UniformBufferSetIndex;
|
||||
public readonly int StorageBufferSetIndex;
|
||||
@ -71,6 +75,8 @@ namespace Ryujinx.Graphics.GAL
|
||||
|
||||
public readonly int GatherBiasPrecision;
|
||||
|
||||
public readonly ulong MaximumGpuMemory;
|
||||
|
||||
public Capabilities(
|
||||
TargetApi api,
|
||||
string vendorName,
|
||||
@ -116,6 +122,10 @@ namespace Ryujinx.Graphics.GAL
|
||||
bool supportsViewportSwizzle,
|
||||
bool supportsIndirectParameters,
|
||||
bool supportsDepthClipControl,
|
||||
bool supportsExtendedDynamicState,
|
||||
bool supportsExtendedDynamicState2,
|
||||
bool supportsLogicOpDynamicState,
|
||||
bool supportsPatchControlPointsDynamicState,
|
||||
int uniformBufferSetIndex,
|
||||
int storageBufferSetIndex,
|
||||
int textureSetIndex,
|
||||
@ -131,7 +141,8 @@ namespace Ryujinx.Graphics.GAL
|
||||
int shaderSubgroupSize,
|
||||
int storageBufferOffsetAlignment,
|
||||
int textureBufferOffsetAlignment,
|
||||
int gatherBiasPrecision)
|
||||
int gatherBiasPrecision,
|
||||
ulong maximumGpuMemory)
|
||||
{
|
||||
Api = api;
|
||||
VendorName = vendorName;
|
||||
@ -177,6 +188,10 @@ namespace Ryujinx.Graphics.GAL
|
||||
SupportsViewportSwizzle = supportsViewportSwizzle;
|
||||
SupportsIndirectParameters = supportsIndirectParameters;
|
||||
SupportsDepthClipControl = supportsDepthClipControl;
|
||||
SupportsExtendedDynamicState = supportsExtendedDynamicState;
|
||||
SupportsExtendedDynamicState2 = supportsExtendedDynamicState2;
|
||||
SupportsLogicOpDynamicState = supportsLogicOpDynamicState;
|
||||
SupportsPatchControlPointsDynamicState = supportsPatchControlPointsDynamicState;
|
||||
UniformBufferSetIndex = uniformBufferSetIndex;
|
||||
StorageBufferSetIndex = storageBufferSetIndex;
|
||||
TextureSetIndex = textureSetIndex;
|
||||
@ -193,6 +208,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
||||
TextureBufferOffsetAlignment = textureBufferOffsetAlignment;
|
||||
GatherBiasPrecision = gatherBiasPrecision;
|
||||
MaximumGpuMemory = maximumGpuMemory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@ -46,7 +47,11 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
private const int MinCountForDeletion = 32;
|
||||
private const int MaxCapacity = 2048;
|
||||
private const ulong MaxTextureSizeCapacity = 1024 * 1024 * 1024; // MB;
|
||||
private const ulong MinTextureSizeCapacity = 512 * 1024 * 1024;
|
||||
private const ulong MaxTextureSizeCapacity = 4UL * 1024 * 1024 * 1024;
|
||||
private const ulong DefaultTextureSizeCapacity = 1UL * 1024 * 1024 * 1024;
|
||||
private const float MemoryScaleFactor = 0.50f;
|
||||
private ulong _maxCacheMemoryUsage = 0;
|
||||
|
||||
private readonly LinkedList<Texture> _textures;
|
||||
private ulong _totalSize;
|
||||
@ -56,6 +61,25 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
private readonly Dictionary<TextureDescriptor, ShortTextureCacheEntry> _shortCacheLookup;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the cache, setting the maximum texture capacity for the specified GPU context.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the backend GPU has 0 memory capacity, the cache size defaults to `DefaultTextureSizeCapacity`.
|
||||
/// </remarks>
|
||||
/// <param name="context">The GPU context that the cache belongs to</param>
|
||||
public void Initialize(GpuContext context)
|
||||
{
|
||||
var cacheMemory = (ulong)(context.Capabilities.MaximumGpuMemory * MemoryScaleFactor);
|
||||
|
||||
_maxCacheMemoryUsage = Math.Clamp(cacheMemory, MinTextureSizeCapacity, MaxTextureSizeCapacity);
|
||||
|
||||
if (context.Capabilities.MaximumGpuMemory == 0)
|
||||
{
|
||||
_maxCacheMemoryUsage = DefaultTextureSizeCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the automatic deletion cache.
|
||||
/// </summary>
|
||||
@ -85,7 +109,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
texture.CacheNode = _textures.AddLast(texture);
|
||||
|
||||
if (_textures.Count > MaxCapacity ||
|
||||
(_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion))
|
||||
(_totalSize > _maxCacheMemoryUsage && _textures.Count >= MinCountForDeletion))
|
||||
{
|
||||
RemoveLeastUsedTexture();
|
||||
}
|
||||
@ -110,7 +134,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
_textures.AddLast(texture.CacheNode);
|
||||
}
|
||||
|
||||
if (_totalSize > MaxTextureSizeCapacity && _textures.Count >= MinCountForDeletion)
|
||||
if (_totalSize > _maxCacheMemoryUsage && _textures.Count >= MinCountForDeletion)
|
||||
{
|
||||
RemoveLeastUsedTexture();
|
||||
}
|
||||
|
@ -68,6 +68,14 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
_cache = new AutoDeleteCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the cache, setting the maximum texture capacity for the specified GPU context.
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
_cache.Initialize(_context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles marking of textures written to a memory region being (partially) remapped.
|
||||
/// </summary>
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Ryujinx.Common.Memory;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Memory;
|
||||
using Ryujinx.Memory.Range;
|
||||
using System;
|
||||
@ -64,6 +65,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
MemoryUnmapped += Physical.BufferCache.MemoryUnmappedHandler;
|
||||
MemoryUnmapped += VirtualRangeCache.MemoryUnmappedHandler;
|
||||
MemoryUnmapped += CounterCache.MemoryUnmappedHandler;
|
||||
Physical.TextureCache.Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
private const ushort FileFormatVersionMajor = 1;
|
||||
private const ushort FileFormatVersionMinor = 2;
|
||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||
private const uint CodeGenVersion = 7331;
|
||||
private const uint CodeGenVersion = 6877;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
|
@ -22,6 +22,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
private readonly CancellationToken _cancellationToken;
|
||||
private readonly Action<ShaderCacheState, int, int> _stateChangeCallback;
|
||||
|
||||
private readonly HashSet<ProgramPipelineState> _pipelineStateSet = new();
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the cache should be loaded.
|
||||
/// </summary>
|
||||
@ -232,10 +234,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
|
||||
for (int index = 0; index < ThreadCount; index++)
|
||||
{
|
||||
workThreads[index] = new Thread(ProcessAsyncQueue)
|
||||
{
|
||||
Name = $"GPU.AsyncTranslationThread.{index}",
|
||||
};
|
||||
workThreads[index] = new Thread(ProcessAsyncQueue) { Name = $"GPU.AsyncTranslationThread.{index}", };
|
||||
}
|
||||
|
||||
int programCount = _hostStorage.GetProgramCount();
|
||||
@ -305,6 +304,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
using var streams = _hostStorage.GetOutputStreams(_context);
|
||||
|
||||
int packagedShaders = 0;
|
||||
ProgramPipelineState currentPipelineState = default;
|
||||
|
||||
foreach (var kv in _programList)
|
||||
{
|
||||
if (!Active)
|
||||
@ -314,12 +315,53 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
|
||||
(CachedShaderProgram program, byte[] binaryCode) = kv.Value;
|
||||
|
||||
_hostStorage.AddShader(_context, program, binaryCode, streams);
|
||||
if (program.SpecializationState.PipelineState.HasValue && _context.Capabilities.SupportsExtendedDynamicState)
|
||||
{
|
||||
currentPipelineState = program.SpecializationState.PipelineState.Value;
|
||||
|
||||
_stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count);
|
||||
if (_context.Capabilities.SupportsExtendedDynamicState)
|
||||
{
|
||||
currentPipelineState.StencilTest = default;
|
||||
|
||||
currentPipelineState.CullMode = 0;
|
||||
currentPipelineState.FrontFace = 0;
|
||||
currentPipelineState.DepthTest = default;
|
||||
currentPipelineState.Topology = ConvertToClass(currentPipelineState.Topology);
|
||||
}
|
||||
|
||||
if (_context.Capabilities.SupportsExtendedDynamicState2)
|
||||
{
|
||||
currentPipelineState.PrimitiveRestartEnable = false;
|
||||
currentPipelineState.BiasEnable = 0;
|
||||
currentPipelineState.RasterizerDiscard = false;
|
||||
}
|
||||
|
||||
if (_context.Capabilities.SupportsLogicOpDynamicState)
|
||||
{
|
||||
currentPipelineState.LogicOp = 0;
|
||||
}
|
||||
|
||||
if (_context.Capabilities.SupportsPatchControlPointsDynamicState)
|
||||
{
|
||||
currentPipelineState.PatchControlPoints = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_pipelineStateSet.Contains(currentPipelineState) ||
|
||||
!_context.Capabilities.SupportsExtendedDynamicState ||
|
||||
!program.SpecializationState.PipelineState.HasValue)
|
||||
{
|
||||
_hostStorage.AddShader(_context, program, binaryCode, streams);
|
||||
|
||||
_stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count);
|
||||
|
||||
if (_context.Capabilities.SupportsExtendedDynamicState)
|
||||
{
|
||||
_pipelineStateSet.Add(currentPipelineState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully.");
|
||||
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {packagedShaders} shaders successfully.");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -343,6 +385,29 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
_stateChangeCallback(ShaderCacheState.Loaded, programCount, programCount);
|
||||
}
|
||||
|
||||
private PrimitiveTopology ConvertToClass(PrimitiveTopology topology)
|
||||
{
|
||||
return topology switch
|
||||
{
|
||||
PrimitiveTopology.Points => PrimitiveTopology.Points,
|
||||
PrimitiveTopology.Lines or
|
||||
PrimitiveTopology.LineStrip or
|
||||
PrimitiveTopology.LinesAdjacency or
|
||||
PrimitiveTopology.LineStripAdjacency => PrimitiveTopology.Lines,
|
||||
PrimitiveTopology.Triangles or
|
||||
PrimitiveTopology.TriangleStrip or
|
||||
PrimitiveTopology.TriangleFan or
|
||||
PrimitiveTopology.TrianglesAdjacency or
|
||||
PrimitiveTopology.TriangleStripAdjacency or
|
||||
PrimitiveTopology.Polygon => PrimitiveTopology.TriangleStrip,
|
||||
PrimitiveTopology.Patches => PrimitiveTopology.Patches,
|
||||
PrimitiveTopology.Quads => PrimitiveTopology.Quads,
|
||||
PrimitiveTopology.QuadStrip => PrimitiveTopology.QuadStrip,
|
||||
PrimitiveTopology.LineLoop => PrimitiveTopology.LineLoop,
|
||||
_ => PrimitiveTopology.TriangleStrip,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueues a host program for compilation.
|
||||
/// </summary>
|
||||
|
@ -743,7 +743,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
constantBufferUsePerStageMask &= ~(1 << index);
|
||||
}
|
||||
|
||||
if (checkTextures)
|
||||
if (checkTextures && _allTextures.Length > 0)
|
||||
{
|
||||
TexturePool pool = channel.TextureManager.GetTexturePool(poolState.TexturePoolGpuVa, poolState.TexturePoolMaximumId);
|
||||
|
||||
|
@ -187,6 +187,10 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
||||
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
||||
supportsDepthClipControl: true,
|
||||
supportsExtendedDynamicState: false,
|
||||
supportsExtendedDynamicState2: false,
|
||||
supportsLogicOpDynamicState: false,
|
||||
supportsPatchControlPointsDynamicState: false,
|
||||
uniformBufferSetIndex: 0,
|
||||
storageBufferSetIndex: 1,
|
||||
textureSetIndex: 2,
|
||||
@ -202,7 +206,8 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
shaderSubgroupSize: Constants.MaxSubgroupSize,
|
||||
storageBufferOffsetAlignment: HwCapabilities.StorageBufferOffsetAlignment,
|
||||
textureBufferOffsetAlignment: HwCapabilities.TextureBufferOffsetAlignment,
|
||||
gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0); // Precision is 8 for these vendors on Vulkan.
|
||||
gatherBiasPrecision: intelWindows || amdWindows ? 8 : 0, // Precision is 8 for these vendors on Vulkan.
|
||||
maximumGpuMemory: 0);
|
||||
}
|
||||
|
||||
public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan<byte> data)
|
||||
|
@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
if (stage == ShaderStage.Vertex)
|
||||
{
|
||||
InitializePositionOutput(context);
|
||||
InitializeVertexOutputs(context);
|
||||
}
|
||||
|
||||
UInt128 usedAttributes = context.TranslatorContext.AttributeUsage.NextInputAttributesComponents;
|
||||
@ -236,12 +236,20 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitializePositionOutput(EmitterContext context)
|
||||
private static void InitializeVertexOutputs(EmitterContext context)
|
||||
{
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
context.Store(StorageKind.Output, IoVariable.Position, null, Const(c), ConstF(c == 3 ? 1f : 0f));
|
||||
}
|
||||
|
||||
if (context.Program.ClipDistancesWritten != 0)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
context.Store(StorageKind.Output, IoVariable.ClipDistance, null, Const(i), ConstF(0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void InitializeOutput(EmitterContext context, int location, bool perPatch)
|
||||
|
@ -100,6 +100,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public ulong DrawCount { get; private set; }
|
||||
public bool RenderPassActive { get; private set; }
|
||||
|
||||
private readonly int[] _vertexBufferBindings;
|
||||
|
||||
public unsafe PipelineBase(VulkanRenderer gd, Device device)
|
||||
{
|
||||
Gd = gd;
|
||||
@ -138,7 +140,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_bindingsSet = false;
|
||||
|
||||
_newState.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, _supportExtDynamic && (!Gd.IsMoltenVk || Gd.SupportsMTL31) ? null : 0, VertexInputRate.Vertex);
|
||||
_vertexBufferBindings = new int[Constants.MaxVertexBuffers];
|
||||
for (int i = 0; i < Constants.MaxVertexBuffers; i++)
|
||||
{
|
||||
_vertexBufferBindings[i] = i + 1;
|
||||
}
|
||||
|
||||
_newState.Initialize(gd.Capabilities);
|
||||
}
|
||||
@ -1397,6 +1403,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (!_bindingsSet)
|
||||
{
|
||||
_newState.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, _supportExtDynamic && (!Gd.IsMoltenVk || Gd.SupportsMTL31) ? null : 0, VertexInputRate.Vertex);
|
||||
|
||||
for (int i = 1; i < count; i++)
|
||||
{
|
||||
_newState.Internal.VertexBindingDescriptions[i] = new VertexInputBindingDescription((uint)i);
|
||||
@ -1431,17 +1439,22 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (_supportExtDynamic && (!Gd.IsMoltenVk || Gd.SupportsMTL31))
|
||||
{
|
||||
if (_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate != inputRate)
|
||||
if (_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate != inputRate ||
|
||||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding != _vertexBufferBindings[i])
|
||||
{
|
||||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate = inputRate;
|
||||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding = (uint)_vertexBufferBindings[i];
|
||||
|
||||
vertexBindingDescriptionChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate != inputRate || _newState.Internal.VertexBindingDescriptions[descriptorIndex].Stride != vertexBuffer.Stride)
|
||||
if (_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate != inputRate ||
|
||||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Stride != vertexBuffer.Stride ||
|
||||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding != _vertexBufferBindings[i])
|
||||
{
|
||||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding = (uint)_vertexBufferBindings[i];
|
||||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Stride = (uint)vertexBuffer.Stride;
|
||||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate = inputRate;
|
||||
|
||||
@ -1463,7 +1476,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
ref var buffer = ref _vertexBuffers[descriptorIndex];
|
||||
ref var buffer = ref _vertexBuffers[_vertexBufferBindings[i]];
|
||||
int oldScalarAlign = buffer.AttributeScalarAlignment;
|
||||
|
||||
if (Gd.Capabilities.VertexBufferAlignment < 2 &&
|
||||
@ -1480,7 +1493,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
vbSize,
|
||||
vertexBuffer.Stride);
|
||||
|
||||
buffer.BindVertexBuffer(Gd, Cbs, (uint)descriptorIndex, ref _newState, _vertexBufferUpdater);
|
||||
buffer.BindVertexBuffer(Gd, Cbs, (uint)_vertexBufferBindings[i], ref _newState, _vertexBufferUpdater);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1496,7 +1509,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
vbSize,
|
||||
vertexBuffer.Stride);
|
||||
|
||||
_vertexBuffersDirty |= 1UL << descriptorIndex;
|
||||
_vertexBuffersDirty |= 1UL << _vertexBufferBindings[i];
|
||||
}
|
||||
|
||||
buffer.AttributeScalarAlignment = oldScalarAlign;
|
||||
|
@ -793,6 +793,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
supportsViewportSwizzle: false,
|
||||
supportsIndirectParameters: true,
|
||||
supportsDepthClipControl: Capabilities.SupportsDepthClipControl,
|
||||
supportsExtendedDynamicState: Capabilities.SupportsExtendedDynamicState,
|
||||
supportsExtendedDynamicState2: Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2,
|
||||
supportsLogicOpDynamicState: Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp,
|
||||
supportsPatchControlPointsDynamicState: Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints,
|
||||
uniformBufferSetIndex: PipelineBase.UniformSetIndex,
|
||||
storageBufferSetIndex: PipelineBase.StorageSetIndex,
|
||||
textureSetIndex: PipelineBase.TextureSetIndex,
|
||||
@ -808,7 +812,26 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
shaderSubgroupSize: (int)Capabilities.SubgroupSize,
|
||||
storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment,
|
||||
textureBufferOffsetAlignment: (int)limits.MinTexelBufferOffsetAlignment,
|
||||
gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0);
|
||||
gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0,
|
||||
maximumGpuMemory: GetTotalGPUMemory());
|
||||
}
|
||||
|
||||
private ulong GetTotalGPUMemory()
|
||||
{
|
||||
ulong totalMemory = 0;
|
||||
|
||||
Api.GetPhysicalDeviceMemoryProperties(_physicalDevice.PhysicalDevice, out PhysicalDeviceMemoryProperties memoryProperties);
|
||||
|
||||
for (int i = 0; i < memoryProperties.MemoryHeapCount; i++)
|
||||
{
|
||||
var heap = memoryProperties.MemoryHeaps[i];
|
||||
if ((heap.Flags & MemoryHeapFlags.DeviceLocalBit) == MemoryHeapFlags.DeviceLocalBit)
|
||||
{
|
||||
totalMemory += heap.Size;
|
||||
}
|
||||
}
|
||||
|
||||
return totalMemory;
|
||||
}
|
||||
|
||||
public HardwareInfo GetHardwareInfo()
|
||||
@ -892,6 +915,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private void PrintGpuInformation()
|
||||
{
|
||||
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
|
||||
Logger.Notice.Print(LogClass.Gpu, $"GPU Memory: {GetTotalGPUMemory() / (1024 * 1024)} MiB");
|
||||
}
|
||||
|
||||
public void Initialize(GraphicsDebugLevel logLevel)
|
||||
|
@ -8,5 +8,6 @@ namespace Ryujinx.Horizon.Sdk.Audio
|
||||
|
||||
public static Result DeviceNotFound => new(ModuleId, 1);
|
||||
public static Result UnsupportedRevision => new(ModuleId, 2);
|
||||
public static Result NotImplemented => new(ModuleId, 513);
|
||||
}
|
||||
}
|
||||
|
@ -233,6 +233,48 @@ namespace Ryujinx.Horizon.Sdk.Audio.Detail
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(15)] // 17.0.0+
|
||||
public Result AcquireAudioOutputDeviceNotification([CopyHandle] out int eventHandle, ulong deviceId)
|
||||
{
|
||||
eventHandle = 0;
|
||||
|
||||
return AudioResult.NotImplemented;
|
||||
}
|
||||
|
||||
[CmifCommand(16)] // 17.0.0+
|
||||
public Result ReleaseAudioOutputDeviceNotification(ulong deviceId)
|
||||
{
|
||||
return AudioResult.NotImplemented;
|
||||
}
|
||||
|
||||
[CmifCommand(17)] // 17.0.0+
|
||||
public Result AcquireAudioInputDeviceNotification([CopyHandle] out int eventHandle, ulong deviceId)
|
||||
{
|
||||
eventHandle = 0;
|
||||
|
||||
return AudioResult.NotImplemented;
|
||||
}
|
||||
|
||||
[CmifCommand(18)] // 17.0.0+
|
||||
public Result ReleaseAudioInputDeviceNotification(ulong deviceId)
|
||||
{
|
||||
return AudioResult.NotImplemented;
|
||||
}
|
||||
|
||||
[CmifCommand(19)] // 18.0.0+
|
||||
public Result SetAudioDeviceOutputVolumeAutoTuneEnabled(bool enabled)
|
||||
{
|
||||
return AudioResult.NotImplemented;
|
||||
}
|
||||
|
||||
[CmifCommand(20)] // 18.0.0+
|
||||
public Result IsAudioDeviceOutputVolumeAutoTuneEnabled(out bool enabled)
|
||||
{
|
||||
enabled = false;
|
||||
|
||||
return AudioResult.NotImplemented;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
@ -53,6 +53,7 @@ namespace Ryujinx.SDL2.Common
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_SetHint(SDL_HINT_APP_NAME, "Ryujinx");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1");
|
||||
SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
|
||||
|
@ -55,6 +55,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
@ -83,6 +84,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
@ -111,6 +113,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.70f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
@ -139,6 +142,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.75f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(1, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
@ -167,6 +171,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
@ -195,6 +200,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
@ -223,6 +229,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(2, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
@ -251,6 +258,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
@ -279,6 +287,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
||||
Assert.IsFalse(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(3, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
@ -307,6 +316,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
||||
Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsFalse(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(4, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
@ -335,6 +345,7 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
||||
Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsFalse(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
@ -363,6 +374,36 @@ namespace Ryujinx.Tests.Audio.Renderer.Server
|
||||
Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsTrue(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsFalse(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
Assert.AreEqual(2, behaviourContext.GetPerformanceMetricsDataFormat());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRevision13()
|
||||
{
|
||||
BehaviourContext behaviourContext = new();
|
||||
|
||||
behaviourContext.SetUserRevision(BehaviourContext.BaseRevisionMagic + BehaviourContext.Revision13);
|
||||
|
||||
Assert.IsTrue(behaviourContext.IsAdpcmLoopContextBugFixed());
|
||||
Assert.IsTrue(behaviourContext.IsSplitterSupported());
|
||||
Assert.IsTrue(behaviourContext.IsLongSizePreDelaySupported());
|
||||
Assert.IsTrue(behaviourContext.IsAudioUsbDeviceOutputSupported());
|
||||
Assert.IsTrue(behaviourContext.IsFlushVoiceWaveBuffersSupported());
|
||||
Assert.IsTrue(behaviourContext.IsSplitterBugFixed());
|
||||
Assert.IsTrue(behaviourContext.IsElapsedFrameCountSupported());
|
||||
Assert.IsTrue(behaviourContext.IsDecodingBehaviourFlagSupported());
|
||||
Assert.IsTrue(behaviourContext.IsBiquadFilterEffectStateClearBugFixed());
|
||||
Assert.IsTrue(behaviourContext.IsMixInParameterDirtyOnlyUpdateSupported());
|
||||
Assert.IsTrue(behaviourContext.IsWaveBufferVersion2Supported());
|
||||
Assert.IsTrue(behaviourContext.IsEffectInfoVersion2Supported());
|
||||
Assert.IsTrue(behaviourContext.UseMultiTapBiquadFilterProcessing());
|
||||
Assert.IsTrue(behaviourContext.IsNewEffectChannelMappingSupported());
|
||||
Assert.IsTrue(behaviourContext.IsBiquadFilterParameterForSplitterEnabled());
|
||||
Assert.IsTrue(behaviourContext.IsSplitterPrevVolumeResetSupported());
|
||||
|
||||
Assert.AreEqual(0.80f, behaviourContext.GetAudioRendererProcessingTimeLimit());
|
||||
Assert.AreEqual(5, behaviourContext.GetCommandProcessingTimeEstimatorVersion());
|
||||
|
Loading…
Reference in New Issue
Block a user