diff --git a/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs index 31f614d67..2122f2b44 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/BiquadFilterHelper.cs @@ -26,12 +26,15 @@ namespace Ryujinx.Audio.Renderer.Dsp ReadOnlySpan inputBuffer, uint sampleCount) { - float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter); - float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter); - float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter); + Span numeratorSpan = parameter.Numerator.AsSpan(); + Span denominatorSpan = parameter.Denominator.AsSpan(); + + float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter); + float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter); + float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter); - float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter); - float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter); + float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter); + float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter); for (int i = 0; i < sampleCount; i++) { @@ -64,12 +67,15 @@ namespace Ryujinx.Audio.Renderer.Dsp uint sampleCount, float volume) { - float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter); - float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter); - float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter); + Span numeratorSpan = parameter.Numerator.AsSpan(); + Span denominatorSpan = parameter.Denominator.AsSpan(); + + float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter); + float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter); + float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter); - float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter); - float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter); + float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter); + float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter); for (int i = 0; i < sampleCount; i++) { @@ -107,12 +113,15 @@ namespace Ryujinx.Audio.Renderer.Dsp float volume, float ramp) { - float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter); - float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter); - float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter); + Span numeratorSpan = parameter.Numerator.AsSpan(); + Span denominatorSpan = parameter.Denominator.AsSpan(); + + float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter); + float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter); + float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter); - float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter); - float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter); + float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter); + float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter); float mixState = 0f; @@ -157,13 +166,16 @@ namespace Ryujinx.Audio.Renderer.Dsp BiquadFilterParameter parameter = parameters[stageIndex]; ref BiquadFilterState state = ref states[stageIndex]; + + Span numeratorSpan = parameter.Numerator.AsSpan(); + Span denominatorSpan = parameter.Denominator.AsSpan(); - float a0 = FixedPointHelper.ToFloat(parameter.Numerator[0], FixedPointPrecisionForParameter); - float a1 = FixedPointHelper.ToFloat(parameter.Numerator[1], FixedPointPrecisionForParameter); - float a2 = FixedPointHelper.ToFloat(parameter.Numerator[2], FixedPointPrecisionForParameter); + float a0 = FixedPointHelper.ToFloat(numeratorSpan[0], FixedPointPrecisionForParameter); + float a1 = FixedPointHelper.ToFloat(numeratorSpan[1], FixedPointPrecisionForParameter); + float a2 = FixedPointHelper.ToFloat(numeratorSpan[2], FixedPointPrecisionForParameter); - float b1 = FixedPointHelper.ToFloat(parameter.Denominator[0], FixedPointPrecisionForParameter); - float b2 = FixedPointHelper.ToFloat(parameter.Denominator[1], FixedPointPrecisionForParameter); + float b1 = FixedPointHelper.ToFloat(denominatorSpan[0], FixedPointPrecisionForParameter); + float b2 = FixedPointHelper.ToFloat(denominatorSpan[1], FixedPointPrecisionForParameter); for (int i = 0; i < sampleCount; i++) { @@ -201,19 +213,25 @@ namespace Ryujinx.Audio.Renderer.Dsp uint sampleCount, float volume) { - float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter); - float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter); - float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter); + Span numerator0Span = parameter0.Numerator.AsSpan(); + Span numerator1Span = parameter1.Numerator.AsSpan(); + Span denominator0Span = parameter0.Denominator.AsSpan(); + Span denominator1Span = parameter1.Denominator.AsSpan(); + + + float a00 = FixedPointHelper.ToFloat(numerator0Span[0], FixedPointPrecisionForParameter); + float a10 = FixedPointHelper.ToFloat(numerator0Span[1], FixedPointPrecisionForParameter); + float a20 = FixedPointHelper.ToFloat(numerator0Span[2], FixedPointPrecisionForParameter); - float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter); - float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter); + float b10 = FixedPointHelper.ToFloat(denominator0Span[0], FixedPointPrecisionForParameter); + float b20 = FixedPointHelper.ToFloat(denominator0Span[1], FixedPointPrecisionForParameter); - float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter); - float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter); - float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter); + float a01 = FixedPointHelper.ToFloat(numerator1Span[0], FixedPointPrecisionForParameter); + float a11 = FixedPointHelper.ToFloat(numerator1Span[1], FixedPointPrecisionForParameter); + float a21 = FixedPointHelper.ToFloat(numerator1Span[2], FixedPointPrecisionForParameter); - float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter); - float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter); + float b11 = FixedPointHelper.ToFloat(denominator1Span[0], FixedPointPrecisionForParameter); + float b21 = FixedPointHelper.ToFloat(denominator1Span[1], FixedPointPrecisionForParameter); for (int i = 0; i < sampleCount; i++) { @@ -261,19 +279,24 @@ namespace Ryujinx.Audio.Renderer.Dsp float volume, float ramp) { - float a00 = FixedPointHelper.ToFloat(parameter0.Numerator[0], FixedPointPrecisionForParameter); - float a10 = FixedPointHelper.ToFloat(parameter0.Numerator[1], FixedPointPrecisionForParameter); - float a20 = FixedPointHelper.ToFloat(parameter0.Numerator[2], FixedPointPrecisionForParameter); + Span numerator0Span = parameter0.Numerator.AsSpan(); + Span numerator1Span = parameter1.Numerator.AsSpan(); + Span denominator0Span = parameter0.Denominator.AsSpan(); + Span denominator1Span = parameter1.Denominator.AsSpan(); + + float a00 = FixedPointHelper.ToFloat(numerator0Span[0], FixedPointPrecisionForParameter); + float a10 = FixedPointHelper.ToFloat(numerator0Span[1], FixedPointPrecisionForParameter); + float a20 = FixedPointHelper.ToFloat(numerator0Span[2], FixedPointPrecisionForParameter); - float b10 = FixedPointHelper.ToFloat(parameter0.Denominator[0], FixedPointPrecisionForParameter); - float b20 = FixedPointHelper.ToFloat(parameter0.Denominator[1], FixedPointPrecisionForParameter); + float b10 = FixedPointHelper.ToFloat(denominator0Span[0], FixedPointPrecisionForParameter); + float b20 = FixedPointHelper.ToFloat(denominator0Span[1], FixedPointPrecisionForParameter); - float a01 = FixedPointHelper.ToFloat(parameter1.Numerator[0], FixedPointPrecisionForParameter); - float a11 = FixedPointHelper.ToFloat(parameter1.Numerator[1], FixedPointPrecisionForParameter); - float a21 = FixedPointHelper.ToFloat(parameter1.Numerator[2], FixedPointPrecisionForParameter); + float a01 = FixedPointHelper.ToFloat(numerator1Span[0], FixedPointPrecisionForParameter); + float a11 = FixedPointHelper.ToFloat(numerator1Span[1], FixedPointPrecisionForParameter); + float a21 = FixedPointHelper.ToFloat(numerator1Span[2], FixedPointPrecisionForParameter); - float b11 = FixedPointHelper.ToFloat(parameter1.Denominator[0], FixedPointPrecisionForParameter); - float b21 = FixedPointHelper.ToFloat(parameter1.Denominator[1], FixedPointPrecisionForParameter); + float b11 = FixedPointHelper.ToFloat(denominator1Span[0], FixedPointPrecisionForParameter); + float b21 = FixedPointHelper.ToFloat(denominator1Span[1], FixedPointPrecisionForParameter); float mixState = 0f; diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs index 51a12b4e7..59786c059 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/AdpcmDataSourceCommandVersion1.cs @@ -39,12 +39,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command OutputBufferIndex = outputBufferIndex; SampleRate = serverState.SampleRate; Pitch = serverState.Pitch; + + Span waveBufferSpan = serverState.WaveBuffers.AsSpan(); WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; for (int i = 0; i < WaveBuffers.Length; i++) { - ref Server.Voice.WaveBuffer voiceWaveBuffer = ref serverState.WaveBuffers[i]; + ref Server.Voice.WaveBuffer voiceWaveBuffer = ref waveBufferSpan[i]; WaveBuffers[i] = voiceWaveBuffer.ToCommon(1); } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs index 59ef70932..d5488b321 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CircularBufferSinkCommand.cs @@ -1,5 +1,6 @@ using Ryujinx.Audio.Renderer.Parameter.Sink; using Ryujinx.Audio.Renderer.Server.MemoryPool; +using System; using System.Diagnostics; namespace Ryujinx.Audio.Renderer.Dsp.Command @@ -28,10 +29,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command Input = new ushort[Constants.ChannelCountMax]; InputCount = parameter.InputCount; + + Span inputSpan = parameter.Input.AsSpan(); for (int i = 0; i < InputCount; i++) { - Input[i] = (ushort)(bufferOffset + parameter.Input[i]); + Input[i] = (ushort)(bufferOffset + inputSpan[i]); } CircularBuffer = circularBufferAddressInfo.GetReference(true); diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs index c6c0956a6..a668155f8 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/CompressorCommand.cs @@ -42,11 +42,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; + + Span inputSpan = _parameter.Input.AsSpan(); + Span outputSpan = _parameter.Output.AsSpan(); 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 + inputSpan[i]); + OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]); } } @@ -171,10 +174,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command statistics.MinimumGain = MathF.Min(statistics.MinimumGain, compressionGain * state.OutputGain); statistics.MaximumMean = MathF.Max(statistics.MaximumMean, mean); + + Span lastSamplesSpan = statistics.LastSamples.AsSpan(); for (int channelIndex = 0; channelIndex < _parameter.ChannelCount; channelIndex++) { - statistics.LastSamples[channelIndex] = MathF.Abs(channelInput[channelIndex] * (1f / 32768f)); + lastSamplesSpan[channelIndex] = MathF.Abs(channelInput[channelIndex] * (1f / 32768f)); } } } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs index e82d403bf..d3d3d2418 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DataSourceVersion2Command.cs @@ -52,12 +52,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command OutputBufferIndex = (ushort)(channelIndex + outputBufferIndex); SampleRate = serverState.SampleRate; Pitch = serverState.Pitch; + + Span waveBufferSpan = serverState.WaveBuffers.AsSpan(); WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; for (int i = 0; i < WaveBuffers.Length; i++) { - ref Server.Voice.WaveBuffer voiceWaveBuffer = ref serverState.WaveBuffers[i]; + ref Server.Voice.WaveBuffer voiceWaveBuffer = ref waveBufferSpan[i]; WaveBuffers[i] = voiceWaveBuffer.ToCommon(2); } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs index ecd3c3c93..491233770 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs @@ -42,11 +42,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; + + Span inputSpan = Parameter.Input.AsSpan(); + Span outputSpan = Parameter.Output.AsSpan(); 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 + inputSpan[i]); + OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]); } DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices, Parameter.ChannelCount); diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs index c64bbdc57..a76f690e4 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DepopPrepareCommand.cs @@ -42,14 +42,15 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command ref VoiceUpdateState state = ref State.Span[0]; Span depopBuffer = DepopBuffer.Span; + Span lastSamplesSpan = state.LastSamples.AsSpan(); for (int i = 0; i < MixBufferCount; i++) { - if (state.LastSamples[i] != 0) + if (lastSamplesSpan[i] != 0) { - depopBuffer[OutputBufferIndices[i]] += state.LastSamples[i]; + depopBuffer[OutputBufferIndices[i]] += lastSamplesSpan[i]; - state.LastSamples[i] = 0; + lastSamplesSpan[i] = 0; } } } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs index 19afc66f4..322c5d386 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs @@ -34,10 +34,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command SessionId = sessionId; InputCount = sink.Parameter.InputCount; InputBufferIndices = new ushort[InputCount]; + + Span inputSpan = sink.Parameter.Input.AsSpan(); for (int i = 0; i < Math.Min(InputCount, Constants.ChannelCountMax); i++) { - InputBufferIndices[i] = (ushort)(bufferOffset + sink.Parameter.Input[i]); + InputBufferIndices[i] = (ushort)(bufferOffset + inputSpan[i]); } if (sink.UpsamplerState != null) diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs index 4e7f67e78..b0a0183f8 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion1.cs @@ -37,11 +37,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; + + Span inputSpan = _parameter.Input.AsSpan(); + Span outputSpan = _parameter.Output.AsSpan(); 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 + inputSpan[i]); + OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]); } } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs index b0032c5b7..36cc0e768 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/LimiterCommandVersion2.cs @@ -48,11 +48,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; + + Span inputSpan = _parameter.Input.AsSpan(); + Span outputSpan = _parameter.Output.AsSpan(); 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 + inputSpan[i]); + OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]); } } @@ -150,8 +153,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command { ref LimiterStatistics statistics = ref MemoryMarshal.Cast(ResultState.Span[0].SpecificData)[0]; - statistics.InputMax[channelIndex] = Math.Max(statistics.InputMax[channelIndex], sampleInputMax); - statistics.CompressionGainMin[channelIndex] = Math.Min(statistics.CompressionGainMin[channelIndex], compressionGain); + Span inputMaxSpan = statistics.InputMax.AsSpan(); + Span compressionGainMinSpan = statistics.CompressionGainMin.AsSpan(); + + inputMaxSpan[channelIndex] = Math.Max(inputMaxSpan[channelIndex], sampleInputMax); + compressionGainMinSpan[channelIndex] = Math.Min(compressionGainMinSpan[channelIndex], compressionGain); } } } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs index 41ac84c1a..bc1f277ac 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/MixRampGroupedCommand.cs @@ -79,6 +79,10 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command public void Process(CommandList context) { + ref VoiceUpdateState state = ref State.Span[0]; + + Span lastSamplesSpan = state.LastSamples.AsSpan(); + for (int i = 0; i < MixBufferCount; i++) { ReadOnlySpan inputBuffer = context.GetBuffer(InputBufferIndices[i]); @@ -87,15 +91,13 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command float volume0 = Volume0[i]; float volume1 = Volume1[i]; - ref VoiceUpdateState state = ref State.Span[0]; - if (volume0 != 0 || volume1 != 0) { - state.LastSamples[i] = ProcessMixRampGrouped(outputBuffer, inputBuffer, volume0, volume1, (int)context.SampleCount); + lastSamplesSpan[i] = ProcessMixRampGrouped(outputBuffer, inputBuffer, volume0, volume1, (int)context.SampleCount); } else { - state.LastSamples[i] = 0; + lastSamplesSpan[i] = 0; } } } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs index 585edc058..2b5f0de72 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmFloatDataSourceCommandVersion1.cs @@ -42,10 +42,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command Pitch = serverState.Pitch; WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; + + Span waveBufferSpan = serverState.WaveBuffers.AsSpan(); for (int i = 0; i < WaveBuffers.Length; i++) { - ref Server.Voice.WaveBuffer voiceWaveBuffer = ref serverState.WaveBuffers[i]; + ref Server.Voice.WaveBuffer voiceWaveBuffer = ref waveBufferSpan[i]; WaveBuffers[i] = voiceWaveBuffer.ToCommon(1); } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs index 6f01219f3..9c30de41d 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/PcmInt16DataSourceCommandVersion1.cs @@ -42,10 +42,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command Pitch = serverState.Pitch; WaveBuffers = new WaveBuffer[Constants.VoiceWaveBufferCount]; + + Span waveBufferSpan = serverState.WaveBuffers.AsSpan(); for (int i = 0; i < WaveBuffers.Length; i++) { - ref Server.Voice.WaveBuffer voiceWaveBuffer = ref serverState.WaveBuffers[i]; + ref Server.Voice.WaveBuffer voiceWaveBuffer = ref waveBufferSpan[i]; WaveBuffers[i] = voiceWaveBuffer.ToCommon(1); } diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs index 5a65c650d..f183641cd 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs @@ -65,11 +65,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; + + Span inputSpan = Parameter.Input.AsSpan(); + Span outputSpan = Parameter.Output.AsSpan(); 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 + inputSpan[i]); + OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]); } // NOTE: We do the opposite as Nintendo here for now to restore previous behaviour diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs index c3d746994..20a8422d1 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs @@ -63,11 +63,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command InputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; OutputBufferIndices = new ushort[Constants.VoiceChannelCountMax]; + + Span inputSpan = Parameter.Input.AsSpan(); + Span outputSpan = Parameter.Output.AsSpan(); 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 + inputSpan[i]); + OutputBufferIndices[i] = (ushort)(bufferOffset + outputSpan[i]); } IsLongSizePreDelaySupported = isLongSizePreDelaySupported; diff --git a/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs index 98657bd13..130836c6b 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs @@ -74,7 +74,7 @@ namespace Ryujinx.Audio.Renderer.Dsp { int tempBufferIndex = 0; - if (!info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion)) + if ((info.DecodingBehaviour & DecodingBehaviour.SkipPitchAndSampleRateConversion) != DecodingBehaviour.SkipPitchAndSampleRateConversion) { voiceState.Pitch.AsSpan()[..pitchMaxLength].CopyTo(tempBuffer); tempBufferIndex += pitchMaxLength; @@ -208,7 +208,7 @@ namespace Ryujinx.Audio.Renderer.Dsp break; } - if (info.DecodingBehaviour.HasFlag(DecodingBehaviour.PlayedSampleCountResetWhenLooping)) + if ((info.DecodingBehaviour & DecodingBehaviour.PlayedSampleCountResetWhenLooping) == DecodingBehaviour.PlayedSampleCountResetWhenLooping) { playedSampleCount = 0; } @@ -222,7 +222,7 @@ namespace Ryujinx.Audio.Renderer.Dsp Span outputSpanInt = MemoryMarshal.Cast(outputBuffer[i..]); - if (info.DecodingBehaviour.HasFlag(DecodingBehaviour.SkipPitchAndSampleRateConversion)) + if ((info.DecodingBehaviour & DecodingBehaviour.SkipPitchAndSampleRateConversion) == DecodingBehaviour.SkipPitchAndSampleRateConversion) { for (int j = 0; j < y; j++) { diff --git a/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs b/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs index 8d833b712..f0a026406 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/UpsamplerHelper.cs @@ -41,11 +41,12 @@ namespace Ryujinx.Audio.Renderer.Dsp } Array20 result = new(); + Span resultSpan = result.AsSpan(); for (int i = 0; i < FilterBankLength; i++) { float x = (Bank0CenterIndex - i) + offset; - result[i] = Sinc(x) * BlackmanWindow(x / FilterBankLength + 0.5f); + resultSpan[i] = Sinc(x) * BlackmanWindow(x / FilterBankLength + 0.5f); } return result; @@ -78,6 +79,9 @@ namespace Ryujinx.Audio.Renderer.Dsp Debug.Assert(state.History.Length == HistoryLength); Debug.Assert(bank.Length == FilterBankLength); + + Span bankSpan = bank.AsSpan(); + Span historySpan = state.History.AsSpan(); int curIdx = 0; if (Vector.IsHardwareAccelerated) @@ -88,15 +92,15 @@ namespace Ryujinx.Audio.Renderer.Dsp while (curIdx < stopIdx) { result += Vector.Dot( - new Vector(bank.AsSpan().Slice(curIdx, Vector.Count)), - new Vector(state.History.AsSpan().Slice(curIdx, Vector.Count))); + new Vector(bankSpan[curIdx..(curIdx + Vector.Count)]), + new Vector(historySpan[curIdx..(curIdx + Vector.Count)])); curIdx += Vector.Count; } } while (curIdx < FilterBankLength) { - result += bank[curIdx] * state.History[curIdx]; + result += bankSpan[curIdx] * historySpan[curIdx]; curIdx++; } diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs index 0b789537a..519de9b65 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandGenerator.cs @@ -141,18 +141,20 @@ namespace Ryujinx.Audio.Renderer.Server { bool supportsOptimizedPath = _rendererContext.BehaviourContext.UseMultiTapBiquadFilterProcessing(); - if (supportsOptimizedPath && voiceState.BiquadFilters[0].Enable && voiceState.BiquadFilters[1].Enable) + Span biquadFiltersSpan = voiceState.BiquadFilters.AsSpan(); + + if (supportsOptimizedPath && biquadFiltersSpan[0].Enable && biquadFiltersSpan[1].Enable) { Memory biquadStateRawMemory = SpanMemoryManager.Cast(state)[..(Unsafe.SizeOf() * Constants.VoiceBiquadFilterCount)]; Memory stateMemory = SpanMemoryManager.Cast(biquadStateRawMemory); - _commandBuffer.GenerateMultiTapBiquadFilter(baseIndex, voiceState.BiquadFilters.AsSpan(), stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId); + _commandBuffer.GenerateMultiTapBiquadFilter(baseIndex, biquadFiltersSpan, stateMemory, bufferOffset, bufferOffset, voiceState.BiquadFilterNeedInitialization, nodeId); } else { - for (int i = 0; i < voiceState.BiquadFilters.Length; i++) + for (int i = 0; i < biquadFiltersSpan.Length; i++) { - ref BiquadFilterParameter filter = ref voiceState.BiquadFilters[i]; + ref BiquadFilterParameter filter = ref biquadFiltersSpan[i]; if (filter.Enable) { @@ -311,12 +313,15 @@ namespace Ryujinx.Audio.Renderer.Server { int nodeId = voiceState.NodeId; uint channelsCount = voiceState.ChannelsCount; + + Span channelResourceIdsSpan = voiceState.ChannelResourceIds.AsSpan(); + Span biquadFiltersSpan = voiceState.BiquadFilters.AsSpan(); for (int channelIndex = 0; channelIndex < channelsCount; channelIndex++) { - Memory dspStateMemory = _voiceContext.GetUpdateStateForDsp(voiceState.ChannelResourceIds[channelIndex]); + Memory dspStateMemory = _voiceContext.GetUpdateStateForDsp(channelResourceIdsSpan[channelIndex]); - ref VoiceChannelResource channelResource = ref _voiceContext.GetChannelResource(voiceState.ChannelResourceIds[channelIndex]); + ref VoiceChannelResource channelResource = ref _voiceContext.GetChannelResource(channelResourceIdsSpan[channelIndex]); PerformanceDetailType dataSourceDetailType = PerformanceDetailType.Adpcm; @@ -476,7 +481,7 @@ namespace Ryujinx.Audio.Renderer.Server for (int i = 0; i < voiceState.BiquadFilterNeedInitialization.Length; i++) { - voiceState.BiquadFilterNeedInitialization[i] = voiceState.BiquadFilters[i].Enable; + voiceState.BiquadFilterNeedInitialization[i] = biquadFiltersSpan[i].Enable; } } } @@ -526,15 +531,19 @@ namespace Ryujinx.Audio.Renderer.Server if (effect.IsEnabled) { + Span volumesSpan = effect.Parameter.Volumes.AsSpan(); + Span inputSpan = effect.Parameter.Input.AsSpan(); + Span outputSpan = effect.Parameter.Output.AsSpan(); + for (int i = 0; i < effect.Parameter.MixesCount; i++) { - if (effect.Parameter.Volumes[i] != 0.0f) + if (volumesSpan[i] != 0.0f) { _commandBuffer.GenerateMix( - (uint)bufferOffset + effect.Parameter.Input[i], - (uint)bufferOffset + effect.Parameter.Output[i], + (uint)bufferOffset + inputSpan[i], + (uint)bufferOffset + outputSpan[i], nodeId, - effect.Parameter.Volumes[i]); + volumesSpan[i]); } } } @@ -554,6 +563,10 @@ namespace Ryujinx.Audio.Renderer.Server { int i = 0; uint writeOffset = 0; + + Span inputSpan = effect.Parameter.Input.AsSpan(); + Span outputSpan = effect.Parameter.Output.AsSpan(); + for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--) { uint newUpdateCount = writeOffset + _commandBuffer.CommandList.SampleCount; @@ -571,8 +584,8 @@ namespace Ryujinx.Audio.Renderer.Server _commandBuffer.GenerateAuxEffect( bufferOffset, - effect.Parameter.Input[i], - effect.Parameter.Output[i], + inputSpan[i], + outputSpan[i], ref effect.State, effect.IsEnabled, effect.Parameter.BufferStorageSize, @@ -619,6 +632,9 @@ namespace Ryujinx.Audio.Renderer.Server private void GenerateBiquadFilterEffect(uint bufferOffset, BiquadFilterEffect effect, int nodeId) { Debug.Assert(effect.Type == EffectType.BiquadFilter); + + Span inputSpan = effect.Parameter.Input.AsSpan(); + Span outputSpan = effect.Parameter.Output.AsSpan(); if (effect.IsEnabled) { @@ -639,8 +655,8 @@ namespace Ryujinx.Audio.Renderer.Server (int)bufferOffset, ref parameter, effect.State.Slice(i, 1), - effect.Parameter.Input[i], - effect.Parameter.Output[i], + inputSpan[i], + outputSpan[i], needInitialization, nodeId); } @@ -649,8 +665,8 @@ namespace Ryujinx.Audio.Renderer.Server { for (int i = 0; i < effect.Parameter.ChannelCount; i++) { - uint inputBufferIndex = bufferOffset + effect.Parameter.Input[i]; - uint outputBufferIndex = bufferOffset + effect.Parameter.Output[i]; + uint inputBufferIndex = bufferOffset + inputSpan[i]; + uint outputBufferIndex = bufferOffset + outputSpan[i]; // If the input and output isn't the same, generate a command. if (inputBufferIndex != outputBufferIndex) @@ -701,6 +717,8 @@ namespace Ryujinx.Audio.Renderer.Server { int i = 0; uint writeOffset = 0; + + Span inputSpan = effect.Parameter.Input.AsSpan(); for (uint channelIndex = effect.Parameter.ChannelCount; channelIndex != 0; channelIndex--) { @@ -719,7 +737,7 @@ namespace Ryujinx.Audio.Renderer.Server _commandBuffer.GenerateCaptureEffect( bufferOffset, - effect.Parameter.Input[i], + inputSpan[i], effect.State.SendBufferInfo, effect.IsEnabled, effect.Parameter.BufferStorageSize, diff --git a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs index 1a4a38e44..3102ccdf0 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Splitter/SplitterDestinationVersion2.cs @@ -218,7 +218,8 @@ namespace Ryujinx.Audio.Renderer.Server.Splitter /// True if any biquad filter is enabled. public bool IsBiquadFilterEnabled() { - return _biquadFilters[0].Enable || _biquadFilters[1].Enable; + Span biquadFiltersSpan = _biquadFilters.AsSpan(); + return biquadFiltersSpan[0].Enable || biquadFiltersSpan[1].Enable; } /// diff --git a/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs b/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs index 9c4d1243a..27207a90d 100644 --- a/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs +++ b/src/Ryujinx.Audio/Renderer/Server/StateUpdater.cs @@ -162,9 +162,11 @@ namespace Ryujinx.Audio.Renderer.Server { ref VoiceState currentVoiceState = ref context.GetState(i); + Span channelResourceIdsSpan = parameter.ChannelResourceIds.AsSpan(); + for (int channelResourceIndex = 0; channelResourceIndex < parameter.ChannelCount; channelResourceIndex++) { - int channelId = parameter.ChannelResourceIds[channelResourceIndex]; + int channelId = channelResourceIdsSpan[channelResourceIndex]; Debug.Assert(channelId >= 0 && channelId < context.GetCount()); diff --git a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs index 7ce7143ed..827d24fe5 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceContext.cs @@ -126,9 +126,9 @@ namespace Ryujinx.Audio.Renderer.Server.Voice _sortedVoices.Span[i] = i; } - int[] sortedVoicesTemp = _sortedVoices[..(int)GetCount()].ToArray(); + Span sortedVoicesTemp = _sortedVoices[..(int)_voiceCount].Span; - Array.Sort(sortedVoicesTemp, (a, b) => + sortedVoicesTemp.Sort((a, b) => { ref VoiceState aState = ref GetState(a); ref VoiceState bState = ref GetState(b); @@ -143,7 +143,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice return result; }); - sortedVoicesTemp.AsSpan().CopyTo(_sortedVoices.Span); + // sortedVoicesTemp.CopyTo(_sortedVoices.Span); } } } diff --git a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs index 040c70e6c..9fcff199d 100644 --- a/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs +++ b/src/Ryujinx.Audio/Renderer/Server/Voice/VoiceState.cs @@ -219,15 +219,17 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// private void InitializeWaveBuffers() { - for (int i = 0; i < WaveBuffers.Length; i++) + Span waveBuffersSpan = WaveBuffers.AsSpan(); + + for (int i = 0; i < waveBuffersSpan.Length; i++) { - WaveBuffers[i].StartSampleOffset = 0; - WaveBuffers[i].EndSampleOffset = 0; - WaveBuffers[i].ShouldLoop = false; - WaveBuffers[i].IsEndOfStream = false; - WaveBuffers[i].BufferAddressInfo.Setup(0, 0); - WaveBuffers[i].ContextAddressInfo.Setup(0, 0); - WaveBuffers[i].IsSendToAudioProcessor = true; + waveBuffersSpan[i].StartSampleOffset = 0; + waveBuffersSpan[i].EndSampleOffset = 0; + waveBuffersSpan[i].ShouldLoop = false; + waveBuffersSpan[i].IsEndOfStream = false; + waveBuffersSpan[i].BufferAddressInfo.Setup(0, 0); + waveBuffersSpan[i].ContextAddressInfo.Setup(0, 0); + waveBuffersSpan[i].IsSendToAudioProcessor = true; } } @@ -446,10 +448,13 @@ namespace Ryujinx.Audio.Renderer.Server.Voice } ref VoiceUpdateState voiceUpdateState = ref voiceUpdateStates[0].Span[0]; + + Span waveBuffersSpan = WaveBuffers.AsSpan(); + Span pWaveBuffersSpan = parameter.WaveBuffers.AsSpan(); for (int i = 0; i < Constants.VoiceWaveBufferCount; i++) { - UpdateWaveBuffer(errorInfos.AsSpan(i * 2, 2), ref WaveBuffers[i], ref parameter.WaveBuffers[i], parameter.SampleFormat, voiceUpdateState.IsWaveBufferValid[i], mapper, ref behaviourContext); + UpdateWaveBuffer(errorInfos.AsSpan(i * 2, 2), ref waveBuffersSpan[i], ref pWaveBuffersSpan[i], parameter.SampleFormat, voiceUpdateState.IsWaveBufferValid[i], mapper, ref behaviourContext); } } @@ -534,9 +539,11 @@ namespace Ryujinx.Audio.Renderer.Server.Voice /// The voice context. private void ResetResources(VoiceContext context) { + Span channelResourceIdsSpan = ChannelResourceIds.AsSpan(); + for (int i = 0; i < ChannelsCount; i++) { - int channelResourceId = ChannelResourceIds[i]; + int channelResourceId = channelResourceIdsSpan[i]; ref VoiceChannelResource voiceChannelResource = ref context.GetChannelResource(channelResourceId); @@ -559,10 +566,12 @@ namespace Ryujinx.Audio.Renderer.Server.Voice private void FlushWaveBuffers(uint waveBufferCount, Memory[] voiceUpdateStates, uint channelCount) { uint waveBufferIndex = WaveBuffersIndex; + + Span waveBuffersSpan = WaveBuffers.AsSpan(); for (int i = 0; i < waveBufferCount; i++) { - WaveBuffers[(int)waveBufferIndex].IsSendToAudioProcessor = true; + waveBuffersSpan[(int)waveBufferIndex].IsSendToAudioProcessor = true; for (int j = 0; j < channelCount; j++) { @@ -591,14 +600,18 @@ namespace Ryujinx.Audio.Renderer.Server.Voice FlushWaveBufferCount = 0; } + Span waveBuffersSpan; + switch (PlayState) { case PlayState.Started: - for (int i = 0; i < WaveBuffers.Length; i++) + waveBuffersSpan = WaveBuffers.AsSpan(); + + for (int i = 0; i < waveBuffersSpan.Length; i++) { - ref WaveBuffer wavebuffer = ref WaveBuffers[i]; + ref WaveBuffer waveBuffer = ref waveBuffersSpan[i]; - if (!wavebuffer.IsSendToAudioProcessor) + if (!waveBuffer.IsSendToAudioProcessor) { for (int y = 0; y < ChannelsCount; y++) { @@ -607,7 +620,7 @@ namespace Ryujinx.Audio.Renderer.Server.Voice voiceUpdateStates[y].Span[0].IsWaveBufferValid[i] = true; } - wavebuffer.IsSendToAudioProcessor = true; + waveBuffer.IsSendToAudioProcessor = true; } } @@ -626,11 +639,13 @@ namespace Ryujinx.Audio.Renderer.Server.Voice return false; case PlayState.Stopping: - for (int i = 0; i < WaveBuffers.Length; i++) + waveBuffersSpan = WaveBuffers.AsSpan(); + + for (int i = 0; i < waveBuffersSpan.Length; i++) { - ref WaveBuffer wavebuffer = ref WaveBuffers[i]; + ref WaveBuffer waveBuffer = ref waveBuffersSpan[i]; - wavebuffer.IsSendToAudioProcessor = true; + waveBuffer.IsSendToAudioProcessor = true; for (int j = 0; j < ChannelsCount; j++) { @@ -702,9 +717,11 @@ namespace Ryujinx.Audio.Renderer.Server.Voice Memory[] voiceUpdateStates = new Memory[Constants.VoiceChannelCountMax]; + Span channelResourceIdsSpan = ChannelResourceIds.AsSpan(); + for (int i = 0; i < ChannelsCount; i++) { - voiceUpdateStates[i] = context.GetUpdateStateForDsp(ChannelResourceIds[i]); + voiceUpdateStates[i] = context.GetUpdateStateForDsp(channelResourceIdsSpan[i]); } return UpdateParametersForCommandGeneration(voiceUpdateStates); diff --git a/src/Ryujinx.Common/Collections/IntrusiveRedBlackTree.cs b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTree.cs index 18e60687d..d951a6024 100644 --- a/src/Ryujinx.Common/Collections/IntrusiveRedBlackTree.cs +++ b/src/Ryujinx.Common/Collections/IntrusiveRedBlackTree.cs @@ -131,7 +131,6 @@ namespace Ryujinx.Common.Collections if (parent.Predecessor != null) { newNode.Predecessor = parent.Predecessor; - parent.Predecessor = newNode; newNode.Predecessor.Successor = newNode; } diff --git a/src/Ryujinx.Common/Memory/IArray.cs b/src/Ryujinx.Common/Memory/IArray.cs index 3e0385c2d..3c77b039b 100644 --- a/src/Ryujinx.Common/Memory/IArray.cs +++ b/src/Ryujinx.Common/Memory/IArray.cs @@ -1,3 +1,5 @@ +using System; + namespace Ryujinx.Common.Memory { /// @@ -17,5 +19,10 @@ namespace Ryujinx.Common.Memory /// Number of elements on the array. /// int Length { get; } + + /// + /// Number of elements on the array. + /// + public Span AsSpan(); } } diff --git a/src/Ryujinx.Common/Memory/MemoryOwner.cs b/src/Ryujinx.Common/Memory/MemoryOwner.cs index b7fe1db77..f99553dc8 100644 --- a/src/Ryujinx.Common/Memory/MemoryOwner.cs +++ b/src/Ryujinx.Common/Memory/MemoryOwner.cs @@ -1,6 +1,7 @@ #nullable enable using System; using System.Buffers; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -10,11 +11,126 @@ namespace Ryujinx.Common.Memory { /// /// An implementation with an embedded length and fast - /// accessor, with memory allocated from . + /// accessor, with memory allocated from . /// /// The type of item to store. public sealed class MemoryOwner : IMemoryOwner { + private static class ArrayPooling + { + public class Holder(T[]? array = null) : IComparable, IComparable + { + public int SkipCount; + public readonly T[]? Array = array; + + public int CompareTo(Holder? other) + { + return Array!.Length.CompareTo(other!.Array!.Length); + } + + public int CompareTo(int other) + { + int self = Array!.Length; + + if (self < other) + { + SkipCount++; + return -1; + } + + if (self > other * 4) + { + return 1; + } + + return 0; + } + } + + // ReSharper disable once StaticMemberInGenericType + private static int _maxCacheCount = 50; + + private const int MaxSkipCount = 50; + + static readonly List _pool = new(); + + // ReSharper disable once StaticMemberInGenericType + static readonly Lock _lock = new(); + + private static int BinarySearch(List list, int size) + { + int min = 0; + int max = list.Count-1; + + while (min <= max) + { + int mid = (min + max) / 2; + int comparison = list[mid].CompareTo(size); + if (comparison == 0) + { + return mid; + } + if (comparison < 0) + { + min = mid+1; + } + else + { + max = mid-1; + } + } + return ~min; + } + + public static T[] Get(int minimumSize) + { + lock (_lock) + { + int index = BinarySearch(_pool, minimumSize); + + if (index >= 0) + { + Holder holder = _pool[index]; + + _pool.Remove(holder); + return holder.Array!; + } + + return new T[minimumSize]; + } + } + + public static void Return(T[] array) + { + + lock (_lock) + { + Holder holder = new(array); + int i = _pool.BinarySearch(holder); + if (i < 0) + { + _pool.Insert(~i, holder); + } + + if (_pool.Count >= _maxCacheCount) + { + for (int index = 0; index < _pool.Count; index++) + { + Holder h = _pool[index]; + + if (h.SkipCount >= MaxSkipCount) + { + _pool.Remove(h); + index--; + } + } + + _maxCacheCount = _pool.Count * 2; + } + } + } + } + private readonly int _length; private T[]? _array; @@ -25,7 +141,7 @@ namespace Ryujinx.Common.Memory private MemoryOwner(int length) { _length = length; - _array = ArrayPool.Shared.Rent(length); + _array = ArrayPooling.Get(length); } /// @@ -124,7 +240,7 @@ namespace Ryujinx.Common.Memory if (array is not null) { - ArrayPool.Shared.Return(array, RuntimeHelpers.IsReferenceOrContainsReferences()); + ArrayPooling.Return(array); } } diff --git a/src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs b/src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs index 009aff628..24573e0f5 100644 --- a/src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs +++ b/src/Ryujinx.Common/Memory/PartialUnmaps/ThreadLocalMap.cs @@ -1,3 +1,4 @@ +using System; using System.Runtime.InteropServices; using System.Threading; using static Ryujinx.Common.Memory.PartialUnmaps.PartialUnmapHelpers; @@ -42,10 +43,13 @@ namespace Ryujinx.Common.Memory.PartialUnmaps public int GetOrReserve(int threadId, T initial) { // Try get a match first. + + Span threadIdsSpan = ThreadIds.AsSpan(); + Span structsSpan = Structs.AsSpan(); for (int i = 0; i < MapSize; i++) { - int compare = Interlocked.CompareExchange(ref ThreadIds[i], threadId, threadId); + int compare = Interlocked.CompareExchange(ref threadIdsSpan[i], threadId, threadId); if (compare == threadId) { @@ -57,11 +61,11 @@ namespace Ryujinx.Common.Memory.PartialUnmaps for (int i = 0; i < MapSize; i++) { - int compare = Interlocked.CompareExchange(ref ThreadIds[i], threadId, 0); + int compare = Interlocked.CompareExchange(ref threadIdsSpan[i], threadId, 0); if (compare == 0) { - Structs[i] = initial; + structsSpan[i] = initial; return i; } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs index f62a4c01a..466614579 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/MME/MacroHLE.cs @@ -219,13 +219,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME state.Write(LogicOpOffset, 0); Array8 enable = new(); + Span enableSpan = enable.AsSpan(); for (int i = 0; i < 8; i++) { - enable[i] = new Boolean32((uint)(arg0 >> (i + 8)) & 1); + enableSpan[i] = new Boolean32((uint)(arg0 >> (i + 8)) & 1); } - _processor.ThreedClass.UpdateBlendEnable(ref enable); + _processor.ThreedClass.UpdateBlendEnable(enableSpan); } /// @@ -236,13 +237,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME private void UpdateColorMasks(IDeviceState state, int arg0) { Array8 masks = new(); + Span masksSpan = masks.AsSpan(); int index = 0; for (int i = 0; i < 4; i++) { - masks[index++] = new RtColorMask((uint)arg0 & 0x1fff); - masks[index++] = new RtColorMask(((uint)arg0 >> 16) & 0x1fff); + masksSpan[index++] = new RtColorMask((uint)arg0 & 0x1fff); + masksSpan[index++] = new RtColorMask(((uint)arg0 >> 16) & 0x1fff); if (i != 3) { @@ -250,7 +252,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.MME } } - _processor.ThreedClass.UpdateColorMasks(ref masks); + _processor.ThreedClass.UpdateColorMasks(masksSpan); } /// diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs index 18428eda9..01d8feae5 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/Blender/AdvancedBlendManager.cs @@ -75,10 +75,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.Blender { bool constantsMatch = true; + Span blendUcodeConstantsSpan = _state.State.BlendUcodeConstants.AsSpan(); + for (int i = 0; i < entry.Constants.Length; i++) { RgbFloat constant = entry.Constants[i]; - RgbHalf constant2 = _state.State.BlendUcodeConstants[i]; + RgbHalf constant2 = blendUcodeConstantsSpan[i]; if ((Half)constant.R != constant2.UnpackR() || (Half)constant.G != constant2.UnpackG() || diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VertexInfoBufferUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VertexInfoBufferUpdater.cs index 65f556fcb..e4895fe6c 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VertexInfoBufferUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VertexInfoBufferUpdater.cs @@ -1,6 +1,7 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Shader; +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -76,9 +77,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw /// Number of components that the format has public void SetVertexStride(int index, int stride, int componentCount) { - if (_data.VertexStrides[index].X != stride) + Span> vertexStridesSpan = _data.VertexStrides.AsSpan(); + + if (vertexStridesSpan[index].X != stride) { - _data.VertexStrides[index].X = stride; + vertexStridesSpan[index].X = stride; MarkDirty(VertexInfoBuffer.VertexStridesOffset + index * Unsafe.SizeOf>(), sizeof(int)); } @@ -86,7 +89,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw { int value = c < componentCount ? 1 : 0; - ref int currentValue = ref GetElementRef(ref _data.VertexStrides[index], c); + ref int currentValue = ref GetElementRef(ref vertexStridesSpan[index], c); if (currentValue != value) { @@ -104,15 +107,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw /// If the draw is instanced, should have the vertex divisor value, otherwise should be zero public void SetVertexOffset(int index, int offset, int divisor) { - if (_data.VertexOffsets[index].X != offset) + Span> vertexOffsetsSpan = _data.VertexOffsets.AsSpan(); + + if (vertexOffsetsSpan[index].X != offset) { - _data.VertexOffsets[index].X = offset; + vertexOffsetsSpan[index].X = offset; MarkDirty(VertexInfoBuffer.VertexOffsetsOffset + index * Unsafe.SizeOf>(), sizeof(int)); } - if (_data.VertexOffsets[index].Y != divisor) + if (vertexOffsetsSpan[index].Y != divisor) { - _data.VertexOffsets[index].Y = divisor; + vertexOffsetsSpan[index].Y = divisor; MarkDirty(VertexInfoBuffer.VertexOffsetsOffset + index * Unsafe.SizeOf>() + sizeof(int), sizeof(int)); } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs index a66ea7dbe..23a73908d 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ComputeDraw/VtgAsComputeState.cs @@ -125,9 +125,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw _vacContext.VertexInfoBufferUpdater.SetVertexCounts(_count, _instanceCount, _firstVertex, _firstInstance); _vacContext.VertexInfoBufferUpdater.SetGeometryCounts(primitivesCount); + Span vertexAttribStateSpan = _state.State.VertexAttribState.AsSpan(); + Span vertexBufferEndAddressSpan = _state.State.VertexBufferEndAddress.AsSpan(); + Span vertexBufferStateSpan = _state.State.VertexBufferState.AsSpan(); + Span vertexBufferInstancedSpan = _state.State.VertexBufferInstanced.AsSpan(); + for (int index = 0; index < Constants.TotalVertexAttribs; index++) { - VertexAttribState vertexAttrib = _state.State.VertexAttribState[index]; + VertexAttribState vertexAttrib = vertexAttribStateSpan[index]; if (!FormatTable.TryGetSingleComponentAttribFormat(vertexAttrib.UnpackFormat(), out Format format, out int componentsCount)) { @@ -153,9 +158,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed.ComputeDraw int bufferIndex = vertexAttrib.UnpackBufferIndex(); - GpuVa endAddress = _state.State.VertexBufferEndAddress[bufferIndex]; - VertexBufferState vertexBuffer = _state.State.VertexBufferState[bufferIndex]; - bool instanced = _state.State.VertexBufferInstanced[bufferIndex]; + GpuVa endAddress = vertexBufferEndAddressSpan[bufferIndex]; + VertexBufferState vertexBuffer = vertexBufferStateSpan[bufferIndex]; + bool instanced = vertexBufferInstancedSpan[bufferIndex]; ulong address = vertexBuffer.Address.Pack(); diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs index e3b981f40..c9f3e8bad 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/DrawManager.cs @@ -821,6 +821,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed // on the screen scissor state, then we need to force only one texture to be bound to avoid // host clipping. ScreenScissorState screenScissorState = _state.State.ScreenScissorState; + + Span scissorStateSpan = _state.State.ScissorState.AsSpan(); bool clearAffectedByStencilMask = (_state.State.ClearFlags & 1) != 0; bool clearAffectedByScissor = (_state.State.ClearFlags & 0x100) != 0; @@ -831,9 +833,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed bool fullClear = screenScissorState.X == 0 && screenScissorState.Y == 0; - if (fullClear && clearAffectedByScissor && _state.State.ScissorState[0].Enable) + if (fullClear && clearAffectedByScissor && scissorStateSpan[0].Enable) { - ref ScissorState scissorState = ref _state.State.ScissorState[0]; + ref ScissorState scissorState = ref scissorStateSpan[0]; fullClear = scissorState.X1 == screenScissorState.X && scissorState.Y1 == screenScissorState.Y && @@ -892,9 +894,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed int scissorW = screenScissorState.Width; int scissorH = screenScissorState.Height; - if (clearAffectedByScissor && _state.State.ScissorState[0].Enable) + if (clearAffectedByScissor && scissorStateSpan[0].Enable) { - ref ScissorState scissorState = ref _state.State.ScissorState[0]; + ref ScissorState scissorState = ref scissorStateSpan[0]; scissorX = Math.Max(scissorX, scissorState.X1); scissorY = Math.Max(scissorY, scissorState.Y1); diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs index c76adbca6..db8bde751 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/SpecializationStateUpdater.cs @@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Shader; +using System; namespace Ryujinx.Graphics.Gpu.Engine.Threed { @@ -214,10 +215,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Updates the type of the vertex attributes consumed by the shader. /// /// The new state - public void SetAttributeTypes(ref Array32 state) + public void SetAttributeTypes(ReadOnlySpan state) { bool changed = false; - ref Array32 attributeTypes = ref _graphics.AttributeTypes; + // ref Array32 attributeTypes = ref _graphics.AttributeTypes; + Span attributeTypesSpan = _graphics.AttributeTypes.AsSpan(); bool mayConvertVtgToCompute = ShaderCache.MayConvertVtgToCompute(ref _context.Capabilities); bool supportsScaledFormats = _context.Capabilities.SupportsScaledVertexFormats && !mayConvertVtgToCompute; @@ -261,9 +263,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed } } - if (attributeTypes[location] != value) + if (attributeTypesSpan[location] != value) { - attributeTypes[location] = value; + attributeTypesSpan[location] = value; changed = true; } } @@ -279,10 +281,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// /// The render target control register /// The color attachment state - public void SetFragmentOutputTypes(RtControl rtControl, ref Array8 state) + public void SetFragmentOutputTypes(RtControl rtControl, ReadOnlySpan state) { bool changed = false; int count = rtControl.UnpackCount(); + + Span fragmentOutputTypesSpan = _graphics.FragmentOutputTypes.AsSpan(); for (int index = 0; index < Constants.TotalRenderTargets; index++) { @@ -296,9 +300,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed AttributeType type = format.IsInteger() ? (format.IsSint() ? AttributeType.Sint : AttributeType.Uint) : AttributeType.Float; - if (type != _graphics.FragmentOutputTypes[index]) + if (type != fragmentOutputTypesSpan[index]) { - _graphics.FragmentOutputTypes[index] = type; + fragmentOutputTypesSpan[index] = type; changed = true; } } diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs index e6af3fb5e..7bcf16ae7 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs @@ -423,9 +423,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// private void UpdateTfBufferState() { + Span tfBufferStateSpan = _state.State.TfBufferState.AsSpan(); + for (int index = 0; index < Constants.TotalTransformFeedbackBuffers; index++) { - TfBufferState tfb = _state.State.TfBufferState[index]; + TfBufferState tfb = tfBufferStateSpan[index]; if (!tfb.Enable) { @@ -466,10 +468,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed MemoryManager memoryManager = _channel.MemoryManager; RtControl rtControl = _state.State.RtControl; - bool useControl = updateFlags.HasFlag(RenderTargetUpdateFlags.UseControl); - bool layered = updateFlags.HasFlag(RenderTargetUpdateFlags.Layered); - bool singleColor = updateFlags.HasFlag(RenderTargetUpdateFlags.SingleColor); - bool discard = updateFlags.HasFlag(RenderTargetUpdateFlags.DiscardClip); + bool useControl = (updateFlags & RenderTargetUpdateFlags.UseControl) == RenderTargetUpdateFlags.UseControl; + bool layered = (updateFlags & RenderTargetUpdateFlags.Layered) == RenderTargetUpdateFlags.Layered; + bool singleColor = (updateFlags & RenderTargetUpdateFlags.SingleColor) == RenderTargetUpdateFlags.SingleColor; + bool discard = (updateFlags & RenderTargetUpdateFlags.DiscardClip) == RenderTargetUpdateFlags.DiscardClip; int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets; @@ -487,11 +489,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed bool changedScale = false; uint rtNoAlphaMask = 0; + Span rtColorStateSpan = _state.State.RtColorState.AsSpan(); + for (int index = 0; index < Constants.TotalRenderTargets; index++) { int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index; - RtColorState colorState = _state.State.RtColorState[rtIndex]; + RtColorState colorState = rtColorStateSpan[rtIndex]; if (index >= count || !IsRtEnabled(colorState) || (singleColor && index != singleUse)) { @@ -539,7 +543,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed Image.Texture depthStencil = null; - if (dsEnable && updateFlags.HasFlag(RenderTargetUpdateFlags.UpdateDepthStencil)) + if (dsEnable && (updateFlags & RenderTargetUpdateFlags.UpdateDepthStencil) == RenderTargetUpdateFlags.UpdateDepthStencil) { RtDepthStencilState dsState = _state.State.RtDepthStencilState; Size3D dsSize = _state.State.RtDepthStencilSize; @@ -599,7 +603,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// public void UpdateRenderTargetSpecialization() { - _currentSpecState.SetFragmentOutputTypes(_state.State.RtControl, ref _state.State.RtColorState); + _currentSpecState.SetFragmentOutputTypes(_state.State.RtControl, _state.State.RtColorState.AsSpan()); } /// @@ -624,10 +628,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed const int MaxH = 0xffff; Span> regions = stackalloc Rectangle[Constants.TotalViewports]; - + Span scissorStateSpan = _state.State.ScissorState.AsSpan(); + for (int index = 0; index < Constants.TotalViewports; index++) { - ScissorState scissor = _state.State.ScissorState[index]; + ScissorState scissor = scissorStateSpan[index]; bool enable = scissor.Enable && (scissor.X1 != MinX || scissor.Y1 != MinY || @@ -731,6 +736,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed UpdateDepthMode(); Span viewports = stackalloc Viewport[Constants.TotalViewports]; + Span viewportTransformSpan = _state.State.ViewportTransform.AsSpan(); + Span viewportExtentsSpan = _state.State.ViewportExtents.AsSpan(); for (int index = 0; index < Constants.TotalViewports; index++) { @@ -745,8 +752,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed continue; } - ref ViewportTransform transform = ref _state.State.ViewportTransform[index]; - ref ViewportExtents extents = ref _state.State.ViewportExtents[index]; + ref ViewportTransform transform = ref viewportTransformSpan[index]; + ref ViewportExtents extents = ref viewportExtentsSpan[index]; float scaleX = MathF.Abs(transform.ScaleX); float scaleY = transform.ScaleY; @@ -968,10 +975,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed uint vbEnableMask = _vbEnableMask; Span vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs]; - + Span vertexAttribStateSpan = _state.State.VertexAttribState.AsSpan(); + for (int index = 0; index < Constants.TotalVertexAttribs; index++) { - VertexAttribState vertexAttrib = _state.State.VertexAttribState[index]; + VertexAttribState vertexAttrib = vertexAttribStateSpan[index]; int bufferIndex = vertexAttrib.UnpackBufferIndex(); @@ -1015,7 +1023,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed _pipeline.SetVertexAttribs(vertexAttribs); _context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs); - _currentSpecState.SetAttributeTypes(ref _state.State.VertexAttribState); + _currentSpecState.SetAttributeTypes(_state.State.VertexAttribState.AsSpan()); } /// @@ -1113,20 +1121,25 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed int drawFirstVertex = _drawState.DrawFirstVertex; int drawVertexCount = _drawState.DrawVertexCount; uint vbEnableMask = 0; + + Span vertexBufferStateSpan = _state.State.VertexBufferState.AsSpan(); + Span vertexBuffersSpan = _pipeline.VertexBuffers.AsSpan(); + Span vertexBufferEndAddressSpan = _state.State.VertexBufferEndAddress.AsSpan(); + Span vertexBufferInstancedSpan = _state.State.VertexBufferInstanced.AsSpan(); for (int index = 0; index < Constants.TotalVertexBuffers; index++) { - VertexBufferState vertexBuffer = _state.State.VertexBufferState[index]; + VertexBufferState vertexBuffer = vertexBufferStateSpan[index]; if (!vertexBuffer.UnpackEnable()) { - _pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(false, 0, 0); + vertexBuffersSpan[index] = new BufferPipelineDescriptor(false, 0, 0); _channel.BufferManager.SetVertexBuffer(index, 0, 0, 0, 0); continue; } - GpuVa endAddress = _state.State.VertexBufferEndAddress[index]; + GpuVa endAddress = vertexBufferEndAddressSpan[index]; ulong address = vertexBuffer.Address.Pack(); @@ -1137,7 +1150,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed int stride = vertexBuffer.UnpackStride(); - bool instanced = _state.State.VertexBufferInstanced[index]; + bool instanced = vertexBufferInstancedSpan[index]; int divisor = instanced ? vertexBuffer.Divisor : 0; @@ -1184,7 +1197,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed size = Math.Min(vbSize, (ulong)((firstInstance + drawFirstVertex + drawVertexCount) * stride)); } - _pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(_channel.MemoryManager.IsMapped(address), stride, divisor); + vertexBuffersSpan[index] = new BufferPipelineDescriptor(_channel.MemoryManager.IsMapped(address), stride, divisor); _channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor); } @@ -1237,10 +1250,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed bool rtColorMaskShared = _state.State.RtColorMaskShared; Span componentMasks = stackalloc uint[Constants.TotalRenderTargets]; + Span rtColorMaskSpan = _state.State.RtColorMask.AsSpan(); + Span colorWriteMaskSpan = _pipeline.ColorWriteMask.AsSpan(); for (int index = 0; index < Constants.TotalRenderTargets; index++) { - RtColorMask colorMask = _state.State.RtColorMask[rtColorMaskShared ? 0 : index]; + RtColorMask colorMask = rtColorMaskSpan[rtColorMaskShared ? 0 : index]; uint componentMask; @@ -1250,7 +1265,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed componentMask |= (colorMask.UnpackAlpha() ? 8u : 0u); componentMasks[index] = componentMask; - _pipeline.ColorWriteMask[index] = componentMask; + colorWriteMaskSpan[index] = componentMask; } _context.Renderer.Pipeline.SetRenderTargetColorMasks(componentMasks); @@ -1282,10 +1297,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed if (blendIndependent) { + Span blendEnableSpan = _state.State.BlendEnable.AsSpan(); + Span blendStateSpan = _state.State.BlendState.AsSpan(); + Span blendDescriptorsSpan = _pipeline.BlendDescriptors.AsSpan(); + for (int index = 0; index < Constants.TotalRenderTargets; index++) { - bool enable = _state.State.BlendEnable[index]; - BlendState blend = _state.State.BlendState[index]; + bool enable = blendEnableSpan[index]; + BlendState blend = blendStateSpan[index]; BlendDescriptor descriptor = new( enable, @@ -1306,7 +1325,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed dualSourceBlendEnabled = true; } - _pipeline.BlendDescriptors[index] = descriptor; + blendDescriptorsSpan[index] = descriptor; _context.Renderer.Pipeline.SetBlendState(index, descriptor); } } @@ -1333,10 +1352,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed { dualSourceBlendEnabled = true; } + + Span blendDescriptorsSpan = _pipeline.BlendDescriptors.AsSpan(); for (int index = 0; index < Constants.TotalRenderTargets; index++) { - _pipeline.BlendDescriptors[index] = descriptor; + blendDescriptorsSpan[index] = descriptor; _context.Renderer.Pipeline.SetBlendState(index, descriptor); } } @@ -1422,12 +1443,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed ShaderAddresses addresses = new(); Span addressesSpan = addresses.AsSpan(); + Span shaderStateSpan = _state.State.ShaderState.AsSpan(); ulong baseAddress = _state.State.ShaderBaseAddress.Pack(); for (int index = 0; index < 6; index++) { - ShaderState shader = _state.State.ShaderState[index]; + ShaderState shader = shaderStateSpan[index]; if (!shader.UnpackEnable() && index != 1) { continue; diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs index a96979cb2..409dbab41 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClass.cs @@ -242,22 +242,22 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Second struct /// True if equal, false otherwise [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool UnsafeEquals32Byte(ref T lhs, ref T rhs) where T : unmanaged + private static bool UnsafeEquals32Byte(Span lhs, Span rhs) where T : unmanaged { if (Vector256.IsHardwareAccelerated) { return Vector256.EqualsAll( - Unsafe.As>(ref lhs), - Unsafe.As>(ref rhs) + Unsafe.As, ReadOnlySpan>>(ref lhs)[0], + Unsafe.As, ReadOnlySpan>>(ref rhs)[0] ); } else { - ref Vector128 lhsVec = ref Unsafe.As>(ref lhs); - ref Vector128 rhsVec = ref Unsafe.As>(ref rhs); + ReadOnlySpan> lhsVec = Unsafe.As, ReadOnlySpan>>(ref lhs); + ReadOnlySpan> rhsVec = Unsafe.As, ReadOnlySpan>>(ref rhs); - return Vector128.EqualsAll(lhsVec, rhsVec) && - Vector128.EqualsAll(Unsafe.Add(ref lhsVec, 1), Unsafe.Add(ref rhsVec, 1)); + return Vector128.EqualsAll(lhsVec[0], rhsVec[0]) && + Vector128.EqualsAll(lhsVec[1], rhsVec[1]); } } @@ -265,26 +265,26 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Updates blend enable. Respects current shadow mode. /// /// Blend enable - public void UpdateBlendEnable(ref Array8 enable) + public void UpdateBlendEnable(Span enable) { SetMmeShadowRamControlMode shadow = ShadowMode; - ref Array8 state = ref _state.State.BlendEnable; + Span state = _state.State.BlendEnable.AsSpan(); if (shadow.IsReplay()) { - enable = _state.ShadowState.BlendEnable; + state.CopyTo(enable); } - if (!UnsafeEquals32Byte(ref enable, ref state)) + if (!UnsafeEquals32Byte(enable, state)) { - state = enable; + enable.CopyTo(state); _stateUpdater.ForceDirty(StateUpdater.BlendStateIndex); } if (shadow.IsTrack()) { - _state.ShadowState.BlendEnable = enable; + enable.CopyTo(state); } } @@ -292,26 +292,26 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed /// Updates color masks. Respects current shadow mode. /// /// Color masks - public void UpdateColorMasks(ref Array8 masks) + public void UpdateColorMasks(Span masks) { SetMmeShadowRamControlMode shadow = ShadowMode; - ref Array8 state = ref _state.State.RtColorMask; + Span state = _state.State.RtColorMask.AsSpan(); if (shadow.IsReplay()) { - masks = _state.ShadowState.RtColorMask; + state.CopyTo(masks); } - if (!UnsafeEquals32Byte(ref masks, ref state)) + if (!UnsafeEquals32Byte(masks, state)) { - state = masks; + masks.CopyTo(state); _stateUpdater.ForceDirty(StateUpdater.RtColorMaskIndex); } if (shadow.IsTrack()) { - _state.ShadowState.RtColorMask = masks; + masks.CopyTo(state); } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs index 8c1132d9b..dc8f4e45f 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferModifiedRangeList.cs @@ -72,7 +72,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// class BufferModifiedRangeList : NonOverlappingRangeList { - private new const int BackingInitialSize = 8; + private const int BackingInitialSize = 8; private readonly GpuContext _context; private readonly Buffer _parent; @@ -80,6 +80,8 @@ namespace Ryujinx.Graphics.Gpu.Memory private BufferMigration _source; private BufferModifiedRangeList _migrationTarget; + + private List> _overlaps; /// /// Whether the modified range list has any entries or not. @@ -106,6 +108,7 @@ namespace Ryujinx.Graphics.Gpu.Memory _context = context; _parent = parent; _flushAction = flushAction; + _overlaps = []; } /// @@ -295,23 +298,24 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The action to call for each modified range public void GetRanges(ulong address, ulong size, Action rangeAction) { - List> overlaps = []; - // We use the non-span method here because keeping the lock will cause a deadlock. Lock.EnterReadLock(); + + _overlaps.Clear(); + (RangeItem first, RangeItem last) = FindOverlaps(address, size); RangeItem current = first; while (last != null && current != last.Next) { - overlaps.Add(current); + _overlaps.Add(current); current = current.Next; } Lock.ExitReadLock(); - for (int i = 0; i < overlaps.Count; i++) + for (int i = 0; i < _overlaps.Count; i++) { - BufferModifiedRange overlap = overlaps[i].Value; + BufferModifiedRange overlap = _overlaps[i].Value; rangeAction(overlap.Address, overlap.Size); } } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/BufferStage.cs b/src/Ryujinx.Graphics.Gpu/Memory/BufferStage.cs index c299731f8..67adcc7d4 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/BufferStage.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/BufferStage.cs @@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Gpu.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public static BufferStage FromUsage(BufferUsageFlags flags) { - if (flags.HasFlag(BufferUsageFlags.Write)) + if ((flags & BufferUsageFlags.Write) == BufferUsageFlags.Write) { return BufferStage.StorageWrite; } @@ -67,7 +67,7 @@ namespace Ryujinx.Graphics.Gpu.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public static BufferStage FromUsage(TextureUsageFlags flags) { - if (flags.HasFlag(TextureUsageFlags.ImageStore)) + if ((flags & TextureUsageFlags.ImageStore) == TextureUsageFlags.ImageStore) { return BufferStage.StorageWrite; } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs index b6ece6f83..da19bd064 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/SupportBufferUpdater.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; +using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -104,9 +105,11 @@ namespace Ryujinx.Graphics.Gpu.Memory /// Scale value public void UpdateRenderScale(int index, float scale) { - if (_data.RenderScale[1 + index].X != scale) + Span> renderScaleSpan = _data.RenderScale.AsSpan(); + + if (renderScaleSpan[1 + index].X != scale) { - _data.RenderScale[1 + index].X = scale; + renderScaleSpan[1 + index].X = scale; DirtyRenderScale(1 + index, 1); } } @@ -133,11 +136,13 @@ namespace Ryujinx.Graphics.Gpu.Memory /// True if the format is BGRA< false otherwise public void SetRenderTargetIsBgra(int index, bool isBgra) { - bool isBgraChanged = _data.FragmentIsBgra[index].X != 0 != isBgra; + Span> fragmentIsBgraSpan = _data.FragmentIsBgra.AsSpan(); + + bool isBgraChanged = fragmentIsBgraSpan[index].X != 0 != isBgra; if (isBgraChanged) { - _data.FragmentIsBgra[index].X = isBgra ? 1 : 0; + fragmentIsBgraSpan[index].X = isBgra ? 1 : 0; DirtyFragmentIsBgra(index, 1); } } diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index 2ab413848..dd0f45651 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Engine.Types; @@ -258,21 +259,25 @@ namespace Ryujinx.Graphics.Gpu.Shader int count = rtControl.UnpackCount(); + Span rtColorStateSpan = state.RtColorState.AsSpan(); + Span attachmentEnableSpan = pipeline.AttachmentEnable.AsSpan(); + Span attachmentFormatsSpan = pipeline.AttachmentFormats.AsSpan(); + for (int index = 0; index < Constants.TotalRenderTargets; index++) { int rtIndex = rtControl.UnpackPermutationIndex(index); - var colorState = state.RtColorState[rtIndex]; + var colorState = rtColorStateSpan[rtIndex]; if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0) { - pipeline.AttachmentEnable[index] = false; - pipeline.AttachmentFormats[index] = Format.R8G8B8A8Unorm; + attachmentEnableSpan[index] = false; + attachmentFormatsSpan[index] = Format.R8G8B8A8Unorm; } else { - pipeline.AttachmentEnable[index] = true; - pipeline.AttachmentFormats[index] = colorState.Format.Convert().Format; + attachmentEnableSpan[index] = true; + attachmentFormatsSpan[index] = colorState.Format.Convert().Format; } } @@ -580,15 +585,18 @@ namespace Ryujinx.Graphics.Gpu.Shader TransformFeedbackDescriptor[] descs = new TransformFeedbackDescriptor[Constants.TotalTransformFeedbackBuffers]; + Span tfStateSpan = state.TfState.AsSpan(); + Span> tfVaryingLocationsSpan = state.TfVaryingLocations.AsSpan(); + for (int i = 0; i < Constants.TotalTransformFeedbackBuffers; i++) { - var tf = state.TfState[i]; + var tf = tfStateSpan[i]; descs[i] = new TransformFeedbackDescriptor( tf.BufferIndex, tf.Stride, tf.VaryingsCount, - ref state.TfVaryingLocations[i]); + ref tfVaryingLocationsSpan[i]); } return descs; diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs index 4bb4392e1..8ce937e44 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderSpecializationState.cs @@ -580,10 +580,13 @@ namespace Ryujinx.Graphics.Gpu.Shader if (ShaderCache.MayConvertVtgToCompute(ref channel.Capabilities) && !vertexAsCompute) { - for (int index = 0; index < graphicsState.AttributeTypes.Length; index++) + Span attributeTypesSpan = graphicsState.AttributeTypes.AsSpan(); + Span gAttributeTypesSpan = GraphicsState.AttributeTypes.AsSpan(); + + for (int index = 0; index < attributeTypesSpan.Length; index++) { - AttributeType lType = FilterAttributeType(channel, graphicsState.AttributeTypes[index]); - AttributeType rType = FilterAttributeType(channel, GraphicsState.AttributeTypes[index]); + AttributeType lType = FilterAttributeType(channel, attributeTypesSpan[index]); + AttributeType rType = FilterAttributeType(channel, gAttributeTypesSpan[index]); if (lType != rType) { @@ -729,6 +732,8 @@ namespace Ryujinx.Graphics.Gpu.Shader { int constantBufferUsePerStageMask = _constantBufferUsePerStage; + Span constantBufferUseSpan = ConstantBufferUse.AsSpan(); + while (constantBufferUsePerStageMask != 0) { int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask); @@ -737,7 +742,7 @@ namespace Ryujinx.Graphics.Gpu.Shader ? channel.BufferManager.GetComputeUniformBufferUseMask() : channel.BufferManager.GetGraphicsUniformBufferUseMask(index); - if (ConstantBufferUse[index] != useMask) + if (constantBufferUseSpan[index] != useMask) { return false; } @@ -801,7 +806,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { if (specializationState != null) { - if (specializationState.Value.QueriedFlags.HasFlag(QueriedTextureStateFlags.CoordNormalized) && + if ((specializationState.Value.QueriedFlags & QueriedTextureStateFlags.CoordNormalized) == QueriedTextureStateFlags.CoordNormalized && specializationState.Value.CoordNormalized != descriptor.UnpackTextureCoordNormalized()) { return false; @@ -877,10 +882,12 @@ namespace Ryujinx.Graphics.Gpu.Shader int constantBufferUsePerStageMask = specState._constantBufferUsePerStage; + Span constantBufferUseSpan = specState.ConstantBufferUse.AsSpan(); + while (constantBufferUsePerStageMask != 0) { int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask); - dataReader.Read(ref specState.ConstantBufferUse[index]); + dataReader.Read(ref constantBufferUseSpan[index]); constantBufferUsePerStageMask &= ~(1 << index); } @@ -979,11 +986,13 @@ namespace Ryujinx.Graphics.Gpu.Shader dataWriter.Write(ref _constantBufferUsePerStage); int constantBufferUsePerStageMask = _constantBufferUsePerStage; + + Span constantBufferUseSpan = ConstantBufferUse.AsSpan(); while (constantBufferUsePerStageMask != 0) { int index = BitOperations.TrailingZeroCount(constantBufferUsePerStageMask); - dataWriter.Write(ref ConstantBufferUse[index]); + dataWriter.Write(ref constantBufferUseSpan[index]); constantBufferUsePerStageMask &= ~(1 << index); } diff --git a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs index 5e9e2869e..43802c34e 100644 --- a/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs +++ b/src/Ryujinx.Graphics.Nvdec.FFmpeg/H264/SpsAndPpsReconstruction.cs @@ -92,20 +92,24 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 if (pictureInfo.ScalingMatrixPresent) { + Span> scalingLists4x4Span = pictureInfo.ScalingLists4x4.AsSpan(); + for (int index = 0; index < 6; index++) { writer.WriteBit(true); - WriteScalingList(ref writer, pictureInfo.ScalingLists4x4[index]); + WriteScalingList(ref writer, scalingLists4x4Span[index]); } if (pictureInfo.Transform8x8ModeFlag) { + Span> scalingLists8x8Span = pictureInfo.ScalingLists8x8.AsSpan(); + for (int index = 0; index < 2; index++) { writer.WriteBit(true); - WriteScalingList(ref writer, pictureInfo.ScalingLists8x8[index]); + WriteScalingList(ref writer, scalingLists8x8Span[index]); } } } @@ -144,9 +148,11 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.H264 int lastScale = 8; - for (int index = 0; index < list.Length; index++) + Span listSpan = list.AsSpan(); + + for (int index = 0; index < listSpan.Length; index++) { - byte value = list[scan[index]]; + byte value = listSpan[scan[index]]; int deltaScale = value - lastScale; diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs index 541f29e96..39498f0a2 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeFrame.cs @@ -21,49 +21,67 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static void ReadTxModeProbs(ref Vp9EntropyProbs txProbs, ref Reader r) { + Span> tx8x8ProbSpan1 = txProbs.Tx8x8Prob.AsSpan(); + Span> tx16x16ProbSpan1 = txProbs.Tx16x16Prob.AsSpan(); + Span> tx32x32ProbSpan1 = txProbs.Tx32x32Prob.AsSpan(); + for (int i = 0; i < EntropyMode.TxSizeContexts; ++i) { + Span tx8x8ProbSpan2 = tx8x8ProbSpan1[i].AsSpan(); + for (int j = 0; j < (int)TxSize.TxSizes - 3; ++j) { - r.DiffUpdateProb(ref txProbs.Tx8x8Prob[i][j]); + r.DiffUpdateProb(ref tx8x8ProbSpan2[j]); } } for (int i = 0; i < EntropyMode.TxSizeContexts; ++i) { + Span tx16x16ProbSpan2 = tx16x16ProbSpan1[i].AsSpan(); + for (int j = 0; j < (int)TxSize.TxSizes - 2; ++j) { - r.DiffUpdateProb(ref txProbs.Tx16x16Prob[i][j]); + r.DiffUpdateProb(ref tx16x16ProbSpan2[j]); } } for (int i = 0; i < EntropyMode.TxSizeContexts; ++i) { + Span tx32x32ProbSpan2 = tx32x32ProbSpan1[i].AsSpan(); + for (int j = 0; j < (int)TxSize.TxSizes - 1; ++j) { - r.DiffUpdateProb(ref txProbs.Tx32x32Prob[i][j]); + r.DiffUpdateProb(ref tx32x32ProbSpan2[j]); } } } private static void ReadSwitchableInterpProbs(ref Vp9EntropyProbs fc, ref Reader r) { - for (int j = 0; j < Constants.SwitchableFilterContexts; ++j) + Span> switchableInterpProbSpan1 = fc.SwitchableInterpProb.AsSpan(); + + for (int i = 0; i < Constants.SwitchableFilterContexts; ++i) { - for (int i = 0; i < Constants.SwitchableFilters - 1; ++i) + Span switchableInterpProbSpan2 = switchableInterpProbSpan1[i].AsSpan(); + + for (int j = 0; j < Constants.SwitchableFilters - 1; ++j) { - r.DiffUpdateProb(ref fc.SwitchableInterpProb[j][i]); + r.DiffUpdateProb(ref switchableInterpProbSpan2[j]); } } } private static void ReadInterModeProbs(ref Vp9EntropyProbs fc, ref Reader r) { + Span> interModeProbSpan1 = fc.InterModeProb.AsSpan(); + for (int i = 0; i < Constants.InterModeContexts; ++i) { + Span interModeProbSpan2 = interModeProbSpan1[i].AsSpan(); + for (int j = 0; j < Constants.InterModes - 1; ++j) { - r.DiffUpdateProb(ref fc.InterModeProb[i][j]); + r.DiffUpdateProb(ref interModeProbSpan2[j]); } } } @@ -72,30 +90,43 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { r.UpdateMvProbs(ctx.Joints.AsSpan(), EntropyMv.Joints - 1); - for (int i = 0; i < 2; ++i) - { - r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref ctx.Sign[i], 1), 1); - r.UpdateMvProbs(ctx.Classes[i].AsSpan(), EntropyMv.Classes - 1); - r.UpdateMvProbs(ctx.Class0[i].AsSpan(), EntropyMv.Class0Size - 1); - r.UpdateMvProbs(ctx.Bits[i].AsSpan(), EntropyMv.OffsetBits); - } + Span signSpan = ctx.Sign.AsSpan(); + Span> classesSpan = ctx.Classes.AsSpan(); + Span> class0Span = ctx.Class0.AsSpan(); + Span> bitsSpan = ctx.Bits.AsSpan(); for (int i = 0; i < 2; ++i) { + r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref signSpan[i], 1), 1); + r.UpdateMvProbs(classesSpan[i].AsSpan(), EntropyMv.Classes - 1); + r.UpdateMvProbs(class0Span[i].AsSpan(), EntropyMv.Class0Size - 1); + r.UpdateMvProbs(bitsSpan[i].AsSpan(), EntropyMv.OffsetBits); + } + + Span>> class0FpSpan1 = ctx.Class0Fp.AsSpan(); + Span> fpSpan = ctx.Fp.AsSpan(); + + for (int i = 0; i < 2; ++i) + { + Span> class0FpSpan2 = class0FpSpan1[i].AsSpan(); + for (int j = 0; j < EntropyMv.Class0Size; ++j) { - r.UpdateMvProbs(ctx.Class0Fp[i][j].AsSpan(), EntropyMv.FpSize - 1); + r.UpdateMvProbs(class0FpSpan2[j].AsSpan(), EntropyMv.FpSize - 1); } - r.UpdateMvProbs(ctx.Fp[i].AsSpan(), 3); + r.UpdateMvProbs(fpSpan[i].AsSpan(), 3); } if (allowHp) { + Span class0HpSpan = ctx.Class0Hp.AsSpan(); + Span hpSpan = ctx.Hp.AsSpan(); + for (int i = 0; i < 2; ++i) { - r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref ctx.Class0Hp[i], 1), 1); - r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref ctx.Hp[i], 1), 1); + r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref class0HpSpan[i], 1), 1); + r.UpdateMvProbs(MemoryMarshal.CreateSpan(ref hpSpan[i], 1), 1); } } } @@ -751,10 +782,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int refr; bool isScaled; + Span refFrameSpan = mi.RefFrame.AsSpan(); + Span mvSpan = mi.Mv.AsSpan(); + Span frameRefsSpan = cm.FrameRefs.AsSpan(); + Span> blockRefsSpan = xd.BlockRefs.AsSpan(); + Span planeSpan = xd.Plane.AsSpan(); + for (refr = 0; refr < 1 + isCompound; ++refr) { - int frame = mi.RefFrame[refr]; - ref RefBuffer refBuf = ref cm.FrameRefs[frame - Constants.LastFrame]; + int frame = refFrameSpan[refr]; + ref RefBuffer refBuf = ref frameRefsSpan[frame - Constants.LastFrame]; ref ScaleFactors sf = ref refBuf.Sf; ref Surface refFrameBuf = ref refBuf.Buf; @@ -767,13 +804,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 isScaled = sf.IsScaled(); ReconInter.SetupPrePlanes(ref xd, refr, ref refFrameBuf, miRow, miCol, isScaled ? new Ptr(ref sf) : Ptr.Null); - xd.BlockRefs[refr] = new Ptr(ref refBuf); + blockRefsSpan[refr] = new Ptr(ref refBuf); if (sbType < BlockSize.Block8X8) { for (plane = 0; plane < Constants.MaxMbPlane; ++plane) { - ref MacroBlockDPlane pd = ref xd.Plane[plane]; + ref MacroBlockDPlane pd = ref planeSpan[plane]; ref Buf2D dstBuf = ref pd.Dst; int num4X4W = pd.N4W; int num4X4H = pd.N4H; @@ -811,10 +848,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } else { - Mv mv = mi.Mv[refr]; + Mv mv = mvSpan[refr]; for (plane = 0; plane < Constants.MaxMbPlane; ++plane) { - ref MacroBlockDPlane pd = ref xd.Plane[plane]; + ref MacroBlockDPlane pd = ref planeSpan[plane]; ref Buf2D dstBuf = ref pd.Dst; int num4X4W = pd.N4W; int num4X4H = pd.N4H; @@ -847,12 +884,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static void SetPlaneN4(ref MacroBlockD xd, int bw, int bh, int bwl, int bhl) { + Span planeSpan = xd.Plane.AsSpan(); + for (int i = 0; i < Constants.MaxMbPlane; i++) { - xd.Plane[i].N4W = (ushort)((bw << 1) >> xd.Plane[i].SubsamplingX); - xd.Plane[i].N4H = (ushort)((bh << 1) >> xd.Plane[i].SubsamplingY); - xd.Plane[i].N4Wl = (byte)(bwl - xd.Plane[i].SubsamplingX); - xd.Plane[i].N4Hl = (byte)(bhl - xd.Plane[i].SubsamplingY); + planeSpan[i].N4W = (ushort)((bw << 1) >> planeSpan[i].SubsamplingX); + planeSpan[i].N4H = (ushort)((bh << 1) >> planeSpan[i].SubsamplingY); + planeSpan[i].N4Wl = (byte)(bwl - planeSpan[i].SubsamplingX); + planeSpan[i].N4Hl = (byte)(bhl - planeSpan[i].SubsamplingY); } } @@ -892,7 +931,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // as they are always compared to values that are in 1/8th pel units xd.SetMiRowCol(ref tile, miRow, bh, miCol, bw, cm.MiRows, cm.MiCols); - ReconInter.SetupDstPlanes(ref xd.Plane, ref xd.CurBuf, miRow, miCol); + ReconInter.SetupDstPlanes(xd.Plane.AsSpan(), ref xd.CurBuf, miRow, miCol); return ref xd.Mi[0].Value; } @@ -933,10 +972,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (!mi.IsInterBlock()) { - int plane; - for (plane = 0; plane < Constants.MaxMbPlane; ++plane) + Span planeSpan = xd.Plane.AsSpan(); + + for (int plane = 0; plane < Constants.MaxMbPlane; ++plane) { - ref MacroBlockDPlane pd = ref xd.Plane[plane]; + ref MacroBlockDPlane pd = ref planeSpan[plane]; TxSize txSize = plane != 0 ? mi.GetUvTxSize(ref pd) : mi.TxSize; int num4X4W = pd.N4W; int num4X4H = pd.N4H; @@ -967,12 +1007,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Reconstruction if (mi.Skip == 0) { + Span planeSpan = xd.Plane.AsSpan(); + int eobtotal = 0; - int plane; - for (plane = 0; plane < Constants.MaxMbPlane; ++plane) + for (int plane = 0; plane < Constants.MaxMbPlane; ++plane) { - ref MacroBlockDPlane pd = ref xd.Plane[plane]; + ref MacroBlockDPlane pd = ref planeSpan[plane]; TxSize txSize = plane != 0 ? mi.GetUvTxSize(ref pd) : mi.TxSize; int num4X4W = pd.N4W; int num4X4H = pd.N4H; @@ -1159,22 +1200,30 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - private static void ReadCoefProbsCommon(ref Array2>>>> coefProbs, + private static void ReadCoefProbsCommon(ReadOnlySpan>>>> coefProbs1, ref Reader r, int txSize) { if (r.ReadBit() != 0) { for (int i = 0; i < Constants.PlaneTypes; ++i) { + Span>>> coefProbs2 = coefProbs1[i].AsSpan(); + for (int j = 0; j < Entropy.RefTypes; ++j) { + Span>> coefProbs3 = coefProbs2[j].AsSpan(); + for (int k = 0; k < Entropy.CoefBands; ++k) { + Span> coefProbs4 = coefProbs3[k].AsSpan(); + for (int l = 0; l < Entropy.BAND_COEFF_CONTEXTS(k); ++l) { + Span coefProbs5 = coefProbs4[l].AsSpan(); + for (int m = 0; m < Entropy.UnconstrainedNodes; ++m) { - r.DiffUpdateProb(ref coefProbs[i][j][k][l][m]); + r.DiffUpdateProb(ref coefProbs5[m]); } } } @@ -1185,10 +1234,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static void ReadCoefProbs(ref Vp9EntropyProbs fc, TxMode txMode, ref Reader r) { + Span>>>>> coefProbsSpan = fc.CoefProbs.AsSpan(); + int maxTxSize = (int)Luts.TxModeToBiggestTxSize[(int)txMode]; + for (int txSize = (int)TxSize.Tx4X4; txSize <= maxTxSize; ++txSize) { - ReadCoefProbsCommon(ref fc.CoefProbs[txSize], ref r, txSize); + ReadCoefProbsCommon(coefProbsSpan[txSize].AsSpan(), ref r, txSize); } } @@ -1207,11 +1259,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 lf.ModeRefDeltaUpdate = rb.ReadBit() != 0; if (lf.ModeRefDeltaUpdate) { + Span refDeltasSpan = lf.RefDeltas.AsSpan(); + Span modeDeltasSpan = lf.ModeDeltas.AsSpan(); + for (int i = 0; i < LoopFilter.MaxRefLfDeltas; i++) { if (rb.ReadBit() != 0) { - lf.RefDeltas[i] = (sbyte)rb.ReadSignedLiteral(6); + refDeltasSpan[i] = (sbyte)rb.ReadSignedLiteral(6); } } @@ -1219,7 +1274,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (rb.ReadBit() != 0) { - lf.ModeDeltas[i] = (sbyte)rb.ReadSignedLiteral(6); + modeDeltasSpan[i] = (sbyte)rb.ReadSignedLiteral(6); } } } @@ -1267,6 +1322,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 cm.ResizeContextBuffers(allocator, width, height); SetupRenderSize(ref cm, ref rb); + Span frameBuffsSpan = pool.FrameBufs.AsSpan(); + if (cm.GetFrameNewBuffer().ReallocFrameBuffer( allocator, cm.Width, @@ -1276,21 +1333,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 cm.UseHighBitDepth, Surface.DecBorderInPixels, cm.ByteAlignment, - new Ptr(ref pool.FrameBufs[cm.NewFbIdx].RawFrameBuffer), + new Ptr(ref frameBuffsSpan[cm.NewFbIdx].RawFrameBuffer), FrameBuffers.GetFrameBuffer, pool.CbPriv) != 0) { cm.Error.InternalError(CodecErr.MemError, "Failed to allocate frame buffer"); } - pool.FrameBufs[cm.NewFbIdx].Released = 0; - pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX; - pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY; - pool.FrameBufs[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth; - pool.FrameBufs[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace; - pool.FrameBufs[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange; - pool.FrameBufs[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth; - pool.FrameBufs[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight; + frameBuffsSpan[cm.NewFbIdx].Released = 0; + frameBuffsSpan[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX; + frameBuffsSpan[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY; + frameBuffsSpan[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth; + frameBuffsSpan[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace; + frameBuffsSpan[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange; + frameBuffsSpan[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth; + frameBuffsSpan[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight; } private static bool ValidRefFrameImgFmt( @@ -1311,13 +1368,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 bool hasValidRefFrame = false; ref BufferPool pool = ref cm.BufferPool.Value; + Span frameRefsSpan = cm.FrameRefs.AsSpan(); + for (int i = 0; i < Constants.RefsPerFrame; ++i) { if (rb.ReadBit() != 0) { - if (cm.FrameRefs[i].Idx != RefBuffer.InvalidIdx) + if (frameRefsSpan[i].Idx != RefBuffer.InvalidIdx) { - ref Surface buf = ref cm.FrameRefs[i].Buf; + ref Surface buf = ref frameRefsSpan[i].Buf; width = buf.YCropWidth; height = buf.YCropHeight; found = true; @@ -1342,7 +1401,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // has valid dimensions. for (int i = 0; i < Constants.RefsPerFrame; ++i) { - ref RefBuffer refFrame = ref cm.FrameRefs[i]; + ref RefBuffer refFrame = ref frameRefsSpan[i]; hasValidRefFrame |= refFrame.Idx != RefBuffer.InvalidIdx && ScaleFactors.ValidRefFrameSize(refFrame.Buf.YCropWidth, refFrame.Buf.YCropHeight, width, @@ -1356,7 +1415,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (int i = 0; i < Constants.RefsPerFrame; ++i) { - ref RefBuffer refFrame = ref cm.FrameRefs[i]; + ref RefBuffer refFrame = ref frameRefsSpan[i]; if (refFrame.Idx == RefBuffer.InvalidIdx || !ValidRefFrameImgFmt( (BitDepth)refFrame.Buf.BitDepth, @@ -1373,6 +1432,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 cm.ResizeContextBuffers(allocator, width, height); SetupRenderSize(ref cm, ref rb); + + Span frameBuffsSpan = pool.FrameBufs.AsSpan(); if (cm.GetFrameNewBuffer().ReallocFrameBuffer( allocator, @@ -1383,21 +1444,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 cm.UseHighBitDepth, Surface.DecBorderInPixels, cm.ByteAlignment, - new Ptr(ref pool.FrameBufs[cm.NewFbIdx].RawFrameBuffer), + new Ptr(ref frameBuffsSpan[cm.NewFbIdx].RawFrameBuffer), FrameBuffers.GetFrameBuffer, pool.CbPriv) != 0) { cm.Error.InternalError(CodecErr.MemError, "Failed to allocate frame buffer"); } - pool.FrameBufs[cm.NewFbIdx].Released = 0; - pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX; - pool.FrameBufs[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY; - pool.FrameBufs[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth; - pool.FrameBufs[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace; - pool.FrameBufs[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange; - pool.FrameBufs[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth; - pool.FrameBufs[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight; + frameBuffsSpan[cm.NewFbIdx].Released = 0; + frameBuffsSpan[cm.NewFbIdx].Buf.SubsamplingX = cm.SubsamplingX; + frameBuffsSpan[cm.NewFbIdx].Buf.SubsamplingY = cm.SubsamplingY; + frameBuffsSpan[cm.NewFbIdx].Buf.BitDepth = (uint)cm.BitDepth; + frameBuffsSpan[cm.NewFbIdx].Buf.ColorSpace = cm.ColorSpace; + frameBuffsSpan[cm.NewFbIdx].Buf.ColorRange = cm.ColorRange; + frameBuffsSpan[cm.NewFbIdx].Buf.RenderWidth = cm.RenderWidth; + frameBuffsSpan[cm.NewFbIdx].Buf.RenderHeight = cm.RenderHeight; } // Reads the next tile returning its size and adjusting '*data' accordingly @@ -1437,7 +1498,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } private static void GetTileBuffers(ref Vp9Common cm, ArrayPtr data, int tileCols, - ref Array64 tileBuffers) + Span tileBuffers) { for (int c = 0; c < tileCols; ++c) { @@ -1453,14 +1514,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ArrayPtr data, int tileCols, int tileRows, - ref Array4> tileBuffers) + Span> tileBuffers1) { for (int r = 0; r < tileRows; ++r) { + Span tileBuffers2 = tileBuffers1[r].AsSpan(); + for (int c = 0; c < tileCols; ++c) { bool isLast = r == tileRows - 1 && c == tileCols - 1; - ref TileBuffer buf = ref tileBuffers[r][c]; + ref TileBuffer buf = ref tileBuffers2[c]; GetTileBuffer(isLast, ref cm.Error, ref data, ref buf); } } @@ -1484,15 +1547,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 MemoryUtil.Fill(cm.AboveSegContext.ToPointer(), (sbyte)0, alignedCols); LoopFilter.ResetLfm(ref cm); + + Span> tileBuffers1 = tileBuffers.AsSpan(); + Span tileWorkerDataSpan = cm.TileWorkerData.AsSpan(); - GetTileBuffers(ref cm, data, tileCols, tileRows, ref tileBuffers); + GetTileBuffers(ref cm, data, tileCols, tileRows, tileBuffers1); // Load all tile information into tile_data. for (tileRow = 0; tileRow < tileRows; ++tileRow) { + Span tileBuffers2 = tileBuffers1[tileRow].AsSpan(); + for (tileCol = 0; tileCol < tileCols; ++tileCol) { - ref TileBuffer buf = ref tileBuffers[tileRow][tileCol]; - ref TileWorkerData tileData = ref cm.TileWorkerData[(tileCols * tileRow) + tileCol]; + ref TileBuffer buf = ref tileBuffers2[tileCol]; + ref TileWorkerData tileData = ref tileWorkerDataSpan[(tileCols * tileRow) + tileCol]; tileData.Xd = cm.Mb; tileData.Xd.Corrupted = false; tileData.Xd.Counts = cm.Counts; @@ -1512,7 +1580,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (tileCol = 0; tileCol < tileCols; ++tileCol) { int col = tileCol; - ref TileWorkerData tileData = ref cm.TileWorkerData[(tileCols * tileRow) + col]; + ref TileWorkerData tileData = ref tileWorkerDataSpan[(tileCols * tileRow) + col]; tile.SetCol(ref cm, col); tileData.Xd.LeftContext = new Array3>(); tileData.Xd.LeftSegContext = new Array8(); @@ -1531,11 +1599,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } // Get last tile data. - return cm.TileWorkerData[(tileCols * tileRows) - 1].BitReader.FindEnd(); + return tileWorkerDataSpan[(tileCols * tileRows) - 1].BitReader.FindEnd(); } private static bool DecodeTileCol(ref TileWorkerData tileData, ref Vp9Common cm, - ref Array64 tileBuffers) + Span tileBuffers) { ref TileInfo tile = ref tileData.Xd.Tile; int finalCol = (1 << cm.Log2TileCols) - 1; @@ -1593,10 +1661,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 cm.AboveContext.AsSpan().Clear(); cm.AboveSegContext.AsSpan().Clear(); + + Span tileWorkerDataSpan = cm.TileWorkerData.AsSpan(); for (n = 0; n < numWorkers; ++n) { - ref TileWorkerData tileData = ref cm.TileWorkerData[n + totalTiles]; + ref TileWorkerData tileData = ref tileWorkerDataSpan[n + totalTiles]; tileData.Xd = cm.Mb; tileData.Xd.Counts = new Ptr(ref tileData.Counts); @@ -1604,17 +1674,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } Array64 tileBuffers = new(); + Span tileBuffersSpan = tileBuffers.AsSpan(); - GetTileBuffers(ref cm, data, tileCols, ref tileBuffers); + GetTileBuffers(ref cm, data, tileCols, tileBuffersSpan); - tileBuffers.AsSpan()[..tileCols].Sort(CompareTileBuffers); + tileBuffersSpan[..tileCols].Sort(CompareTileBuffers); if (numWorkers == tileCols) { - TileBuffer largest = tileBuffers[0]; - Span buffers = tileBuffers.AsSpan(); - buffers[1..].CopyTo(buffers[..(tileBuffers.Length - 1)]); - tileBuffers[tileCols - 1] = largest; + TileBuffer largest = tileBuffersSpan[0]; + tileBuffersSpan[1..].CopyTo(tileBuffersSpan[..^1]); + tileBuffersSpan[tileCols - 1] = largest; } else { @@ -1625,9 +1695,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // larger tile implies it is more difficult to decode. while (start < end) { - tmp = tileBuffers[start]; - tileBuffers[start] = tileBuffers[end]; - tileBuffers[end] = tmp; + tmp = tileBuffersSpan[start]; + tileBuffersSpan[start] = tileBuffersSpan[end]; + tileBuffersSpan[end] = tmp; start += 2; end -= 2; } @@ -1640,7 +1710,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (n = 0; n < numWorkers; ++n) { int count = baseVal + ((remain + n) / numWorkers); - ref TileWorkerData tileData = ref cm.TileWorkerData[n + totalTiles]; + ref TileWorkerData tileData = ref tileWorkerDataSpan[n + totalTiles]; tileData.BufStart = bufStart; tileData.BufEnd = bufStart + count - 1; @@ -1654,7 +1724,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { ref TileWorkerData tileData = ref cmPtr.Value.TileWorkerData[n + totalTiles]; - if (!DecodeTileCol(ref tileData, ref cmPtr.Value, ref tileBuffers)) + if (!DecodeTileCol(ref tileData, ref cmPtr.Value, tileBuffers.AsSpan())) { cmPtr.Value.Mb.Corrupted = true; } @@ -1664,14 +1734,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (bitReaderEnd.IsNull) { - ref TileWorkerData tileData = ref cm.TileWorkerData[n - 1 + totalTiles]; + ref TileWorkerData tileData = ref tileWorkerDataSpan[n - 1 + totalTiles]; bitReaderEnd = tileData.DataEnd; } } for (n = 0; n < numWorkers; ++n) { - ref TileWorkerData tileData = ref cm.TileWorkerData[n + totalTiles]; + ref TileWorkerData tileData = ref tileWorkerDataSpan[n + totalTiles]; AccumulateFrameCounts(ref cm.Counts.Value, ref tileData.Counts); } @@ -1705,7 +1775,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (cm.FrameType == FrameType.KeyFrame && cm.CurrentVideoFrame > 0) { - ref Array12 frameBufs = ref cm.BufferPool.Value.FrameBufs; + Span frameBuffs = cm.BufferPool.Value.FrameBufs.AsSpan(); ref BufferPool pool = ref cm.BufferPool.Value; for (int i = 0; i < Constants.FrameBuffers; ++i) @@ -1715,11 +1785,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 continue; } - frameBufs[i].RefCount = 0; - if (frameBufs[i].Released == 0) + frameBuffs[i].RefCount = 0; + if (frameBuffs[i].Released == 0) { - FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBufs[i].RawFrameBuffer); - frameBufs[i].Released = 1; + FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBuffs[i].RawFrameBuffer); + frameBuffs[i].Released = 1; } } } @@ -1742,7 +1812,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 readSyncCode2 == SyncCode2; } - private static void RefCntFb(ref Array12 bufs, ref int idx, int newIdx) + private static void RefCntFb(Span bufs, ref int idx, int newIdx) { int refIndex = idx; @@ -1761,13 +1831,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { ref Vp9Common cm = ref pbi.Common; ref BufferPool pool = ref cm.BufferPool.Value; - ref Array12 frameBufs = ref pool.FrameBufs; + Span frameBuffs = pool.FrameBufs.AsSpan(); int mask, refIndex = 0; ulong sz; cm.LastFrameType = cm.FrameType; cm.LastIntraOnly = cm.IntraOnly; + Span refFrameSpan = cm.RefFrameMap.AsSpan(); + if (rb.ReadLiteral(2) != FrameMarker) { cm.Error.InternalError(CodecErr.UnsupBitstream, "Invalid frame marker"); @@ -1783,14 +1855,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (cm.ShowExistingFrame != 0) { // Show an existing frame directly. - int frameToShow = cm.RefFrameMap[rb.ReadLiteral(3)]; - if (frameToShow < 0 || frameBufs[frameToShow].RefCount < 1) + int frameToShow = refFrameSpan[rb.ReadLiteral(3)]; + if (frameToShow < 0 || frameBuffs[frameToShow].RefCount < 1) { cm.Error.InternalError(CodecErr.UnsupBitstream, $"Buffer {frameToShow} does not contain a decoded frame"); } - RefCntFb(ref frameBufs, ref cm.NewFbIdx, frameToShow); + RefCntFb(frameBuffs, ref cm.NewFbIdx, frameToShow); pbi.RefreshFrameFlags = 0; cm.Lf.FilterLevel = 0; cm.ShowFrame = 1; @@ -1812,16 +1884,18 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 cm.ReadBitdepthColorspaceSampling(ref rb); pbi.RefreshFrameFlags = (1 << Constants.RefFrames) - 1; + Span frameRefsSpan = cm.FrameRefs.AsSpan(); + for (int i = 0; i < Constants.RefsPerFrame; ++i) { - cm.FrameRefs[i].Idx = RefBuffer.InvalidIdx; - cm.FrameRefs[i].Buf = default; + frameRefsSpan[i].Idx = RefBuffer.InvalidIdx; + frameRefsSpan[i].Buf = default; } SetupFrameSize(allocator, ref cm, ref rb); if (pbi.NeedResync != 0) { - cm.RefFrameMap.AsSpan().Fill(-1); + refFrameSpan.Fill(-1); FlushAllFbOnKey(ref cm); pbi.NeedResync = 0; } @@ -1860,22 +1934,25 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 SetupFrameSize(allocator, ref cm, ref rb); if (pbi.NeedResync != 0) { - cm.RefFrameMap.AsSpan().Fill(-1); + refFrameSpan.Fill(-1); pbi.NeedResync = 0; } } else if (pbi.NeedResync != 1) { + Span frameRefsSpan = cm.FrameRefs.AsSpan(); + Span refFrameSignBiasSpan = cm.RefFrameSignBias.AsSpan(); + /* Skip if need resync */ pbi.RefreshFrameFlags = rb.ReadLiteral(Constants.RefFrames); for (int i = 0; i < Constants.RefsPerFrame; ++i) { int refr = rb.ReadLiteral(Constants.RefFramesLog2); - int idx = cm.RefFrameMap[refr]; - ref RefBuffer refFrame = ref cm.FrameRefs[i]; + int idx = refFrameSpan[refr]; + ref RefBuffer refFrame = ref frameRefsSpan[i]; refFrame.Idx = idx; - refFrame.Buf = frameBufs[idx].Buf; - cm.RefFrameSignBias[Constants.LastFrame + i] = (sbyte)rb.ReadBit(); + refFrame.Buf = frameBuffs[idx].Buf; + refFrameSignBiasSpan[Constants.LastFrame + i] = (sbyte)rb.ReadBit(); } SetupFrameSizeWithRefs(allocator, ref cm, ref rb); @@ -1885,7 +1962,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (int i = 0; i < Constants.RefsPerFrame; ++i) { - ref RefBuffer refBuf = ref cm.FrameRefs[i]; + ref RefBuffer refBuf = ref frameRefsSpan[i]; refBuf.Sf.SetupScaleFactorsForFrame( refBuf.Buf.YCropWidth, refBuf.Buf.YCropHeight, @@ -1926,23 +2003,25 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // below, forcing the use of context 0 for those frame types. cm.FrameContextIdx = (uint)rb.ReadLiteral(Constants.FrameContextsLog2); + Span nextRefFrameMapSpan = cm.NextRefFrameMap.AsSpan(); + // Generate next_ref_frame_map. for (mask = pbi.RefreshFrameFlags; mask != 0; mask >>= 1) { if ((mask & 1) != 0) { - cm.NextRefFrameMap[refIndex] = cm.NewFbIdx; - ++frameBufs[cm.NewFbIdx].RefCount; + nextRefFrameMapSpan[refIndex] = cm.NewFbIdx; + ++frameBuffs[cm.NewFbIdx].RefCount; } else { - cm.NextRefFrameMap[refIndex] = cm.RefFrameMap[refIndex]; + nextRefFrameMapSpan[refIndex] = cm.RefFrameMap[refIndex]; } // Current thread holds the reference frame. if (cm.RefFrameMap[refIndex] >= 0) { - ++frameBufs[cm.RefFrameMap[refIndex]].RefCount; + ++frameBuffs[cm.RefFrameMap[refIndex]].RefCount; } ++refIndex; @@ -1950,11 +2029,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (; refIndex < Constants.RefFrames; ++refIndex) { - cm.NextRefFrameMap[refIndex] = cm.RefFrameMap[refIndex]; + nextRefFrameMapSpan[refIndex] = refFrameSpan[refIndex]; // Current thread holds the reference frame. - if (cm.RefFrameMap[refIndex] >= 0) + if (refFrameSpan[refIndex] >= 0) { - ++frameBufs[cm.RefFrameMap[refIndex]].RefCount; + ++frameBuffs[refFrameSpan[refIndex]].RefCount; } } @@ -2001,9 +2080,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ReadCoefProbs(ref fc, cm.TxMode, ref r); + Span skipProbSpan = fc.SkipProb.AsSpan(); + for (int k = 0; k < Constants.SkipContexts; ++k) { - r.DiffUpdateProb(ref fc.SkipProb[k]); + r.DiffUpdateProb(ref skipProbSpan[k]); } if (!cm.FrameIsIntraOnly()) @@ -2014,10 +2095,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { ReadSwitchableInterpProbs(ref fc, ref r); } + + Span intraInterProbSpan = fc.IntraInterProb.AsSpan(); for (int i = 0; i < Constants.IntraInterContexts; i++) { - r.DiffUpdateProb(ref fc.IntraInterProb[i]); + r.DiffUpdateProb(ref intraInterProbSpan[i]); } cm.ReferenceMode = cm.ReadFrameReferenceMode(ref r); @@ -2027,20 +2110,28 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } cm.ReadFrameReferenceModeProbs(ref r); + + Span> yModeProbSpan1 = fc.YModeProb.AsSpan(); for (int j = 0; j < EntropyMode.BlockSizeGroups; j++) { + Span yModeProbSpan2 = yModeProbSpan1[j].AsSpan(); + for (int i = 0; i < Constants.IntraModes - 1; ++i) { - r.DiffUpdateProb(ref fc.YModeProb[j][i]); + r.DiffUpdateProb(ref yModeProbSpan2[i]); } } + + Span> partitionProbSpan1 = fc.PartitionProb.AsSpan(); for (int j = 0; j < Constants.PartitionContexts; ++j) { + Span partitionProbSpan2 = partitionProbSpan1[j].AsSpan(); + for (int i = 0; i < Constants.PartitionTypes - 1; ++i) { - r.DiffUpdateProb(ref fc.PartitionProb[j][i]); + r.DiffUpdateProb(ref partitionProbSpan2[i]); } } @@ -2098,7 +2189,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 xd.SetupBlockPlanes(cm.SubsamplingX, cm.SubsamplingY); - cm.Fc = new Ptr(ref cm.FrameContexts[(int)cm.FrameContextIdx]); + Span frameContextsSpan = cm.FrameContexts.AsSpan(); + + cm.Fc = new Ptr(ref frameContextsSpan[(int)cm.FrameContextIdx]); xd.Corrupted = false; newFb.Corrupted = ReadCompressedHeader(ref pbi, data, firstPartitionSize) ? 1 : 0; @@ -2167,7 +2260,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Non frame parallel update frame context here. if (cm.RefreshFrameContext != 0 && contextUpdated == 0) { - cm.FrameContexts[(int)cm.FrameContextIdx] = cm.Fc.Value; + frameContextsSpan[(int)cm.FrameContextIdx] = cm.Fc.Value; } } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs index b2c8d494d..68a33b821 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/DecodeMv.cs @@ -50,9 +50,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return PredictionMode.NearestMv + mode; } - private static int ReadSegmentId(ref Reader r, ref Array7 segTreeProbs) + private static int ReadSegmentId(ref Reader r, ReadOnlySpan segTreeProbs) { - return r.ReadTree(Luts.SegmentTree, segTreeProbs.AsSpan()); + return r.ReadTree(Luts.SegmentTree, segTreeProbs); } private static ReadOnlySpan GetTxProbs(ref Vp9EntropyProbs fc, TxSize maxTxSize, int ctx) @@ -187,7 +187,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return 0; } - segmentId = ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb); + segmentId = ReadSegmentId(ref r, cm.Fc.Value.SegTreeProb.AsSpan()); SetSegmentId(ref cm, miOffset, xMis, yMis, segmentId); return segmentId; } @@ -223,15 +223,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (seg.TemporalUpdate) { - byte predProb = Segmentation.GetPredProbSegId(ref cm.Fc.Value.SegPredProb, ref xd); + byte predProb = Segmentation.GetPredProbSegId(cm.Fc.Value.SegPredProb.AsSpan(), ref xd); mi.SegIdPredicted = (sbyte)r.Read(predProb); segmentId = mi.SegIdPredicted != 0 ? predictedSegmentId - : ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb); + : ReadSegmentId(ref r, cm.Fc.Value.SegTreeProb.AsSpan()); } else { - segmentId = ReadSegmentId(ref r, ref cm.Fc.Value.SegTreeProb); + segmentId = ReadSegmentId(ref r, cm.Fc.Value.SegTreeProb.AsSpan()); } SetSegmentId(ref cm, miOffset, xMis, yMis, segmentId); @@ -272,10 +272,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { int n = (int)mvClass + Constants.Class0Bits - 1; // Number of bits + Span bitsSpan = fc.Bits[mvcomp].AsSpan(); + d = 0; for (int i = 0; i < n; ++i) { - d |= r.Read(fc.Bits[mvcomp][i]) << i; + d |= r.Read(bitsSpan[i]) << i; } mag = Constants.Class0Size << ((int)mvClass + 2); @@ -343,7 +345,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref MacroBlockD xd, ref Reader r, int segmentId, - ref Array2 refFrame) + Span refFrame) { ref Vp9EntropyProbs fc = ref cm.Fc.Value; @@ -418,23 +420,25 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { BlockSize bsize = mi.SbType; + Span bmiSpan = mi.Bmi.AsSpan(); + switch (bsize) { case BlockSize.Block4X4: for (int i = 0; i < 4; ++i) { - mi.Bmi[i].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); + bmiSpan[i].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); } - mi.Mode = mi.Bmi[3].Mode; + mi.Mode = bmiSpan[3].Mode; break; case BlockSize.Block4X8: - mi.Bmi[0].Mode = mi.Bmi[2].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); - mi.Bmi[1].Mode = mi.Bmi[3].Mode = mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); + bmiSpan[0].Mode = bmiSpan[2].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); + bmiSpan[1].Mode = bmiSpan[3].Mode = mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); break; case BlockSize.Block8X4: - mi.Bmi[0].Mode = mi.Bmi[1].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); - mi.Bmi[2].Mode = mi.Bmi[3].Mode = mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); + bmiSpan[0].Mode = bmiSpan[1].Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); + bmiSpan[2].Mode = bmiSpan[3].Mode = mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, 0); break; default: mi.Mode = ReadIntraModeY(ref cm, ref xd, ref r, Luts.SizeGroupLookup[(int)bsize]); @@ -447,29 +451,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // modes in GetPredContextSwitchableInterp() mi.InterpFilter = Constants.SwitchableFilters; - mi.RefFrame[0] = Constants.IntraFrame; - mi.RefFrame[1] = Constants.None; - } - - private static void CopyPair(ref Array2 dst, ref Array2 src) - { - dst[0] = src[0]; - dst[1] = src[1]; - } - - private static void ZeroPair(ref Array2 dst) - { - dst[0] = new Mv(); - dst[1] = new Mv(); + Span refFrameSpan = mi.RefFrame.AsSpan(); + + refFrameSpan[0] = Constants.IntraFrame; + refFrameSpan[1] = Constants.None; } private static bool Assign( ref Vp9Common cm, ref MacroBlockD xd, PredictionMode mode, - ref Array2 mv, - ref Array2 refMv, - ref Array2 nearNearestMv, + Span mv, + Span refMv, + Span nearNearestMv, int isCompound, bool allowHp, ref Reader r) @@ -491,12 +485,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 case PredictionMode.NearMv: case PredictionMode.NearestMv: { - CopyPair(ref mv, ref nearNearestMv); + nearNearestMv.CopyTo(mv); break; } case PredictionMode.ZeroMv: { - ZeroPair(ref mv); + mv.Clear(); break; } default: @@ -559,26 +553,29 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static bool IsDiffRefFrameAddEb( ref ModeInfo mbmi, sbyte refFrame, - ref Array4 refSignBias, + Span refSignBias, ref int refmvCount, Span mvRefList, bool earlyBreak) { if (mbmi.IsInterBlock()) { - if (mbmi.RefFrame[0] != refFrame) + Span refFrameSpan = mbmi.RefFrame.AsSpan(); + Span mvSpan = mbmi.Mv.AsSpan(); + + if (refFrameSpan[0] != refFrame) { - if (AddRefListEb(mbmi.ScaleMv(0, refFrame, ref refSignBias), ref refmvCount, mvRefList, + if (AddRefListEb(mbmi.ScaleMv(0, refFrame, refSignBias), ref refmvCount, mvRefList, earlyBreak)) { return true; } } - if (mbmi.HasSecondRef() && mbmi.RefFrame[1] != refFrame && - Unsafe.As(ref mbmi.Mv[1]) != Unsafe.As(ref mbmi.Mv[0])) + if (mbmi.HasSecondRef() && refFrameSpan[1] != refFrame && + Unsafe.As(ref mvSpan[1]) != Unsafe.As(ref mvSpan[0])) { - if (AddRefListEb(mbmi.ScaleMv(1, refFrame, ref refSignBias), ref refmvCount, mvRefList, + if (AddRefListEb(mbmi.ScaleMv(1, refFrame, refSignBias), ref refmvCount, mvRefList, earlyBreak)) { return true; @@ -603,7 +600,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int block, int isSub8X8) { - ref Array4 refSignBias = ref cm.RefFrameSignBias; + Span refSignBias = cm.RefFrameSignBias.AsSpan(); int i, refmvCount = 0; bool differentRefFound = false; Ptr prevFrameMvs = cm.UsePrevFrameMvs @@ -616,7 +613,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Blank the reference vector list mvRefList[..Constants.MaxMvRefCandidates].Clear(); - + i = 0; if (isSub8X8 != 0) { @@ -630,7 +627,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref ModeInfo candidateMi = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value; differentRefFound = true; - if (candidateMi.RefFrame[0] == refFrame) + Span refFrameSpan = candidateMi.RefFrame.AsSpan(); + + if (refFrameSpan[0] == refFrame) { if (AddRefListEb(candidateMi.GetSubBlockMv(0, mvRef.Col, block), ref refmvCount, mvRefList, earlyBreak)) @@ -638,7 +637,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 goto Done; } } - else if (candidateMi.RefFrame[1] == refFrame) + else if (refFrameSpan[1] == refFrame) { if (AddRefListEb(candidateMi.GetSubBlockMv(1, mvRef.Col, block), ref refmvCount, mvRefList, earlyBreak)) @@ -660,17 +659,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { ref ModeInfo candidate = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value; differentRefFound = true; + + Span refFrameSpan = candidate.RefFrame.AsSpan(); + Span mvSpan = candidate.Mv.AsSpan(); - if (candidate.RefFrame[0] == refFrame) + if (refFrameSpan[0] == refFrame) { - if (AddRefListEb(candidate.Mv[0], ref refmvCount, mvRefList, earlyBreak)) + if (AddRefListEb(mvSpan[0], ref refmvCount, mvRefList, earlyBreak)) { goto Done; } } - else if (candidate.RefFrame[1] == refFrame) + else if (refFrameSpan[1] == refFrame) { - if (AddRefListEb(candidate.Mv[1], ref refmvCount, mvRefList, earlyBreak)) + if (AddRefListEb(mvSpan[1], ref refmvCount, mvRefList, earlyBreak)) { goto Done; } @@ -681,16 +683,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Check the last frame's mode and mv info. if (!prevFrameMvs.IsNull) { - if (prevFrameMvs.Value.RefFrame[0] == refFrame) + Span refFrameSpan = prevFrameMvs.Value.RefFrame.AsSpan(); + Span mvSpan = prevFrameMvs.Value.Mv.AsSpan(); + + if (refFrameSpan[0] == refFrame) { - if (AddRefListEb(prevFrameMvs.Value.Mv[0], ref refmvCount, mvRefList, earlyBreak)) + if (AddRefListEb(mvSpan[0], ref refmvCount, mvRefList, earlyBreak)) { goto Done; } } - else if (prevFrameMvs.Value.RefFrame[1] == refFrame) + else if (refFrameSpan[1] == refFrame) { - if (AddRefListEb(prevFrameMvs.Value.Mv[1], ref refmvCount, mvRefList, earlyBreak)) + if (AddRefListEb(mvSpan[1], ref refmvCount, mvRefList, earlyBreak)) { goto Done; } @@ -710,7 +715,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref ModeInfo candidate = ref xd.Mi[mvRef.Col + (mvRef.Row * xd.MiStride)].Value; // If the candidate is Intra we don't want to consider its mv. - if (IsDiffRefFrameAddEb(ref candidate, refFrame, ref refSignBias, ref refmvCount, mvRefList, + if (IsDiffRefFrameAddEb(ref candidate, refFrame, refSignBias, ref refmvCount, mvRefList, earlyBreak)) { goto Done; @@ -722,10 +727,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Since we still don't have a candidate we'll try the last frame. if (!prevFrameMvs.IsNull) { - if (prevFrameMvs.Value.RefFrame[0] != refFrame && prevFrameMvs.Value.RefFrame[0] > Constants.IntraFrame) + Span refFrameSpan = prevFrameMvs.Value.RefFrame.AsSpan(); + Span mvSpan = prevFrameMvs.Value.Mv.AsSpan(); + + if (refFrameSpan[0] != refFrame && refFrameSpan[0] > Constants.IntraFrame) { - Mv mv = prevFrameMvs.Value.Mv[0]; - if (refSignBias[prevFrameMvs.Value.RefFrame[0]] != refSignBias[refFrame]) + Mv mv = mvSpan[0]; + if (refSignBias[refFrameSpan[0]] != refSignBias[refFrame]) { mv.Row *= -1; mv.Col *= -1; @@ -737,13 +745,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } } - if (prevFrameMvs.Value.RefFrame[1] > Constants.IntraFrame && - prevFrameMvs.Value.RefFrame[1] != refFrame && - Unsafe.As(ref prevFrameMvs.Value.Mv[1]) != - Unsafe.As(ref prevFrameMvs.Value.Mv[0])) + if (refFrameSpan[1] > Constants.IntraFrame && + refFrameSpan[1] != refFrame && + Unsafe.As(ref mvSpan[1]) != + Unsafe.As(ref mvSpan[0])) { - Mv mv = prevFrameMvs.Value.Mv[1]; - if (refSignBias[prevFrameMvs.Value.RefFrame[1]] != refSignBias[refFrame]) + Mv mv = mvSpan[1]; + if (refSignBias[refFrameSpan[1]] != refSignBias[refFrame]) { mv.Row *= -1; mv.Col *= -1; @@ -789,7 +797,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { Span mvList = stackalloc Mv[Constants.MaxMvRefCandidates]; ref ModeInfo mi = ref xd.Mi[0].Value; - ref Array4 bmi = ref mi.Bmi; + Span bmi = mi.Bmi.AsSpan(); int refmvCount; Debug.Assert(Constants.MaxMvRefCandidates == 2); @@ -884,11 +892,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 BlockSize bsize = mi.SbType; bool allowHp = cm.AllowHighPrecisionMv; Array2 bestRefMvs = new(); + Span bestRefMvsSpan = bestRefMvs.AsSpan(); int refr, isCompound; byte interModeCtx; Span mvRefSearch = Luts.MvRefBlocks[(int)bsize]; - ReadRefFrames(ref cm, ref xd, ref r, mi.SegmentId, ref mi.RefFrame); + ReadRefFrames(ref cm, ref xd, ref r, mi.SegmentId, mi.RefFrame.AsSpan()); isCompound = mi.HasSecondRef() ? 1 : 0; interModeCtx = GetModeContext(ref cm, ref xd, mvRefSearch, miRow, miCol); @@ -920,16 +929,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (mi.Mode != PredictionMode.ZeroMv) { Span tmpMvs = stackalloc Mv[Constants.MaxMvRefCandidates]; + Span refFrameSpan = mi.RefFrame.AsSpan(); for (refr = 0; refr < 1 + isCompound; ++refr) { - sbyte frame = mi.RefFrame[refr]; + sbyte frame = refFrameSpan[refr]; int refmvCount; refmvCount = DecFindRefs(ref cm, ref xd, mi.Mode, frame, mvRefSearch, tmpMvs, miRow, miCol, -1, 0); - DecFindBestRefs(allowHp, tmpMvs, ref bestRefMvs[refr], refmvCount); + DecFindBestRefs(allowHp, tmpMvs, ref bestRefMvsSpan[refr], refmvCount); } } } @@ -945,10 +955,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int idx, idy; PredictionMode bMode = 0; Array2 bestSub8X8 = new(); + Span bestSub8X8Span = bestSub8X8.AsSpan(); + Span bmiSpan = mi.Bmi.AsSpan(); const uint InvalidMv = 0x80008000; // Initialize the 2nd element as even though it won't be used meaningfully // if isCompound is false. - Unsafe.As(ref bestSub8X8[1]) = InvalidMv; + Unsafe.As(ref bestSub8X8Span[1]) = InvalidMv; for (idy = 0; idy < 2; idy += num4X4H) { for (idx = 0; idx < 2; idx += num4X4W) @@ -961,11 +973,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (refr = 0; refr < 1 + isCompound; ++refr) { AppendSub8X8ForIdx(ref cm, ref xd, mvRefSearch, bMode, j, refr, miRow, miCol, - ref bestSub8X8[refr]); + ref bestSub8X8Span[refr]); } } - if (!Assign(ref cm, ref xd, bMode, ref mi.Bmi[j].Mv, ref bestRefMvs, ref bestSub8X8, + if (!Assign(ref cm, ref xd, bMode, bmiSpan[j].Mv.AsSpan(), bestRefMvs.AsSpan(), bestSub8X8.AsSpan(), isCompound, allowHp, ref r)) { xd.Corrupted |= true; @@ -974,23 +986,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if (num4X4H == 2) { - mi.Bmi[j + 2] = mi.Bmi[j]; + bmiSpan[j + 2] = bmiSpan[j]; } if (num4X4W == 2) { - mi.Bmi[j + 1] = mi.Bmi[j]; + bmiSpan[j + 1] = bmiSpan[j]; } } } mi.Mode = bMode; - - CopyPair(ref mi.Mv, ref mi.Bmi[3].Mv); + bmiSpan[3].Mv.AsSpan().CopyTo(mi.Mv.AsSpan()); } else { - xd.Corrupted |= !Assign(ref cm, ref xd, mi.Mode, ref mi.Mv, ref bestRefMvs, ref bestRefMvs, + xd.Corrupted |= !Assign(ref cm, ref xd, mi.Mode, mi.Mv.AsSpan(), bestRefMvs.AsSpan(), bestRefMvs.AsSpan(), isCompound, allowHp, ref r); } } @@ -1082,33 +1093,42 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int miOffset = (miRow * cm.MiCols) + miCol; + Span refFrameSpan = mi.Value.RefFrame.AsSpan(); + mi.Value.SegmentId = (sbyte)ReadIntraSegmentId(ref cm, miOffset, xMis, yMis, ref r); mi.Value.Skip = (sbyte)ReadSkip(ref cm, ref xd, mi.Value.SegmentId, ref r); mi.Value.TxSize = ReadTxSize(ref cm, ref xd, true, ref r); - mi.Value.RefFrame[0] = Constants.IntraFrame; - mi.Value.RefFrame[1] = Constants.None; + refFrameSpan[0] = Constants.IntraFrame; + refFrameSpan[1] = Constants.None; + Span bmiSpan; switch (bsize) { case BlockSize.Block4X4: + bmiSpan = mi.Value.Bmi.AsSpan(); + for (int i = 0; i < 4; ++i) { - mi.Value.Bmi[i].Mode = + bmiSpan[i].Mode = ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, i)); } - mi.Value.Mode = mi.Value.Bmi[3].Mode; + mi.Value.Mode = bmiSpan[3].Mode; break; case BlockSize.Block4X8: - mi.Value.Bmi[0].Mode = mi.Value.Bmi[2].Mode = + bmiSpan = mi.Value.Bmi.AsSpan(); + + bmiSpan[0].Mode = bmiSpan[2].Mode = ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 0)); - mi.Value.Bmi[1].Mode = mi.Value.Bmi[3].Mode = mi.Value.Mode = + bmiSpan[1].Mode = bmiSpan[3].Mode = mi.Value.Mode = ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 1)); break; case BlockSize.Block8X4: - mi.Value.Bmi[0].Mode = mi.Value.Bmi[1].Mode = + bmiSpan = mi.Value.Bmi.AsSpan(); + + bmiSpan[0].Mode = bmiSpan[1].Mode = ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 0)); - mi.Value.Bmi[2].Mode = mi.Value.Bmi[3].Mode = mi.Value.Mode = + bmiSpan[2].Mode = bmiSpan[3].Mode = mi.Value.Mode = ReadIntraMode(ref r, GetYModeProbs(ref cm.Fc.Value, mi, aboveMi, leftMi, 2)); break; default: @@ -1119,12 +1139,6 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 mi.Value.UvMode = ReadIntraMode(ref r, cm.Fc.Value.KfUvModeProb[(int)mi.Value.Mode].AsSpan()); } - private static void CopyRefFramePair(ref Array2 dst, ref Array2 src) - { - dst[0] = src[0]; - dst[1] = src[1]; - } - public static void ReadModeInfo( ref TileWorkerData twd, ref Vp9Common cm, @@ -1151,8 +1165,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (int w = 0; w < xMis; ++w) { ref MvRef mv = ref frameMvs[w]; - CopyRefFramePair(ref mv.RefFrame, ref mi.RefFrame); - CopyPair(ref mv.Mv, ref mi.Mv); + mi.RefFrame.AsSpan().CopyTo(mv.RefFrame.AsSpan()); + mi.Mv.AsSpan().CopyTo(mv.Mv.AsSpan()); } frameMvs = frameMvs.Slice(cm.MiCols); diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs index f788d57f4..666b82792 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Decoder.cs @@ -89,9 +89,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 cm.Lf.RefDeltas = pictureInfo.RefDeltas; cm.Lf.ModeDeltas = pictureInfo.ModeDeltas; - cm.FrameRefs[0].Buf = (Surface)pictureInfo.LastReference; - cm.FrameRefs[1].Buf = (Surface)pictureInfo.GoldenReference; - cm.FrameRefs[2].Buf = (Surface)pictureInfo.AltReference; + Span frameRefsSpan = cm.FrameRefs.AsSpan(); + frameRefsSpan[0].Buf = (Surface)pictureInfo.LastReference; + frameRefsSpan[1].Buf = (Surface)pictureInfo.GoldenReference; + frameRefsSpan[2].Buf = (Surface)pictureInfo.AltReference; cm.Mb.CurBuf = (Surface)output; cm.Mb.SetupBlockPlanes(1, 1); diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs index 530eec441..88a83b207 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Detokenize.cs @@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 PlaneType type, Span dqcoeff, TxSize txSize, - ref Array2 dq, + ReadOnlySpan dq, int ctx, ReadOnlySpan scan, ReadOnlySpan nb, @@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref Vp9EntropyProbs fc = ref xd.Fc.Value; int refr = xd.Mi[0].Value.IsInterBlock() ? 1 : 0; int band, c = 0; - ref Array6>> coefProbs = ref fc.CoefProbs[(int)txSize][(int)type][refr]; + ReadOnlySpan>> coefProbs = fc.CoefProbs[(int)txSize][(int)type][refr].AsSpan(); Span tokenCache = stackalloc byte[32 * 32]; ReadOnlySpan bandTranslate = Luts.GetBandTranslate(txSize); int dqShift = txSize == TxSize.Tx32X32 ? 1 : 0; @@ -56,23 +56,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ulong value = r.Value; uint range = r.Range; int count = r.Count; + + ReadOnlySpan>> coefSpan = counts.Coef[(int)txSize][(int)type][refr].AsSpan(); + ReadOnlySpan> eobBranchSpan = counts.EobBranch[(int)txSize][(int)type][refr].AsSpan(); while (c < maxEob) { int val = -1; band = bandTranslate[0]; bandTranslate = bandTranslate[1..]; - ref Array3 prob = ref coefProbs[band][ctx]; + ReadOnlySpan prob = coefProbs[band][ctx].AsSpan(); if (!xd.Counts.IsNull) { - ++counts.EobBranch[(int)txSize][(int)type][refr][band][ctx]; + ++eobBranchSpan[band][ctx]; } if (r.ReadBool(prob[EobContextNode], ref value, ref count, ref range) == 0) { if (!xd.Counts.IsNull) { - ++counts.Coef[(int)txSize][(int)type][refr][band][ctx][Constants.EobModelToken]; + ++coefSpan[band][ctx][Constants.EobModelToken]; } break; @@ -82,7 +85,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (!xd.Counts.IsNull) { - ++counts.Coef[(int)txSize][(int)type][refr][band][ctx][Constants.ZeroToken]; + ++coefSpan[band][ctx][Constants.ZeroToken]; } dqv = dq[1]; @@ -99,7 +102,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ctx = GetCoefContext(nb, tokenCache, c); band = bandTranslate[0]; bandTranslate = bandTranslate[1..]; - prob = ref coefProbs[band][ctx]; + prob = coefProbs[band][ctx].AsSpan(); } if (r.ReadBool(prob[OneContextNode], ref value, ref count, ref range) != 0) @@ -107,7 +110,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ReadOnlySpan p = Luts.Pareto8Full[prob[Constants.PivotNode] - 1]; if (!xd.Counts.IsNull) { - ++counts.Coef[(int)txSize][(int)type][refr][band][ctx][Constants.TwoToken]; + ++coefSpan[band][ctx][Constants.TwoToken]; } if (r.ReadBool(p[0], ref value, ref count, ref range) != 0) @@ -175,7 +178,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { if (!xd.Counts.IsNull) { - ++counts.Coef[(int)txSize][(int)type][refr][band][ctx][Constants.OneToken]; + ++coefSpan[band][ctx][Constants.OneToken]; } tokenCache[scan[c]] = 1; @@ -232,7 +235,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ref Reader r = ref twd.BitReader; ref MacroBlockD xd = ref twd.Xd; ref MacroBlockDPlane pd = ref xd.Plane[plane]; - ref Array2 dequant = ref pd.SegDequant[segId]; + Span dequant = pd.SegDequant[segId].AsSpan(); int eob; Span a = pd.AboveContext.AsSpan()[x..]; Span l = pd.LeftContext.AsSpan()[y..]; @@ -250,7 +253,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 GetPlaneType(plane), pd.DqCoeff.AsSpan(), txSize, - ref dequant, + dequant, ctx, sc.Scan, sc.Neighbors, @@ -266,7 +269,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 GetPlaneType(plane), pd.DqCoeff.AsSpan(), txSize, - ref dequant, + dequant, ctx, sc.Scan, sc.Neighbors, @@ -283,7 +286,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 GetPlaneType(plane), pd.DqCoeff.AsSpan(), txSize, - ref dequant, + dequant, ctx, sc.Scan, sc.Neighbors, @@ -303,7 +306,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 GetPlaneType(plane), pd.DqCoeff.AsSpan(), txSize, - ref dequant, + dequant, ctx, sc.Scan, sc.Neighbors, diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs index 839a9c400..fd9d72713 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Dsp/Convolve.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Common; +using System; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; @@ -129,7 +130,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (int x = 0; x < w; ++x) { byte* srcX = &src[xQ4 >> SubpelBits]; - ref Array8 xFilter = ref xFilters[xQ4 & SubpelMask]; + Span xFilter = xFilters[xQ4 & SubpelMask].AsSpan(); int sum = 0; for (int k = 0; k < SubpelTaps; ++k) { @@ -164,7 +165,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (int x = 0; x < w; ++x) { byte* srcX = &src[xQ4 >> SubpelBits]; - ref Array8 xFilter = ref xFilters[xQ4 & SubpelMask]; + Span xFilter = xFilters[xQ4 & SubpelMask].AsSpan(); int sum = 0; for (int k = 0; k < SubpelTaps; ++k) { @@ -275,7 +276,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (int y = 0; y < h; ++y) { byte* srcY = &src[(yQ4 >> SubpelBits) * srcStride]; - ref Array8 yFilter = ref yFilters[yQ4 & SubpelMask]; + Span yFilter = yFilters[yQ4 & SubpelMask].AsSpan(); int sum = 0; for (int k = 0; k < SubpelTaps; ++k) { @@ -310,7 +311,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (int y = 0; y < h; ++y) { byte* srcY = &src[(yQ4 >> SubpelBits) * srcStride]; - ref Array8 yFilter = ref yFilters[yQ4 & SubpelMask]; + Span yFilter = yFilters[yQ4 & SubpelMask].AsSpan(); int sum = 0; for (int k = 0; k < SubpelTaps; ++k) { @@ -619,7 +620,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (int x = 0; x < w; ++x) { ushort* srcX = &src[xQ4 >> SubpelBits]; - ref Array8 xFilter = ref xFilters[xQ4 & SubpelMask]; + Span xFilter = xFilters[xQ4 & SubpelMask].AsSpan(); int sum = 0; for (int k = 0; k < SubpelTaps; ++k) { @@ -655,7 +656,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (int x = 0; x < w; ++x) { ushort* srcX = &src[xQ4 >> SubpelBits]; - ref Array8 xFilter = ref xFilters[xQ4 & SubpelMask]; + Span xFilter = xFilters[xQ4 & SubpelMask].AsSpan(); int sum = 0; for (int k = 0; k < SubpelTaps; ++k) { @@ -692,7 +693,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (int y = 0; y < h; ++y) { ushort* srcY = &src[(yQ4 >> SubpelBits) * srcStride]; - ref Array8 yFilter = ref yFilters[yQ4 & SubpelMask]; + Span yFilter = yFilters[yQ4 & SubpelMask].AsSpan(); int sum = 0; for (int k = 0; k < SubpelTaps; ++k) { @@ -728,7 +729,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp for (int y = 0; y < h; ++y) { ushort* srcY = &src[(yQ4 >> SubpelBits) * srcStride]; - ref Array8 yFilter = ref yFilters[yQ4 & SubpelMask]; + Span yFilter = yFilters[yQ4 & SubpelMask].AsSpan(); int sum = 0; for (int k = 0; k < SubpelTaps; ++k) { diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/EntropyMode.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/EntropyMode.cs index 624acbaa5..80e2f8c8d 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/EntropyMode.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/EntropyMode.cs @@ -304,31 +304,40 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } internal static void TxCountsToBranchCounts32X32(ReadOnlySpan txCount32X32P, - ref Array3> ct32X32P) + ReadOnlySpan> ct32X32P) { - ct32X32P[0][0] = txCount32X32P[(int)TxSize.Tx4X4]; - ct32X32P[0][1] = txCount32X32P[(int)TxSize.Tx8X8] + txCount32X32P[(int)TxSize.Tx16X16] + + Span ct32X32PSpan0 = ct32X32P[0].AsSpan(); + Span ct32X32PSpan1 = ct32X32P[1].AsSpan(); + Span ct32X32PSpan2 = ct32X32P[2].AsSpan(); + + ct32X32PSpan0[0] = txCount32X32P[(int)TxSize.Tx4X4]; + ct32X32PSpan0[1] = txCount32X32P[(int)TxSize.Tx8X8] + txCount32X32P[(int)TxSize.Tx16X16] + txCount32X32P[(int)TxSize.Tx32X32]; - ct32X32P[1][0] = txCount32X32P[(int)TxSize.Tx8X8]; - ct32X32P[1][1] = txCount32X32P[(int)TxSize.Tx16X16] + txCount32X32P[(int)TxSize.Tx32X32]; - ct32X32P[2][0] = txCount32X32P[(int)TxSize.Tx16X16]; - ct32X32P[2][1] = txCount32X32P[(int)TxSize.Tx32X32]; + ct32X32PSpan1[0] = txCount32X32P[(int)TxSize.Tx8X8]; + ct32X32PSpan1[1] = txCount32X32P[(int)TxSize.Tx16X16] + txCount32X32P[(int)TxSize.Tx32X32]; + ct32X32PSpan2[0] = txCount32X32P[(int)TxSize.Tx16X16]; + ct32X32PSpan2[1] = txCount32X32P[(int)TxSize.Tx32X32]; } internal static void TxCountsToBranchCounts16X16(ReadOnlySpan txCount16X16P, - ref Array2> ct16X16P) + ReadOnlySpan> ct16X16P) { - ct16X16P[0][0] = txCount16X16P[(int)TxSize.Tx4X4]; - ct16X16P[0][1] = txCount16X16P[(int)TxSize.Tx8X8] + txCount16X16P[(int)TxSize.Tx16X16]; - ct16X16P[1][0] = txCount16X16P[(int)TxSize.Tx8X8]; - ct16X16P[1][1] = txCount16X16P[(int)TxSize.Tx16X16]; + Span ct16X16PSpan0 = ct16X16P[0].AsSpan(); + Span ct16X16PSpan1 = ct16X16P[1].AsSpan(); + + ct16X16PSpan0[0] = txCount16X16P[(int)TxSize.Tx4X4]; + ct16X16PSpan0[1] = txCount16X16P[(int)TxSize.Tx8X8] + txCount16X16P[(int)TxSize.Tx16X16]; + ct16X16PSpan1[0] = txCount16X16P[(int)TxSize.Tx8X8]; + ct16X16PSpan1[1] = txCount16X16P[(int)TxSize.Tx16X16]; } internal static void TxCountsToBranchCounts8X8(ReadOnlySpan txCount8X8P, - ref Array1> ct8X8P) + ReadOnlySpan> ct8X8P) { - ct8X8P[0][0] = txCount8X8P[(int)TxSize.Tx4X4]; - ct8X8P[0][1] = txCount8X8P[(int)TxSize.Tx8X8]; + Span ct8X8PSpan = ct8X8P[0].AsSpan(); + + ct8X8PSpan[0] = txCount8X8P[(int)TxSize.Tx4X4]; + ct8X8PSpan[1] = txCount8X8P[(int)TxSize.Tx8X8]; } public static unsafe void SetupPastIndependence(ref Vp9Common cm) diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs index ada7ae400..f4ddba36a 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/LoopFilter.cs @@ -378,9 +378,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int index = shiftY; + Span lflYSpan = lfm.LflY.AsSpan(); + for (int i = 0; i < bh; i++) { - MemoryMarshal.CreateSpan(ref lfm.LflY[index], 64 - index)[..bw].Fill((byte)filterLevel); + MemoryMarshal.CreateSpan(ref lflYSpan[index], 64 - index)[..bw].Fill((byte)filterLevel); index += 8; } @@ -444,25 +446,30 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 const ulong AboveBorder = 0x000000ff000000ffUL; const ushort LeftBorderUv = 0x1111; const ushort AboveBorderUv = 0x000f; + + Span leftYSpan = lfm.LeftY.AsSpan(); + Span aboveYSpan = lfm.AboveY.AsSpan(); + Span leftUvSpan = lfm.LeftUv.AsSpan(); + Span aboveUvSpan = lfm.AboveUv.AsSpan(); // The largest loopfilter we have is 16x16 so we use the 16x16 mask // for 32x32 transforms also. - lfm.LeftY[(int)TxSize.Tx16X16] |= lfm.LeftY[(int)TxSize.Tx32X32]; - lfm.AboveY[(int)TxSize.Tx16X16] |= lfm.AboveY[(int)TxSize.Tx32X32]; - lfm.LeftUv[(int)TxSize.Tx16X16] |= lfm.LeftUv[(int)TxSize.Tx32X32]; - lfm.AboveUv[(int)TxSize.Tx16X16] |= lfm.AboveUv[(int)TxSize.Tx32X32]; + leftYSpan[(int)TxSize.Tx16X16] |= leftYSpan[(int)TxSize.Tx32X32]; + aboveYSpan[(int)TxSize.Tx16X16] |= aboveYSpan[(int)TxSize.Tx32X32]; + leftUvSpan[(int)TxSize.Tx16X16] |= leftUvSpan[(int)TxSize.Tx32X32]; + aboveUvSpan[(int)TxSize.Tx16X16] |= aboveUvSpan[(int)TxSize.Tx32X32]; // We do at least 8 tap filter on every 32x32 even if the transform size // is 4x4. So if the 4x4 is set on a border pixel add it to the 8x8 and // remove it from the 4x4. - lfm.LeftY[(int)TxSize.Tx8X8] |= lfm.LeftY[(int)TxSize.Tx4X4] & LeftBorder; - lfm.LeftY[(int)TxSize.Tx4X4] &= ~LeftBorder; - lfm.AboveY[(int)TxSize.Tx8X8] |= lfm.AboveY[(int)TxSize.Tx4X4] & AboveBorder; - lfm.AboveY[(int)TxSize.Tx4X4] &= ~AboveBorder; - lfm.LeftUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.LeftUv[(int)TxSize.Tx4X4] & LeftBorderUv); - lfm.LeftUv[(int)TxSize.Tx4X4] &= unchecked((ushort)~LeftBorderUv); - lfm.AboveUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.AboveUv[(int)TxSize.Tx4X4] & AboveBorderUv); - lfm.AboveUv[(int)TxSize.Tx4X4] &= unchecked((ushort)~AboveBorderUv); + leftYSpan[(int)TxSize.Tx8X8] |= leftYSpan[(int)TxSize.Tx4X4] & LeftBorder; + leftYSpan[(int)TxSize.Tx4X4] &= ~LeftBorder; + aboveYSpan[(int)TxSize.Tx8X8] |= aboveYSpan[(int)TxSize.Tx4X4] & AboveBorder; + aboveYSpan[(int)TxSize.Tx4X4] &= ~AboveBorder; + leftUvSpan[(int)TxSize.Tx8X8] |= (ushort)(leftUvSpan[(int)TxSize.Tx4X4] & LeftBorderUv); + leftUvSpan[(int)TxSize.Tx4X4] &= unchecked((ushort)~LeftBorderUv); + aboveUvSpan[(int)TxSize.Tx8X8] |= (ushort)(aboveUvSpan[(int)TxSize.Tx4X4] & AboveBorderUv); + aboveUvSpan[(int)TxSize.Tx4X4] &= unchecked((ushort)~AboveBorderUv); // We do some special edge handling. if (miRow + Constants.MiBlockSize > cm.MiRows) @@ -476,10 +483,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Remove values completely outside our border. for (int i = 0; i < (int)TxSize.Tx32X32; i++) { - lfm.LeftY[i] &= maskY; - lfm.AboveY[i] &= maskY; - lfm.LeftUv[i] &= maskUv; - lfm.AboveUv[i] &= maskUv; + leftYSpan[i] &= maskY; + aboveYSpan[i] &= maskY; + leftUvSpan[i] &= maskUv; + aboveUvSpan[i] &= maskUv; } lfm.Int4X4Y &= maskY; @@ -489,14 +496,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // apply the shorter one instead. if (rows == 1) { - lfm.AboveUv[(int)TxSize.Tx8X8] |= lfm.AboveUv[(int)TxSize.Tx16X16]; - lfm.AboveUv[(int)TxSize.Tx16X16] = 0; + aboveUvSpan[(int)TxSize.Tx8X8] |= aboveUvSpan[(int)TxSize.Tx16X16]; + aboveUvSpan[(int)TxSize.Tx16X16] = 0; } if (rows == 5) { - lfm.AboveUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.AboveUv[(int)TxSize.Tx16X16] & 0xff00); - lfm.AboveUv[(int)TxSize.Tx16X16] &= (ushort)~(lfm.AboveUv[(int)TxSize.Tx16X16] & 0xff00); + aboveUvSpan[(int)TxSize.Tx8X8] |= (ushort)(aboveUvSpan[(int)TxSize.Tx16X16] & 0xff00); + aboveUvSpan[(int)TxSize.Tx16X16] &= (ushort)~(aboveUvSpan[(int)TxSize.Tx16X16] & 0xff00); } } @@ -516,10 +523,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Remove the bits outside the image edge. for (int i = 0; i < (int)TxSize.Tx32X32; i++) { - lfm.LeftY[i] &= maskY; - lfm.AboveY[i] &= maskY; - lfm.LeftUv[i] &= maskUv; - lfm.AboveUv[i] &= maskUv; + leftYSpan[i] &= maskY; + aboveYSpan[i] &= maskY; + leftUvSpan[i] &= maskUv; + aboveUvSpan[i] &= maskUv; } lfm.Int4X4Y &= maskY; @@ -529,14 +536,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // apply the shorter one instead. if (columns == 1) { - lfm.LeftUv[(int)TxSize.Tx8X8] |= lfm.LeftUv[(int)TxSize.Tx16X16]; - lfm.LeftUv[(int)TxSize.Tx16X16] = 0; + leftUvSpan[(int)TxSize.Tx8X8] |= leftUvSpan[(int)TxSize.Tx16X16]; + leftUvSpan[(int)TxSize.Tx16X16] = 0; } if (columns == 5) { - lfm.LeftUv[(int)TxSize.Tx8X8] |= (ushort)(lfm.LeftUv[(int)TxSize.Tx16X16] & 0xcccc); - lfm.LeftUv[(int)TxSize.Tx16X16] &= (ushort)~(lfm.LeftUv[(int)TxSize.Tx16X16] & 0xcccc); + leftUvSpan[(int)TxSize.Tx8X8] |= (ushort)(leftUvSpan[(int)TxSize.Tx16X16] & 0xcccc); + leftUvSpan[(int)TxSize.Tx16X16] &= (ushort)~(leftUvSpan[(int)TxSize.Tx16X16] & 0xcccc); } } @@ -546,28 +553,28 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { for (int i = 0; i < (int)TxSize.Tx32X32; i++) { - lfm.LeftY[i] &= 0xfefefefefefefefeUL; - lfm.LeftUv[i] &= 0xeeee; + leftYSpan[i] &= 0xfefefefefefefefeUL; + leftUvSpan[i] &= 0xeeee; } } // Assert if we try to apply 2 different loop filters at the same position. - Debug.Assert((lfm.LeftY[(int)TxSize.Tx16X16] & lfm.LeftY[(int)TxSize.Tx8X8]) == 0); - Debug.Assert((lfm.LeftY[(int)TxSize.Tx16X16] & lfm.LeftY[(int)TxSize.Tx4X4]) == 0); - Debug.Assert((lfm.LeftY[(int)TxSize.Tx8X8] & lfm.LeftY[(int)TxSize.Tx4X4]) == 0); - Debug.Assert((lfm.Int4X4Y & lfm.LeftY[(int)TxSize.Tx16X16]) == 0); - Debug.Assert((lfm.LeftUv[(int)TxSize.Tx16X16] & lfm.LeftUv[(int)TxSize.Tx8X8]) == 0); - Debug.Assert((lfm.LeftUv[(int)TxSize.Tx16X16] & lfm.LeftUv[(int)TxSize.Tx4X4]) == 0); - Debug.Assert((lfm.LeftUv[(int)TxSize.Tx8X8] & lfm.LeftUv[(int)TxSize.Tx4X4]) == 0); - Debug.Assert((lfm.Int4X4Uv & lfm.LeftUv[(int)TxSize.Tx16X16]) == 0); - Debug.Assert((lfm.AboveY[(int)TxSize.Tx16X16] & lfm.AboveY[(int)TxSize.Tx8X8]) == 0); - Debug.Assert((lfm.AboveY[(int)TxSize.Tx16X16] & lfm.AboveY[(int)TxSize.Tx4X4]) == 0); - Debug.Assert((lfm.AboveY[(int)TxSize.Tx8X8] & lfm.AboveY[(int)TxSize.Tx4X4]) == 0); - Debug.Assert((lfm.Int4X4Y & lfm.AboveY[(int)TxSize.Tx16X16]) == 0); - Debug.Assert((lfm.AboveUv[(int)TxSize.Tx16X16] & lfm.AboveUv[(int)TxSize.Tx8X8]) == 0); - Debug.Assert((lfm.AboveUv[(int)TxSize.Tx16X16] & lfm.AboveUv[(int)TxSize.Tx4X4]) == 0); - Debug.Assert((lfm.AboveUv[(int)TxSize.Tx8X8] & lfm.AboveUv[(int)TxSize.Tx4X4]) == 0); - Debug.Assert((lfm.Int4X4Uv & lfm.AboveUv[(int)TxSize.Tx16X16]) == 0); + Debug.Assert((leftYSpan[(int)TxSize.Tx16X16] & leftYSpan[(int)TxSize.Tx8X8]) == 0); + Debug.Assert((leftYSpan[(int)TxSize.Tx16X16] & leftYSpan[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((leftYSpan[(int)TxSize.Tx8X8] & leftYSpan[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((lfm.Int4X4Y & leftYSpan[(int)TxSize.Tx16X16]) == 0); + Debug.Assert((leftUvSpan[(int)TxSize.Tx16X16] & leftUvSpan[(int)TxSize.Tx8X8]) == 0); + Debug.Assert((leftUvSpan[(int)TxSize.Tx16X16] & leftUvSpan[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((leftUvSpan[(int)TxSize.Tx8X8] & leftUvSpan[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((lfm.Int4X4Uv & leftUvSpan[(int)TxSize.Tx16X16]) == 0); + Debug.Assert((aboveYSpan[(int)TxSize.Tx16X16] & aboveYSpan[(int)TxSize.Tx8X8]) == 0); + Debug.Assert((aboveYSpan[(int)TxSize.Tx16X16] & aboveYSpan[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((aboveYSpan[(int)TxSize.Tx8X8] & aboveYSpan[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((lfm.Int4X4Y & aboveYSpan[(int)TxSize.Tx16X16]) == 0); + Debug.Assert((aboveUvSpan[(int)TxSize.Tx16X16] & aboveUvSpan[(int)TxSize.Tx8X8]) == 0); + Debug.Assert((aboveUvSpan[(int)TxSize.Tx16X16] & aboveUvSpan[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((aboveUvSpan[(int)TxSize.Tx8X8] & aboveUvSpan[(int)TxSize.Tx4X4]) == 0); + Debug.Assert((lfm.Int4X4Uv & aboveUvSpan[(int)TxSize.Tx16X16]) == 0); } public static unsafe void ResetLfm(ref Vp9Common cm) @@ -583,6 +590,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { int lvl; + Span lFThrSpan = lfi.Lfthr.AsSpan(); + // For each possible value for the loop filter fill out limits for (lvl = 0; lvl <= MaxLoopFilter; lvl++) { @@ -602,8 +611,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 blockInsideLimit = 1; } - lfi.Lfthr[lvl].Lim.AsSpan().Fill((byte)blockInsideLimit); - lfi.Lfthr[lvl].Mblim.AsSpan().Fill((byte)((2 * (lvl + 2)) + blockInsideLimit)); + lFThrSpan[lvl].Lim.AsSpan().Fill((byte)blockInsideLimit); + lFThrSpan[lvl].Mblim.AsSpan().Fill((byte)((2 * (lvl + 2)) + blockInsideLimit)); } } @@ -625,6 +634,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 lf.LastSharpnessLevel = lf.SharpnessLevel; } + Span>> lvlSpan = lfi.Lvl.AsSpan(); + Span refDeltasSpan = lf.RefDeltas.AsSpan(); + Span modeDeltasSpan = lf.ModeDeltas.AsSpan(); + sbyte intraFrameRefDelta = refDeltasSpan[Constants.IntraFrame]; + for (segId = 0; segId < Constants.MaxSegments; segId++) { int lvlSeg = defaultFiltLvl; @@ -639,20 +653,24 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { // We could get rid of this if we assume that deltas are set to // zero when not in use; encoder always uses deltas - MemoryMarshal.Cast, byte>(lfi.Lvl[segId].AsSpan()).Fill((byte)lvlSeg); + MemoryMarshal.Cast, byte>(lvlSpan[segId].AsSpan()).Fill((byte)lvlSeg); } else { int refr, mode; - int intraLvl = lvlSeg + (lf.RefDeltas[Constants.IntraFrame] * scale); - lfi.Lvl[segId][Constants.IntraFrame][0] = (byte)Math.Clamp(intraLvl, 0, MaxLoopFilter); + int intraLvl = lvlSeg + (intraFrameRefDelta * scale); + lvlSpan[segId][Constants.IntraFrame][0] = (byte)Math.Clamp(intraLvl, 0, MaxLoopFilter); + + Span> lvlSpan2 = lvlSpan[segId].AsSpan(); for (refr = Constants.LastFrame; refr < Constants.MaxRefFrames; ++refr) { + Span lvlSpan3 = lvlSpan2[refr].AsSpan(); + for (mode = 0; mode < MaxModeLfDeltas; ++mode) { - int interLvl = lvlSeg + (lf.RefDeltas[refr] * scale) + (lf.ModeDeltas[mode] * scale); - lfi.Lvl[segId][refr][mode] = (byte)Math.Clamp(interLvl, 0, MaxLoopFilter); + int interLvl = lvlSeg + (refDeltasSpan[refr] * scale) + (modeDeltasSpan[mode] * scale); + lvlSpan3[mode] = (byte)Math.Clamp(interLvl, 0, MaxLoopFilter); } } } @@ -811,18 +829,32 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 lfis[1] = lfthr[lfl[lflForward]]; ss[1] = ss[0].Slice(8 * pitch); + Span mblim0Span = lfis[0].Mblim.AsSpan(); + Span lim0Span = lfis[0].Lim.AsSpan(); + Span hevThr0Span = lfis[0].HevThr.AsSpan(); + Span mblim1Span = lfis[1].Mblim.AsSpan(); + Span lim1Span = lfis[1].Lim.AsSpan(); + Span hevThr1Span = lfis[1].HevThr.AsSpan(); + if ((mask16X16 & dualOne) != 0) { if ((mask16X16 & dualOne) == dualOne) { - LoopFilterScalar.HighBdLpfVertical16Dual(ss[0], pitch, lfis[0].Mblim[0], lfis[0].Lim[0], - lfis[0].HevThr[0], bd); + LoopFilterScalar.HighBdLpfVertical16Dual(ss[0], pitch, mblim0Span[0], lim0Span[0], + hevThr0Span[0], bd); } else { - ref LoopFilterThresh lfi = ref lfis[(mask16X16 & 1) == 0 ? 1 : 0]; - LoopFilterScalar.HighBdLpfVertical16(ss[(mask16X16 & 1) == 0 ? 1 : 0], pitch, lfi.Mblim[0], - lfi.Lim[0], lfi.HevThr[0], bd); + if ((mask16X16 & 1) == 0) + { + LoopFilterScalar.HighBdLpfVertical16(ss[1], pitch, mblim1Span[0], + lim1Span[0], hevThr1Span[0], bd); + } + else + { + LoopFilterScalar.HighBdLpfVertical16(ss[0], pitch, mblim0Span[0], + lim0Span[0], hevThr0Span[0], bd); + } } } @@ -833,24 +865,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 LoopFilterScalar.HighBdLpfVertical8Dual( ss[0], pitch, - lfis[0].Mblim[0], - lfis[0].Lim[0], - lfis[0].HevThr[0], - lfis[1].Mblim[0], - lfis[1].Lim[0], - lfis[1].HevThr[0], + mblim0Span[0], + lim0Span[0], + hevThr0Span[0], + mblim1Span[0], + lim1Span[0], + hevThr1Span[0], bd); } else { - ref LoopFilterThresh lfi = ref lfis[(mask8X8 & 1) == 0 ? 1 : 0]; - LoopFilterScalar.HighBdLpfVertical8( - ss[(mask8X8 & 1) == 0 ? 1 : 0], - pitch, - lfi.Mblim[0], - lfi.Lim[0], - lfi.HevThr[0], - bd); + if ((mask8X8 & 1) == 0) + { + LoopFilterScalar.HighBdLpfVertical8(ss[1], pitch, mblim1Span[0], + lim1Span[0], hevThr1Span[0], bd); + } + else + { + LoopFilterScalar.HighBdLpfVertical8(ss[0], pitch, mblim0Span[0], + lim0Span[0], hevThr0Span[0], bd); + } } } @@ -861,19 +895,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 LoopFilterScalar.HighBdLpfVertical4Dual( ss[0], pitch, - lfis[0].Mblim[0], - lfis[0].Lim[0], - lfis[0].HevThr[0], - lfis[1].Mblim[0], - lfis[1].Lim[0], - lfis[1].HevThr[0], + mblim0Span[0], + lim0Span[0], + hevThr0Span[0], + mblim1Span[0], + lim1Span[0], + hevThr1Span[0], bd); } else { - ref LoopFilterThresh lfi = ref lfis[(mask4X4 & 1) == 0 ? 1 : 0]; - LoopFilterScalar.HighBdLpfVertical4(ss[(mask4X4 & 1) == 0 ? 1 : 0], pitch, lfi.Mblim[0], - lfi.Lim[0], lfi.HevThr[0], bd); + if ((mask4X4 & 1) == 0) + { + LoopFilterScalar.HighBdLpfVertical4(ss[1], pitch, mblim1Span[0], + lim1Span[0], hevThr1Span[0], bd); + } + else + { + LoopFilterScalar.HighBdLpfVertical4(ss[0], pitch, mblim0Span[0], + lim0Span[0], hevThr0Span[0], bd); + } } } @@ -884,19 +925,26 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 LoopFilterScalar.HighBdLpfVertical4Dual( ss[0].Slice(4), pitch, - lfis[0].Mblim[0], - lfis[0].Lim[0], - lfis[0].HevThr[0], - lfis[1].Mblim[0], - lfis[1].Lim[0], - lfis[1].HevThr[0], + mblim0Span[0], + lim0Span[0], + hevThr0Span[0], + mblim1Span[0], + lim1Span[0], + hevThr1Span[0], bd); } else { - ref LoopFilterThresh lfi = ref lfis[(mask4X4Int & 1) == 0 ? 1 : 0]; - LoopFilterScalar.HighBdLpfVertical4(ss[(mask4X4Int & 1) == 0 ? 1 : 0].Slice(4), pitch, - lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd); + if ((mask4X4Int & 1) == 0) + { + LoopFilterScalar.HighBdLpfVertical4(ss[1].Slice(4), pitch, mblim1Span[0], + lim1Span[0], hevThr1Span[0], bd); + } + else + { + LoopFilterScalar.HighBdLpfVertical4(ss[0].Slice(4), pitch, mblim0Span[0], + lim0Span[0], hevThr0Span[0], bd); + } } } } @@ -1086,18 +1134,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 if ((mask & 1) != 0) { LoopFilterThresh lfi = lfthr[lfl[0]]; + + Span mblimSpan = lfi.Mblim.AsSpan(); + Span limSpan = lfi.Lim.AsSpan(); + Span hevThrSpan = lfi.HevThr.AsSpan(); if ((mask16X16 & 1) != 0) { if ((mask16X16 & 3) == 3) { - LoopFilterScalar.HighBdLpfHorizontal16Dual(s, pitch, lfi.Mblim[0], lfi.Lim[0], - lfi.HevThr[0], bd); + LoopFilterScalar.HighBdLpfHorizontal16Dual(s, pitch, mblimSpan[0], limSpan[0], + hevThrSpan[0], bd); count = 2; } else { - LoopFilterScalar.HighBdLpfHorizontal16(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], + LoopFilterScalar.HighBdLpfHorizontal16(s, pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], bd); } } @@ -1107,16 +1159,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { // Next block's thresholds. LoopFilterThresh lfin = lfthr[lfl[1]]; + + Span nMblimSpan = lfin.Mblim.AsSpan(); + Span nLimSpan = lfin.Lim.AsSpan(); + Span nHevThrSpan = lfin.HevThr.AsSpan(); LoopFilterScalar.HighBdLpfHorizontal8Dual( s, pitch, - lfi.Mblim[0], - lfi.Lim[0], - lfi.HevThr[0], - lfin.Mblim[0], - lfin.Lim[0], - lfin.HevThr[0], + mblimSpan[0], + limSpan[0], + hevThrSpan[0], + nMblimSpan[0], + nLimSpan[0], + nHevThrSpan[0], bd); if ((mask4X4Int & 3) == 3) @@ -1124,36 +1180,36 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 LoopFilterScalar.HighBdLpfHorizontal4Dual( s.Slice(4 * pitch), pitch, - lfi.Mblim[0], - lfi.Lim[0], - lfi.HevThr[0], - lfin.Mblim[0], - lfin.Lim[0], - lfin.HevThr[0], + mblimSpan[0], + limSpan[0], + hevThrSpan[0], + nMblimSpan[0], + nLimSpan[0], + nHevThrSpan[0], bd); } else if ((mask4X4Int & 1) != 0) { - LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0], - lfi.Lim[0], lfi.HevThr[0], bd); + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0], + limSpan[0], hevThrSpan[0], bd); } else if ((mask4X4Int & 2) != 0) { - LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, lfin.Mblim[0], - lfin.Lim[0], lfin.HevThr[0], bd); + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, nMblimSpan[0], + nLimSpan[0], nHevThrSpan[0], bd); } count = 2; } else { - LoopFilterScalar.HighBdLpfHorizontal8(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], + LoopFilterScalar.HighBdLpfHorizontal8(s, pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], bd); if ((mask4X4Int & 1) != 0) { - LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0], - lfi.Lim[0], lfi.HevThr[0], bd); + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0], + limSpan[0], hevThrSpan[0], bd); } } } @@ -1163,16 +1219,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { // Next block's thresholds. LoopFilterThresh lfin = lfthr[lfl[1]]; + + Span nMblimSpan = lfin.Mblim.AsSpan(); + Span nLimSpan = lfin.Lim.AsSpan(); + Span nHevThrSpan = lfin.HevThr.AsSpan(); LoopFilterScalar.HighBdLpfHorizontal4Dual( s, pitch, - lfi.Mblim[0], - lfi.Lim[0], - lfi.HevThr[0], - lfin.Mblim[0], - lfin.Lim[0], - lfin.HevThr[0], + mblimSpan[0], + limSpan[0], + hevThrSpan[0], + nMblimSpan[0], + nLimSpan[0], + nHevThrSpan[0], bd); if ((mask4X4Int & 3) == 3) @@ -1180,43 +1240,43 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 LoopFilterScalar.HighBdLpfHorizontal4Dual( s.Slice(4 * pitch), pitch, - lfi.Mblim[0], - lfi.Lim[0], - lfi.HevThr[0], - lfin.Mblim[0], - lfin.Lim[0], - lfin.HevThr[0], + mblimSpan[0], + limSpan[0], + hevThrSpan[0], + nMblimSpan[0], + nLimSpan[0], + nHevThrSpan[0], bd); } else if ((mask4X4Int & 1) != 0) { - LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0], - lfi.Lim[0], lfi.HevThr[0], bd); + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0], + limSpan[0], hevThrSpan[0], bd); } else if ((mask4X4Int & 2) != 0) { - LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, lfin.Mblim[0], - lfin.Lim[0], lfin.HevThr[0], bd); + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(8 + (4 * pitch)), pitch, nMblimSpan[0], + nLimSpan[0], nHevThrSpan[0], bd); } count = 2; } else { - LoopFilterScalar.HighBdLpfHorizontal4(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], + LoopFilterScalar.HighBdLpfHorizontal4(s, pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], bd); if ((mask4X4Int & 1) != 0) { - LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0], - lfi.Lim[0], lfi.HevThr[0], bd); + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0], + limSpan[0], hevThrSpan[0], bd); } } } else { - LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, lfi.Mblim[0], lfi.Lim[0], - lfi.HevThr[0], bd); + LoopFilterScalar.HighBdLpfHorizontal4(s.Slice(4 * pitch), pitch, mblimSpan[0], limSpan[0], + hevThrSpan[0], bd); } } @@ -1291,26 +1351,30 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 for (uint mask = mask16X16 | mask8X8 | mask4X4 | mask4X4Int; mask != 0; mask >>= 1) { LoopFilterThresh lfi = lfthr[lfl[0]]; + + Span mblimSpan = lfi.Mblim.AsSpan(); + Span limSpan = lfi.Lim.AsSpan(); + Span hevThrSpan = lfi.HevThr.AsSpan(); if ((mask & 1) != 0) { if ((mask16X16 & 1) != 0) { - LoopFilterScalar.HighBdLpfVertical16(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd); + LoopFilterScalar.HighBdLpfVertical16(s, pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], bd); } else if ((mask8X8 & 1) != 0) { - LoopFilterScalar.HighBdLpfVertical8(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd); + LoopFilterScalar.HighBdLpfVertical8(s, pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], bd); } else if ((mask4X4 & 1) != 0) { - LoopFilterScalar.HighBdLpfVertical4(s, pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd); + LoopFilterScalar.HighBdLpfVertical4(s, pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], bd); } } if ((mask4X4Int & 1) != 0) { - LoopFilterScalar.HighBdLpfVertical4(s.Slice(4), pitch, lfi.Mblim[0], lfi.Lim[0], lfi.HevThr[0], bd); + LoopFilterScalar.HighBdLpfVertical4(s.Slice(4), pitch, mblimSpan[0], limSpan[0], hevThrSpan[0], bd); } s = s.Slice(8); @@ -1555,9 +1619,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { ref Buf2D dst = ref plane.Dst; ArrayPtr dst0 = dst.Buf; - ulong mask16X16 = lfm.LeftY[(int)TxSize.Tx16X16]; - ulong mask8X8 = lfm.LeftY[(int)TxSize.Tx8X8]; - ulong mask4X4 = lfm.LeftY[(int)TxSize.Tx4X4]; + Span leftYSpan = lfm.LeftY.AsSpan(); + ulong mask16X16 = leftYSpan[(int)TxSize.Tx16X16]; + ulong mask8X8 = leftYSpan[(int)TxSize.Tx8X8]; + ulong mask4X4 = leftYSpan[(int)TxSize.Tx4X4]; ulong mask4X4Int = lfm.Int4X4Y; Debug.Assert(plane.SubsamplingX == 0 && plane.SubsamplingY == 0); @@ -1604,9 +1669,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Horizontal pass dst.Buf = dst0; - mask16X16 = lfm.AboveY[(int)TxSize.Tx16X16]; - mask8X8 = lfm.AboveY[(int)TxSize.Tx8X8]; - mask4X4 = lfm.AboveY[(int)TxSize.Tx4X4]; + Span aboveYSpan = lfm.AboveY.AsSpan(); + mask16X16 = aboveYSpan[(int)TxSize.Tx16X16]; + mask8X8 = aboveYSpan[(int)TxSize.Tx8X8]; + mask4X4 = aboveYSpan[(int)TxSize.Tx4X4]; mask4X4Int = lfm.Int4X4Y; for (int r = 0; r < Constants.MiBlockSize && miRow + r < cm.MiRows; r++) @@ -1669,10 +1735,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 ArrayPtr dst0 = dst.Buf; Span lflUv = stackalloc byte[16]; + Span lflY = lfm.LflY.AsSpan(); - ushort mask16X16 = lfm.LeftUv[(int)TxSize.Tx16X16]; - ushort mask8X8 = lfm.LeftUv[(int)TxSize.Tx8X8]; - ushort mask4X4 = lfm.LeftUv[(int)TxSize.Tx4X4]; + Span leftUvSpan = lfm.LeftUv.AsSpan(); + ushort mask16X16 = leftUvSpan[(int)TxSize.Tx16X16]; + ushort mask8X8 = leftUvSpan[(int)TxSize.Tx8X8]; + ushort mask4X4 = leftUvSpan[(int)TxSize.Tx4X4]; ushort mask4X4Int = lfm.Int4X4Uv; Debug.Assert(plane.SubsamplingX == 1 && plane.SubsamplingY == 1); @@ -1682,8 +1750,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { for (int c = 0; c < Constants.MiBlockSize >> 1; c++) { - lflUv[(r << 1) + c] = lfm.LflY[(r << 3) + (c << 1)]; - lflUv[((r + 2) << 1) + c] = lfm.LflY[((r + 2) << 3) + (c << 1)]; + lflUv[(r << 1) + c] = lflY[(r << 3) + (c << 1)]; + lflUv[((r + 2) << 1) + c] = lflY[((r + 2) << 3) + (c << 1)]; } if (cm.UseHighBitDepth) @@ -1725,9 +1793,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Horizontal pass dst.Buf = dst0; - mask16X16 = lfm.AboveUv[(int)TxSize.Tx16X16]; - mask8X8 = lfm.AboveUv[(int)TxSize.Tx8X8]; - mask4X4 = lfm.AboveUv[(int)TxSize.Tx4X4]; + Span aboveUvSpan = lfm.AboveUv.AsSpan(); + mask16X16 = aboveUvSpan[(int)TxSize.Tx16X16]; + mask8X8 = aboveUvSpan[(int)TxSize.Tx8X8]; + mask4X4 = aboveUvSpan[(int)TxSize.Tx4X4]; mask4X4Int = lfm.Int4X4Uv; for (int r = 0; r < Constants.MiBlockSize && miRow + r < cm.MiRows; r += 2) @@ -1806,16 +1875,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 int sbCols = TileInfo.MiColsAlignedToSb(cm.MiCols) >> Constants.MiBlockSizeLog2; LfPath path; int miRow, miCol; + Span planesSpan = planes.AsSpan(); if (yOnly) { path = LfPath.LfPath444; } - else if (planes[1].SubsamplingY == 1 && planes[1].SubsamplingX == 1) + else if (planesSpan[1].SubsamplingY == 1 && planesSpan[1].SubsamplingX == 1) { path = LfPath.LfPath420; } - else if (planes[1].SubsamplingY == 0 && planes[1].SubsamplingX == 0) + else if (planesSpan[1].SubsamplingY == 0 && planesSpan[1].SubsamplingX == 0) { path = LfPath.LfPath444; } @@ -1837,23 +1907,23 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 lfSync.SyncRead(r, c); - ReconInter.SetupDstPlanes(ref planes, ref frameBuffer, miRow, miCol); + ReconInter.SetupDstPlanes(planesSpan, ref frameBuffer, miRow, miCol); AdjustMask(ref cm, miRow, miCol, ref lfm[0]); - FilterBlockPlaneSs00(ref cm, ref planes[0], miRow, ref lfm[0]); + FilterBlockPlaneSs00(ref cm, ref planesSpan[0], miRow, ref lfm[0]); for (plane = 1; plane < numPlanes; ++plane) { switch (path) { case LfPath.LfPath420: - FilterBlockPlaneSs11(ref cm, ref planes[plane], miRow, ref lfm[0]); + FilterBlockPlaneSs11(ref cm, ref planesSpan[plane], miRow, ref lfm[0]); break; case LfPath.LfPath444: - FilterBlockPlaneSs00(ref cm, ref planes[plane], miRow, ref lfm[0]); + FilterBlockPlaneSs00(ref cm, ref planesSpan[plane], miRow, ref lfm[0]); break; case LfPath.LfPathSlow: - FilterBlockPlaneNon420(ref cm, ref planes[plane], mi.Slice(miCol), miRow, + FilterBlockPlaneNon420(ref cm, ref planesSpan[plane], mi.Slice(miCol), miRow, miCol); break; } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs index acd771284..21643e32e 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Luts.cs @@ -270,17 +270,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private static Array8 NewArray8Short(short e0, short e1, short e2, short e3, short e4, short e5, short e6, short e7) { - Array8 output = new() - { - [0] = e0, - [1] = e1, - [2] = e2, - [3] = e3, - [4] = e4, - [5] = e5, - [6] = e6, - [7] = e7 - }; + Array8 output = new(); + Span outputSpan = output.AsSpan(); + outputSpan[0] = e0; + outputSpan[1] = e1; + outputSpan[2] = e2; + outputSpan[3] = e3; + outputSpan[4] = e4; + outputSpan[5] = e5; + outputSpan[6] = e6; + outputSpan[7] = e7; return output; } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs index cf91eedeb..ff3a8f813 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/PredCommon.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Nvdec.Vp9.Types; +using System; using System.Diagnostics; namespace Ryujinx.Graphics.Nvdec.Vp9 @@ -109,15 +110,17 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 sbyte vrfa = aSg ? xd.AboveMi.Value.RefFrame[0] : xd.AboveMi.Value.RefFrame[varRefIdx]; sbyte vrfl = lSg ? xd.LeftMi.Value.RefFrame[0] : xd.LeftMi.Value.RefFrame[varRefIdx]; - if (vrfa == vrfl && cm.CompVarRef[1] == vrfa) + Span compVarRefSpan = cm.CompVarRef.AsSpan(); + + if (vrfa == vrfl && compVarRefSpan[1] == vrfa) { predContext = 0; } else if (lSg && aSg) { // Single/Single - if ((vrfa == cm.CompFixedRef && vrfl == cm.CompVarRef[0]) || - (vrfl == cm.CompFixedRef && vrfa == cm.CompVarRef[0])) + if ((vrfa == cm.CompFixedRef && vrfl == compVarRefSpan[0]) || + (vrfl == cm.CompFixedRef && vrfa == compVarRefSpan[0])) { predContext = 4; } @@ -135,11 +138,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Single/Comp sbyte vrfc = lSg ? vrfa : vrfl; sbyte rfs = aSg ? vrfa : vrfl; - if (vrfc == cm.CompVarRef[1] && rfs != cm.CompVarRef[1]) + if (vrfc == compVarRefSpan[1] && rfs != compVarRefSpan[1]) { predContext = 1; } - else if (rfs == cm.CompVarRef[1] && vrfc != cm.CompVarRef[1]) + else if (rfs == compVarRefSpan[1] && vrfc != compVarRefSpan[1]) { predContext = 2; } @@ -212,14 +215,15 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { // Intra/Inter or Inter/Intra ref ModeInfo edgeMi = ref aboveIntra ? ref xd.LeftMi.Value : ref xd.AboveMi.Value; + Span refFrameSpan = edgeMi.RefFrame.AsSpan(); if (!edgeMi.HasSecondRef()) { - predContext = 4 * (edgeMi.RefFrame[0] == Constants.LastFrame ? 1 : 0); + predContext = 4 * (refFrameSpan[0] == Constants.LastFrame ? 1 : 0); } else { - predContext = 1 + (edgeMi.RefFrame[0] == Constants.LastFrame || - edgeMi.RefFrame[1] == Constants.LastFrame + predContext = 1 + (refFrameSpan[0] == Constants.LastFrame || + refFrameSpan[1] == Constants.LastFrame ? 1 : 0); } @@ -229,10 +233,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Inter/Inter bool aboveHasSecond = xd.AboveMi.Value.HasSecondRef(); bool leftHasSecond = xd.LeftMi.Value.HasSecondRef(); - sbyte above0 = xd.AboveMi.Value.RefFrame[0]; - sbyte above1 = xd.AboveMi.Value.RefFrame[1]; - sbyte left0 = xd.LeftMi.Value.RefFrame[0]; - sbyte left1 = xd.LeftMi.Value.RefFrame[1]; + Span aRefFrameSpan = xd.AboveMi.Value.RefFrame.AsSpan(); + sbyte above0 = aRefFrameSpan[0]; + sbyte above1 = aRefFrameSpan[1]; + Span lRefFrameSpan = xd.LeftMi.Value.RefFrame.AsSpan(); + sbyte left0 = lRefFrameSpan[0]; + sbyte left1 = lRefFrameSpan[1]; if (aboveHasSecond && leftHasSecond) { @@ -281,8 +287,10 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } else { - predContext = 1 + (edgeMi.RefFrame[0] == Constants.LastFrame || - edgeMi.RefFrame[1] == Constants.LastFrame + Span refFrameSpan = edgeMi.RefFrame.AsSpan(); + + predContext = 1 + (refFrameSpan[0] == Constants.LastFrame || + refFrameSpan[1] == Constants.LastFrame ? 1 : 0); } @@ -321,21 +329,22 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { // Intra/Inter or Inter/Intra ref ModeInfo edgeMi = ref aboveIntra ? ref xd.LeftMi.Value : ref xd.AboveMi.Value; + Span refFrameSpan = edgeMi.RefFrame.AsSpan(); if (!edgeMi.HasSecondRef()) { - if (edgeMi.RefFrame[0] == Constants.LastFrame) + if (refFrameSpan[0] == Constants.LastFrame) { predContext = 3; } else { - predContext = 4 * (edgeMi.RefFrame[0] == Constants.GoldenFrame ? 1 : 0); + predContext = 4 * (refFrameSpan[0] == Constants.GoldenFrame ? 1 : 0); } } else { - predContext = 1 + (2 * (edgeMi.RefFrame[0] == Constants.GoldenFrame || - edgeMi.RefFrame[1] == Constants.GoldenFrame + predContext = 1 + (2 * (refFrameSpan[0] == Constants.GoldenFrame || + refFrameSpan[1] == Constants.GoldenFrame ? 1 : 0)); } @@ -345,10 +354,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 // Inter/Inter bool aboveHasSecond = xd.AboveMi.Value.HasSecondRef(); bool leftHasSecond = xd.LeftMi.Value.HasSecondRef(); - sbyte above0 = xd.AboveMi.Value.RefFrame[0]; - sbyte above1 = xd.AboveMi.Value.RefFrame[1]; - sbyte left0 = xd.LeftMi.Value.RefFrame[0]; - sbyte left1 = xd.LeftMi.Value.RefFrame[1]; + Span aRefFrameSpan = xd.AboveMi.Value.RefFrame.AsSpan(); + sbyte above0 = aRefFrameSpan[0]; + sbyte above1 = aRefFrameSpan[1]; + Span lRefFrameSpan = xd.LeftMi.Value.RefFrame.AsSpan(); + sbyte left0 = lRefFrameSpan[0]; + sbyte left1 = lRefFrameSpan[1]; if (aboveHasSecond && leftHasSecond) { @@ -407,19 +418,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 { // One edge available ref ModeInfo edgeMi = ref !xd.AboveMi.IsNull ? ref xd.AboveMi.Value : ref xd.LeftMi.Value; + Span refFrameSpan = edgeMi.RefFrame.AsSpan(); - if (!edgeMi.IsInterBlock() || (edgeMi.RefFrame[0] == Constants.LastFrame && !edgeMi.HasSecondRef())) + if (!edgeMi.IsInterBlock() || (refFrameSpan[0] == Constants.LastFrame && !edgeMi.HasSecondRef())) { predContext = 2; } else if (!edgeMi.HasSecondRef()) { - predContext = 4 * (edgeMi.RefFrame[0] == Constants.GoldenFrame ? 1 : 0); + predContext = 4 * (refFrameSpan[0] == Constants.GoldenFrame ? 1 : 0); } else { - predContext = 3 * (edgeMi.RefFrame[0] == Constants.GoldenFrame || - edgeMi.RefFrame[1] == Constants.GoldenFrame + predContext = 3 * (refFrameSpan[0] == Constants.GoldenFrame || + refFrameSpan[1] == Constants.GoldenFrame ? 1 : 0); } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Prob.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Prob.cs index 6967e63a6..05bf9f69c 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Prob.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Prob.cs @@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 return (byte)BitUtils.RoundPowerOfTwo((prob1 * (256 - factor)) + (prob2 * factor), 8); } - public static byte MergeProbs(byte preProb, ref Array2 ct, uint countSat, uint maxUpdateFactor) + public static byte MergeProbs(byte preProb, ReadOnlySpan ct, uint countSat, uint maxUpdateFactor) { byte prob = GetBinaryProb(ct[0], ct[1]); uint count = Math.Min(ct[0] + ct[1], countSat); @@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 private const int ModeMvCountSat = 20; - public static byte ModeMvMergeProbs(byte preProb, ref Array2 ct) + public static byte ModeMvMergeProbs(byte preProb, ReadOnlySpan ct) { uint den = ct[0] + ct[1]; if (den == 0) @@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 Array2 ct = new(); ct[0] = leftCount; ct[1] = rightCount; - probs[(int)(i >> 1)] = ModeMvMergeProbs(preProbs[(int)(i >> 1)], ref ct); + probs[(int)(i >> 1)] = ModeMvMergeProbs(preProbs[(int)(i >> 1)], ct.AsSpan()); return leftCount + rightCount; } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs index e332fd208..6102d3fa4 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/ReconInter.cs @@ -164,7 +164,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 } public static void SetupDstPlanes( - ref Array3 planes, + Span planes, ref Surface src, int miRow, int miCol) @@ -205,9 +205,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9 strides[1] = src.UvStride; strides[2] = src.UvStride; + Span planeSpan = xd.Plane.AsSpan(); + for (int i = 0; i < Constants.MaxMbPlane; ++i) { - ref MacroBlockDPlane pd = ref xd.Plane[i]; + ref MacroBlockDPlane pd = ref planeSpan[i]; SetupPredPlanes(ref pd.Pre[idx], buffers[i], strides[i], miRow, miCol, sf, pd.SubsamplingX, pd.SubsamplingY); } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilter.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilter.cs index 2c75b780b..44f66fc9b 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilter.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/LoopFilter.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Memory; +using System; namespace Ryujinx.Graphics.Nvdec.Vp9.Types { @@ -28,13 +29,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { ModeRefDeltaEnabled = true; ModeRefDeltaUpdate = true; + + Span refDeltasSpan = RefDeltas.AsSpan(); + Span modeDeltasSpan = ModeDeltas.AsSpan(); - RefDeltas[Constants.IntraFrame] = 1; - RefDeltas[Constants.LastFrame] = 0; - RefDeltas[Constants.GoldenFrame] = -1; - RefDeltas[Constants.AltRefFrame] = -1; - ModeDeltas[0] = 0; - ModeDeltas[1] = 0; + refDeltasSpan[Constants.IntraFrame] = 1; + refDeltasSpan[Constants.LastFrame] = 0; + refDeltasSpan[Constants.GoldenFrame] = -1; + refDeltasSpan[Constants.AltRefFrame] = -1; + modeDeltasSpan[0] = 0; + modeDeltasSpan[1] = 0; } } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs index 47c218994..c0c291dd4 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/MacroBlockD.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Common; using Ryujinx.Graphics.Video; +using System; namespace Ryujinx.Graphics.Nvdec.Vp9.Types { @@ -147,10 +148,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public void SetupBlockPlanes(int ssX, int ssY) { + Span planeSpan = Plane.AsSpan(); + for (int i = 0; i < Constants.MaxMbPlane; i++) { - Plane[i].SubsamplingX = i != 0 ? ssX : 0; - Plane[i].SubsamplingY = i != 0 ? ssY : 0; + planeSpan[i].SubsamplingX = i != 0 ? ssX : 0; + planeSpan[i].SubsamplingY = i != 0 ? ssY : 0; } } @@ -158,12 +161,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { int aboveIdx = miCol * 2; int leftIdx = (miRow * 2) & 15; + + Span planeSpan = Plane.AsSpan(); + Span> aboveContextSpan = AboveContext.AsSpan(); + Span> leftContextSpan = LeftContext.AsSpan(); for (int i = 0; i < Constants.MaxMbPlane; ++i) { - ref MacroBlockDPlane pd = ref Plane[i]; - pd.AboveContext = AboveContext[i].Slice(aboveIdx >> pd.SubsamplingX); - pd.LeftContext = new ArrayPtr(ref LeftContext[i][leftIdx >> pd.SubsamplingY], + ref MacroBlockDPlane pd = ref planeSpan[i]; + pd.AboveContext = aboveContextSpan[i].Slice(aboveIdx >> pd.SubsamplingX); + pd.LeftContext = new ArrayPtr(ref leftContextSpan[i][leftIdx >> pd.SubsamplingY], 16 - (leftIdx >> pd.SubsamplingY)); } } @@ -182,9 +189,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public unsafe void DecResetSkipContext() { + Span planeSpan = Plane.AsSpan(); + for (int i = 0; i < Constants.MaxMbPlane; i++) { - ref MacroBlockDPlane pd = ref Plane[i]; + ref MacroBlockDPlane pd = ref planeSpan[i]; MemoryUtil.Fill(pd.AboveContext.ToPointer(), (sbyte)0, pd.N4W); MemoryUtil.Fill(pd.LeftContext.ToPointer(), (sbyte)0, pd.N4H); } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs index 9fd961bb9..46e970635 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/ModeInfo.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Memory; +using System; using System.Diagnostics; namespace Ryujinx.Graphics.Nvdec.Vp9.Types @@ -65,36 +66,41 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public Mv MvPredQ4(int idx) { + Span bmiSpan = Bmi.AsSpan(); + Mv res = new() { Row = (short)ReconInter.RoundMvCompQ4( - Bmi[0].Mv[idx].Row + Bmi[1].Mv[idx].Row + - Bmi[2].Mv[idx].Row + Bmi[3].Mv[idx].Row), + bmiSpan[0].Mv[idx].Row + bmiSpan[1].Mv[idx].Row + + bmiSpan[2].Mv[idx].Row + bmiSpan[3].Mv[idx].Row), Col = (short)ReconInter.RoundMvCompQ4( - Bmi[0].Mv[idx].Col + Bmi[1].Mv[idx].Col + - Bmi[2].Mv[idx].Col + Bmi[3].Mv[idx].Col) + bmiSpan[0].Mv[idx].Col + bmiSpan[1].Mv[idx].Col + + bmiSpan[2].Mv[idx].Col + bmiSpan[3].Mv[idx].Col) }; return res; } public Mv MvPredQ2(int idx, int block0, int block1) { + Span bmiSpan = Bmi.AsSpan(); + Mv res = new() { Row = (short)ReconInter.RoundMvCompQ2( - Bmi[block0].Mv[idx].Row + - Bmi[block1].Mv[idx].Row), + bmiSpan[block0].Mv[idx].Row + + bmiSpan[block1].Mv[idx].Row), Col = (short)ReconInter.RoundMvCompQ2( - Bmi[block0].Mv[idx].Col + - Bmi[block1].Mv[idx].Col) + bmiSpan[block0].Mv[idx].Col + + bmiSpan[block1].Mv[idx].Col) }; return res; } // Performs mv sign inversion if indicated by the reference frame combination. - public Mv ScaleMv(int refr, sbyte thisRefFrame, ref Array4 refSignBias) + public Mv ScaleMv(int refr, sbyte thisRefFrame, Span refSignBias) { Mv mv = Mv[refr]; + if (refSignBias[RefFrame[refr]] != refSignBias[thisRefFrame]) { mv.Row *= -1; diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs index 8a70107b3..69f78d27e 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Mv.cs @@ -99,10 +99,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types } else { + Span> bitsSpan = counts.Bits[comp].AsSpan(); + int b = c + Constants.Class0Bits - 1; // Number of bits for (int i = 0; i < b; ++i) { - counts.Bits[comp][i][(d >> i) & 1] += (uint)incr; + bitsSpan[i][(d >> i) & 1] += (uint)incr; } counts.Fp[comp][f] += (uint)incr; diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs index c34545791..71a0d1bde 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Segmentation.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public Array8 FeatureMask; public int AqAvOffset; - public static byte GetPredProbSegId(ref Array3 segPredProbs, ref MacroBlockD xd) + public static byte GetPredProbSegId(ReadOnlySpan segPredProbs, ref MacroBlockD xd) { return segPredProbs[xd.GetPredContextSegId()]; } @@ -105,9 +105,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types UpdateMap = rb.ReadBit() != 0; if (UpdateMap) { + Span segTreeProbSpan = fc.SegTreeProb.AsSpan(); + Span segPredProbSpan = fc.SegPredProb.AsSpan(); + for (int i = 0; i < SegTreeProbs; i++) { - fc.SegTreeProb[i] = rb.ReadBit() != 0 + segTreeProbSpan[i] = rb.ReadBit() != 0 ? (byte)rb.ReadLiteral(8) : (byte)Prob.MaxProb; } @@ -117,7 +120,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { for (int i = 0; i < PredictionProbs; i++) { - fc.SegPredProb[i] = rb.ReadBit() != 0 + segPredProbSpan[i] = rb.ReadBit() != 0 ? (byte)rb.ReadLiteral(8) : (byte)Prob.MaxProb; } @@ -126,7 +129,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { for (int i = 0; i < PredictionProbs; i++) { - fc.SegPredProb[i] = Prob.MaxProb; + segPredProbSpan[i] = Prob.MaxProb; } } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs index 3fc31127a..b4eda171f 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Common.cs @@ -155,9 +155,11 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public bool CompoundReferenceAllowed() { + Span refFrameSignBiasSpan = RefFrameSignBias.AsSpan(); + for (int i = 1; i < Constants.RefsPerFrame; ++i) { - if (RefFrameSignBias[i + 1] != RefFrameSignBias[1]) + if (refFrameSignBiasSpan[i + 1] != refFrameSignBiasSpan[1]) { return true; } @@ -173,13 +175,13 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public readonly int GetFreeFb() { - ref Array12 frameBufs = ref BufferPool.Value.FrameBufs; + Span frameBuffs = BufferPool.Value.FrameBufs.AsSpan(); int i; for (i = 0; i < Constants.FrameBuffers; ++i) { - if (frameBufs[i].RefCount == 0) + if (frameBuffs[i].RefCount == 0) { break; } @@ -187,7 +189,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types if (i != Constants.FrameBuffers) { - frameBufs[i].RefCount = 1; + frameBuffs[i].RefCount = 1; } else { @@ -240,25 +242,29 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types private void AllocSegMap(MemoryAllocator allocator, int segMapSize) { + Span> segMapArraySpan = SegMapArray.AsSpan(); + for (int i = 0; i < Constants.NumPingPongBuffers; ++i) { - SegMapArray[i] = allocator.Allocate(segMapSize); + segMapArraySpan[i] = allocator.Allocate(segMapSize); } // Init the index. SegMapIdx = 0; PrevSegMapIdx = 1; - CurrentFrameSegMap = SegMapArray[SegMapIdx]; - LastFrameSegMap = SegMapArray[PrevSegMapIdx]; + CurrentFrameSegMap = segMapArraySpan[SegMapIdx]; + LastFrameSegMap = segMapArraySpan[PrevSegMapIdx]; } private void FreeSegMap(MemoryAllocator allocator) { + Span> segMapArraySpan = SegMapArray.AsSpan(); + for (int i = 0; i < Constants.NumPingPongBuffers; ++i) { - allocator.Free(SegMapArray[i]); - SegMapArray[i] = ArrayPtr.Null; + allocator.Free(segMapArraySpan[i]); + segMapArraySpan[i] = ArrayPtr.Null; } CurrentFrameSegMap = ArrayPtr.Null; @@ -366,18 +372,21 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types internal void InitMacroBlockD(ref MacroBlockD xd, ArrayPtr dqcoeff) { + Span planeSpan = xd.Plane.AsSpan(); + Span> aboveContextSpan = xd.AboveContext.AsSpan(); + for (int i = 0; i < Constants.MaxMbPlane; ++i) { - xd.Plane[i].DqCoeff = dqcoeff; - xd.AboveContext[i] = AboveContext.Slice(i * 2 * TileInfo.MiColsAlignedToSb(MiCols)); + planeSpan[i].DqCoeff = dqcoeff; + aboveContextSpan[i] = AboveContext.Slice(i * 2 * TileInfo.MiColsAlignedToSb(MiCols)); if (i == 0) { - MemoryUtil.Copy(ref xd.Plane[i].SegDequant, ref YDequant); + MemoryUtil.Copy(ref planeSpan[i].SegDequant, ref YDequant); } else { - MemoryUtil.Copy(ref xd.Plane[i].SegDequant, ref UvDequant); + MemoryUtil.Copy(ref planeSpan[i].SegDequant, ref UvDequant); } xd.Fc = new Ptr(ref Fc.Value); @@ -395,32 +404,43 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types // Build y/uv dequant values based on segmentation. if (Seg.Enabled) { + Span> yDequantSpan1 = YDequant.AsSpan(); + Span> uvDequantSpan1 = UvDequant.AsSpan(); + for (int i = 0; i < Constants.MaxSegments; ++i) { + Span yDequantSpan2 = yDequantSpan1[i].AsSpan(); + Span uvDequantSpan2 = uvDequantSpan1[i].AsSpan(); + int qindex = Seg.GetQIndex(i, BaseQindex); - YDequant[i][0] = QuantCommon.DcQuant(qindex, YDcDeltaQ, BitDepth); - YDequant[i][1] = QuantCommon.AcQuant(qindex, 0, BitDepth); - UvDequant[i][0] = QuantCommon.DcQuant(qindex, UvDcDeltaQ, BitDepth); - UvDequant[i][1] = QuantCommon.AcQuant(qindex, UvAcDeltaQ, BitDepth); + yDequantSpan2[0] = QuantCommon.DcQuant(qindex, YDcDeltaQ, BitDepth); + yDequantSpan2[1] = QuantCommon.AcQuant(qindex, 0, BitDepth); + uvDequantSpan2[0] = QuantCommon.DcQuant(qindex, UvDcDeltaQ, BitDepth); + uvDequantSpan2[1] = QuantCommon.AcQuant(qindex, UvAcDeltaQ, BitDepth); } } else { + Span yDequantSpan = YDequant[0].AsSpan(); + Span uvDequantSpan = UvDequant[0].AsSpan(); + int qindex = BaseQindex; // When segmentation is disabled, only the first value is used. The // remaining are don't cares. - YDequant[0][0] = QuantCommon.DcQuant(qindex, YDcDeltaQ, BitDepth); - YDequant[0][1] = QuantCommon.AcQuant(qindex, 0, BitDepth); - UvDequant[0][0] = QuantCommon.DcQuant(qindex, UvDcDeltaQ, BitDepth); - UvDequant[0][1] = QuantCommon.AcQuant(qindex, UvAcDeltaQ, BitDepth); + yDequantSpan[0] = QuantCommon.DcQuant(qindex, YDcDeltaQ, BitDepth); + yDequantSpan[1] = QuantCommon.AcQuant(qindex, 0, BitDepth); + uvDequantSpan[0] = QuantCommon.DcQuant(qindex, UvDcDeltaQ, BitDepth); + uvDequantSpan[1] = QuantCommon.AcQuant(qindex, UvAcDeltaQ, BitDepth); } } public void SetupScaleFactors() { + Span frameRefsSpan = FrameRefs.AsSpan(); + for (int i = 0; i < Constants.RefsPerFrame; ++i) { - ref RefBuffer refBuf = ref FrameRefs[i]; + ref RefBuffer refBuf = ref frameRefsSpan[i]; refBuf.Sf.SetupScaleFactorsForFrame(refBuf.Buf.Width, refBuf.Buf.Height, Width, Height); } } @@ -431,26 +451,34 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types if (ReferenceMode == ReferenceMode.Select) { + Span compInterProbSpan = fc.CompInterProb.AsSpan(); + for (int i = 0; i < Constants.CompInterContexts; ++i) { - r.DiffUpdateProb(ref fc.CompInterProb[i]); + r.DiffUpdateProb(ref compInterProbSpan[i]); } } if (ReferenceMode != ReferenceMode.Compound) { + Span> singleRefProbSpan1 = fc.SingleRefProb.AsSpan(); + for (int i = 0; i < Constants.RefContexts; ++i) { - r.DiffUpdateProb(ref fc.SingleRefProb[i][0]); - r.DiffUpdateProb(ref fc.SingleRefProb[i][1]); + Span singleRefProbSpan2 = singleRefProbSpan1[i].AsSpan(); + + r.DiffUpdateProb(ref singleRefProbSpan2[0]); + r.DiffUpdateProb(ref singleRefProbSpan2[1]); } } if (ReferenceMode != ReferenceMode.Single) { + Span compRefProbSpan = fc.CompRefProb.AsSpan(); + for (int i = 0; i < Constants.RefContexts; ++i) { - r.DiffUpdateProb(ref fc.CompRefProb[i]); + r.DiffUpdateProb(ref compRefProbSpan[i]); } } } @@ -469,99 +497,124 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public void SetupCompoundReferenceMode() { - if (RefFrameSignBias[Constants.LastFrame] == RefFrameSignBias[Constants.GoldenFrame]) + Span refFrameSignBiasSpan = RefFrameSignBias.AsSpan(); + Span compVarRefSpan = CompVarRef.AsSpan(); + + if (refFrameSignBiasSpan[Constants.LastFrame] == refFrameSignBiasSpan[Constants.GoldenFrame]) { CompFixedRef = Constants.AltRefFrame; - CompVarRef[0] = Constants.LastFrame; - CompVarRef[1] = Constants.GoldenFrame; + compVarRefSpan[0] = Constants.LastFrame; + compVarRefSpan[1] = Constants.GoldenFrame; } - else if (RefFrameSignBias[Constants.LastFrame] == RefFrameSignBias[Constants.AltRefFrame]) + else if (refFrameSignBiasSpan[Constants.LastFrame] == refFrameSignBiasSpan[Constants.AltRefFrame]) { CompFixedRef = Constants.GoldenFrame; - CompVarRef[0] = Constants.LastFrame; - CompVarRef[1] = Constants.AltRefFrame; + compVarRefSpan[0] = Constants.LastFrame; + compVarRefSpan[1] = Constants.AltRefFrame; } else { CompFixedRef = Constants.LastFrame; - CompVarRef[0] = Constants.GoldenFrame; - CompVarRef[1] = Constants.AltRefFrame; + compVarRefSpan[0] = Constants.GoldenFrame; + compVarRefSpan[1] = Constants.AltRefFrame; } } public readonly void InitMvProbs() { - Fc.Value.Joints[0] = 32; - Fc.Value.Joints[1] = 64; - Fc.Value.Joints[2] = 96; + Span jointsSpan = Fc.Value.Joints.AsSpan(); + Span signSpan = Fc.Value.Sign.AsSpan(); + Span> classesSpan = Fc.Value.Classes.AsSpan(); + Span classes0Span = classesSpan[0].AsSpan(); + Span classes1Span = classesSpan[1].AsSpan(); + Span> class0Span = Fc.Value.Class0.AsSpan(); + Span> bitsSpan = Fc.Value.Bits.AsSpan(); + Span bits0Span = bitsSpan[0].AsSpan(); + Span bits1Span = bitsSpan[1].AsSpan(); + Span>> class0FpSpan = Fc.Value.Class0Fp.AsSpan(); + Span> class0Fp0Span = class0FpSpan[0].AsSpan(); + Span> class0Fp1Span = class0FpSpan[1].AsSpan(); + Span class0Fp00Span = class0Fp0Span[0].AsSpan(); + Span class0Fp01Span = class0Fp0Span[1].AsSpan(); + Span class0Fp10Span = class0Fp1Span[0].AsSpan(); + Span class0Fp11Span = class0Fp1Span[1].AsSpan(); + Span> fpSpan = Fc.Value.Fp.AsSpan(); + Span fp0Span = fpSpan[0].AsSpan(); + Span fp1Span = fpSpan[1].AsSpan(); + Span class0HpSpan = Fc.Value.Class0Hp.AsSpan(); + Span hpSpan = Fc.Value.Hp.AsSpan(); + + jointsSpan[0] = 32; + jointsSpan[1] = 64; + jointsSpan[2] = 96; - Fc.Value.Sign[0] = 128; - Fc.Value.Classes[0][0] = 224; - Fc.Value.Classes[0][1] = 144; - Fc.Value.Classes[0][2] = 192; - Fc.Value.Classes[0][3] = 168; - Fc.Value.Classes[0][4] = 192; - Fc.Value.Classes[0][5] = 176; - Fc.Value.Classes[0][6] = 192; - Fc.Value.Classes[0][7] = 198; - Fc.Value.Classes[0][8] = 198; - Fc.Value.Classes[0][9] = 245; - Fc.Value.Class0[0][0] = 216; - Fc.Value.Bits[0][0] = 136; - Fc.Value.Bits[0][1] = 140; - Fc.Value.Bits[0][2] = 148; - Fc.Value.Bits[0][3] = 160; - Fc.Value.Bits[0][4] = 176; - Fc.Value.Bits[0][5] = 192; - Fc.Value.Bits[0][6] = 224; - Fc.Value.Bits[0][7] = 234; - Fc.Value.Bits[0][8] = 234; - Fc.Value.Bits[0][9] = 240; - Fc.Value.Class0Fp[0][0][0] = 128; - Fc.Value.Class0Fp[0][0][1] = 128; - Fc.Value.Class0Fp[0][0][2] = 64; - Fc.Value.Class0Fp[0][1][0] = 96; - Fc.Value.Class0Fp[0][1][1] = 112; - Fc.Value.Class0Fp[0][1][2] = 64; - Fc.Value.Fp[0][0] = 64; - Fc.Value.Fp[0][1] = 96; - Fc.Value.Fp[0][2] = 64; - Fc.Value.Class0Hp[0] = 160; - Fc.Value.Hp[0] = 128; + signSpan[0] = 128; + classes0Span[0] = 224; + classes0Span[1] = 144; + classes0Span[2] = 192; + classes0Span[3] = 168; + classes0Span[4] = 192; + classes0Span[5] = 176; + classes0Span[6] = 192; + classes0Span[7] = 198; + classes0Span[8] = 198; + classes0Span[9] = 245; + class0Span[0][0] = 216; + bits0Span[0] = 136; + bits0Span[1] = 140; + bits0Span[2] = 148; + bits0Span[3] = 160; + bits0Span[4] = 176; + bits0Span[5] = 192; + bits0Span[6] = 224; + bits0Span[7] = 234; + bits0Span[8] = 234; + bits0Span[9] = 240; + class0Fp00Span[0] = 128; + class0Fp00Span[1] = 128; + class0Fp00Span[2] = 64; + class0Fp01Span[0] = 96; + class0Fp01Span[1] = 112; + class0Fp01Span[2] = 64; + fp0Span[0] = 64; + fp0Span[1] = 96; + fp0Span[2] = 64; + class0HpSpan[0] = 160; + hpSpan[0] = 128; - Fc.Value.Sign[1] = 128; - Fc.Value.Classes[1][0] = 216; - Fc.Value.Classes[1][1] = 128; - Fc.Value.Classes[1][2] = 176; - Fc.Value.Classes[1][3] = 160; - Fc.Value.Classes[1][4] = 176; - Fc.Value.Classes[1][5] = 176; - Fc.Value.Classes[1][6] = 192; - Fc.Value.Classes[1][7] = 198; - Fc.Value.Classes[1][8] = 198; - Fc.Value.Classes[1][9] = 208; - Fc.Value.Class0[1][0] = 208; - Fc.Value.Bits[1][0] = 136; - Fc.Value.Bits[1][1] = 140; - Fc.Value.Bits[1][2] = 148; - Fc.Value.Bits[1][3] = 160; - Fc.Value.Bits[1][4] = 176; - Fc.Value.Bits[1][5] = 192; - Fc.Value.Bits[1][6] = 224; - Fc.Value.Bits[1][7] = 234; - Fc.Value.Bits[1][8] = 234; - Fc.Value.Bits[1][9] = 240; - Fc.Value.Class0Fp[1][0][0] = 128; - Fc.Value.Class0Fp[1][0][1] = 128; - Fc.Value.Class0Fp[1][0][2] = 64; - Fc.Value.Class0Fp[1][1][0] = 96; - Fc.Value.Class0Fp[1][1][1] = 112; - Fc.Value.Class0Fp[1][1][2] = 64; - Fc.Value.Fp[1][0] = 64; - Fc.Value.Fp[1][1] = 96; - Fc.Value.Fp[1][2] = 64; - Fc.Value.Class0Hp[1] = 160; - Fc.Value.Hp[1] = 128; + signSpan[1] = 128; + classes1Span[0] = 216; + classes1Span[1] = 128; + classes1Span[2] = 176; + classes1Span[3] = 160; + classes1Span[4] = 176; + classes1Span[5] = 176; + classes1Span[6] = 192; + classes1Span[7] = 198; + classes1Span[8] = 198; + classes1Span[9] = 208; + class0Span[1][0] = 208; + bits1Span[0] = 136; + bits1Span[1] = 140; + bits1Span[2] = 148; + bits1Span[3] = 160; + bits1Span[4] = 176; + bits1Span[5] = 192; + bits1Span[6] = 224; + bits1Span[7] = 234; + bits1Span[8] = 234; + bits1Span[9] = 240; + class0Fp10Span[0] = 128; + class0Fp10Span[1] = 128; + class0Fp10Span[2] = 64; + class0Fp11Span[0] = 96; + class0Fp11Span[1] = 112; + class0Fp11Span[2] = 64; + fp1Span[0] = 64; + fp1Span[1] = 96; + fp1Span[2] = 64; + class0HpSpan[1] = 160; + hpSpan[1] = 128; } public void AdaptMvProbs(bool allowHp) @@ -576,41 +629,74 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types counts.Joints.AsSpan(), fc.Joints.AsSpan()); + Span fSignSpan = fc.Sign.AsSpan(); + Span pSignSpan = preFc.Sign.AsSpan(); + Span> cSignSpan = counts.Sign.AsSpan(); + Span> fClassesSpan = fc.Classes.AsSpan(); + Span> pClassesSpan = preFc.Classes.AsSpan(); + Span> cClassesSpan = counts.Classes.AsSpan(); + Span> fClass0Span = fc.Class0.AsSpan(); + Span> pClass0Span = preFc.Class0.AsSpan(); + Span> cClass0Span = counts.Class0.AsSpan(); + Span> fBitsSpan1 = fc.Bits.AsSpan(); + Span> pBitsSpan1 = preFc.Bits.AsSpan(); + Span>> cBitsSpan1 = counts.Bits.AsSpan(); + Span>> fClass0FpSpan1 = fc.Class0Fp.AsSpan(); + Span>> pClass0FpSpan1 = preFc.Class0Fp.AsSpan(); + Span>> cClass0FpSpan1 = counts.Class0Fp.AsSpan(); + Span> fFpSpan = fc.Fp.AsSpan(); + Span> pFpSpan = preFc.Fp.AsSpan(); + Span> cFpSpan = counts.Fp.AsSpan(); + Span fClass0HpSpan = fc.Class0Hp.AsSpan(); + Span pClass0HpSpan = preFc.Class0Hp.AsSpan(); + Span> cClass0HpSpan = counts.Class0Hp.AsSpan(); + Span fHpSpan = fc.Hp.AsSpan(); + Span pHpSpan = preFc.Hp.AsSpan(); + Span> cHpSpan = counts.Hp.AsSpan(); + for (int i = 0; i < 2; ++i) { - fc.Sign[i] = Prob.ModeMvMergeProbs(preFc.Sign[i], ref counts.Sign[i]); + fSignSpan[i] = Prob.ModeMvMergeProbs(pSignSpan[i], cSignSpan[i].AsSpan()); Prob.VpxTreeMergeProbs( EntropyMv.ClassTree, - preFc.Classes[i].AsSpan(), - counts.Classes[i].AsSpan(), - fc.Classes[i].AsSpan()); + pClassesSpan[i].AsSpan(), + cClassesSpan[i].AsSpan(), + fClassesSpan[i].AsSpan()); Prob.VpxTreeMergeProbs( EntropyMv.Class0Tree, - preFc.Class0[i].AsSpan(), - counts.Class0[i].AsSpan(), - fc.Class0[i].AsSpan()); + pClass0Span[i].AsSpan(), + cClass0Span[i].AsSpan(), + fClass0Span[i].AsSpan()); + + Span fBitsSpan2 = fBitsSpan1[i].AsSpan(); + Span pBitsSpan2 = pBitsSpan1[i].AsSpan(); + Span> cBitsSpan2 = cBitsSpan1[i].AsSpan(); for (int j = 0; j < EntropyMv.OffsetBits; ++j) { - fc.Bits[i][j] = Prob.ModeMvMergeProbs(preFc.Bits[i][j], ref counts.Bits[i][j]); + fBitsSpan2[j] = Prob.ModeMvMergeProbs(pBitsSpan2[j], cBitsSpan2[j].AsSpan()); } + Span> fClass0FpSpan2 = fClass0FpSpan1[i].AsSpan(); + Span> pClass0FpSpan2 = pClass0FpSpan1[i].AsSpan(); + Span> cClass0FpSpan2 = cClass0FpSpan1[i].AsSpan(); + for (int j = 0; j < EntropyMv.Class0Size; ++j) { Prob.VpxTreeMergeProbs( EntropyMv.FpTree, - preFc.Class0Fp[i][j].AsSpan(), - counts.Class0Fp[i][j].AsSpan(), - fc.Class0Fp[i][j].AsSpan()); + pClass0FpSpan2[j].AsSpan(), + cClass0FpSpan2[j].AsSpan(), + fClass0FpSpan2[j].AsSpan()); } - Prob.VpxTreeMergeProbs(EntropyMv.FpTree, preFc.Fp[i].AsSpan(), counts.Fp[i].AsSpan(), - fc.Fp[i].AsSpan()); + Prob.VpxTreeMergeProbs(EntropyMv.FpTree, pFpSpan[i].AsSpan(), cFpSpan[i].AsSpan(), + fFpSpan[i].AsSpan()); if (allowHp) { - fc.Class0Hp[i] = Prob.ModeMvMergeProbs(preFc.Class0Hp[i], ref counts.Class0Hp[i]); - fc.Hp[i] = Prob.ModeMvMergeProbs(preFc.Hp[i], ref counts.Hp[i]); + fClass0HpSpan[i] = Prob.ModeMvMergeProbs(pClass0HpSpan[i], cClass0HpSpan[i].AsSpan()); + fHpSpan[i] = Prob.ModeMvMergeProbs(pHpSpan[i], cHpSpan[i].AsSpan()); } } } @@ -769,75 +855,115 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types ref Vp9EntropyProbs preFc = ref FrameContexts[(int)FrameContextIdx]; ref Vp9BackwardUpdates counts = ref Counts.Value; + Span fIntraInterProbSpan = fc.IntraInterProb.AsSpan(); + Span pIntraInterProbSpan = preFc.IntraInterProb.AsSpan(); + Span> cIntraInterSpan = counts.IntraInter.AsSpan(); + for (int i = 0; i < Constants.IntraInterContexts; i++) { - fc.IntraInterProb[i] = Prob.ModeMvMergeProbs(preFc.IntraInterProb[i], ref counts.IntraInter[i]); + fIntraInterProbSpan[i] = Prob.ModeMvMergeProbs(pIntraInterProbSpan[i], cIntraInterSpan[i].AsSpan()); } + + Span fCompInterProbSpan = fc.CompInterProb.AsSpan(); + Span pCompInterProbSpan = preFc.CompInterProb.AsSpan(); + Span> cCompInterSpan = counts.CompInter.AsSpan(); for (int i = 0; i < Constants.CompInterContexts; i++) { - fc.CompInterProb[i] = Prob.ModeMvMergeProbs(preFc.CompInterProb[i], ref counts.CompInter[i]); + fCompInterProbSpan[i] = Prob.ModeMvMergeProbs(pCompInterProbSpan[i], cCompInterSpan[i].AsSpan()); } + + Span fCompRefProbSpan = fc.CompRefProb.AsSpan(); + Span pCompRefProbSpan = preFc.CompRefProb.AsSpan(); + Span> cCompRefSpan = counts.CompRef.AsSpan(); for (int i = 0; i < Constants.RefContexts; i++) { - fc.CompRefProb[i] = Prob.ModeMvMergeProbs(preFc.CompRefProb[i], ref counts.CompRef[i]); + fCompRefProbSpan[i] = Prob.ModeMvMergeProbs(pCompRefProbSpan[i], cCompRefSpan[i].AsSpan()); } + + Span> fSingleRefProbSpan1 = fc.SingleRefProb.AsSpan(); + Span> pSingleRefProbSpan1 = preFc.SingleRefProb.AsSpan(); + Span>> cSingleRefSpan1 = counts.SingleRef.AsSpan(); for (int i = 0; i < Constants.RefContexts; i++) { + Span fSingleRefProbSpan2 = fSingleRefProbSpan1[i].AsSpan(); + Span pSingleRefProbSpan2 = pSingleRefProbSpan1[i].AsSpan(); + Span> cSingleRefSpan2 = cSingleRefSpan1[i].AsSpan(); + for (int j = 0; j < 2; j++) { - fc.SingleRefProb[i][j] = - Prob.ModeMvMergeProbs(preFc.SingleRefProb[i][j], ref counts.SingleRef[i][j]); + fSingleRefProbSpan2[j] = + Prob.ModeMvMergeProbs(pSingleRefProbSpan2[j], cSingleRefSpan2[j].AsSpan()); } } + + Span> fInterModeProbSpan = fc.InterModeProb.AsSpan(); + Span> pInterModeProbSpan = preFc.InterModeProb.AsSpan(); + Span> cInterModeSpan = counts.InterMode.AsSpan(); for (int i = 0; i < Constants.InterModeContexts; i++) { Prob.VpxTreeMergeProbs( EntropyMode.InterModeTree, - preFc.InterModeProb[i].AsSpan(), - counts.InterMode[i].AsSpan(), - fc.InterModeProb[i].AsSpan()); + pInterModeProbSpan[i].AsSpan(), + cInterModeSpan[i].AsSpan(), + fInterModeProbSpan[i].AsSpan()); } + + Span> fYModeProbSpan = fc.YModeProb.AsSpan(); + Span> pYModeProbSpan = preFc.YModeProb.AsSpan(); + Span> cYModeSpan = counts.YMode.AsSpan(); for (int i = 0; i < EntropyMode.BlockSizeGroups; i++) { Prob.VpxTreeMergeProbs( EntropyMode.IntraModeTree, - preFc.YModeProb[i].AsSpan(), - counts.YMode[i].AsSpan(), - fc.YModeProb[i].AsSpan()); + pYModeProbSpan[i].AsSpan(), + cYModeSpan[i].AsSpan(), + fYModeProbSpan[i].AsSpan()); } + + Span> fUvModeProbSpan = fc.UvModeProb.AsSpan(); + Span> pUvModeProbSpan = preFc.UvModeProb.AsSpan(); + Span> cUvModeSpan = counts.UvMode.AsSpan(); for (int i = 0; i < Constants.IntraModes; ++i) { Prob.VpxTreeMergeProbs( EntropyMode.IntraModeTree, - preFc.UvModeProb[i].AsSpan(), - counts.UvMode[i].AsSpan(), - fc.UvModeProb[i].AsSpan()); + pUvModeProbSpan[i].AsSpan(), + cUvModeSpan[i].AsSpan(), + fUvModeProbSpan[i].AsSpan()); } + + Span> fPartitionProbSpan = fc.PartitionProb.AsSpan(); + Span> pPartitionProbSpan = preFc.PartitionProb.AsSpan(); + Span> cPartitionSpan = counts.Partition.AsSpan(); for (int i = 0; i < Constants.PartitionContexts; i++) { Prob.VpxTreeMergeProbs( EntropyMode.PartitionTree, - preFc.PartitionProb[i].AsSpan(), - counts.Partition[i].AsSpan(), - fc.PartitionProb[i].AsSpan()); + pPartitionProbSpan[i].AsSpan(), + cPartitionSpan[i].AsSpan(), + fPartitionProbSpan[i].AsSpan()); } if (InterpFilter == Constants.Switchable) { + Span> fSwitchableInterpProbSpan = fc.SwitchableInterpProb.AsSpan(); + Span> pSwitchableInterpProbSpan = preFc.SwitchableInterpProb.AsSpan(); + Span> cSwitchableInterpSpan = counts.SwitchableInterp.AsSpan(); + for (int i = 0; i < Constants.SwitchableFilterContexts; i++) { Prob.VpxTreeMergeProbs( EntropyMode.SwitchableInterpTree, - preFc.SwitchableInterpProb[i].AsSpan(), - counts.SwitchableInterp[i].AsSpan(), - fc.SwitchableInterpProb[i].AsSpan()); + pSwitchableInterpProbSpan[i].AsSpan(), + cSwitchableInterpSpan[i].AsSpan(), + fSwitchableInterpProbSpan[i].AsSpan()); } } @@ -846,34 +972,62 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types Array1> branchCt8X8P = new(); Array2> branchCt16X16P = new(); Array3> branchCt32X32P = new(); + + Span> branchCt8X8PSpan = branchCt8X8P.AsSpan(); + Span> branchCt16X16PSpan = branchCt16X16P.AsSpan(); + Span> branchCt32X32PSpan = branchCt32X32P.AsSpan(); + + Span> tx8x8Span = counts.Tx8x8.AsSpan(); + Span> tx16x16Span = counts.Tx8x8.AsSpan(); + Span> tx32x32Span = counts.Tx8x8.AsSpan(); + + //There is no need for a Span2, as there is only ever 1 iteration + Span> fTx8x8ProbSpan = fc.Tx8x8Prob.AsSpan(); + Span> pTx8x8ProbSpan = preFc.Tx8x8Prob.AsSpan(); + + Span> fTx16x16ProbSpan1 = fc.Tx16x16Prob.AsSpan(); + Span> pTx16x16ProbSpan1 = preFc.Tx16x16Prob.AsSpan(); + + Span> fTx32x32ProbSpan1 = fc.Tx32x32Prob.AsSpan(); + Span> pTx32x32ProbSpan1 = preFc.Tx32x32Prob.AsSpan(); for (int i = 0; i < EntropyMode.TxSizeContexts; ++i) { - EntropyMode.TxCountsToBranchCounts8X8(counts.Tx8x8[i].AsSpan(), ref branchCt8X8P); + EntropyMode.TxCountsToBranchCounts8X8(tx8x8Span[i].AsSpan(), branchCt8X8P.AsSpan()); for (int j = 0; j < (int)TxSize.TxSizes - 3; ++j) { - fc.Tx8x8Prob[i][j] = Prob.ModeMvMergeProbs(preFc.Tx8x8Prob[i][j], ref branchCt8X8P[j]); + fTx8x8ProbSpan[i][j] = Prob.ModeMvMergeProbs(pTx8x8ProbSpan[i][j], branchCt8X8PSpan[j].AsSpan()); } + + Span fTx16x16ProbSpan2 = fTx16x16ProbSpan1[i].AsSpan(); + Span pTx16x16ProbSpan2 = pTx16x16ProbSpan1[i].AsSpan(); - EntropyMode.TxCountsToBranchCounts16X16(counts.Tx16x16[i].AsSpan(), ref branchCt16X16P); + EntropyMode.TxCountsToBranchCounts16X16(tx16x16Span[i].AsSpan(), branchCt16X16P.AsSpan()); for (int j = 0; j < (int)TxSize.TxSizes - 2; ++j) { - fc.Tx16x16Prob[i][j] = - Prob.ModeMvMergeProbs(preFc.Tx16x16Prob[i][j], ref branchCt16X16P[j]); + fTx16x16ProbSpan2[j] = + Prob.ModeMvMergeProbs(pTx16x16ProbSpan2[j], branchCt16X16PSpan[j].AsSpan()); } + + Span fTx32x32ProbSpan2 = fTx32x32ProbSpan1[i].AsSpan(); + Span pTx32x32ProbSpan2 = pTx32x32ProbSpan1[i].AsSpan(); - EntropyMode.TxCountsToBranchCounts32X32(counts.Tx32x32[i].AsSpan(), ref branchCt32X32P); + EntropyMode.TxCountsToBranchCounts32X32(tx32x32Span[i].AsSpan(), branchCt32X32P.AsSpan()); for (int j = 0; j < (int)TxSize.TxSizes - 1; ++j) { - fc.Tx32x32Prob[i][j] = - Prob.ModeMvMergeProbs(preFc.Tx32x32Prob[i][j], ref branchCt32X32P[j]); + fTx32x32ProbSpan2[j] = + Prob.ModeMvMergeProbs(pTx32x32ProbSpan2[j], branchCt32X32PSpan[j].AsSpan()); } } } + + Span fSkipProbSpan = fc.SkipProb.AsSpan(); + Span pSkipProbSpan = preFc.SkipProb.AsSpan(); + Span> cSkipSpan = counts.Skip.AsSpan(); for (int i = 0; i < Constants.SkipContexts; ++i) { - fc.SkipProb[i] = Prob.ModeMvMergeProbs(preFc.SkipProb[i], ref counts.Skip[i]); + fSkipProbSpan[i] = Prob.ModeMvMergeProbs(pSkipProbSpan[i], cSkipSpan[i].AsSpan()); } } @@ -916,13 +1070,19 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { ref MvRef mv = ref PrevFrameMvs[i]; - mv.Mv[0].Row = mvs[i].Mvs[0].Row; - mv.Mv[0].Col = mvs[i].Mvs[0].Col; - mv.Mv[1].Row = mvs[i].Mvs[1].Row; - mv.Mv[1].Col = mvs[i].Mvs[1].Col; + Span mvSpan = mv.Mv.AsSpan(); + Span mvsSpan = mvs[i].Mvs.AsSpan(); - mv.RefFrame[0] = (sbyte)mvs[i].RefFrames[0]; - mv.RefFrame[1] = (sbyte)mvs[i].RefFrames[1]; + mvSpan[0].Row = mvsSpan[0].Row; + mvSpan[0].Col = mvsSpan[0].Col; + mvSpan[1].Row = mvsSpan[1].Row; + mvSpan[1].Col = mvsSpan[1].Col; + + Span refFrameSpan = mv.RefFrame.AsSpan(); + Span refFramesSpan = mvs[i].RefFrames.AsSpan(); + + refFrameSpan[0] = (sbyte)refFramesSpan[0]; + refFrameSpan[1] = (sbyte)refFramesSpan[1]; } } @@ -937,47 +1097,76 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types for (int i = 0; i < mvs.Length; i++) { ref MvRef mv = ref CurFrameMvs[i]; + + Span mvSpan = mv.Mv.AsSpan(); + Span mvsSpan = mvs[i].Mvs.AsSpan(); - mvs[i].Mvs[0].Row = mv.Mv[0].Row; - mvs[i].Mvs[0].Col = mv.Mv[0].Col; - mvs[i].Mvs[1].Row = mv.Mv[1].Row; - mvs[i].Mvs[1].Col = mv.Mv[1].Col; + mvsSpan[0].Row = mvSpan[0].Row; + mvsSpan[0].Col = mvSpan[0].Col; + mvsSpan[1].Row = mvSpan[1].Row; + mvsSpan[1].Col = mvSpan[1].Col; + + Span refFrameSpan = mv.RefFrame.AsSpan(); + Span refFramesSpan = mvs[i].RefFrames.AsSpan(); - mvs[i].RefFrames[0] = mv.RefFrame[0]; - mvs[i].RefFrames[1] = mv.RefFrame[1]; + refFramesSpan[0] = refFrameSpan[0]; + refFramesSpan[1] = refFrameSpan[1]; } } private void AdaptCoefProbs(byte txSize, uint countSat, uint updateFactor) { ref Vp9EntropyProbs preFc = ref FrameContexts[(int)FrameContextIdx]; - ref Array2>>>> probs = ref Fc.Value.CoefProbs[txSize]; - ref Array2>>>> preProbs = ref preFc.CoefProbs[txSize]; - ref Array2>>>> counts = ref Counts.Value.Coef[txSize]; - ref Array2>>> eobCounts = ref Counts.Value.EobBranch[txSize]; + Span>>>> probsSpan1 = Fc.Value.CoefProbs[txSize].AsSpan(); + Span>>>> preProbsSpan1 = preFc.CoefProbs[txSize].AsSpan(); + Span>>>> countsSpan1 = Counts.Value.Coef[txSize].AsSpan(); + Span>>> eobCountsSpan1 = Counts.Value.EobBranch[txSize].AsSpan(); for (int i = 0; i < Constants.PlaneTypes; ++i) { + Span>>> probsSpan2 = probsSpan1[i].AsSpan(); + Span>>> preProbsSpan2 = preProbsSpan1[i].AsSpan(); + Span>>> countsSpan2 = countsSpan1[i].AsSpan(); + Span>> eobCountsSpan2 = eobCountsSpan1[i].AsSpan(); + for (int j = 0; j < Entropy.RefTypes; ++j) { + Span>> probsSpan3 = probsSpan2[j].AsSpan(); + Span>> preProbsSpan3 = preProbsSpan2[j].AsSpan(); + Span>> countsSpan3 = countsSpan2[j].AsSpan(); + Span> eobCountsSpan3 = eobCountsSpan2[j].AsSpan(); + for (int k = 0; k < Entropy.CoefBands; ++k) { + Span> probsSpan4 = probsSpan3[k].AsSpan(); + Span> preProbsSpan4 = preProbsSpan3[k].AsSpan(); + Span> countsSpan4 = countsSpan3[k].AsSpan(); + Span eobCountsSpan4 = eobCountsSpan3[k].AsSpan(); + for (int l = 0; l < Entropy.BAND_COEFF_CONTEXTS(k); ++l) { - int n0 = (int)counts[i][j][k][l][Entropy.ZeroToken]; - int n1 = (int)counts[i][j][k][l][Entropy.OneToken]; - int n2 = (int)counts[i][j][k][l][Entropy.TwoToken]; - int neob = (int)counts[i][j][k][l][Entropy.EobModelToken]; + Span probsSpan5 = probsSpan4[l].AsSpan(); + Span preProbsSpan5 = preProbsSpan4[l].AsSpan(); + Span countsSpan5 = countsSpan4[l].AsSpan(); + + int n0 = (int)countsSpan5[Entropy.ZeroToken]; + int n1 = (int)countsSpan5[Entropy.OneToken]; + int n2 = (int)countsSpan5[Entropy.TwoToken]; + int neob = (int)countsSpan5[Entropy.EobModelToken]; Array3> branchCt = new(); - branchCt[0][0] = (uint)neob; - branchCt[0][1] = (uint)(eobCounts[i][j][k][l] - neob); - branchCt[1][0] = (uint)n0; - branchCt[1][1] = (uint)(n1 + n2); - branchCt[2][0] = (uint)n1; - branchCt[2][1] = (uint)n2; + Span> branchCtSpan = branchCt.AsSpan(); + Span branchCt0Span = branchCtSpan[0].AsSpan(); + Span branchCt1Span = branchCtSpan[1].AsSpan(); + Span branchCt2Span = branchCtSpan[2].AsSpan(); + branchCt0Span[0] = (uint)neob; + branchCt0Span[1] = (uint)(eobCountsSpan4[l] - neob); + branchCt1Span[0] = (uint)n0; + branchCt1Span[1] = (uint)(n1 + n2); + branchCt2Span[0] = (uint)n1; + branchCt2Span[1] = (uint)n2; for (int m = 0; m < Entropy.UnconstrainedNodes; ++m) { - probs[i][j][k][l][m] = Prob.MergeProbs(preProbs[i][j][k][l][m], ref branchCt[m], + probsSpan5[m] = Prob.MergeProbs(preProbsSpan5[m], branchCt[m].AsSpan(), countSat, updateFactor); } } diff --git a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Decoder.cs b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Decoder.cs index 4df8ef549..29315b286 100644 --- a/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Decoder.cs +++ b/src/Ryujinx.Graphics.Nvdec.Vp9/Types/Vp9Decoder.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.Nvdec.Vp9.Common; using Ryujinx.Graphics.Video; +using System; using System.Diagnostics; namespace Ryujinx.Graphics.Nvdec.Vp9.Types @@ -16,20 +17,20 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types public int NeedResync; // Wait for key/intra-only frame. public int HoldRefBuf; // Hold the reference buffer. - private static void DecreaseRefCount(int idx, ref Array12 frameBufs, ref BufferPool pool) + private static void DecreaseRefCount(int idx, Span frameBuffs, ref BufferPool pool) { - if (idx >= 0 && frameBufs[idx].RefCount > 0) + if (idx >= 0 && frameBuffs[idx].RefCount > 0) { - --frameBufs[idx].RefCount; + --frameBuffs[idx].RefCount; // A worker may only get a free framebuffer index when calling GetFreeFb. // But the private buffer is not set up until finish decoding header. // So any error happens during decoding header, the frame_bufs will not // have valid priv buffer. - if (frameBufs[idx].Released == 0 && frameBufs[idx].RefCount == 0 && - !frameBufs[idx].RawFrameBuffer.Priv.IsNull) + if (frameBuffs[idx].Released == 0 && frameBuffs[idx].RefCount == 0 && + !frameBuffs[idx].RawFrameBuffer.Priv.IsNull) { - FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBufs[idx].RawFrameBuffer); - frameBufs[idx].Released = 1; + FrameBuffers.ReleaseFrameBuffer(pool.CbPriv, ref frameBuffs[idx].RawFrameBuffer); + frameBuffs[idx].Released = 1; } } } @@ -43,22 +44,32 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types cm.CheckMemError(ref cm.FrameContexts, allocator.Allocate(Constants.FrameContexts)); + Span>> cKfYModeProbSpan1 = cm.Fc.Value.KfYModeProb.AsSpan(); + for (int i = 0; i < EntropyMode.KfYModeProb.Length; i++) { + Span> cKfYModeProbSpan2 = cKfYModeProbSpan1[i].AsSpan(); + for (int j = 0; j < EntropyMode.KfYModeProb[i].Length; j++) { + Span cKfYModeProbSpan3 = cKfYModeProbSpan2[j].AsSpan(); + for (int k = 0; k < EntropyMode.KfYModeProb[i][j].Length; k++) { - cm.Fc.Value.KfYModeProb[i][j][k] = EntropyMode.KfYModeProb[i][j][k]; + cKfYModeProbSpan3[k] = EntropyMode.KfYModeProb[i][j][k]; } } } + + Span> cKfUvModeProbSpan1 = cm.Fc.Value.KfUvModeProb.AsSpan(); for (int i = 0; i < EntropyMode.KfUvModeProb.Length; i++) { + Span cKfUvModeProbSpan2 = cKfUvModeProbSpan1[i].AsSpan(); + for (int j = 0; j < EntropyMode.KfUvModeProb[i].Length; j++) { - cm.Fc.Value.KfUvModeProb[i][j] = EntropyMode.KfUvModeProb[i][j]; + cKfUvModeProbSpan2[j] = EntropyMode.KfUvModeProb[i][j]; } } @@ -88,12 +99,16 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types [57, 15, 9], // l split, a not split [12, 3, 3] // a/l both split ]; + + Span> cKfPartitionProbSpan1 = cm.Fc.Value.KfPartitionProb.AsSpan(); for (int i = 0; i < kfPartitionProbs.Length; i++) { + Span cKfPartitionProbSpan2 = cKfPartitionProbSpan1[i].AsSpan(); + for (int j = 0; j < kfPartitionProbs[i].Length; j++) { - cm.Fc.Value.KfPartitionProb[i][j] = kfPartitionProbs[i][j]; + cKfPartitionProbSpan2[j] = kfPartitionProbs[i][j]; } } @@ -101,11 +116,14 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types NeedResync = 1; + Span refFrameMapSpan = cm.RefFrameMap.AsSpan(); + Span nextRefFrameMapSpan = cm.NextRefFrameMap.AsSpan(); + // Initialize the references to not point to any frame buffers. for (int i = 0; i < 8; i++) { - cm.RefFrameMap[i] = -1; - cm.NextRefFrameMap[i] = -1; + refFrameMapSpan[i] = -1; + nextRefFrameMapSpan[i] = -1; } cm.CurrentVideoFrame = 0; @@ -124,30 +142,34 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types int refIndex = 0, mask; ref Vp9Common cm = ref Common; ref BufferPool pool = ref cm.BufferPool.Value; - ref Array12 frameBufs = ref cm.BufferPool.Value.FrameBufs; + Span frameBufs = cm.BufferPool.Value.FrameBufs.AsSpan(); + + Span refFrameMapSpan = cm.RefFrameMap.AsSpan(); + Span nextRefFrameMapSpan = cm.NextRefFrameMap.AsSpan(); + Span frameRefsSpan = cm.FrameRefs.AsSpan(); for (mask = RefreshFrameFlags; mask != 0; mask >>= 1) { - int oldIdx = cm.RefFrameMap[refIndex]; + int oldIdx = refFrameMapSpan[refIndex]; // Current thread releases the holding of reference frame. - DecreaseRefCount(oldIdx, ref frameBufs, ref pool); + DecreaseRefCount(oldIdx, frameBufs, ref pool); // Release the reference frame in reference map. if ((mask & 1) != 0) { - DecreaseRefCount(oldIdx, ref frameBufs, ref pool); + DecreaseRefCount(oldIdx, frameBufs, ref pool); } - cm.RefFrameMap[refIndex] = cm.NextRefFrameMap[refIndex]; + refFrameMapSpan[refIndex] = nextRefFrameMapSpan[refIndex]; ++refIndex; } // Current thread releases the holding of reference frame. for (; refIndex < Constants.RefFrames && cm.ShowExistingFrame == 0; ++refIndex) { - int oldIdx = cm.RefFrameMap[refIndex]; - DecreaseRefCount(oldIdx, ref frameBufs, ref pool); - cm.RefFrameMap[refIndex] = cm.NextRefFrameMap[refIndex]; + int oldIdx = refFrameMapSpan[refIndex]; + DecreaseRefCount(oldIdx, frameBufs, ref pool); + refFrameMapSpan[refIndex] = nextRefFrameMapSpan[refIndex]; } HoldRefBuf = 0; @@ -158,7 +180,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types // Invalidate these references until the next frame starts. for (refIndex = 0; refIndex < 3; refIndex++) { - cm.FrameRefs[refIndex].Idx = RefBuffer.InvalidIdx; + frameRefsSpan[refIndex].Idx = RefBuffer.InvalidIdx; } } @@ -166,7 +188,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types { ref Vp9Common cm = ref Common; ref BufferPool pool = ref cm.BufferPool.Value; - ref Array12 frameBufs = ref cm.BufferPool.Value.FrameBufs; + Span frameBufs = cm.BufferPool.Value.FrameBufs.AsSpan(); ArrayPtr source = psource; CodecErr retcode = 0; cm.Error.ErrorCode = CodecErr.Ok; @@ -177,10 +199,12 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types // We do not know if the missing frame(s) was supposed to update // any of the reference buffers, but we act conservative and // mark only the last buffer as corrupted. + + Span frameRefsSpan = cm.FrameRefs.AsSpan(); - if (cm.FrameRefs[0].Idx > 0) + if (frameRefsSpan[0].Idx > 0) { - cm.FrameRefs[0].Buf.Corrupted = 1; + frameRefsSpan[0].Buf.Corrupted = 1; } } @@ -279,8 +303,9 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types ArrayPtr dataStart = data; CodecErr res; Array8 frameSizes = new(); - - res = Decoder.ParseSuperframeIndex(data, (ulong)data.Length, ref frameSizes, out int frameCount); + Span frameSizesSpan = frameSizes.AsSpan(); + + res = Decoder.ParseSuperframeIndex(data, (ulong)data.Length, frameSizesSpan, out int frameCount); if (res != CodecErr.Ok) { return res; @@ -292,7 +317,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types for (int i = 0; i < frameCount; ++i) { ArrayPtr dataStartCopy = dataStart; - uint frameSize = frameSizes[i]; + uint frameSize = frameSizesSpan[i]; if (frameSize > (uint)dataStart.Length) { return CodecErr.CorruptFrame; @@ -343,7 +368,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types return data[0]; } - public static CodecErr ParseSuperframeIndex(ArrayPtr data, ulong dataSz, ref Array8 sizes, out int count) + public static CodecErr ParseSuperframeIndex(ArrayPtr data, ulong dataSz, Span sizes, out int count) { // A chunk ending with a byte matching 0xc0 is an invalid chunk unless // it is a super frame index. If the last byte of real video compression diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/BackwardUpdates.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/BackwardUpdates.cs index 20e815132..a65768405 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/BackwardUpdates.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/BackwardUpdates.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.Video; +using System; namespace Ryujinx.Graphics.Nvdec.Types.Vp9 { @@ -33,15 +34,24 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9 public BackwardUpdates(ref Vp9BackwardUpdates counts) { InterModeCounts = new Array7>>(); + + Span>> interModeCountsSpan1 = InterModeCounts.AsSpan(); + Span> interModeSpan1 = counts.InterMode.AsSpan(); for (int i = 0; i < 7; i++) { - InterModeCounts[i][0][0] = counts.InterMode[i][2]; - InterModeCounts[i][0][1] = counts.InterMode[i][0] + counts.InterMode[i][1] + counts.InterMode[i][3]; - InterModeCounts[i][1][0] = counts.InterMode[i][0]; - InterModeCounts[i][1][1] = counts.InterMode[i][1] + counts.InterMode[i][3]; - InterModeCounts[i][2][0] = counts.InterMode[i][1]; - InterModeCounts[i][2][1] = counts.InterMode[i][3]; + Span> interModeCountsSpan2 = interModeCountsSpan1[i].AsSpan(); + Span interModeCountsSpan20 = interModeCountsSpan2[0].AsSpan(); + Span interModeCountsSpan21 = interModeCountsSpan2[1].AsSpan(); + Span interModeCountsSpan22 = interModeCountsSpan2[2].AsSpan(); + Span interModeSpan2 = interModeSpan1[i].AsSpan(); + + interModeCountsSpan20[0] = interModeSpan2[2]; + interModeCountsSpan20[1] = interModeSpan2[0] + interModeSpan2[1] + interModeSpan2[3]; + interModeCountsSpan21[0] = interModeSpan2[0]; + interModeCountsSpan21[1] = interModeSpan2[1] + interModeSpan2[3]; + interModeCountsSpan22[0] = interModeSpan2[1]; + interModeCountsSpan22[1] = interModeSpan2[3]; } YModeCounts = counts.YMode; diff --git a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/EntropyProbs.cs b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/EntropyProbs.cs index 82a09866a..c9fa1f47e 100644 --- a/src/Ryujinx.Graphics.Nvdec/Types/Vp9/EntropyProbs.cs +++ b/src/Ryujinx.Graphics.Nvdec/Types/Vp9/EntropyProbs.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Memory; using Ryujinx.Graphics.Video; +using System; namespace Ryujinx.Graphics.Nvdec.Types.Vp9 { @@ -46,57 +47,98 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9 public void Convert(ref Vp9EntropyProbs fc) { + Span>> kfYModeProbSpan1 = fc.KfYModeProb.AsSpan(); + Span>> kfYModeProbE0ToE7Span1 = KfYModeProbE0ToE7.AsSpan(); + for (int i = 0; i < 10; i++) { + Span> kfYModeProbSpan2 = kfYModeProbSpan1[i].AsSpan(); + Span> kfYModeProbE0ToE7Span2 = kfYModeProbE0ToE7Span1[i].AsSpan(); + for (int j = 0; j < 10; j++) { + Span kfYModeProbSpan3 = kfYModeProbSpan2[j].AsSpan(); + Span kfYModeProbE0ToE7Span3 = kfYModeProbE0ToE7Span2[j].AsSpan(); + for (int k = 0; k < 9; k++) { - fc.KfYModeProb[i][j][k] = k < 8 ? KfYModeProbE0ToE7[i][j][k] : KfYModeProbE8[i][j]; + kfYModeProbSpan3[k] = k < 8 ? kfYModeProbE0ToE7Span3[k] : KfYModeProbE8[i][j]; } } } fc.SegTreeProb = SegTreeProbs; fc.SegPredProb = SegPredProbs; + + Span> interModeProbSpan1 = fc.InterModeProb.AsSpan(); + Span> gInterModeProbSpan1 = InterModeProb.AsSpan(); for (int i = 0; i < 7; i++) { + Span interModeProbSpan2 = interModeProbSpan1[i].AsSpan(); + Span gInterModeProbSpan2 = gInterModeProbSpan1[i].AsSpan(); + for (int j = 0; j < 3; j++) { - fc.InterModeProb[i][j] = InterModeProb[i][j]; + interModeProbSpan2[j] = gInterModeProbSpan2[j]; } } fc.IntraInterProb = IntraInterProb; + Span> kfUvModeProbSpan1 = fc.KfUvModeProb.AsSpan(); + Span> kfUvModeProbE0ToE7Span1 = KfUvModeProbE0ToE7.AsSpan(); + Span> uvModeProbSpan1 = fc.UvModeProb.AsSpan(); + Span> uvModeProbE0ToE7Span1 = UvModeProbE0ToE7.AsSpan(); + for (int i = 0; i < 10; i++) { + Span kfUvModeProbSpan2 = kfUvModeProbSpan1[i].AsSpan(); + Span kfUvModeProbE0ToE7Span2 = kfUvModeProbE0ToE7Span1[i].AsSpan(); + Span uvModeProbSpan2 = uvModeProbSpan1[i].AsSpan(); + Span uvModeProbE0ToE7Span2 = uvModeProbE0ToE7Span1[i].AsSpan(); + for (int j = 0; j < 9; j++) { - fc.KfUvModeProb[i][j] = j < 8 ? KfUvModeProbE0ToE7[i][j] : KfUvModeProbE8[i]; - fc.UvModeProb[i][j] = j < 8 ? UvModeProbE0ToE7[i][j] : UvModeProbE8[i]; + kfUvModeProbSpan2[j] = j < 8 ? kfUvModeProbE0ToE7Span2[j] : KfUvModeProbE8[i]; + uvModeProbSpan2[j] = j < 8 ? uvModeProbE0ToE7Span2[j] : UvModeProbE8[i]; } } fc.Tx8x8Prob = Tx8x8Prob; fc.Tx16x16Prob = Tx16x16Prob; fc.Tx32x32Prob = Tx32x32Prob; + + Span> yModeProbSpan1 = fc.YModeProb.AsSpan(); + Span> yModeProbE0ToE7Span1 = YModeProbE0ToE7.AsSpan(); for (int i = 0; i < 4; i++) { + Span yModeProbSpan2 = yModeProbSpan1[i].AsSpan(); + Span yModeProbE0ToE7Span2 = yModeProbE0ToE7Span1[i].AsSpan(); + for (int j = 0; j < 9; j++) { - fc.YModeProb[i][j] = j < 8 ? YModeProbE0ToE7[i][j] : YModeProbE8[i]; + yModeProbSpan2[j] = j < 8 ? yModeProbE0ToE7Span2[j] : YModeProbE8[i]; } } + + Span> kfPartitionProbSpan1 = fc.KfPartitionProb.AsSpan(); + Span> gKfPartitionProbSpan1 = KfPartitionProb.AsSpan(); + Span> partitionProbSpan1 = fc.PartitionProb.AsSpan(); + Span> gPartitionProbSpan1 = PartitionProb.AsSpan(); for (int i = 0; i < 16; i++) { + Span kfPartitionProbSpan2 = kfPartitionProbSpan1[i].AsSpan(); + Span gKfPartitionProbSpan2 = gKfPartitionProbSpan1[i].AsSpan(); + Span partitionProbSpan2 = partitionProbSpan1[i].AsSpan(); + Span gPartitionProbSpan2 = gPartitionProbSpan1[i].AsSpan(); + for (int j = 0; j < 3; j++) { - fc.KfPartitionProb[i][j] = KfPartitionProb[i][j]; - fc.PartitionProb[i][j] = PartitionProb[i][j]; + kfPartitionProbSpan2[j] = gKfPartitionProbSpan2[j]; + partitionProbSpan2[j] = gPartitionProbSpan2[j]; } } @@ -116,20 +158,38 @@ namespace Ryujinx.Graphics.Nvdec.Types.Vp9 fc.Bits = Bits; fc.SingleRefProb = SingleRefProb; fc.CompRefProb = CompRefProb; + + Span>>>>> coefProbsSpan1 = fc.CoefProbs.AsSpan(); + Span>>>>> gCoefProbsSpan1 = CoefProbs.AsSpan(); for (int i = 0; i < 4; i++) { + Span>>>> coefProbsSpan2 = coefProbsSpan1[i].AsSpan(); + Span>>>> gCoefProbsSpan2 = gCoefProbsSpan1[i].AsSpan(); + for (int j = 0; j < 2; j++) { + Span>>> coefProbsSpan3 = coefProbsSpan2[j].AsSpan(); + Span>>> gCoefProbsSpan3 = gCoefProbsSpan2[j].AsSpan(); + for (int k = 0; k < 2; k++) { + Span>> coefProbsSpan4 = coefProbsSpan3[k].AsSpan(); + Span>> gCoefProbsSpan4 = gCoefProbsSpan3[k].AsSpan(); + for (int l = 0; l < 6; l++) { + Span> coefProbsSpan5 = coefProbsSpan4[l].AsSpan(); + Span> gCoefProbsSpan5 = gCoefProbsSpan4[l].AsSpan(); + for (int m = 0; m < 6; m++) { + Span coefProbsSpan6 = coefProbsSpan5[m].AsSpan(); + Span gCoefProbsSpan6 = gCoefProbsSpan5[m].AsSpan(); + for (int n = 0; n < 3; n++) { - fc.CoefProbs[i][j][k][l][m][n] = CoefProbs[i][j][k][l][m][n]; + coefProbsSpan6[n] = gCoefProbsSpan6[n]; } } } diff --git a/src/Ryujinx.Graphics.Shader/AttributeType.cs b/src/Ryujinx.Graphics.Shader/AttributeType.cs index d2d146ecb..4c2913416 100644 --- a/src/Ryujinx.Graphics.Shader/AttributeType.cs +++ b/src/Ryujinx.Graphics.Shader/AttributeType.cs @@ -3,6 +3,7 @@ using System; namespace Ryujinx.Graphics.Shader { + [Flags] public enum AttributeType : byte { // Generic types. diff --git a/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs b/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs index 2c6ff0511..2747ae805 100644 --- a/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs +++ b/src/Ryujinx.Graphics.Vic/Image/SurfaceReader.cs @@ -17,12 +17,12 @@ namespace Ryujinx.Graphics.Vic.Image ResourceManager rm, ref SlotConfig config, ref SlotSurfaceConfig surfaceConfig, - ref Array8 offsets) + Span offsets) { switch (surfaceConfig.SlotPixelFormat) { case PixelFormat.Y8___V8U8_N420: - return ReadNv12(rm, ref config, ref surfaceConfig, ref offsets); + return ReadNv12(rm, ref config, ref surfaceConfig, offsets); } Logger.Error?.Print(LogClass.Vic, $"Unsupported pixel format \"{surfaceConfig.SlotPixelFormat}\"."); @@ -37,9 +37,9 @@ namespace Ryujinx.Graphics.Vic.Image ResourceManager rm, ref SlotConfig config, ref SlotSurfaceConfig surfaceConfig, - ref Array8 offsets) + Span offsets) { - InputSurface input = ReadSurface(rm, ref config, ref surfaceConfig, ref offsets, 1, 2); + InputSurface input = ReadSurface(rm, ref config, ref surfaceConfig, offsets, 1, 2); int width = input.Width; int height = input.Height; @@ -273,7 +273,7 @@ namespace Ryujinx.Graphics.Vic.Image ResourceManager rm, ref SlotConfig config, ref SlotSurfaceConfig surfaceConfig, - ref Array8 offsets, + Span offsets, int bytesPerPixel, int planes) { @@ -301,17 +301,17 @@ namespace Ryujinx.Graphics.Vic.Image if (planes > 0) { - surface.SetBuffer0(ReadBuffer(rm, ref config, ref offsets, linear, 0, lw, lh, bytesPerPixel, gobBlocksInY)); + surface.SetBuffer0(ReadBuffer(rm, ref config, offsets, linear, 0, lw, lh, bytesPerPixel, gobBlocksInY)); } if (planes > 1) { - surface.SetBuffer1(ReadBuffer(rm, ref config, ref offsets, linear, 1, cw, ch, planes == 2 ? 2 : 1, gobBlocksInY)); + surface.SetBuffer1(ReadBuffer(rm, ref config, offsets, linear, 1, cw, ch, planes == 2 ? 2 : 1, gobBlocksInY)); } if (planes > 2) { - surface.SetBuffer2(ReadBuffer(rm, ref config, ref offsets, linear, 2, cw, ch, 1, gobBlocksInY)); + surface.SetBuffer2(ReadBuffer(rm, ref config, offsets, linear, 2, cw, ch, 1, gobBlocksInY)); } return surface; @@ -320,7 +320,7 @@ namespace Ryujinx.Graphics.Vic.Image private static RentedBuffer ReadBuffer( ResourceManager rm, scoped ref SlotConfig config, - scoped ref Array8 offsets, + Span offsets, bool linear, int plane, int width, diff --git a/src/Ryujinx.Graphics.Vic/VicDevice.cs b/src/Ryujinx.Graphics.Vic/VicDevice.cs index 7cc5dda54..66b6cebed 100644 --- a/src/Ryujinx.Graphics.Vic/VicDevice.cs +++ b/src/Ryujinx.Graphics.Vic/VicDevice.cs @@ -35,18 +35,21 @@ namespace Ryujinx.Graphics.Vic config.OutputSurfaceConfig.OutSurfaceWidth + 1, config.OutputSurfaceConfig.OutSurfaceHeight + 1); - for (int i = 0; i < config.SlotStruct.Length; i++) + Span slotStructSpan = config.SlotStruct.AsSpan(); + Span> setSurfacexSlotxSpan = _state.State.SetSurfacexSlotx.AsSpan(); + + for (int i = 0; i < slotStructSpan.Length; i++) { - ref SlotStruct slot = ref config.SlotStruct[i]; + ref SlotStruct slot = ref slotStructSpan[i]; if (!slot.SlotConfig.SlotEnable) { continue; } - ref Array8 offsets = ref _state.State.SetSurfacexSlotx[i]; + Span offsets = setSurfacexSlotxSpan[i].AsSpan(); - using Surface src = SurfaceReader.Read(_rm, ref slot.SlotConfig, ref slot.SlotSurfaceConfig, ref offsets); + using Surface src = SurfaceReader.Read(_rm, ref slot.SlotConfig, ref slot.SlotSurfaceConfig, offsets); int x1 = config.OutputConfig.TargetRectLeft; int y1 = config.OutputConfig.TargetRectTop; diff --git a/src/Ryujinx.Graphics.Vulkan/BitMapStruct.cs b/src/Ryujinx.Graphics.Vulkan/BitMapStruct.cs index 3ec2c7f59..e1f109dd7 100644 --- a/src/Ryujinx.Graphics.Vulkan/BitMapStruct.cs +++ b/src/Ryujinx.Graphics.Vulkan/BitMapStruct.cs @@ -26,13 +26,17 @@ namespace Ryujinx.Graphics.Vulkan public bool BecomesUnsetFrom(in BitMapStruct from, ref BitMapStruct into) { bool result = false; + + Span masksSpan = _masks.AsSpan(); + Span fMasksSpan = from._masks.AsSpan(); + Span iMasksSpan = into._masks.AsSpan(); - int masks = _masks.Length; + int masks = masksSpan.Length; for (int i = 0; i < masks; i++) { - long fromMask = from._masks[i]; - long unsetMask = (~fromMask) & (fromMask ^ _masks[i]); - into._masks[i] = unsetMask; + long fromMask = fMasksSpan[i]; + long unsetMask = (~fromMask) & (fromMask ^ masksSpan[i]); + iMasksSpan[i] = unsetMask; result |= unsetMask != 0; } @@ -49,11 +53,11 @@ namespace Ryujinx.Graphics.Vulkan // Iterate the set bits in the result, and signal them. int offset = 0; - int masks = _masks.Length; - ref T resultMasks = ref result._masks; + Span rMasksSpan = result._masks.AsSpan(); + int masks = rMasksSpan.Length; for (int i = 0; i < masks; i++) { - long value = resultMasks[i]; + long value = rMasksSpan[i]; while (value != 0) { int bit = BitOperations.TrailingZeroCount((ulong)value); @@ -75,10 +79,11 @@ namespace Ryujinx.Graphics.Vulkan // Iterate the set bits in the result, and signal them. int offset = 0; - int masks = _masks.Length; + Span masksSpan = _masks.AsSpan(); + int masks = masksSpan.Length; for (int i = 0; i < masks; i++) { - long value = _masks[i]; + long value = masksSpan[i]; while (value != 0) { int bit = BitOperations.TrailingZeroCount((ulong)value); @@ -94,9 +99,11 @@ namespace Ryujinx.Graphics.Vulkan public bool AnySet() { - for (int i = 0; i < _masks.Length; i++) + Span masksSpan = _masks.AsSpan(); + + for (int i = 0; i < masksSpan.Length; i++) { - if (_masks[i] != 0) + if (masksSpan[i] != 0) { return true; } @@ -139,10 +146,12 @@ namespace Ryujinx.Graphics.Vulkan { return true; } + + Span masksSpan = _masks.AsSpan(); for (int i = startIndex + 1; i < endIndex; i++) { - if (_masks[i] != 0) + if (masksSpan[i] != 0) { return true; } @@ -200,21 +209,23 @@ namespace Ryujinx.Graphics.Vulkan int endIndex = end >> IntShift; int endBit = end & IntMask; long endMask = (long)(ulong.MaxValue >> (IntMask - endBit)); + + Span masksSpan = _masks.AsSpan(); if (startIndex == endIndex) { - _masks[startIndex] |= startMask & endMask; + masksSpan[startIndex] |= startMask & endMask; } else { - _masks[startIndex] |= startMask; + masksSpan[startIndex] |= startMask; for (int i = startIndex + 1; i < endIndex; i++) { - _masks[i] |= -1L; + masksSpan[i] |= -1L; } - _masks[endIndex] |= endMask; + masksSpan[endIndex] |= endMask; } } @@ -222,13 +233,13 @@ namespace Ryujinx.Graphics.Vulkan { BitMapStruct result = new(); - ref T masks = ref _masks; - ref T otherMasks = ref other._masks; - ref T newMasks = ref result._masks; + Span masksSpan = _masks.AsSpan(); + Span oMasksSpan = other._masks.AsSpan(); + Span nMasksSpan = result._masks.AsSpan(); - for (int i = 0; i < masks.Length; i++) + for (int i = 0; i < masksSpan.Length; i++) { - newMasks[i] = masks[i] | otherMasks[i]; + nMasksSpan[i] = masksSpan[i] | oMasksSpan[i]; } return result; @@ -246,17 +257,21 @@ namespace Ryujinx.Graphics.Vulkan public void Clear() { - for (int i = 0; i < _masks.Length; i++) + Span masksSpan = _masks.AsSpan(); + + for (int i = 0; i < masksSpan.Length; i++) { - _masks[i] = 0; + masksSpan[i] = 0; } } public void ClearInt(int start, int end) { + Span masksSpan = _masks.AsSpan(); + for (int i = start; i <= end; i++) { - _masks[i] = 0; + masksSpan[i] = 0; } } } diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index a0a238e38..2daa04856 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -680,7 +680,7 @@ namespace Ryujinx.Graphics.Vulkan ShaderCollection program = _program; - if (_dirty.HasFlag(DirtyFlags.Uniform)) + if ((_dirty & DirtyFlags.Uniform) == DirtyFlags.Uniform) { if (program.UsePushDescriptors) { @@ -692,12 +692,12 @@ namespace Ryujinx.Graphics.Vulkan } } - if (_dirty.HasFlag(DirtyFlags.Storage)) + if ((_dirty & DirtyFlags.Storage) == DirtyFlags.Storage) { UpdateAndBind(cbs, program, PipelineBase.StorageSetIndex, pbp); } - if (_dirty.HasFlag(DirtyFlags.Texture)) + if ((_dirty & DirtyFlags.Texture) == DirtyFlags.Texture) { if (program.UpdateTexturesWithoutTemplate) { @@ -709,7 +709,7 @@ namespace Ryujinx.Graphics.Vulkan } } - if (_dirty.HasFlag(DirtyFlags.Image)) + if ((_dirty & DirtyFlags.Image) == DirtyFlags.Image) { UpdateAndBind(cbs, program, PipelineBase.ImageSetIndex, pbp); } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index eac47e60e..fb244d307 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -710,9 +710,11 @@ namespace Ryujinx.Graphics.Vulkan public void SetBlendState(AdvancedBlendDescriptor blend) { + Span colorBlendAttachmentStateSpan = _newState.Internal.ColorBlendAttachmentState.AsSpan(); + for (int index = 0; index < Constants.MaxRenderTargets; index++) { - ref PipelineColorBlendAttachmentState vkBlend = ref _newState.Internal.ColorBlendAttachmentState[index]; + ref PipelineColorBlendAttachmentState vkBlend = ref colorBlendAttachmentStateSpan[index]; if (index == 0) { @@ -985,10 +987,12 @@ namespace Ryujinx.Graphics.Vulkan { int count = Math.Min(Constants.MaxRenderTargets, componentMask.Length); int writtenAttachments = 0; + + Span colorBlendAttachmentStateSpan = _newState.Internal.ColorBlendAttachmentState.AsSpan(); for (int i = 0; i < count; i++) { - ref PipelineColorBlendAttachmentState vkBlend = ref _newState.Internal.ColorBlendAttachmentState[i]; + ref PipelineColorBlendAttachmentState vkBlend = ref colorBlendAttachmentStateSpan[i]; ColorComponentFlags newMask = (ColorComponentFlags)componentMask[i]; // When color write mask is 0, remove all blend state to help the pipeline cache. @@ -1166,6 +1170,8 @@ namespace Ryujinx.Graphics.Vulkan int count = Math.Min(Constants.MaxVertexAttributes, vertexAttribs.Length); uint dirtyVbSizes = 0; + + Span vertexAttributeDescriptionsSpan = _newState.Internal.VertexAttributeDescriptions.AsSpan(); for (int i = 0; i < count; i++) { @@ -1179,7 +1185,7 @@ namespace Ryujinx.Graphics.Vulkan dirtyVbSizes |= 1u << rawIndex; } - _newState.Internal.VertexAttributeDescriptions[i] = new VertexInputAttributeDescription( + vertexAttributeDescriptionsSpan[i] = new VertexInputAttributeDescription( (uint)i, (uint)bufferIndex, formatCapabilities.ConvertToVertexVkFormat(attribute.Format), @@ -1214,7 +1220,9 @@ namespace Ryujinx.Graphics.Vulkan int validCount = 1; BufferHandle lastHandle = default; - Auto lastBuffer = default; + Auto lastBuffer = null; + + Span vertexBindingDescriptionsSpan = _newState.Internal.VertexBindingDescriptions.AsSpan(); for (int i = 0; i < count; i++) { @@ -1236,7 +1244,7 @@ namespace Ryujinx.Graphics.Vulkan int binding = i + 1; int descriptorIndex = validCount++; - _newState.Internal.VertexBindingDescriptions[descriptorIndex] = new VertexInputBindingDescription( + vertexBindingDescriptionsSpan[descriptorIndex] = new VertexInputBindingDescription( (uint)binding, (uint)vertexBuffer.Stride, inputRate); @@ -1405,6 +1413,9 @@ namespace Ryujinx.Graphics.Vulkan // Look for textures that are masked out. + Span colorBlendAttachmentStateSpan = + _newState.Internal.ColorBlendAttachmentState.AsSpan(); + for (int i = 0; i < colors.Length; i++) { if (colors[i] == null) @@ -1412,7 +1423,7 @@ namespace Ryujinx.Graphics.Vulkan continue; } - ref PipelineColorBlendAttachmentState vkBlend = ref _newState.Internal.ColorBlendAttachmentState[i]; + ref PipelineColorBlendAttachmentState vkBlend = ref colorBlendAttachmentStateSpan[i]; for (int j = 0; j < i; j++) { @@ -1421,7 +1432,7 @@ namespace Ryujinx.Graphics.Vulkan if (colors[i] == colors[j]) { // Prefer the binding with no write mask. - ref PipelineColorBlendAttachmentState vkBlend2 = ref _newState.Internal.ColorBlendAttachmentState[j]; + ref PipelineColorBlendAttachmentState vkBlend2 = ref colorBlendAttachmentStateSpan[j]; if (vkBlend.ColorWriteMask == 0) { colors[i] = null; diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 372577cac..c259d91a9 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -2,7 +2,8 @@ using Ryujinx.Common; using Ryujinx.Graphics.GAL; using Silk.NET.Vulkan; using System; -using Format = Silk.NET.Vulkan.Format; +using VulkanFormat = Silk.NET.Vulkan.Format; +using GALFormat = Ryujinx.Graphics.GAL.Format; using PolygonMode = Silk.NET.Vulkan.PolygonMode; namespace Ryujinx.Graphics.Vulkan @@ -23,7 +24,7 @@ namespace Ryujinx.Graphics.Vulkan AttachmentReference* attachmentReferences = stackalloc AttachmentReference[MaxAttachments]; Span attachmentIndices = stackalloc int[MaxAttachments]; - Span attachmentFormats = stackalloc Format[MaxAttachments]; + Span attachmentFormats = stackalloc VulkanFormat[MaxAttachments]; int attachmentCount = 0; int colorCount = 0; @@ -32,14 +33,17 @@ namespace Ryujinx.Graphics.Vulkan bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample || !state.DepthStencilFormat.IsImageCompatible(); - for (int i = 0; i < state.AttachmentEnable.Length; i++) + Span attachmentEnableSpan = state.AttachmentEnable.AsSpan(); + Span attachmentFormatsSpan = state.AttachmentFormats.AsSpan(); + + for (int i = 0; i < attachmentEnableSpan.Length; i++) { - if (state.AttachmentEnable[i]) + if (attachmentEnableSpan[i]) { bool isNotMsOrSupportsStorageAttachments = gd.Capabilities.SupportsShaderStorageImageMultisample || - !state.AttachmentFormats[i].IsImageCompatible(); + !attachmentFormatsSpan[i].IsImageCompatible(); - attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i], isNotMsOrSupportsStorageAttachments); + attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(attachmentFormatsSpan[i], isNotMsOrSupportsStorageAttachments); attachmentIndices[attachmentCount++] = i; colorCount++; @@ -222,13 +226,15 @@ namespace Ryujinx.Graphics.Vulkan int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount); Span vbScalarSizes = stackalloc int[vbCount]; + Span vertexAttribsSpan = state.VertexAttribs.AsSpan(); + Span vertexAttributeDescriptionsSpan = pipeline.Internal.VertexAttributeDescriptions.AsSpan(); for (int i = 0; i < vaCount; i++) { - VertexAttribDescriptor attribute = state.VertexAttribs[i]; + VertexAttribDescriptor attribute = vertexAttribsSpan[i]; int bufferIndex = attribute.IsZero ? 0 : attribute.BufferIndex + 1; - pipeline.Internal.VertexAttributeDescriptions[i] = new VertexInputAttributeDescription( + vertexAttributeDescriptionsSpan[i] = new VertexInputAttributeDescription( (uint)i, (uint)bufferIndex, gd.FormatCapabilities.ConvertToVertexVkFormat(attribute.Format), @@ -243,9 +249,12 @@ namespace Ryujinx.Graphics.Vulkan int descriptorIndex = 1; pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex); + Span vertexBuffersSpan = state.VertexBuffers.AsSpan(); + Span vertexBindingDescriptionsSpan = pipeline.Internal.VertexBindingDescriptions.AsSpan(); + for (int i = 0; i < vbCount; i++) { - BufferPipelineDescriptor vertexBuffer = state.VertexBuffers[i]; + BufferPipelineDescriptor vertexBuffer = vertexBuffersSpan[i]; if (vertexBuffer.Enable) { @@ -259,7 +268,7 @@ namespace Ryujinx.Graphics.Vulkan } // TODO: Support divisor > 1 - pipeline.Internal.VertexBindingDescriptions[descriptorIndex++] = new VertexInputBindingDescription( + vertexBindingDescriptionsSpan[descriptorIndex++] = new VertexInputBindingDescription( (uint)i + 1, (uint)alignedStride, inputRate); @@ -268,15 +277,19 @@ namespace Ryujinx.Graphics.Vulkan pipeline.VertexBindingDescriptionsCount = (uint)descriptorIndex; + Span blendDescriptorsSpan = state.BlendDescriptors.AsSpan(); + Span colorWriteMaskSpan = state.ColorWriteMask.AsSpan(); + Span colorBlendAttachmentStateSpan = pipeline.Internal.ColorBlendAttachmentState.AsSpan(); + // NOTE: Viewports, Scissors are dynamic. for (int i = 0; i < Constants.MaxRenderTargets; i++) { - BlendDescriptor blend = state.BlendDescriptors[i]; + BlendDescriptor blend = blendDescriptorsSpan[i]; - if (blend.Enable && state.ColorWriteMask[i] != 0) + if (blend.Enable && colorWriteMaskSpan[i] != 0) { - pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState( + colorBlendAttachmentStateSpan[i] = new PipelineColorBlendAttachmentState( blend.Enable, blend.ColorSrcFactor.Convert(), blend.ColorDstFactor.Convert(), @@ -284,12 +297,12 @@ namespace Ryujinx.Graphics.Vulkan blend.AlphaSrcFactor.Convert(), blend.AlphaDstFactor.Convert(), blend.AlphaOp.Convert(), - (ColorComponentFlags)state.ColorWriteMask[i]); + (ColorComponentFlags)colorWriteMaskSpan[i]); } else { - pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState( - colorWriteMask: (ColorComponentFlags)state.ColorWriteMask[i]); + colorBlendAttachmentStateSpan[i] = new PipelineColorBlendAttachmentState( + colorWriteMask: (ColorComponentFlags)colorWriteMaskSpan[i]); } } @@ -297,23 +310,27 @@ namespace Ryujinx.Graphics.Vulkan int maxColorAttachmentIndex = -1; uint attachmentIntegerFormatMask = 0; bool allFormatsFloatOrSrgb = true; + + Span attachmentEnableSpan = state.AttachmentEnable.AsSpan(); + Span attachmentFormatsSpan = state.AttachmentFormats.AsSpan(); + Span pAttachmentFormatsSpan = pipeline.Internal.AttachmentFormats.AsSpan(); for (int i = 0; i < Constants.MaxRenderTargets; i++) { - if (state.AttachmentEnable[i]) + if (attachmentEnableSpan[i]) { bool isNotMsOrSupportsStorage = gd.Capabilities.SupportsShaderStorageImageMultisample || - !state.AttachmentFormats[i].IsImageCompatible(); + !attachmentFormatsSpan[i].IsImageCompatible(); - pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i], isNotMsOrSupportsStorage); + pAttachmentFormatsSpan[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(attachmentFormatsSpan[i], isNotMsOrSupportsStorage); maxColorAttachmentIndex = i; - if (state.AttachmentFormats[i].IsInteger()) + if (attachmentFormatsSpan[i].IsInteger()) { attachmentIntegerFormatMask |= 1u << i; } - allFormatsFloatOrSrgb &= state.AttachmentFormats[i].IsFloatOrSrgb(); + allFormatsFloatOrSrgb &= attachmentFormatsSpan[i].IsFloatOrSrgb(); } } @@ -322,7 +339,7 @@ namespace Ryujinx.Graphics.Vulkan bool isNotMsOrSupportsStorage = !state.DepthStencilFormat.IsImageCompatible() || gd.Capabilities.SupportsShaderStorageImageMultisample; - pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat, isNotMsOrSupportsStorage); + pAttachmentFormatsSpan[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat, isNotMsOrSupportsStorage); } pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1); diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs index 3ed87206f..5fddbea59 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs @@ -121,32 +121,32 @@ namespace Ryujinx.Graphics.Vulkan { Vk api = gd.Api; - if (_dirty.HasFlag(DirtyFlags.Blend)) + if ((_dirty & DirtyFlags.Blend) == DirtyFlags.Blend) { RecordBlend(api, commandBuffer); } - if (_dirty.HasFlag(DirtyFlags.DepthBias)) + if ((_dirty & DirtyFlags.DepthBias) == DirtyFlags.DepthBias) { RecordDepthBias(api, commandBuffer); } - if (_dirty.HasFlag(DirtyFlags.Scissor)) + if ((_dirty & DirtyFlags.Scissor) == DirtyFlags.Scissor) { RecordScissor(api, commandBuffer); } - if (_dirty.HasFlag(DirtyFlags.Stencil)) + if ((_dirty & DirtyFlags.Stencil) == DirtyFlags.Stencil) { RecordStencilMasks(api, commandBuffer); } - if (_dirty.HasFlag(DirtyFlags.Viewport)) + if ((_dirty & DirtyFlags.Viewport) == DirtyFlags.Viewport) { RecordViewport(api, commandBuffer); } - if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop) + if ((_dirty & DirtyFlags.FeedbackLoop) == DirtyFlags.FeedbackLoop && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop) { RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer); } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index fa815d96c..36f5804bb 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -520,6 +520,9 @@ namespace Ryujinx.Graphics.Vulkan }; uint blendEnables = 0; + + Span colorBlendAttachmentStateSpan = + Internal.ColorBlendAttachmentState.AsSpan(); if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0) { @@ -530,12 +533,12 @@ namespace Ryujinx.Graphics.Vulkan { int i = BitOperations.TrailingZeroCount(attachmentIntegerFormatMask); - if (Internal.ColorBlendAttachmentState[i].BlendEnable) + if (colorBlendAttachmentStateSpan[i].BlendEnable) { blendEnables |= 1u << i; } - Internal.ColorBlendAttachmentState[i].BlendEnable = false; + colorBlendAttachmentStateSpan[i].BlendEnable = false; attachmentIntegerFormatMask &= ~(1u << i); } } @@ -656,7 +659,7 @@ namespace Ryujinx.Graphics.Vulkan { int i = BitOperations.TrailingZeroCount(blendEnables); - Internal.ColorBlendAttachmentState[i].BlendEnable = true; + colorBlendAttachmentStateSpan[i].BlendEnable = true; blendEnables &= ~(1u << i); } } @@ -675,14 +678,21 @@ namespace Ryujinx.Graphics.Vulkan // To work around this, we reduce the format to something that doesn't exceed the stride if possible. // The assumption is that the exceeding components are not actually accessed on the shader. + Span vertexAttributeDescriptionsSpan = + Internal.VertexAttributeDescriptions.AsSpan(); + Span vertexBindingDescriptionsSpan = + Internal.VertexBindingDescriptions.AsSpan(); + Span vertexAttributeDescriptions2Span = + _vertexAttributeDescriptions2.AsSpan(); + for (int index = 0; index < VertexAttributeDescriptionsCount; index++) { - VertexInputAttributeDescription attribute = Internal.VertexAttributeDescriptions[index]; + VertexInputAttributeDescription attribute = vertexAttributeDescriptionsSpan[index]; int vbIndex = GetVertexBufferIndex(attribute.Binding); if (vbIndex >= 0) { - ref VertexInputBindingDescription vb = ref Internal.VertexBindingDescriptions[vbIndex]; + ref VertexInputBindingDescription vb = ref vertexBindingDescriptionsSpan[vbIndex]; Format format = attribute.Format; @@ -707,15 +717,18 @@ namespace Ryujinx.Graphics.Vulkan } } - _vertexAttributeDescriptions2[index] = attribute; + vertexAttributeDescriptions2Span[index] = attribute; } } private int GetVertexBufferIndex(uint binding) { + Span vertexBindingDescriptionsSpan = + Internal.VertexBindingDescriptions.AsSpan(); + for (int index = 0; index < VertexBindingDescriptionsCount; index++) { - if (Internal.VertexBindingDescriptions[index].Binding == binding) + if (vertexBindingDescriptionsSpan[index].Binding == binding) { return index; } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs index c56224216..da596e23b 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineUid.cs @@ -86,37 +86,45 @@ namespace Ryujinx.Graphics.Vulkan Id6 * 23 ^ Id7 * 23 ^ Id8 * 23; + + ReadOnlySpan vertexAttributeDescriptionsSpan = VertexAttributeDescriptions.AsSpan(); for (int i = 0; i < (int)VertexAttributeDescriptionsCount; i++) { - hash64 ^= VertexAttributeDescriptions[i].Binding * 23; - hash64 ^= (uint)VertexAttributeDescriptions[i].Format * 23; - hash64 ^= VertexAttributeDescriptions[i].Location * 23; - hash64 ^= VertexAttributeDescriptions[i].Offset * 23; + hash64 ^= vertexAttributeDescriptionsSpan[i].Binding * 23; + hash64 ^= (uint)vertexAttributeDescriptionsSpan[i].Format * 23; + hash64 ^= vertexAttributeDescriptionsSpan[i].Location * 23; + hash64 ^= vertexAttributeDescriptionsSpan[i].Offset * 23; } + + ReadOnlySpan vertexBindingDescriptionsSpan = VertexBindingDescriptions.AsSpan(); for (int i = 0; i < (int)VertexBindingDescriptionsCount; i++) { - hash64 ^= VertexBindingDescriptions[i].Binding * 23; - hash64 ^= (uint)VertexBindingDescriptions[i].InputRate * 23; - hash64 ^= VertexBindingDescriptions[i].Stride * 23; + hash64 ^= vertexBindingDescriptionsSpan[i].Binding * 23; + hash64 ^= (uint)vertexBindingDescriptionsSpan[i].InputRate * 23; + hash64 ^= vertexBindingDescriptionsSpan[i].Stride * 23; } + + ReadOnlySpan colorBlendAttachmentStateSpan = ColorBlendAttachmentState.AsSpan(); for (int i = 0; i < (int)ColorBlendAttachmentStateCount; i++) { - hash64 ^= ColorBlendAttachmentState[i].BlendEnable * 23; - hash64 ^= (uint)ColorBlendAttachmentState[i].SrcColorBlendFactor * 23; - hash64 ^= (uint)ColorBlendAttachmentState[i].DstColorBlendFactor * 23; - hash64 ^= (uint)ColorBlendAttachmentState[i].ColorBlendOp * 23; - hash64 ^= (uint)ColorBlendAttachmentState[i].SrcAlphaBlendFactor * 23; - hash64 ^= (uint)ColorBlendAttachmentState[i].DstAlphaBlendFactor * 23; - hash64 ^= (uint)ColorBlendAttachmentState[i].AlphaBlendOp * 23; - hash64 ^= (uint)ColorBlendAttachmentState[i].ColorWriteMask * 23; + hash64 ^= colorBlendAttachmentStateSpan[i].BlendEnable * 23; + hash64 ^= (uint)colorBlendAttachmentStateSpan[i].SrcColorBlendFactor * 23; + hash64 ^= (uint)colorBlendAttachmentStateSpan[i].DstColorBlendFactor * 23; + hash64 ^= (uint)colorBlendAttachmentStateSpan[i].ColorBlendOp * 23; + hash64 ^= (uint)colorBlendAttachmentStateSpan[i].SrcAlphaBlendFactor * 23; + hash64 ^= (uint)colorBlendAttachmentStateSpan[i].DstAlphaBlendFactor * 23; + hash64 ^= (uint)colorBlendAttachmentStateSpan[i].AlphaBlendOp * 23; + hash64 ^= (uint)colorBlendAttachmentStateSpan[i].ColorWriteMask * 23; } + + ReadOnlySpan attachmentFormatsSpan = AttachmentFormats.AsSpan(); for (int i = 0; i < (int)ColorBlendAttachmentStateCount; i++) { - hash64 ^= (uint)AttachmentFormats[i] * 23; + hash64 ^= (uint)attachmentFormatsSpan[i] * 23; } return (int)hash64 ^ ((int)(hash64 >> 32) * 17); diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs index 54b20ff99..b8e57619c 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs @@ -578,7 +578,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { KThread selectedSuggestedCore = context.Schedulers[suggestedCore]._state.SelectedThread; - if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2)) + if (selectedSuggestedCore == suggested || selectedSuggestedCore is { DynamicPriority: < 2 }) { continue; } diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 20fb426ba..c799b5e7e 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -1,3 +1,4 @@ +using ARMeilleure.State; using Ryujinx.Common.Logging; using Ryujinx.Cpu; using Ryujinx.HLE.Debugger; @@ -684,17 +685,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading const int MaxFpuRegistersAArch32 = 16; ThreadContext context = new(); + + Span registersSpan = context.Registers.AsSpan(); + Span fpuRegistersSpan = context.FpuRegisters.AsSpan(); if (Owner.Flags.HasFlag(ProcessCreationFlags.Is64Bit)) { - for (int i = 0; i < context.Registers.Length; i++) + for (int i = 0; i < registersSpan.Length; i++) { - context.Registers[i] = Context.GetX(i); + registersSpan[i] = Context.GetX(i); } - for (int i = 0; i < context.FpuRegisters.Length; i++) + for (int i = 0; i < fpuRegistersSpan.Length; i++) { - context.FpuRegisters[i] = Context.GetV(i); + fpuRegistersSpan[i] = Context.GetV(i); } context.Fp = Context.GetX(29); @@ -708,12 +712,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { for (int i = 0; i < MaxRegistersAArch32; i++) { - context.Registers[i] = (uint)Context.GetX(i); + registersSpan[i] = (uint)Context.GetX(i); } for (int i = 0; i < MaxFpuRegistersAArch32; i++) { - context.FpuRegisters[i] = Context.GetV(i); + fpuRegistersSpan[i] = Context.GetV(i); } context.Pc = (uint)Context.Pc; diff --git a/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs b/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs index 155077745..c3ed92ed0 100644 --- a/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Fatal/IService.cs @@ -75,17 +75,21 @@ namespace Ryujinx.HLE.HOS.Services.Fatal { errorReport.AppendLine("\tStackTrace:"); + Span stackTraceSpan = cpuContext64.StackTrace.AsSpan(); + for (int i = 0; i < cpuContext64.StackTraceSize; i++) { - errorReport.AppendLine($"\t\t0x{cpuContext64.StackTrace[i]:x16}"); + errorReport.AppendLine($"\t\t0x{stackTraceSpan[i]:x16}"); } } errorReport.AppendLine("\tRegisters:"); + + Span xSpan = cpuContext64.X.AsSpan(); - for (int i = 0; i < cpuContext64.X.Length; i++) + for (int i = 0; i < xSpan.Length; i++) { - errorReport.AppendLine($"\t\tX[{i:d2}]:\t0x{cpuContext64.X[i]:x16}"); + errorReport.AppendLine($"\t\tX[{i:d2}]:\t0x{xSpan[i]:x16}"); } errorReport.AppendLine(); @@ -109,18 +113,22 @@ namespace Ryujinx.HLE.HOS.Services.Fatal if (cpuContext32.StackTraceSize > 0) { errorReport.AppendLine("\tStackTrace:"); + + Span stackTraceSpan = cpuContext32.StackTrace.AsSpan(); for (int i = 0; i < cpuContext32.StackTraceSize; i++) { - errorReport.AppendLine($"\t\t0x{cpuContext32.StackTrace[i]:x16}"); + errorReport.AppendLine($"\t\t0x{stackTraceSpan[i]:x16}"); } } errorReport.AppendLine("\tRegisters:"); + + Span xSpan = cpuContext32.X.AsSpan(); - for (int i = 0; i < cpuContext32.X.Length; i++) + for (int i = 0; i < xSpan.Length; i++) { - errorReport.AppendLine($"\t\tX[{i:d2}]:\t0x{cpuContext32.X[i]:x16}"); + errorReport.AppendLine($"\t\tX[{i:d2}]:\t0x{xSpan[i]:x16}"); } errorReport.AppendLine(); diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs index 834bee6f0..b9235b033 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs @@ -94,6 +94,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid primaryIndex = PlayerIndex.Unknown; configuredCount = 0; + Span nPadsSpan = _device.Hid.SharedMemory.Npads.AsSpan(); + for (int i = 0; i < MaxControllers; ++i) { ControllerType npad = _configuredTypes[i]; @@ -103,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid continue; } - ControllerType currentType = (ControllerType)_device.Hid.SharedMemory.Npads[i].InternalState.StyleSet; + ControllerType currentType = (ControllerType)nPadsSpan[i].InternalState.StyleSet; if (currentType != ControllerType.None && (npad & acceptedTypes) != 0 && _supportedPlayers[i]) { diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs index e8a56933b..be523c16c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/HidDevices/TouchDevice.cs @@ -23,12 +23,14 @@ namespace Ryujinx.HLE.HOS.Services.Hid { newState.TouchesCount = points.Length; - int pointsLength = Math.Min(points.Length, newState.Touches.Length); + Span touchesSpan = newState.Touches.AsSpan(); + + int pointsLength = Math.Min(points.Length, touchesSpan.Length); for (int i = 0; i < pointsLength; ++i) { TouchPoint pi = points[i]; - newState.Touches[i] = new TouchState + touchesSpan[i] = new TouchState { DeltaTime = newState.SamplingNumber, Attribute = pi.Attribute, diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs index 99f2f59e4..f567bbc3c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs @@ -49,12 +49,14 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common } ulong index = ReadCurrentIndex(); + + Span> storageSpan = _storage.AsSpan(); while (true) { int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible) % MaxEntries); - ref AtomicStorage result = ref _storage[inputEntryIndex]; + ref AtomicStorage result = ref storageSpan[inputEntryIndex]; ulong samplingNumber0 = result.ReadSamplingNumberAtomic(); ulong samplingNumber1 = result.ReadSamplingNumberAtomic(); @@ -91,15 +93,17 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common ulong index = ReadCurrentIndex(); AtomicStorage[] result = new AtomicStorage[countAvailaible]; + + Span> storageSpan = _storage.AsSpan(); for (ulong i = 0; i < countAvailaible; i++) { int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible + i) % MaxEntries); int outputEntryIndex = (int)(countAvailaible - i - 1); - ulong samplingNumber0 = _storage[inputEntryIndex].ReadSamplingNumberAtomic(); - result[outputEntryIndex] = _storage[inputEntryIndex]; - ulong samplingNumber1 = _storage[inputEntryIndex].ReadSamplingNumberAtomic(); + ulong samplingNumber0 = storageSpan[inputEntryIndex].ReadSamplingNumberAtomic(); + result[outputEntryIndex] = storageSpan[inputEntryIndex]; + ulong samplingNumber1 = storageSpan[inputEntryIndex].ReadSamplingNumberAtomic(); if (samplingNumber0 != samplingNumber1 && (i > 0 && (result[outputEntryIndex].SamplingNumber - result[outputEntryIndex].SamplingNumber) != 1)) { diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs index 7f3e564fb..adbbbc78b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs @@ -6,6 +6,7 @@ using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Keyboard; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Mouse; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad; using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.TouchScreen; +using System; using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory @@ -61,10 +62,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory Mouse = RingLifo.Create(), Keyboard = RingLifo.Create(), }; + + Span npadsSpan = result.Npads.AsSpan(); - for (int i = 0; i < result.Npads.Length; i++) + for (int i = 0; i < npadsSpan.Length; i++) { - result.Npads[i] = NpadState.Create(); + npadsSpan[i] = NpadState.Create(); } return result; diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NodeLatestUpdate.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NodeLatestUpdate.cs index 826a50458..664a23032 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NodeLatestUpdate.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/Types/NodeLatestUpdate.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Memory; +using System; using System.Runtime.InteropServices; using System.Threading; @@ -19,20 +20,24 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.Types { lock (_lock) { + Span arraySpan = array.AsSpan(); + Span beforeNodesSpan = beforeNodes.AsSpan(); + Span afterNodesSpan = afterNodes.AsSpan(); + for (int i = 0; i < 8; i++) { - if (beforeNodes[i].IsConnected == 0) + if (beforeNodesSpan[i].IsConnected == 0) { - if (afterNodes[i].IsConnected != 0) + if (afterNodesSpan[i].IsConnected != 0) { - array[i].State |= NodeLatestUpdateFlags.Connect; + arraySpan[i].State |= NodeLatestUpdateFlags.Connect; } } else { - if (afterNodes[i].IsConnected == 0) + if (afterNodesSpan[i].IsConnected == 0) { - array[i].State |= NodeLatestUpdateFlags.Disconnect; + arraySpan[i].State |= NodeLatestUpdateFlags.Disconnect; } } } @@ -45,14 +50,16 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.Types lock (_lock) { + Span arraySpan = array.AsSpan(); + for (int i = 0; i < number; i++) { result[i].Reserved = new Array7(); if (i < LdnConst.NodeCountMax) { - result[i].State = array[i].State; - array[i].State = NodeLatestUpdateFlags.None; + result[i].State = arraySpan[i].State; + arraySpan[i].State = NodeLatestUpdateFlags.None; } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanDiscovery.cs b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanDiscovery.cs index b34772219..c29af43da 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanDiscovery.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ldn/UserServiceCreator/LdnMitm/LanDiscovery.cs @@ -77,9 +77,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm }, }; + Span nodesSpan = networkInfo.Ldn.Nodes.AsSpan(); + for (int i = 0; i < LdnConst.NodeCountMax; i++) { - networkInfo.Ldn.Nodes[i] = new NodeInfo + nodesSpan[i] = new NodeInfo { MacAddress = new Array6(), UserName = new Array33(), @@ -229,11 +231,13 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm NetworkInfo.Common.Ssid = _fakeSsid; NetworkInfo.Ldn.Nodes = new Array8(); + + Span nodesSpan = NetworkInfo.Ldn.Nodes.AsSpan(); for (int i = 0; i < LdnConst.NodeCountMax; i++) { - NetworkInfo.Ldn.Nodes[i].NodeId = (byte)i; - NetworkInfo.Ldn.Nodes[i].IsConnected = 0; + nodesSpan[i].NodeId = (byte)i; + nodesSpan[i].IsConnected = 0; } } } @@ -408,11 +412,11 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator.LdnMitm private int LocateEmptyNode() { - Array8 nodes = NetworkInfo.Ldn.Nodes; + Span nodesSpan = NetworkInfo.Ldn.Nodes.AsSpan(); - for (int i = 1; i < nodes.Length; i++) + for (int i = 1; i < nodesSpan.Length; i++) { - if (nodes[i].IsConnected == 0) + if (nodesSpan[i].IsConnected == 0) { return i; } diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs index 9d0104a01..f21efcea4 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs @@ -335,9 +335,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu writeEntries = (uint)_pageSizes.Length; } + Span regionsSpan = arguments.Regions.AsSpan(); + for (uint i = 0; i < writeEntries; i++) { - ref VaRegion region = ref arguments.Regions[(int)i]; + ref VaRegion region = ref regionsSpan[(int)i]; VmRegion vmRegion = _vmRegions[i]; uint pageSize = _pageSizes[i]; diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs index de839e3a0..86f6ecfb5 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfo4.cs @@ -30,21 +30,21 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types { Port = IPAddress.HostToNetworkOrder(Port); - RawIpv4AddressNetworkEndianSwap(ref Address); + RawIpv4AddressNetworkEndianSwap(Address.AsSpan()); } public void ToHostOrder() { Port = IPAddress.NetworkToHostOrder(Port); - RawIpv4AddressNetworkEndianSwap(ref Address); + RawIpv4AddressNetworkEndianSwap(Address.AsSpan()); } - public static void RawIpv4AddressNetworkEndianSwap(ref Array4 address) + public static void RawIpv4AddressNetworkEndianSwap(Span address) { if (BitConverter.IsLittleEndian) { - address.AsSpan().Reverse(); + address.Reverse(); } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs index 471c274ca..19b8d7efc 100644 --- a/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs +++ b/src/Ryujinx.HLE/HOS/Services/Sockets/Sfdnsres/Types/AddrInfoSerialized.cs @@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types { // Nintendo hardcode 4 bytes in that case here. Array4 address = MemoryMarshal.Read>(buffer); - AddrInfo4.RawIpv4AddressNetworkEndianSwap(ref address); + AddrInfo4.RawIpv4AddressNetworkEndianSwap(address.AsSpan()); rawIPv4Address = address; @@ -115,7 +115,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types else { Array4 rawIPv4Address = RawIPv4Address.Value; - AddrInfo4.RawIpv4AddressNetworkEndianSwap(ref rawIPv4Address); + AddrInfo4.RawIpv4AddressNetworkEndianSwap(rawIPv4Address.AsSpan()); MemoryMarshal.Write(buffer, in rawIPv4Address); diff --git a/src/Ryujinx.HLE/UI/Input/NpadReader.cs b/src/Ryujinx.HLE/UI/Input/NpadReader.cs index 8b4888a26..586e924b0 100644 --- a/src/Ryujinx.HLE/UI/Input/NpadReader.cs +++ b/src/Ryujinx.HLE/UI/Input/NpadReader.cs @@ -62,10 +62,10 @@ namespace Ryujinx.HLE.UI.Input public void Update(bool supressEvents = false) { - ref Array10 npads = ref _device.Hid.SharedMemory.Npads; + int npadsCount = _device.Hid.SharedMemory.Npads.Length; // Process each input individually. - for (int npadIndex = 0; npadIndex < npads.Length; npadIndex++) + for (int npadIndex = 0; npadIndex < npadsCount; npadIndex++) { UpdateNpad(npadIndex, supressEvents); } diff --git a/src/Ryujinx.Input/GamepadStateSnapshot.cs b/src/Ryujinx.Input/GamepadStateSnapshot.cs index bf38b7c8a..3af7c9d0f 100644 --- a/src/Ryujinx.Input/GamepadStateSnapshot.cs +++ b/src/Ryujinx.Input/GamepadStateSnapshot.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Memory; +using System; using System.Runtime.CompilerServices; namespace Ryujinx.Input @@ -48,7 +49,7 @@ namespace Ryujinx.Input [MethodImpl(MethodImplOptions.AggressiveInlining)] public (float, float) GetStick(StickInputId inputId) { - Array2 result = _joysticksState[(int)inputId]; + Span result = _joysticksState[(int)inputId].AsSpan(); return (result[0], result[1]); } @@ -62,8 +63,9 @@ namespace Ryujinx.Input [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetStick(StickInputId inputId, float x, float y) { - _joysticksState[(int)inputId][0] = x; - _joysticksState[(int)inputId][1] = y; + Span stateSpan = _joysticksState[(int)inputId].AsSpan(); + stateSpan[0] = x; + stateSpan[1] = y; } } } diff --git a/src/Ryujinx.Input/IGamepad.cs b/src/Ryujinx.Input/IGamepad.cs index 832950660..945ccfa8b 100644 --- a/src/Ryujinx.Input/IGamepad.cs +++ b/src/Ryujinx.Input/IGamepad.cs @@ -104,25 +104,28 @@ namespace Ryujinx.Input { // NOTE: Update Array size if JoystickInputId is changed. Array3> joysticksState = default; + Span> joysticksStateSpan = joysticksState.AsSpan(); for (StickInputId inputId = StickInputId.Left; inputId < StickInputId.Count; inputId++) { (float state0, float state1) = gamepad.GetStick(inputId); Array2 state = default; + Span stateSpan = state.AsSpan(); - state[0] = state0; - state[1] = state1; + stateSpan[0] = state0; + stateSpan[1] = state1; - joysticksState[(int)inputId] = state; + joysticksStateSpan[(int)inputId] = state; } // NOTE: Update Array size if GamepadInputId is changed. Array28 buttonsState = default; + Span buttonsStateSpan = buttonsState.AsSpan(); for (GamepadButtonInputId inputId = GamepadButtonInputId.A; inputId < GamepadButtonInputId.Count; inputId++) { - buttonsState[(int)inputId] = gamepad.IsPressed(inputId); + buttonsStateSpan[(int)inputId] = gamepad.IsPressed(inputId); } return new GamepadStateSnapshot(joysticksState, buttonsState); diff --git a/src/Ryujinx.Memory/Range/NonOverlappingRangeList.cs b/src/Ryujinx.Memory/Range/NonOverlappingRangeList.cs index 7803b03d1..64329147e 100644 --- a/src/Ryujinx.Memory/Range/NonOverlappingRangeList.cs +++ b/src/Ryujinx.Memory/Range/NonOverlappingRangeList.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Memory.Range /// A range list that assumes ranges are non-overlapping, with list items that can be split in two to avoid overlaps. /// /// Type of the range. - public class NonOverlappingRangeList : RangeListBase where T : INonOverlappingRange + public unsafe class NonOverlappingRangeList : RangeListBase where T : class, INonOverlappingRange { private readonly Dictionary> _quickAccess = new(AddressEqualityComparer.Comparer); private readonly Dictionary> _fastQuickAccess = new(AddressEqualityComparer.Comparer); @@ -196,6 +196,16 @@ namespace Ryujinx.Memory.Range int startIndex = BinarySearch(startItem.Address); int endIndex = BinarySearch(endItem.Address); + for (int i = startIndex; i <= endIndex; i++) + { + _quickAccess.Remove(Items[i].Address); + foreach (ulong addr in Items[i].QuickAccessAddresses) + { + _quickAccess.Remove(addr); + _fastQuickAccess.Remove(addr); + } + } + if (endIndex < Count - 1) { Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null; @@ -213,17 +223,6 @@ namespace Ryujinx.Memory.Range } Count -= endIndex - startIndex + 1; - - while (startItem != endItem.Next) - { - _quickAccess.Remove(startItem.Address); - foreach (ulong addr in startItem.QuickAccessAddresses) - { - _quickAccess.Remove(addr); - _fastQuickAccess.Remove(addr); - } - startItem = startItem.Next; - } } /// @@ -240,19 +239,22 @@ namespace Ryujinx.Memory.Range return; } - RangeItem startItem = Items[startIndex]; - int endIndex = startIndex; - while (startItem is not null && startItem.Address < address + size) + while (Items[endIndex] is not null && Items[endIndex].Address < address + size) { - _quickAccess.Remove(startItem.Address); - foreach (ulong addr in startItem.QuickAccessAddresses) + _quickAccess.Remove(Items[endIndex].Address); + foreach (ulong addr in Items[endIndex].QuickAccessAddresses) { _quickAccess.Remove(addr); _fastQuickAccess.Remove(addr); } - startItem = startItem.Next; + + if (endIndex == Count - 1) + { + break; + } + endIndex++; } diff --git a/src/Ryujinx.Memory/Range/RangeList.cs b/src/Ryujinx.Memory/Range/RangeList.cs index 600ff748e..379fc648c 100644 --- a/src/Ryujinx.Memory/Range/RangeList.cs +++ b/src/Ryujinx.Memory/Range/RangeList.cs @@ -6,37 +6,6 @@ using System.Threading; namespace Ryujinx.Memory.Range { - public class RangeItem(TValue value) where TValue : IRange - { - public RangeItem Next; - public RangeItem Previous; - - public readonly ulong Address = value.Address; - public readonly ulong EndAddress = value.Address + value.Size; - - public readonly TValue Value = value; - - public readonly List QuickAccessAddresses = []; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool OverlapsWith(ulong address, ulong endAddress) - { - return Address < endAddress && address < EndAddress; - } - } - - class AddressEqualityComparer : IEqualityComparer - { - public bool Equals(ulong u1, ulong u2) - { - return u1 == u2; - } - - public int GetHashCode(ulong value) => (int)(value >> 5); - - public static readonly AddressEqualityComparer Comparer = new(); - } - /// /// Result of an Overlaps Finder function. WARNING: if the result is from the optimized /// Overlaps Finder, the StartIndex will be -1 even when the result isn't empty @@ -209,43 +178,46 @@ namespace Ryujinx.Memory.Range [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void RemoveRange(RangeItem startItem, RangeItem endItem) { - if (endItem.Next is not null) + if (startItem is null) { - endItem.Next.Previous = startItem.Previous; + return; + } + + if (startItem == endItem) + { + Remove(startItem.Value); + return; } - if (startItem.Previous is not null) - { - startItem.Previous.Next = endItem.Next; - } + int startIndex = BinarySearch(startItem.Address); + int endIndex = BinarySearch(endItem.Address); - RangeItem current = startItem; - while (current != endItem.Next) + for (int i = startIndex; i <= endIndex; i++) { - foreach (ulong address in current.QuickAccessAddresses) + _quickAccess.Remove(Items[i].Address); + foreach (ulong addr in Items[i].QuickAccessAddresses) { - _quickAccess.Remove(address); + _quickAccess.Remove(addr); } - - current = current.Next; } - RangeItem[] array = []; - OverlapResult overlapResult = FindOverlaps(startItem.Address, endItem.EndAddress, ref array); + if (endIndex < Count - 1) + { + Items[endIndex + 1].Previous = startIndex > 0 ? Items[startIndex - 1] : null; + } + + if (startIndex > 0) + { + Items[startIndex - 1].Next = endIndex < Count - 1 ? Items[endIndex + 1] : null; + } - if (overlapResult.EndIndex < Count) + + if (endIndex < Count - 1) { - Array.Copy(Items, overlapResult.EndIndex, Items, overlapResult.StartIndex, Count - overlapResult.EndIndex); - Count -= overlapResult.Count; - } - else if (overlapResult.EndIndex == Count) - { - Count = overlapResult.StartIndex; - } - else - { - Debug.Assert(false); + Array.Copy(Items, endIndex + 1, Items, startIndex, Count - endIndex - 1); } + + Count -= endIndex - startIndex + 1; } /// diff --git a/src/Ryujinx.Memory/Range/RangeListBase.cs b/src/Ryujinx.Memory/Range/RangeListBase.cs index 7e26442f0..7dccd2d38 100644 --- a/src/Ryujinx.Memory/Range/RangeListBase.cs +++ b/src/Ryujinx.Memory/Range/RangeListBase.cs @@ -4,9 +4,40 @@ using System.Runtime.CompilerServices; namespace Ryujinx.Memory.Range { - public abstract class RangeListBase : IEnumerable where T : IRange + public class RangeItem(TValue value) where TValue : IRange { - protected const int BackingInitialSize = 1024; + public RangeItem Next; + public RangeItem Previous; + + public readonly ulong Address = value.Address; + public readonly ulong EndAddress = value.Address + value.Size; + + public readonly TValue Value = value; + + public readonly List QuickAccessAddresses = []; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool OverlapsWith(ulong address, ulong endAddress) + { + return Address < endAddress && address < EndAddress; + } + } + + class AddressEqualityComparer : IEqualityComparer + { + public bool Equals(ulong u1, ulong u2) + { + return u1 == u2; + } + + public int GetHashCode(ulong value) => (int)(value >> 5); + + public static readonly AddressEqualityComparer Comparer = new(); + } + + public unsafe abstract class RangeListBase : IEnumerable where T : IRange + { + private const int BackingInitialSize = 1024; protected RangeItem[] Items; protected readonly int BackingGrowthSize; diff --git a/src/Ryujinx.Memory/WindowsShared/MappingTree.cs b/src/Ryujinx.Memory/WindowsShared/MappingTree.cs index 9ca84b56b..aa6ad5340 100644 --- a/src/Ryujinx.Memory/WindowsShared/MappingTree.cs +++ b/src/Ryujinx.Memory/WindowsShared/MappingTree.cs @@ -36,10 +36,12 @@ namespace Ryujinx.Memory.WindowsShared class RangeNode : IntrusiveRedBlackTreeNode>, IComparable>, IComparable { - public ulong Start { get; } + public ulong Start { get; private set; } public ulong End { get; private set; } - public T Value { get; } + public T Value { get; private set; } + public RangeNode() { } + public RangeNode(ulong start, ulong end, T value) { Start = start; @@ -47,6 +49,22 @@ namespace Ryujinx.Memory.WindowsShared Value = value; } + public RangeNode Init(ulong start, ulong end, T value) + { + Start = start; + End = end; + Value = value; + + Color = true; + Left = null; + Right = null; + Parent = null; + Predecessor = null; + Successor = null; + + return this; + } + public void Extend(ulong sizeDelta) { End += sizeDelta; diff --git a/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs b/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs index f1bbf2f41..436efe98b 100644 --- a/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs +++ b/src/Ryujinx.Memory/WindowsShared/PlaceholderManager.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Collections; using Ryujinx.Common.Memory.PartialUnmaps; using System; +using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.Versioning; @@ -8,6 +9,24 @@ using System.Threading; namespace Ryujinx.Memory.WindowsShared { + public class ObjectPool + { + private readonly Stack _objects; + private readonly Func _objectGenerator; + + public ObjectPool(Func objectGenerator) + { + _objectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator)); + _objects = new Stack(); + } + + public T Get() => _objects.Count > 0 ? _objects.Pop() : _objectGenerator(); + + public void Return(T item) => _objects.Push(item); + + public void Clear() => _objects.Clear(); + } + /// /// Windows memory placeholder manager. /// @@ -18,6 +37,7 @@ namespace Ryujinx.Memory.WindowsShared private readonly MappingTree _mappings; private readonly MappingTree _protections; + private readonly ObjectPool> _protectionObjectPool; private readonly nint _partialUnmapStatePtr; private readonly Thread _partialUnmapTrimThread; @@ -28,6 +48,8 @@ namespace Ryujinx.Memory.WindowsShared { _mappings = new MappingTree(); _protections = new MappingTree(); + + _protectionObjectPool = new ObjectPool>(() => new RangeNode()); _partialUnmapStatePtr = PartialUnmapState.GlobalState; @@ -70,12 +92,12 @@ namespace Ryujinx.Memory.WindowsShared { lock (_mappings) { - _mappings.Add(new RangeNode(address, address + size, ulong.MaxValue)); + _mappings.Add(new RangeNode().Init(address, address + size, ulong.MaxValue)); } lock (_protections) { - _protections.Add(new RangeNode(address, address + size, MemoryPermission.None)); + _protections.Add(_protectionObjectPool.Get().Init(address, address + size, MemoryPermission.None)); } } @@ -214,8 +236,8 @@ namespace Ryujinx.Memory.WindowsShared (nint)size, AllocationType.Release | AllocationType.PreservePlaceholder)); - _mappings.Add(new RangeNode(overlapStart, address, overlapValue)); - _mappings.Add(new RangeNode(endAddress, overlapEnd, AddBackingOffset(overlapValue, endAddress - overlapStart))); + _mappings.Add(new RangeNode().Init(overlapStart, address, overlapValue)); + _mappings.Add(new RangeNode().Init(endAddress, overlapEnd, AddBackingOffset(overlapValue, endAddress - overlapStart))); } else if (overlapStartsBefore) { @@ -226,7 +248,7 @@ namespace Ryujinx.Memory.WindowsShared (nint)overlappedSize, AllocationType.Release | AllocationType.PreservePlaceholder)); - _mappings.Add(new RangeNode(overlapStart, address, overlapValue)); + _mappings.Add(new RangeNode().Init(overlapStart, address, overlapValue)); } else if (overlapEndsAfter) { @@ -237,10 +259,10 @@ namespace Ryujinx.Memory.WindowsShared (nint)overlappedSize, AllocationType.Release | AllocationType.PreservePlaceholder)); - _mappings.Add(new RangeNode(endAddress, overlapEnd, AddBackingOffset(overlapValue, overlappedSize))); + _mappings.Add(new RangeNode().Init(endAddress, overlapEnd, AddBackingOffset(overlapValue, overlappedSize))); } - _mappings.Add(new RangeNode(address, endAddress, backingOffset)); + _mappings.Add(new RangeNode().Init(address, endAddress, backingOffset)); } } @@ -306,7 +328,7 @@ namespace Ryujinx.Memory.WindowsShared lock (_mappings) { _mappings.Remove(overlap); - _mappings.Add(new RangeNode(overlap.Start, overlap.End, ulong.MaxValue)); + _mappings.Add(new RangeNode().Init(overlap.Start, overlap.End, ulong.MaxValue)); } bool overlapStartsBefore = overlap.Start < startAddress; @@ -433,7 +455,7 @@ namespace Ryujinx.Memory.WindowsShared unmappedCount++; } - _mappings.Add(new RangeNode(address, endAddress, ulong.MaxValue)); + _mappings.Add(new RangeNode().Init(address, endAddress, ulong.MaxValue)); } if (unmappedCount > 1) @@ -628,14 +650,16 @@ namespace Ryujinx.Memory.WindowsShared { if (startAddress > protAddress) { - _protections.Add(new RangeNode(protAddress, startAddress, protPermission)); + _protections.Add(_protectionObjectPool.Get().Init(protAddress, startAddress, protPermission)); } if (endAddress < protEndAddress) { - _protections.Add(new RangeNode(endAddress, protEndAddress, protPermission)); + _protections.Add(_protectionObjectPool.Get().Init(endAddress, protEndAddress, protPermission)); } } + + _protectionObjectPool.Return(protection); if (node.End >= endAddress) { @@ -643,7 +667,7 @@ namespace Ryujinx.Memory.WindowsShared } } - _protections.Add(new RangeNode(startAddress, endAddress, permission)); + _protections.Add(_protectionObjectPool.Get().Init(startAddress, endAddress, permission)); } } @@ -674,14 +698,16 @@ namespace Ryujinx.Memory.WindowsShared if (address > protAddress) { - _protections.Add(new RangeNode(protAddress, address, protPermission)); + _protections.Add(_protectionObjectPool.Get().Init(protAddress, address, protPermission)); } if (endAddress < protEndAddress) { - _protections.Add(new RangeNode(endAddress, protEndAddress, protPermission)); + _protections.Add(_protectionObjectPool.Get().Init(endAddress, protEndAddress, protPermission)); } + _protectionObjectPool.Return(protection); + if (node.End >= endAddress) { break;