Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
952f6f8a65 | |||
d04ba51bb0 | |||
55ee261363 | |||
4e3a34412e | |||
3f4fb8f73a | |||
56c56aa34d | |||
d4b960d348 | |||
b2a225558d | |||
0ef0fc044a | |||
04bd87ed5a | |||
5158cdb308 | |||
1402d8391d | |||
e3b36db71c | |||
ba0171d054 | |||
d1146a5af2 |
9
.github/workflows/nightly_pr_comment.yml
vendored
9
.github/workflows/nightly_pr_comment.yml
vendored
@ -36,15 +36,20 @@ jobs:
|
|||||||
return core.error(`No artifacts found`);
|
return core.error(`No artifacts found`);
|
||||||
}
|
}
|
||||||
let body = `Download the artifacts for this pull request:\n`;
|
let body = `Download the artifacts for this pull request:\n`;
|
||||||
|
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
|
||||||
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
|
||||||
for (const art of artifacts) {
|
for (const art of artifacts) {
|
||||||
if(art.name.includes('Debug')){
|
if(art.name.includes('Debug')) {
|
||||||
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
}else{
|
} else if(art.name.includes('headless-sdl2')) {
|
||||||
|
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
|
} else {
|
||||||
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
hidden_headless_artifacts += `\n</details>`;
|
||||||
hidden_debug_artifacts += `\n</details>`;
|
hidden_debug_artifacts += `\n</details>`;
|
||||||
|
body += hidden_headless_artifacts;
|
||||||
body += hidden_debug_artifacts;
|
body += hidden_debug_artifacts;
|
||||||
|
|
||||||
const {data: comments} = await github.issues.listComments({repo, owner, issue_number});
|
const {data: comments} = await github.issues.listComments({repo, owner, issue_number});
|
||||||
|
@ -18,8 +18,10 @@
|
|||||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||||
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||||
using Ryujinx.Audio.Renderer.Server.Effect;
|
using Ryujinx.Audio.Renderer.Server.Effect;
|
||||||
|
using Ryujinx.Audio.Renderer.Utils.Math;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
namespace Ryujinx.Audio.Renderer.Dsp.Command
|
||||||
@ -45,7 +47,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
private const int FixedPointPrecision = 14;
|
private const int FixedPointPrecision = 14;
|
||||||
|
|
||||||
public DelayCommand(uint bufferOffset, DelayParameter parameter, Memory<DelayState> state, bool isEnabled, ulong workBuffer, int nodeId)
|
public DelayCommand(uint bufferOffset, DelayParameter parameter, Memory<DelayState> state, bool isEnabled, ulong workBuffer, int nodeId, bool newEffectChannelMappingSupported)
|
||||||
{
|
{
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
NodeId = nodeId;
|
NodeId = nodeId;
|
||||||
@ -63,9 +65,14 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
||||||
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: We do the opposite as Nintendo here for now to restore previous behaviour
|
||||||
|
// TODO: Update delay processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping.
|
||||||
|
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices);
|
||||||
|
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private unsafe void ProcessDelayMono(ref DelayState state, float* outputBuffer, float* inputBuffer, uint sampleCount)
|
private unsafe void ProcessDelayMono(ref DelayState state, float* outputBuffer, float* inputBuffer, uint sampleCount)
|
||||||
{
|
{
|
||||||
float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision);
|
float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision);
|
||||||
@ -78,133 +85,148 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
float input = inputBuffer[i] * 64;
|
float input = inputBuffer[i] * 64;
|
||||||
float delayLineValue = state.DelayLines[0].Read();
|
float delayLineValue = state.DelayLines[0].Read();
|
||||||
|
|
||||||
float lowPassResult = (input * inGain + delayLineValue * feedbackGain) * state.LowPassBaseGain + state.LowPassZ[0] * state.LowPassFeedbackGain;
|
float temp = input * inGain + delayLineValue * feedbackGain;
|
||||||
|
|
||||||
state.LowPassZ[0] = lowPassResult;
|
state.UpdateLowPassFilter(ref temp, 1);
|
||||||
|
|
||||||
state.DelayLines[0].Update(lowPassResult);
|
|
||||||
|
|
||||||
outputBuffer[i] = (input * dryGain + delayLineValue * outGain) / 64;
|
outputBuffer[i] = (input * dryGain + delayLineValue * outGain) / 64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private unsafe void ProcessDelayStereo(ref DelayState state, Span<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
private unsafe void ProcessDelayStereo(ref DelayState state, Span<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
||||||
{
|
{
|
||||||
const ushort channelCount = 2;
|
const ushort channelCount = 2;
|
||||||
|
|
||||||
Span<float> channelInput = stackalloc float[channelCount];
|
|
||||||
Span<float> delayLineValues = stackalloc float[channelCount];
|
|
||||||
Span<float> temp = stackalloc float[channelCount];
|
|
||||||
|
|
||||||
float delayFeedbackBaseGain = state.DelayFeedbackBaseGain;
|
float delayFeedbackBaseGain = state.DelayFeedbackBaseGain;
|
||||||
float delayFeedbackCrossGain = state.DelayFeedbackCrossGain;
|
float delayFeedbackCrossGain = state.DelayFeedbackCrossGain;
|
||||||
float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision);
|
float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision);
|
||||||
float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
|
float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
|
||||||
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
||||||
|
|
||||||
|
Matrix2x2 delayFeedback = new Matrix2x2(delayFeedbackBaseGain , delayFeedbackCrossGain,
|
||||||
|
delayFeedbackCrossGain, delayFeedbackBaseGain);
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < channelCount; j++)
|
Vector2 channelInput = new Vector2
|
||||||
{
|
{
|
||||||
channelInput[j] = *((float*)inputBuffers[j] + i) * 64;
|
X = *((float*)inputBuffers[0] + i) * 64,
|
||||||
delayLineValues[j] = state.DelayLines[j].Read();
|
Y = *((float*)inputBuffers[1] + i) * 64,
|
||||||
}
|
};
|
||||||
|
|
||||||
temp[0] = channelInput[0] * inGain + delayLineValues[1] * delayFeedbackCrossGain + delayLineValues[0] * delayFeedbackBaseGain;
|
Vector2 delayLineValues = new Vector2()
|
||||||
temp[1] = channelInput[1] * inGain + delayLineValues[0] * delayFeedbackCrossGain + delayLineValues[1] * delayFeedbackBaseGain;
|
|
||||||
|
|
||||||
for (int j = 0; j < channelCount; j++)
|
|
||||||
{
|
{
|
||||||
float lowPassResult = state.LowPassFeedbackGain * state.LowPassZ[j] + temp[j] * state.LowPassBaseGain;
|
X = state.DelayLines[0].Read(),
|
||||||
|
Y = state.DelayLines[1].Read(),
|
||||||
|
};
|
||||||
|
|
||||||
state.LowPassZ[j] = lowPassResult;
|
Vector2 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain;
|
||||||
state.DelayLines[j].Update(lowPassResult);
|
|
||||||
|
|
||||||
*((float*)outputBuffers[j] + i) = (channelInput[j] * dryGain + delayLineValues[j] * outGain) / 64;
|
state.UpdateLowPassFilter(ref Unsafe.As<Vector2, float>(ref temp), channelCount);
|
||||||
}
|
|
||||||
|
*((float*)outputBuffers[0] + i) = (channelInput.X * dryGain + delayLineValues.X * outGain) / 64;
|
||||||
|
*((float*)outputBuffers[1] + i) = (channelInput.Y * dryGain + delayLineValues.Y * outGain) / 64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private unsafe void ProcessDelayQuadraphonic(ref DelayState state, Span<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
private unsafe void ProcessDelayQuadraphonic(ref DelayState state, Span<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
||||||
{
|
{
|
||||||
const ushort channelCount = 4;
|
const ushort channelCount = 4;
|
||||||
|
|
||||||
Span<float> channelInput = stackalloc float[channelCount];
|
|
||||||
Span<float> delayLineValues = stackalloc float[channelCount];
|
|
||||||
Span<float> temp = stackalloc float[channelCount];
|
|
||||||
|
|
||||||
float delayFeedbackBaseGain = state.DelayFeedbackBaseGain;
|
float delayFeedbackBaseGain = state.DelayFeedbackBaseGain;
|
||||||
float delayFeedbackCrossGain = state.DelayFeedbackCrossGain;
|
float delayFeedbackCrossGain = state.DelayFeedbackCrossGain;
|
||||||
float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision);
|
float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision);
|
||||||
float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
|
float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
|
||||||
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
||||||
|
|
||||||
|
Matrix4x4 delayFeedback = new Matrix4x4(delayFeedbackBaseGain , delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f,
|
||||||
|
delayFeedbackCrossGain, delayFeedbackBaseGain , 0.0f , delayFeedbackCrossGain,
|
||||||
|
delayFeedbackCrossGain, 0.0f , delayFeedbackBaseGain , delayFeedbackCrossGain,
|
||||||
|
0.0f , delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain);
|
||||||
|
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < channelCount; j++)
|
Vector4 channelInput = new Vector4
|
||||||
{
|
{
|
||||||
channelInput[j] = *((float*)inputBuffers[j] + i) * 64;
|
X = *((float*)inputBuffers[0] + i) * 64,
|
||||||
delayLineValues[j] = state.DelayLines[j].Read();
|
Y = *((float*)inputBuffers[1] + i) * 64,
|
||||||
}
|
Z = *((float*)inputBuffers[2] + i) * 64,
|
||||||
|
W = *((float*)inputBuffers[3] + i) * 64
|
||||||
|
};
|
||||||
|
|
||||||
temp[0] = channelInput[0] * inGain + (delayLineValues[2] + delayLineValues[1]) * delayFeedbackCrossGain + delayLineValues[0] * delayFeedbackBaseGain;
|
Vector4 delayLineValues = new Vector4()
|
||||||
temp[1] = channelInput[1] * inGain + (delayLineValues[0] + delayLineValues[3]) * delayFeedbackCrossGain + delayLineValues[1] * delayFeedbackBaseGain;
|
|
||||||
temp[2] = channelInput[2] * inGain + (delayLineValues[3] + delayLineValues[0]) * delayFeedbackCrossGain + delayLineValues[2] * delayFeedbackBaseGain;
|
|
||||||
temp[3] = channelInput[3] * inGain + (delayLineValues[1] + delayLineValues[2]) * delayFeedbackCrossGain + delayLineValues[3] * delayFeedbackBaseGain;
|
|
||||||
|
|
||||||
for (int j = 0; j < channelCount; j++)
|
|
||||||
{
|
{
|
||||||
float lowPassResult = state.LowPassFeedbackGain * state.LowPassZ[j] + temp[j] * state.LowPassBaseGain;
|
X = state.DelayLines[0].Read(),
|
||||||
|
Y = state.DelayLines[1].Read(),
|
||||||
|
Z = state.DelayLines[2].Read(),
|
||||||
|
W = state.DelayLines[3].Read()
|
||||||
|
};
|
||||||
|
|
||||||
state.LowPassZ[j] = lowPassResult;
|
Vector4 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain;
|
||||||
state.DelayLines[j].Update(lowPassResult);
|
|
||||||
|
state.UpdateLowPassFilter(ref Unsafe.As<Vector4, float>(ref temp), channelCount);
|
||||||
|
|
||||||
*((float*)outputBuffers[j] + i) = (channelInput[j] * dryGain + delayLineValues[j] * outGain) / 64;
|
*((float*)outputBuffers[0] + i) = (channelInput.X * dryGain + delayLineValues.X * outGain) / 64;
|
||||||
}
|
*((float*)outputBuffers[1] + i) = (channelInput.Y * dryGain + delayLineValues.Y * outGain) / 64;
|
||||||
|
*((float*)outputBuffers[2] + i) = (channelInput.Z * dryGain + delayLineValues.Z * outGain) / 64;
|
||||||
|
*((float*)outputBuffers[3] + i) = (channelInput.W * dryGain + delayLineValues.W * outGain) / 64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private unsafe void ProcessDelaySurround(ref DelayState state, Span<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
private unsafe void ProcessDelaySurround(ref DelayState state, Span<IntPtr> outputBuffers, ReadOnlySpan<IntPtr> inputBuffers, uint sampleCount)
|
||||||
{
|
{
|
||||||
const ushort channelCount = 6;
|
const ushort channelCount = 6;
|
||||||
|
|
||||||
Span<float> channelInput = stackalloc float[channelCount];
|
float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision);
|
||||||
Span<float> delayLineValues = stackalloc float[channelCount];
|
|
||||||
Span<float> temp = stackalloc float[channelCount];
|
|
||||||
|
|
||||||
float delayFeedbackBaseGain = state.DelayFeedbackBaseGain;
|
float delayFeedbackBaseGain = state.DelayFeedbackBaseGain;
|
||||||
float delayFeedbackCrossGain = state.DelayFeedbackCrossGain;
|
float delayFeedbackCrossGain = state.DelayFeedbackCrossGain;
|
||||||
float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision);
|
float inGain = FixedPointHelper.ToFloat(Parameter.InGain, FixedPointPrecision);
|
||||||
float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
|
float dryGain = FixedPointHelper.ToFloat(Parameter.DryGain, FixedPointPrecision);
|
||||||
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
float outGain = FixedPointHelper.ToFloat(Parameter.OutGain, FixedPointPrecision);
|
||||||
|
|
||||||
|
Matrix6x6 delayFeedback = new Matrix6x6(delayFeedbackBaseGain , 0.0f , 0.0f , 0.0f , delayFeedbackCrossGain, delayFeedbackCrossGain,
|
||||||
|
0.0f , delayFeedbackBaseGain , 0.0f , delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f ,
|
||||||
|
delayFeedbackCrossGain, 0.0f , delayFeedbackBaseGain , delayFeedbackCrossGain, 0.0f , 0.0f ,
|
||||||
|
0.0f , delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain , 0.0f , 0.0f ,
|
||||||
|
delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f , 0.0f , delayFeedbackBaseGain , 0.0f ,
|
||||||
|
0.0f , 0.0f , 0.0f , 0.0f , 0.0f , feedbackGain);
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < channelCount; j++)
|
Vector6 channelInput = new Vector6
|
||||||
{
|
{
|
||||||
channelInput[j] = *((float*)inputBuffers[j] + i) * 64;
|
X = *((float*)inputBuffers[0] + i) * 64,
|
||||||
delayLineValues[j] = state.DelayLines[j].Read();
|
Y = *((float*)inputBuffers[1] + i) * 64,
|
||||||
}
|
Z = *((float*)inputBuffers[2] + i) * 64,
|
||||||
|
W = *((float*)inputBuffers[3] + i) * 64,
|
||||||
|
V = *((float*)inputBuffers[4] + i) * 64,
|
||||||
|
U = *((float*)inputBuffers[5] + i) * 64
|
||||||
|
};
|
||||||
|
|
||||||
temp[0] = channelInput[0] * inGain + (delayLineValues[2] + delayLineValues[4]) * delayFeedbackCrossGain + delayLineValues[0] * delayFeedbackBaseGain;
|
Vector6 delayLineValues = new Vector6
|
||||||
temp[1] = channelInput[1] * inGain + (delayLineValues[4] + delayLineValues[3]) * delayFeedbackCrossGain + delayLineValues[1] * delayFeedbackBaseGain;
|
|
||||||
temp[2] = channelInput[2] * inGain + (delayLineValues[3] + delayLineValues[0]) * delayFeedbackCrossGain + delayLineValues[2] * delayFeedbackBaseGain;
|
|
||||||
temp[3] = channelInput[3] * inGain + (delayLineValues[1] + delayLineValues[2]) * delayFeedbackCrossGain + delayLineValues[3] * delayFeedbackBaseGain;
|
|
||||||
temp[4] = channelInput[4] * inGain + (delayLineValues[0] + delayLineValues[1]) * delayFeedbackCrossGain + delayLineValues[4] * delayFeedbackBaseGain;
|
|
||||||
temp[5] = channelInput[5] * inGain + delayLineValues[5] * delayFeedbackBaseGain;
|
|
||||||
|
|
||||||
for (int j = 0; j < channelCount; j++)
|
|
||||||
{
|
{
|
||||||
float lowPassResult = state.LowPassFeedbackGain * state.LowPassZ[j] + temp[j] * state.LowPassBaseGain;
|
X = state.DelayLines[0].Read(),
|
||||||
|
Y = state.DelayLines[1].Read(),
|
||||||
|
Z = state.DelayLines[2].Read(),
|
||||||
|
W = state.DelayLines[3].Read(),
|
||||||
|
V = state.DelayLines[4].Read(),
|
||||||
|
U = state.DelayLines[5].Read()
|
||||||
|
};
|
||||||
|
|
||||||
state.LowPassZ[j] = lowPassResult;
|
Vector6 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain;
|
||||||
state.DelayLines[j].Update(lowPassResult);
|
|
||||||
|
|
||||||
*((float*)outputBuffers[j] + i) = (channelInput[j] * dryGain + delayLineValues[j] * outGain) / 64;
|
state.UpdateLowPassFilter(ref Unsafe.As<Vector6, float>(ref temp), channelCount);
|
||||||
}
|
|
||||||
|
*((float*)outputBuffers[0] + i) = (channelInput.X * dryGain + delayLineValues.X * outGain) / 64;
|
||||||
|
*((float*)outputBuffers[1] + i) = (channelInput.Y * dryGain + delayLineValues.Y * outGain) / 64;
|
||||||
|
*((float*)outputBuffers[2] + i) = (channelInput.Z * dryGain + delayLineValues.Z * outGain) / 64;
|
||||||
|
*((float*)outputBuffers[3] + i) = (channelInput.W * dryGain + delayLineValues.W * outGain) / 64;
|
||||||
|
*((float*)outputBuffers[4] + i) = (channelInput.V * dryGain + delayLineValues.V * outGain) / 64;
|
||||||
|
*((float*)outputBuffers[5] + i) = (channelInput.U * dryGain + delayLineValues.U * outGain) / 64;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
private Reverb3dParameter _parameter;
|
private Reverb3dParameter _parameter;
|
||||||
|
|
||||||
public Reverb3dCommand(uint bufferOffset, Reverb3dParameter parameter, Memory<Reverb3dState> state, bool isEnabled, ulong workBuffer, int nodeId)
|
public Reverb3dCommand(uint bufferOffset, Reverb3dParameter parameter, Memory<Reverb3dState> state, bool isEnabled, ulong workBuffer, int nodeId, bool newEffectChannelMappingSupported)
|
||||||
{
|
{
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
IsEffectEnabled = isEnabled;
|
IsEffectEnabled = isEnabled;
|
||||||
@ -80,6 +80,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
InputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Input[i]);
|
||||||
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
OutputBufferIndices[i] = (ushort)(bufferOffset + Parameter.Output[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: We do the opposite as Nintendo here for now to restore previous behaviour
|
||||||
|
// TODO: Update reverb 3d processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping.
|
||||||
|
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices);
|
||||||
|
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@ -194,7 +199,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
if (isSurround)
|
if (isSurround)
|
||||||
{
|
{
|
||||||
*((float*)outputBuffers[4] + sampleIndex) += (outputValues[4] + state.BackLeftDelayLine.Update((values[2] - values[3]) * 0.5f) + channelInput[4] * state.DryGain);
|
*((float*)outputBuffers[4] + sampleIndex) += (outputValues[4] + state.FrontCenterDelayLine.Update((values[2] - values[3]) * 0.5f) + channelInput[4] * state.DryGain);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
private const int FixedPointPrecision = 14;
|
private const int FixedPointPrecision = 14;
|
||||||
|
|
||||||
public ReverbCommand(uint bufferOffset, ReverbParameter parameter, Memory<ReverbState> state, bool isEnabled, ulong workBuffer, int nodeId, bool isLongSizePreDelaySupported)
|
public ReverbCommand(uint bufferOffset, ReverbParameter parameter, Memory<ReverbState> state, bool isEnabled, ulong workBuffer, int nodeId, bool isLongSizePreDelaySupported, bool newEffectChannelMappingSupported)
|
||||||
{
|
{
|
||||||
Enabled = true;
|
Enabled = true;
|
||||||
IsEffectEnabled = isEnabled;
|
IsEffectEnabled = isEnabled;
|
||||||
@ -85,6 +85,11 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
IsLongSizePreDelaySupported = isLongSizePreDelaySupported;
|
IsLongSizePreDelaySupported = isLongSizePreDelaySupported;
|
||||||
|
|
||||||
|
// NOTE: We do the opposite as Nintendo here for now to restore previous behaviour
|
||||||
|
// TODO: Update reverb processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping.
|
||||||
|
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices);
|
||||||
|
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@ -214,7 +219,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
if (isSurround)
|
if (isSurround)
|
||||||
{
|
{
|
||||||
outputValues[4] += state.BackLeftDelayLine.Update((feedbackOutputValues[2] - feedbackOutputValues[3]) * 0.5f);
|
outputValues[4] += state.FrontCenterDelayLine.Update((feedbackOutputValues[2] - feedbackOutputValues[3]) * 0.5f);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
|
for (int channelIndex = 0; channelIndex < Parameter.ChannelCount; channelIndex++)
|
||||||
|
@ -445,5 +445,39 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
ToIntSlow(output, input, sampleCount);
|
ToIntSlow(output, input, sampleCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void RemapLegacyChannelEffectMappingToChannelResourceMapping(bool isSupported, Span<ushort> bufferIndices)
|
||||||
|
{
|
||||||
|
if (!isSupported && bufferIndices.Length == 6)
|
||||||
|
{
|
||||||
|
ushort backLeft = bufferIndices[2];
|
||||||
|
ushort backRight = bufferIndices[3];
|
||||||
|
ushort frontCenter = bufferIndices[4];
|
||||||
|
ushort lowFrequency = bufferIndices[5];
|
||||||
|
|
||||||
|
bufferIndices[2] = frontCenter;
|
||||||
|
bufferIndices[3] = lowFrequency;
|
||||||
|
bufferIndices[4] = backLeft;
|
||||||
|
bufferIndices[5] = backRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void RemapChannelResourceMappingToLegacy(bool isSupported, Span<ushort> bufferIndices)
|
||||||
|
{
|
||||||
|
if (isSupported && bufferIndices.Length == 6)
|
||||||
|
{
|
||||||
|
ushort frontCenter = bufferIndices[2];
|
||||||
|
ushort lowFrequency = bufferIndices[3];
|
||||||
|
ushort backLeft = bufferIndices[4];
|
||||||
|
ushort backRight = bufferIndices[5];
|
||||||
|
|
||||||
|
bufferIndices[2] = backLeft;
|
||||||
|
bufferIndices[3] = backRight;
|
||||||
|
bufferIndices[4] = frontCenter;
|
||||||
|
bufferIndices[5] = lowFrequency;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
using Ryujinx.Audio.Renderer.Dsp.Effect;
|
||||||
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
using Ryujinx.Audio.Renderer.Parameter.Effect;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace Ryujinx.Audio.Renderer.Dsp.State
|
namespace Ryujinx.Audio.Renderer.Dsp.State
|
||||||
{
|
{
|
||||||
@ -43,7 +44,6 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
|
|||||||
{
|
{
|
||||||
DelayLines[i] = new DelayLine(sampleRate, parameter.DelayTimeMax);
|
DelayLines[i] = new DelayLine(sampleRate, parameter.DelayTimeMax);
|
||||||
DelayLines[i].SetDelay(parameter.DelayTime);
|
DelayLines[i].SetDelay(parameter.DelayTime);
|
||||||
LowPassZ[0] = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateParameter(ref parameter);
|
UpdateParameter(ref parameter);
|
||||||
@ -69,5 +69,16 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
|
|||||||
LowPassFeedbackGain = 0.95f * FixedPointHelper.ToFloat(parameter.LowPassAmount, FixedPointPrecision);
|
LowPassFeedbackGain = 0.95f * FixedPointHelper.ToFloat(parameter.LowPassAmount, FixedPointPrecision);
|
||||||
LowPassBaseGain = 1.0f - LowPassFeedbackGain;
|
LowPassBaseGain = 1.0f - LowPassFeedbackGain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateLowPassFilter(ref float tempRawRef, uint channelCount)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < channelCount; i++)
|
||||||
|
{
|
||||||
|
float lowPassResult = LowPassFeedbackGain * LowPassZ[i] + Unsafe.Add(ref tempRawRef, i) * LowPassBaseGain;
|
||||||
|
|
||||||
|
LowPassZ[i] = lowPassResult;
|
||||||
|
DelayLines[i].Update(lowPassResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
|
|||||||
public DecayDelay[] DecayDelays1 { get; }
|
public DecayDelay[] DecayDelays1 { get; }
|
||||||
public DecayDelay[] DecayDelays2 { get; }
|
public DecayDelay[] DecayDelays2 { get; }
|
||||||
public IDelayLine PreDelayLine { get; }
|
public IDelayLine PreDelayLine { get; }
|
||||||
public IDelayLine BackLeftDelayLine { get; }
|
public IDelayLine FrontCenterDelayLine { get; }
|
||||||
public float DryGain { get; private set; }
|
public float DryGain { get; private set; }
|
||||||
public uint[] EarlyDelayTime { get; private set; }
|
public uint[] EarlyDelayTime { get; private set; }
|
||||||
public float PreviousPreDelayValue { get; set; }
|
public float PreviousPreDelayValue { get; set; }
|
||||||
@ -69,7 +69,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
|
|||||||
}
|
}
|
||||||
|
|
||||||
PreDelayLine = new DelayLine3d(sampleRate, 400);
|
PreDelayLine = new DelayLine3d(sampleRate, 400);
|
||||||
BackLeftDelayLine = new DelayLine3d(sampleRate, 5);
|
FrontCenterDelayLine = new DelayLine3d(sampleRate, 5);
|
||||||
|
|
||||||
UpdateParameter(ref parameter);
|
UpdateParameter(ref parameter);
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
|
|||||||
public DelayLine[] FdnDelayLines { get; }
|
public DelayLine[] FdnDelayLines { get; }
|
||||||
public DecayDelay[] DecayDelays { get; }
|
public DecayDelay[] DecayDelays { get; }
|
||||||
public DelayLine PreDelayLine { get; }
|
public DelayLine PreDelayLine { get; }
|
||||||
public DelayLine BackLeftDelayLine { get; }
|
public DelayLine FrontCenterDelayLine { get; }
|
||||||
public uint[] EarlyDelayTime { get; }
|
public uint[] EarlyDelayTime { get; }
|
||||||
public float[] EarlyGain { get; }
|
public float[] EarlyGain { get; }
|
||||||
public uint PreDelayLineDelayTime { get; private set; }
|
public uint PreDelayLineDelayTime { get; private set; }
|
||||||
@ -149,7 +149,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.State
|
|||||||
}
|
}
|
||||||
|
|
||||||
PreDelayLine = new DelayLine(sampleRate, preDelayTimeMax);
|
PreDelayLine = new DelayLine(sampleRate, preDelayTimeMax);
|
||||||
BackLeftDelayLine = new DelayLine(sampleRate, 5.0f);
|
FrontCenterDelayLine = new DelayLine(sampleRate, 5.0f);
|
||||||
|
|
||||||
UpdateParameter(ref parameter);
|
UpdateParameter(ref parameter);
|
||||||
}
|
}
|
||||||
|
@ -363,6 +363,9 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
case 4:
|
case 4:
|
||||||
_commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion4(_sampleCount, _mixBufferCount);
|
_commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion4(_sampleCount, _mixBufferCount);
|
||||||
break;
|
break;
|
||||||
|
case 5:
|
||||||
|
_commandProcessingTimeEstimator = new CommandProcessingTimeEstimatorVersion5(_sampleCount, _mixBufferCount);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Unsupported processing time estimator version {_behaviourContext.GetCommandProcessingTimeEstimatorVersion()}.");
|
throw new NotImplementedException($"Unsupported processing time estimator version {_behaviourContext.GetCommandProcessingTimeEstimatorVersion()}.");
|
||||||
}
|
}
|
||||||
|
@ -107,10 +107,18 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// <remarks>This was added in system update 13.0.0</remarks>
|
/// <remarks>This was added in system update 13.0.0</remarks>
|
||||||
public const int Revision10 = 10 << 24;
|
public const int Revision10 = 10 << 24;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// REV11:
|
||||||
|
/// The "legacy" effects (Delay, Reverb and Reverb 3D) were updated to match the standard channel mapping used by the audio renderer.
|
||||||
|
/// A new version of the command estimator was added to address timing changes caused by the legacy effects changes.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>This was added in system update 14.0.0</remarks>
|
||||||
|
public const int Revision11 = 11 << 24;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Last revision supported by the implementation.
|
/// Last revision supported by the implementation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int LastRevision = Revision10;
|
public const int LastRevision = Revision11;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Target revision magic supported by the implementation.
|
/// Target revision magic supported by the implementation.
|
||||||
@ -366,12 +374,26 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision10);
|
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the audio renderer should support new channel resource mapping for 5.1 on Delay, Reverb and Reverb 3D effects.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the audio renderer support new channel resource mapping for 5.1.</returns>
|
||||||
|
public bool IsNewEffectChannelMappingSupported()
|
||||||
|
{
|
||||||
|
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision11);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
|
/// Get the version of the <see cref="ICommandProcessingTimeEstimator"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The version of the <see cref="ICommandProcessingTimeEstimator"/>.</returns>
|
/// <returns>The version of the <see cref="ICommandProcessingTimeEstimator"/>.</returns>
|
||||||
public int GetCommandProcessingTimeEstimatorVersion()
|
public int GetCommandProcessingTimeEstimatorVersion()
|
||||||
{
|
{
|
||||||
|
if (CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision11))
|
||||||
|
{
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
if (CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision10))
|
if (CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision10))
|
||||||
{
|
{
|
||||||
return 4;
|
return 4;
|
||||||
|
@ -336,11 +336,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// <param name="workBuffer">The work buffer to use for processing.</param>
|
/// <param name="workBuffer">The work buffer to use for processing.</param>
|
||||||
/// <param name="nodeId">The node id associated to this command.</param>
|
/// <param name="nodeId">The node id associated to this command.</param>
|
||||||
/// <param name="isLongSizePreDelaySupported">If set to true, the long size pre-delay is supported.</param>
|
/// <param name="isLongSizePreDelaySupported">If set to true, the long size pre-delay is supported.</param>
|
||||||
public void GenerateReverbEffect(uint bufferOffset, ReverbParameter parameter, Memory<ReverbState> state, bool isEnabled, CpuAddress workBuffer, int nodeId, bool isLongSizePreDelaySupported)
|
/// <param name="newEffectChannelMappingSupported">If set to true, the new effect channel mapping for 5.1 is supported.</param>
|
||||||
|
public void GenerateReverbEffect(uint bufferOffset, ReverbParameter parameter, Memory<ReverbState> state, bool isEnabled, CpuAddress workBuffer, int nodeId, bool isLongSizePreDelaySupported, bool newEffectChannelMappingSupported)
|
||||||
{
|
{
|
||||||
if (parameter.IsChannelCountValid())
|
if (parameter.IsChannelCountValid())
|
||||||
{
|
{
|
||||||
ReverbCommand command = new ReverbCommand(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, isLongSizePreDelaySupported);
|
ReverbCommand command = new ReverbCommand(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, isLongSizePreDelaySupported, newEffectChannelMappingSupported);
|
||||||
|
|
||||||
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||||
|
|
||||||
@ -357,11 +358,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// <param name="isEnabled">Set to true if the effect should be active.</param>
|
/// <param name="isEnabled">Set to true if the effect should be active.</param>
|
||||||
/// <param name="workBuffer">The work buffer to use for processing.</param>
|
/// <param name="workBuffer">The work buffer to use for processing.</param>
|
||||||
/// <param name="nodeId">The node id associated to this command.</param>
|
/// <param name="nodeId">The node id associated to this command.</param>
|
||||||
public void GenerateReverb3dEffect(uint bufferOffset, Reverb3dParameter parameter, Memory<Reverb3dState> state, bool isEnabled, CpuAddress workBuffer, int nodeId)
|
/// <param name="newEffectChannelMappingSupported">If set to true, the new effect channel mapping for 5.1 is supported.</param>
|
||||||
|
public void GenerateReverb3dEffect(uint bufferOffset, Reverb3dParameter parameter, Memory<Reverb3dState> state, bool isEnabled, CpuAddress workBuffer, int nodeId, bool newEffectChannelMappingSupported)
|
||||||
{
|
{
|
||||||
if (parameter.IsChannelCountValid())
|
if (parameter.IsChannelCountValid())
|
||||||
{
|
{
|
||||||
Reverb3dCommand command = new Reverb3dCommand(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId);
|
Reverb3dCommand command = new Reverb3dCommand(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, newEffectChannelMappingSupported);
|
||||||
|
|
||||||
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||||
|
|
||||||
@ -379,11 +381,12 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// <param name="isEnabled">Set to true if the effect should be active.</param>
|
/// <param name="isEnabled">Set to true if the effect should be active.</param>
|
||||||
/// <param name="workBuffer">The work buffer to use for processing.</param>
|
/// <param name="workBuffer">The work buffer to use for processing.</param>
|
||||||
/// <param name="nodeId">The node id associated to this command.</param>
|
/// <param name="nodeId">The node id associated to this command.</param>
|
||||||
public void GenerateDelayEffect(uint bufferOffset, DelayParameter parameter, Memory<DelayState> state, bool isEnabled, CpuAddress workBuffer, int nodeId)
|
/// <param name="newEffectChannelMappingSupported">If set to true, the new effect channel mapping for 5.1 is supported.</param>
|
||||||
|
public void GenerateDelayEffect(uint bufferOffset, DelayParameter parameter, Memory<DelayState> state, bool isEnabled, CpuAddress workBuffer, int nodeId, bool newEffectChannelMappingSupported)
|
||||||
{
|
{
|
||||||
if (parameter.IsChannelCountValid())
|
if (parameter.IsChannelCountValid())
|
||||||
{
|
{
|
||||||
DelayCommand command = new DelayCommand(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId);
|
DelayCommand command = new DelayCommand(bufferOffset, parameter, state, isEnabled, workBuffer, nodeId, newEffectChannelMappingSupported);
|
||||||
|
|
||||||
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
command.EstimatedProcessingTime = _commandProcessingTimeEstimator.Estimate(command);
|
||||||
|
|
||||||
|
@ -483,31 +483,31 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateDelayEffect(uint bufferOffset, DelayEffect effect, int nodeId)
|
private void GenerateDelayEffect(uint bufferOffset, DelayEffect effect, int nodeId, bool newEffectChannelMappingSupported)
|
||||||
{
|
{
|
||||||
Debug.Assert(effect.Type == EffectType.Delay);
|
Debug.Assert(effect.Type == EffectType.Delay);
|
||||||
|
|
||||||
ulong workBuffer = effect.GetWorkBuffer(-1);
|
ulong workBuffer = effect.GetWorkBuffer(-1);
|
||||||
|
|
||||||
_commandBuffer.GenerateDelayEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId);
|
_commandBuffer.GenerateDelayEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId, newEffectChannelMappingSupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateReverbEffect(uint bufferOffset, ReverbEffect effect, int nodeId, bool isLongSizePreDelaySupported)
|
private void GenerateReverbEffect(uint bufferOffset, ReverbEffect effect, int nodeId, bool isLongSizePreDelaySupported, bool newEffectChannelMappingSupported)
|
||||||
{
|
{
|
||||||
Debug.Assert(effect.Type == EffectType.Reverb);
|
Debug.Assert(effect.Type == EffectType.Reverb);
|
||||||
|
|
||||||
ulong workBuffer = effect.GetWorkBuffer(-1);
|
ulong workBuffer = effect.GetWorkBuffer(-1);
|
||||||
|
|
||||||
_commandBuffer.GenerateReverbEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId, isLongSizePreDelaySupported);
|
_commandBuffer.GenerateReverbEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId, isLongSizePreDelaySupported, newEffectChannelMappingSupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateReverb3dEffect(uint bufferOffset, Reverb3dEffect effect, int nodeId)
|
private void GenerateReverb3dEffect(uint bufferOffset, Reverb3dEffect effect, int nodeId, bool newEffectChannelMappingSupported)
|
||||||
{
|
{
|
||||||
Debug.Assert(effect.Type == EffectType.Reverb3d);
|
Debug.Assert(effect.Type == EffectType.Reverb3d);
|
||||||
|
|
||||||
ulong workBuffer = effect.GetWorkBuffer(-1);
|
ulong workBuffer = effect.GetWorkBuffer(-1);
|
||||||
|
|
||||||
_commandBuffer.GenerateReverb3dEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId);
|
_commandBuffer.GenerateReverb3dEffect(bufferOffset, effect.Parameter, effect.State, effect.IsEnabled, workBuffer, nodeId, newEffectChannelMappingSupported);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GenerateBiquadFilterEffect(uint bufferOffset, BiquadFilterEffect effect, int nodeId)
|
private void GenerateBiquadFilterEffect(uint bufferOffset, BiquadFilterEffect effect, int nodeId)
|
||||||
@ -650,13 +650,13 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
GenerateAuxEffect(mix.BufferOffset, (AuxiliaryBufferEffect)effect, nodeId);
|
GenerateAuxEffect(mix.BufferOffset, (AuxiliaryBufferEffect)effect, nodeId);
|
||||||
break;
|
break;
|
||||||
case EffectType.Delay:
|
case EffectType.Delay:
|
||||||
GenerateDelayEffect(mix.BufferOffset, (DelayEffect)effect, nodeId);
|
GenerateDelayEffect(mix.BufferOffset, (DelayEffect)effect, nodeId, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
break;
|
break;
|
||||||
case EffectType.Reverb:
|
case EffectType.Reverb:
|
||||||
GenerateReverbEffect(mix.BufferOffset, (ReverbEffect)effect, nodeId, mix.IsLongSizePreDelaySupported);
|
GenerateReverbEffect(mix.BufferOffset, (ReverbEffect)effect, nodeId, mix.IsLongSizePreDelaySupported, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
break;
|
break;
|
||||||
case EffectType.Reverb3d:
|
case EffectType.Reverb3d:
|
||||||
GenerateReverb3dEffect(mix.BufferOffset, (Reverb3dEffect)effect, nodeId);
|
GenerateReverb3dEffect(mix.BufferOffset, (Reverb3dEffect)effect, nodeId, _rendererContext.BehaviourContext.IsNewEffectChannelMappingSupported());
|
||||||
break;
|
break;
|
||||||
case EffectType.BiquadFilter:
|
case EffectType.BiquadFilter:
|
||||||
GenerateBiquadFilterEffect(mix.BufferOffset, (BiquadFilterEffect)effect, nodeId);
|
GenerateBiquadFilterEffect(mix.BufferOffset, (BiquadFilterEffect)effect, nodeId);
|
||||||
|
@ -198,7 +198,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
return (uint)1853.2f;
|
return (uint)1853.2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Estimate(DelayCommand command)
|
public virtual uint Estimate(DelayCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
||||||
|
|
||||||
@ -272,7 +272,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Estimate(ReverbCommand command)
|
public virtual uint Estimate(ReverbCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
||||||
|
|
||||||
@ -346,7 +346,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public uint Estimate(Reverb3dCommand command)
|
public virtual uint Estimate(Reverb3dCommand command)
|
||||||
{
|
{
|
||||||
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
||||||
|
|
||||||
|
@ -0,0 +1,253 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2019-2022 Ryujinx
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
|
||||||
|
using Ryujinx.Audio.Renderer.Dsp.Command;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Renderer.Server
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="ICommandProcessingTimeEstimator"/> version 5. (added with REV11)
|
||||||
|
/// </summary>
|
||||||
|
public class CommandProcessingTimeEstimatorVersion5 : CommandProcessingTimeEstimatorVersion4
|
||||||
|
{
|
||||||
|
public CommandProcessingTimeEstimatorVersion5(uint sampleCount, uint bufferCount) : base(sampleCount, bufferCount) { }
|
||||||
|
|
||||||
|
public override uint Estimate(DelayCommand command)
|
||||||
|
{
|
||||||
|
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
||||||
|
|
||||||
|
if (_sampleCount == 160)
|
||||||
|
{
|
||||||
|
if (command.Enabled)
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return 8929;
|
||||||
|
case 2:
|
||||||
|
return 25501;
|
||||||
|
case 4:
|
||||||
|
return 47760;
|
||||||
|
case 6:
|
||||||
|
return 82203;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return (uint)1295.20f;
|
||||||
|
case 2:
|
||||||
|
return (uint)1213.60f;
|
||||||
|
case 4:
|
||||||
|
return (uint)942.03f;
|
||||||
|
case 6:
|
||||||
|
return (uint)1001.6f;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.Enabled)
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return 11941;
|
||||||
|
case 2:
|
||||||
|
return 37197;
|
||||||
|
case 4:
|
||||||
|
return 69750;
|
||||||
|
case 6:
|
||||||
|
return 12004;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return (uint)997.67f;
|
||||||
|
case 2:
|
||||||
|
return (uint)977.63f;
|
||||||
|
case 4:
|
||||||
|
return (uint)792.31f;
|
||||||
|
case 6:
|
||||||
|
return (uint)875.43f;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint Estimate(ReverbCommand command)
|
||||||
|
{
|
||||||
|
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
||||||
|
|
||||||
|
if (_sampleCount == 160)
|
||||||
|
{
|
||||||
|
if (command.Enabled)
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return 81475;
|
||||||
|
case 2:
|
||||||
|
return 84975;
|
||||||
|
case 4:
|
||||||
|
return 91625;
|
||||||
|
case 6:
|
||||||
|
return 95332;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return (uint)536.30f;
|
||||||
|
case 2:
|
||||||
|
return (uint)588.80f;
|
||||||
|
case 4:
|
||||||
|
return (uint)643.70f;
|
||||||
|
case 6:
|
||||||
|
return (uint)706.0f;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.Enabled)
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return 120170;
|
||||||
|
case 2:
|
||||||
|
return 125260;
|
||||||
|
case 4:
|
||||||
|
return 135750;
|
||||||
|
case 6:
|
||||||
|
return 141130;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return (uint)617.64f;
|
||||||
|
case 2:
|
||||||
|
return (uint)659.54f;
|
||||||
|
case 4:
|
||||||
|
return (uint)711.44f;
|
||||||
|
case 6:
|
||||||
|
return (uint)778.07f;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override uint Estimate(Reverb3dCommand command)
|
||||||
|
{
|
||||||
|
Debug.Assert(_sampleCount == 160 || _sampleCount == 240);
|
||||||
|
|
||||||
|
if (_sampleCount == 160)
|
||||||
|
{
|
||||||
|
if (command.Enabled)
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return 116750;
|
||||||
|
case 2:
|
||||||
|
return 125910;
|
||||||
|
case 4:
|
||||||
|
return 146340;
|
||||||
|
case 6:
|
||||||
|
return 165810;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return 735;
|
||||||
|
case 2:
|
||||||
|
return (uint)766.62f;
|
||||||
|
case 4:
|
||||||
|
return (uint)834.07f;
|
||||||
|
case 6:
|
||||||
|
return (uint)875.44f;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command.Enabled)
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return 170290;
|
||||||
|
case 2:
|
||||||
|
return 183880;
|
||||||
|
case 4:
|
||||||
|
return 214700;
|
||||||
|
case 6:
|
||||||
|
return 243850;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (command.Parameter.ChannelCount)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return (uint)508.47f;
|
||||||
|
case 2:
|
||||||
|
return (uint)582.45f;
|
||||||
|
case 4:
|
||||||
|
return (uint)626.42f;
|
||||||
|
case 6:
|
||||||
|
return (uint)682.47f;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException($"{command.Parameter.ChannelCount}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
71
Ryujinx.Audio/Renderer/Utils/Math/Matrix2x2.cs
Normal file
71
Ryujinx.Audio/Renderer/Utils/Math/Matrix2x2.cs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
namespace Ryujinx.Audio.Renderer.Utils.Math
|
||||||
|
{
|
||||||
|
record struct Matrix2x2
|
||||||
|
{
|
||||||
|
public float M11;
|
||||||
|
public float M12;
|
||||||
|
public float M21;
|
||||||
|
public float M22;
|
||||||
|
|
||||||
|
public Matrix2x2(float m11, float m12,
|
||||||
|
float m21, float m22)
|
||||||
|
{
|
||||||
|
M11 = m11;
|
||||||
|
M12 = m12;
|
||||||
|
|
||||||
|
M21 = m21;
|
||||||
|
M22 = m22;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Matrix2x2 operator +(Matrix2x2 value1, Matrix2x2 value2)
|
||||||
|
{
|
||||||
|
Matrix2x2 m;
|
||||||
|
|
||||||
|
m.M11 = value1.M11 + value2.M11;
|
||||||
|
m.M12 = value1.M12 + value2.M12;
|
||||||
|
m.M21 = value1.M21 + value2.M21;
|
||||||
|
m.M22 = value1.M22 + value2.M22;
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Matrix2x2 operator -(Matrix2x2 value1, float value2)
|
||||||
|
{
|
||||||
|
Matrix2x2 m;
|
||||||
|
|
||||||
|
m.M11 = value1.M11 - value2;
|
||||||
|
m.M12 = value1.M12 - value2;
|
||||||
|
m.M21 = value1.M21 - value2;
|
||||||
|
m.M22 = value1.M22 - value2;
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Matrix2x2 operator *(Matrix2x2 value1, float value2)
|
||||||
|
{
|
||||||
|
Matrix2x2 m;
|
||||||
|
|
||||||
|
m.M11 = value1.M11 * value2;
|
||||||
|
m.M12 = value1.M12 * value2;
|
||||||
|
m.M21 = value1.M21 * value2;
|
||||||
|
m.M22 = value1.M22 * value2;
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Matrix2x2 operator *(Matrix2x2 value1, Matrix2x2 value2)
|
||||||
|
{
|
||||||
|
Matrix2x2 m;
|
||||||
|
|
||||||
|
// First row
|
||||||
|
m.M11 = value1.M11 * value2.M11 + value1.M12 * value2.M21;
|
||||||
|
m.M12 = value1.M11 * value2.M12 + value1.M12 * value2.M22;
|
||||||
|
|
||||||
|
// Second row
|
||||||
|
m.M21 = value1.M21 * value2.M11 + value1.M22 * value2.M21;
|
||||||
|
m.M22 = value1.M21 * value2.M12 + value1.M22 * value2.M22;
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
97
Ryujinx.Audio/Renderer/Utils/Math/Matrix6x6.cs
Normal file
97
Ryujinx.Audio/Renderer/Utils/Math/Matrix6x6.cs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
namespace Ryujinx.Audio.Renderer.Utils.Math
|
||||||
|
{
|
||||||
|
record struct Matrix6x6
|
||||||
|
{
|
||||||
|
public float M11;
|
||||||
|
public float M12;
|
||||||
|
public float M13;
|
||||||
|
public float M14;
|
||||||
|
public float M15;
|
||||||
|
public float M16;
|
||||||
|
|
||||||
|
public float M21;
|
||||||
|
public float M22;
|
||||||
|
public float M23;
|
||||||
|
public float M24;
|
||||||
|
public float M25;
|
||||||
|
public float M26;
|
||||||
|
|
||||||
|
public float M31;
|
||||||
|
public float M32;
|
||||||
|
public float M33;
|
||||||
|
public float M34;
|
||||||
|
public float M35;
|
||||||
|
public float M36;
|
||||||
|
|
||||||
|
public float M41;
|
||||||
|
public float M42;
|
||||||
|
public float M43;
|
||||||
|
public float M44;
|
||||||
|
public float M45;
|
||||||
|
public float M46;
|
||||||
|
|
||||||
|
public float M51;
|
||||||
|
public float M52;
|
||||||
|
public float M53;
|
||||||
|
public float M54;
|
||||||
|
public float M55;
|
||||||
|
public float M56;
|
||||||
|
|
||||||
|
public float M61;
|
||||||
|
public float M62;
|
||||||
|
public float M63;
|
||||||
|
public float M64;
|
||||||
|
public float M65;
|
||||||
|
public float M66;
|
||||||
|
|
||||||
|
public Matrix6x6(float m11, float m12, float m13, float m14, float m15, float m16,
|
||||||
|
float m21, float m22, float m23, float m24, float m25, float m26,
|
||||||
|
float m31, float m32, float m33, float m34, float m35, float m36,
|
||||||
|
float m41, float m42, float m43, float m44, float m45, float m46,
|
||||||
|
float m51, float m52, float m53, float m54, float m55, float m56,
|
||||||
|
float m61, float m62, float m63, float m64, float m65, float m66)
|
||||||
|
{
|
||||||
|
M11 = m11;
|
||||||
|
M12 = m12;
|
||||||
|
M13 = m13;
|
||||||
|
M14 = m14;
|
||||||
|
M15 = m15;
|
||||||
|
M16 = m16;
|
||||||
|
|
||||||
|
M21 = m21;
|
||||||
|
M22 = m22;
|
||||||
|
M23 = m23;
|
||||||
|
M24 = m24;
|
||||||
|
M25 = m25;
|
||||||
|
M26 = m26;
|
||||||
|
|
||||||
|
M31 = m31;
|
||||||
|
M32 = m32;
|
||||||
|
M33 = m33;
|
||||||
|
M34 = m34;
|
||||||
|
M35 = m35;
|
||||||
|
M36 = m36;
|
||||||
|
|
||||||
|
M41 = m41;
|
||||||
|
M42 = m42;
|
||||||
|
M43 = m43;
|
||||||
|
M44 = m44;
|
||||||
|
M45 = m45;
|
||||||
|
M46 = m46;
|
||||||
|
|
||||||
|
M51 = m51;
|
||||||
|
M52 = m52;
|
||||||
|
M53 = m53;
|
||||||
|
M54 = m54;
|
||||||
|
M55 = m55;
|
||||||
|
M56 = m56;
|
||||||
|
|
||||||
|
M61 = m61;
|
||||||
|
M62 = m62;
|
||||||
|
M63 = m63;
|
||||||
|
M64 = m64;
|
||||||
|
M65 = m65;
|
||||||
|
M66 = m66;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
Ryujinx.Audio/Renderer/Utils/Math/MatrixHelper.cs
Normal file
45
Ryujinx.Audio/Renderer/Utils/Math/MatrixHelper.cs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
using Ryujinx.Audio.Renderer.Utils.Math;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Renderer.Dsp
|
||||||
|
{
|
||||||
|
static class MatrixHelper
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Vector6 Transform(ref Vector6 value1, ref Matrix6x6 value2)
|
||||||
|
{
|
||||||
|
return new Vector6
|
||||||
|
{
|
||||||
|
X = value2.M11 * value1.X + value2.M12 * value1.Y + value2.M13 * value1.Z + value2.M14 * value1.W + value2.M15 * value1.V + value2.M16 * value1.U,
|
||||||
|
Y = value2.M21 * value1.X + value2.M22 * value1.Y + value2.M23 * value1.Z + value2.M24 * value1.W + value2.M25 * value1.V + value2.M26 * value1.U,
|
||||||
|
Z = value2.M31 * value1.X + value2.M32 * value1.Y + value2.M33 * value1.Z + value2.M34 * value1.W + value2.M35 * value1.V + value2.M36 * value1.U,
|
||||||
|
W = value2.M41 * value1.X + value2.M42 * value1.Y + value2.M43 * value1.Z + value2.M44 * value1.W + value2.M45 * value1.V + value2.M46 * value1.U,
|
||||||
|
V = value2.M51 * value1.X + value2.M52 * value1.Y + value2.M53 * value1.Z + value2.M54 * value1.W + value2.M55 * value1.V + value2.M56 * value1.U,
|
||||||
|
U = value2.M61 * value1.X + value2.M62 * value1.Y + value2.M63 * value1.Z + value2.M64 * value1.W + value2.M65 * value1.V + value2.M66 * value1.U,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Vector4 Transform(ref Vector4 value1, ref Matrix4x4 value2)
|
||||||
|
{
|
||||||
|
return new Vector4
|
||||||
|
{
|
||||||
|
X = value2.M11 * value1.X + value2.M12 * value1.Y + value2.M13 * value1.Z + value2.M14 * value1.W,
|
||||||
|
Y = value2.M21 * value1.X + value2.M22 * value1.Y + value2.M23 * value1.Z + value2.M24 * value1.W,
|
||||||
|
Z = value2.M31 * value1.X + value2.M32 * value1.Y + value2.M33 * value1.Z + value2.M34 * value1.W,
|
||||||
|
W = value2.M41 * value1.X + value2.M42 * value1.Y + value2.M43 * value1.Z + value2.M44 * value1.W
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Vector2 Transform(ref Vector2 value1, ref Matrix2x2 value2)
|
||||||
|
{
|
||||||
|
return new Vector2
|
||||||
|
{
|
||||||
|
X = value2.M11 * value1.X + value2.M12 * value1.Y,
|
||||||
|
Y = value2.M21 * value1.X + value2.M22 * value1.Y,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
56
Ryujinx.Audio/Renderer/Utils/Math/Vector6.cs
Normal file
56
Ryujinx.Audio/Renderer/Utils/Math/Vector6.cs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Audio.Renderer.Utils.Math
|
||||||
|
{
|
||||||
|
record struct Vector6
|
||||||
|
{
|
||||||
|
public float X;
|
||||||
|
public float Y;
|
||||||
|
public float Z;
|
||||||
|
public float W;
|
||||||
|
public float V;
|
||||||
|
public float U;
|
||||||
|
|
||||||
|
public Vector6(float value) : this(value, value, value, value, value, value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector6(float x, float y, float z, float w, float v, float u)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Z = z;
|
||||||
|
W = w;
|
||||||
|
V = v;
|
||||||
|
U = u;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Vector6 operator +(Vector6 left, Vector6 right)
|
||||||
|
{
|
||||||
|
return new Vector6(left.X + right.X,
|
||||||
|
left.Y + right.Y,
|
||||||
|
left.Z + right.Z,
|
||||||
|
left.W + right.W,
|
||||||
|
left.V + right.V,
|
||||||
|
left.U + right.U);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Vector6 operator *(Vector6 left, Vector6 right)
|
||||||
|
{
|
||||||
|
return new Vector6(left.X * right.X,
|
||||||
|
left.Y * right.Y,
|
||||||
|
left.Z * right.Z,
|
||||||
|
left.W * right.W,
|
||||||
|
left.V * right.V,
|
||||||
|
left.U * right.U);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static Vector6 operator *(Vector6 left, float right)
|
||||||
|
{
|
||||||
|
return left * new Vector6(right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,47 +0,0 @@
|
|||||||
namespace Ryujinx.Graphics.GAL
|
|
||||||
{
|
|
||||||
public struct DepthStencilState
|
|
||||||
{
|
|
||||||
public bool DepthTestEnable { get; }
|
|
||||||
public bool DepthWriteEnable { get; }
|
|
||||||
public bool StencilTestEnable { get; }
|
|
||||||
|
|
||||||
public CompareOp DepthFunc { get; }
|
|
||||||
public CompareOp StencilFrontFunc { get; }
|
|
||||||
public StencilOp StencilFrontSFail { get; }
|
|
||||||
public StencilOp StencilFrontDpPass { get; }
|
|
||||||
public StencilOp StencilFrontDpFail { get; }
|
|
||||||
public CompareOp StencilBackFunc { get; }
|
|
||||||
public StencilOp StencilBackSFail { get; }
|
|
||||||
public StencilOp StencilBackDpPass { get; }
|
|
||||||
public StencilOp StencilBackDpFail { get; }
|
|
||||||
|
|
||||||
public DepthStencilState(
|
|
||||||
bool depthTestEnable,
|
|
||||||
bool depthWriteEnable,
|
|
||||||
bool stencilTestEnable,
|
|
||||||
CompareOp depthFunc,
|
|
||||||
CompareOp stencilFrontFunc,
|
|
||||||
StencilOp stencilFrontSFail,
|
|
||||||
StencilOp stencilFrontDpPass,
|
|
||||||
StencilOp stencilFrontDpFail,
|
|
||||||
CompareOp stencilBackFunc,
|
|
||||||
StencilOp stencilBackSFail,
|
|
||||||
StencilOp stencilBackDpPass,
|
|
||||||
StencilOp stencilBackDpFail)
|
|
||||||
{
|
|
||||||
DepthTestEnable = depthTestEnable;
|
|
||||||
DepthWriteEnable = depthWriteEnable;
|
|
||||||
StencilTestEnable = stencilTestEnable;
|
|
||||||
DepthFunc = depthFunc;
|
|
||||||
StencilFrontFunc = stencilFrontFunc;
|
|
||||||
StencilFrontSFail = stencilFrontSFail;
|
|
||||||
StencilFrontDpPass = stencilFrontDpPass;
|
|
||||||
StencilFrontDpFail = stencilFrontDpFail;
|
|
||||||
StencilBackFunc = stencilBackFunc;
|
|
||||||
StencilBackSFail = stencilBackSFail;
|
|
||||||
StencilBackDpPass = stencilBackDpPass;
|
|
||||||
StencilBackDpFail = stencilBackDpFail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,6 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
Texture2DArray,
|
Texture2DArray,
|
||||||
Texture2DMultisample,
|
Texture2DMultisample,
|
||||||
Texture2DMultisampleArray,
|
Texture2DMultisampleArray,
|
||||||
Rectangle,
|
|
||||||
Cubemap,
|
Cubemap,
|
||||||
CubemapArray,
|
CubemapArray,
|
||||||
TextureBuffer
|
TextureBuffer
|
||||||
|
@ -525,7 +525,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
int scissorW = screenScissorState.Width;
|
int scissorW = screenScissorState.Width;
|
||||||
int scissorH = screenScissorState.Height;
|
int scissorH = screenScissorState.Height;
|
||||||
|
|
||||||
if (clearAffectedByScissor)
|
if (clearAffectedByScissor && _state.State.ScissorState[0].Enable)
|
||||||
{
|
{
|
||||||
ref var scissorState = ref _state.State.ScissorState[0];
|
ref var scissorState = ref _state.State.ScissorState[0];
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
public const int RasterizerStateIndex = 1;
|
public const int RasterizerStateIndex = 1;
|
||||||
public const int ScissorStateIndex = 2;
|
public const int ScissorStateIndex = 2;
|
||||||
public const int VertexBufferStateIndex = 3;
|
public const int VertexBufferStateIndex = 3;
|
||||||
|
public const int PrimitiveRestartStateIndex = 4;
|
||||||
|
|
||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
private readonly GpuChannel _channel;
|
private readonly GpuChannel _channel;
|
||||||
@ -34,6 +35,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
private byte _vsClipDistancesWritten;
|
private byte _vsClipDistancesWritten;
|
||||||
|
|
||||||
private bool _prevDrawIndexed;
|
private bool _prevDrawIndexed;
|
||||||
|
private IndexType _prevIndexType;
|
||||||
|
private uint _prevFirstVertex;
|
||||||
private bool _prevTfEnable;
|
private bool _prevTfEnable;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -75,6 +78,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
nameof(ThreedClassState.VertexBufferState),
|
nameof(ThreedClassState.VertexBufferState),
|
||||||
nameof(ThreedClassState.VertexBufferEndAddress)),
|
nameof(ThreedClassState.VertexBufferEndAddress)),
|
||||||
|
|
||||||
|
new StateUpdateCallbackEntry(UpdatePrimitiveRestartState,
|
||||||
|
nameof(ThreedClassState.PrimitiveRestartDrawArrays),
|
||||||
|
nameof(ThreedClassState.PrimitiveRestartState)),
|
||||||
|
|
||||||
new StateUpdateCallbackEntry(UpdateTessellationState,
|
new StateUpdateCallbackEntry(UpdateTessellationState,
|
||||||
nameof(ThreedClassState.TessOuterLevel),
|
nameof(ThreedClassState.TessOuterLevel),
|
||||||
nameof(ThreedClassState.TessInnerLevel),
|
nameof(ThreedClassState.TessInnerLevel),
|
||||||
@ -140,8 +147,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
nameof(ThreedClassState.PointSpriteEnable),
|
nameof(ThreedClassState.PointSpriteEnable),
|
||||||
nameof(ThreedClassState.PointCoordReplace)),
|
nameof(ThreedClassState.PointCoordReplace)),
|
||||||
|
|
||||||
new StateUpdateCallbackEntry(UpdatePrimitiveRestartState, nameof(ThreedClassState.PrimitiveRestartState)),
|
|
||||||
|
|
||||||
new StateUpdateCallbackEntry(UpdateIndexBufferState,
|
new StateUpdateCallbackEntry(UpdateIndexBufferState,
|
||||||
nameof(ThreedClassState.IndexBufferState),
|
nameof(ThreedClassState.IndexBufferState),
|
||||||
nameof(ThreedClassState.IndexBufferCount)),
|
nameof(ThreedClassState.IndexBufferCount)),
|
||||||
@ -197,9 +202,31 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
if (_drawState.DrawIndexed != _prevDrawIndexed)
|
if (_drawState.DrawIndexed != _prevDrawIndexed)
|
||||||
{
|
{
|
||||||
_updateTracker.ForceDirty(VertexBufferStateIndex);
|
_updateTracker.ForceDirty(VertexBufferStateIndex);
|
||||||
|
|
||||||
|
// If PrimitiveRestartDrawArrays is false and this is a non-indexed draw, we need to ensure primitive restart is disabled.
|
||||||
|
// If PrimitiveRestartDrawArrays is false and this is a indexed draw, we need to ensure primitive restart enable matches GPU state.
|
||||||
|
// If PrimitiveRestartDrawArrays is true, then primitive restart enable should always match GPU state.
|
||||||
|
// That is because "PrimitiveRestartDrawArrays" is not configurable on the backend, it is always
|
||||||
|
// true on OpenGL and always false on Vulkan.
|
||||||
|
if (!_state.State.PrimitiveRestartDrawArrays && _state.State.PrimitiveRestartState.Enable)
|
||||||
|
{
|
||||||
|
_updateTracker.ForceDirty(PrimitiveRestartStateIndex);
|
||||||
|
}
|
||||||
|
|
||||||
_prevDrawIndexed = _drawState.DrawIndexed;
|
_prevDrawIndexed = _drawState.DrawIndexed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In some cases, the index type is also used to guess the
|
||||||
|
// vertex buffer size, so we must update it if the type changed too.
|
||||||
|
if (_drawState.DrawIndexed &&
|
||||||
|
(_prevIndexType != _state.State.IndexBufferState.Type ||
|
||||||
|
_prevFirstVertex != _state.State.FirstVertex))
|
||||||
|
{
|
||||||
|
_updateTracker.ForceDirty(VertexBufferStateIndex);
|
||||||
|
_prevIndexType = _state.State.IndexBufferState.Type;
|
||||||
|
_prevFirstVertex = _state.State.FirstVertex;
|
||||||
|
}
|
||||||
|
|
||||||
bool tfEnable = _state.State.TfEnable;
|
bool tfEnable = _state.State.TfEnable;
|
||||||
|
|
||||||
if (!tfEnable && _prevTfEnable)
|
if (!tfEnable && _prevTfEnable)
|
||||||
@ -816,8 +843,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
private void UpdatePrimitiveRestartState()
|
private void UpdatePrimitiveRestartState()
|
||||||
{
|
{
|
||||||
PrimitiveRestartState primitiveRestart = _state.State.PrimitiveRestartState;
|
PrimitiveRestartState primitiveRestart = _state.State.PrimitiveRestartState;
|
||||||
|
bool enable = primitiveRestart.Enable && (_drawState.DrawIndexed || _state.State.PrimitiveRestartDrawArrays);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetPrimitiveRestart(primitiveRestart.Enable, primitiveRestart.Index);
|
_context.Renderer.Pipeline.SetPrimitiveRestart(enable, primitiveRestart.Index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -852,6 +880,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateVertexBufferState()
|
private void UpdateVertexBufferState()
|
||||||
{
|
{
|
||||||
|
IndexType indexType = _state.State.IndexBufferState.Type;
|
||||||
|
bool indexTypeSmall = indexType == IndexType.UByte || indexType == IndexType.UShort;
|
||||||
|
|
||||||
_drawState.IsAnyVbInstanced = false;
|
_drawState.IsAnyVbInstanced = false;
|
||||||
|
|
||||||
for (int index = 0; index < Constants.TotalVertexBuffers; index++)
|
for (int index = 0; index < Constants.TotalVertexBuffers; index++)
|
||||||
@ -883,12 +914,27 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
{
|
{
|
||||||
// This size may be (much) larger than the real vertex buffer size.
|
// This size may be (much) larger than the real vertex buffer size.
|
||||||
// Avoid calculating it this way, unless we don't have any other option.
|
// Avoid calculating it this way, unless we don't have any other option.
|
||||||
|
|
||||||
size = endAddress.Pack() - address + 1;
|
size = endAddress.Pack() - address + 1;
|
||||||
|
|
||||||
|
if (stride > 0 && indexTypeSmall)
|
||||||
|
{
|
||||||
|
// If the index type is a small integer type, then we might be still able
|
||||||
|
// to reduce the vertex buffer size based on the maximum possible index value.
|
||||||
|
|
||||||
|
ulong maxVertexBufferSize = indexType == IndexType.UByte ? 0x100UL : 0x10000UL;
|
||||||
|
|
||||||
|
maxVertexBufferSize += _state.State.FirstVertex;
|
||||||
|
maxVertexBufferSize *= (uint)stride;
|
||||||
|
|
||||||
|
size = Math.Min(size, maxVertexBufferSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// For non-indexed draws, we can guess the size from the vertex count
|
// For non-indexed draws, we can guess the size from the vertex count
|
||||||
// and stride.
|
// and stride.
|
||||||
|
|
||||||
int firstInstance = (int)_state.State.FirstInstance;
|
int firstInstance = (int)_state.State.FirstInstance;
|
||||||
|
|
||||||
var drawState = _state.State.VertexBufferDrawState;
|
var drawState = _state.State.VertexBufferDrawState;
|
||||||
|
@ -730,7 +730,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
public int PatchVertices;
|
public int PatchVertices;
|
||||||
public fixed uint ReservedDD0[4];
|
public fixed uint ReservedDD0[4];
|
||||||
public uint TextureBarrier;
|
public uint TextureBarrier;
|
||||||
public fixed uint ReservedDE4[7];
|
public uint WatchdogTimer;
|
||||||
|
public Boolean32 PrimitiveRestartDrawArrays;
|
||||||
|
public fixed uint ReservedDEC[5];
|
||||||
public Array16<ScissorState> ScissorState;
|
public Array16<ScissorState> ScissorState;
|
||||||
public fixed uint ReservedF00[21];
|
public fixed uint ReservedF00[21];
|
||||||
public StencilBackMasks StencilBackMasks;
|
public StencilBackMasks StencilBackMasks;
|
||||||
|
@ -255,6 +255,49 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes data to GPU mapped memory, stopping at the first unmapped page at the memory region, if any.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="va">GPU virtual address to write the data into</param>
|
||||||
|
/// <param name="data">The data to be written</param>
|
||||||
|
public void WriteMapped(ulong va, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
if (IsContiguous(va, data.Length))
|
||||||
|
{
|
||||||
|
Physical.Write(Translate(va), data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int offset = 0, size;
|
||||||
|
|
||||||
|
if ((va & PageMask) != 0)
|
||||||
|
{
|
||||||
|
ulong pa = Translate(va);
|
||||||
|
|
||||||
|
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
|
||||||
|
|
||||||
|
if (pa != PteUnmapped && Physical.IsMapped(pa))
|
||||||
|
{
|
||||||
|
Physical.Write(pa, data.Slice(0, size));
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; offset < data.Length; offset += size)
|
||||||
|
{
|
||||||
|
ulong pa = Translate(va + (ulong)offset);
|
||||||
|
|
||||||
|
size = Math.Min(data.Length - offset, (int)PageSize);
|
||||||
|
|
||||||
|
if (pa != PteUnmapped && Physical.IsMapped(pa))
|
||||||
|
{
|
||||||
|
Physical.Write(pa, data.Slice(offset, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maps a given range of pages to the specified CPU virtual address.
|
/// Maps a given range of pages to the specified CPU virtual address.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -340,6 +340,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
return _cpuMemory.BeginSmartGranularTracking(address, size, granularity);
|
return _cpuMemory.BeginSmartGranularTracking(address, size, granularity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if the page at a given address is mapped on CPU memory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">CPU virtual address of the page to check</param>
|
||||||
|
/// <returns>True if mapped, false otherwise</returns>
|
||||||
|
public bool IsMapped(ulong address)
|
||||||
|
{
|
||||||
|
return _cpuMemory.IsMapped(address);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Release our reference to the CPU memory manager.
|
/// Release our reference to the CPU memory manager.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -15,11 +15,13 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
|
|||||||
public Plane UPlane => new Plane((IntPtr)Frame->data[1], UvStride * UvHeight);
|
public Plane UPlane => new Plane((IntPtr)Frame->data[1], UvStride * UvHeight);
|
||||||
public Plane VPlane => new Plane((IntPtr)Frame->data[2], UvStride * UvHeight);
|
public Plane VPlane => new Plane((IntPtr)Frame->data[2], UvStride * UvHeight);
|
||||||
|
|
||||||
|
public FrameField Field => Frame->interlaced_frame != 0 ? FrameField.Interlaced : FrameField.Progressive;
|
||||||
|
|
||||||
public int Width => Frame->width;
|
public int Width => Frame->width;
|
||||||
public int Height => Frame->height;
|
public int Height => Frame->height;
|
||||||
public int Stride => Frame->linesize[0];
|
public int Stride => Frame->linesize[0];
|
||||||
public int UvWidth => (Frame->width + 1) >> 1;
|
public int UvWidth => (Width + 1) >> 1;
|
||||||
public int UvHeight => (Frame->height + 1) >> 1;
|
public int UvHeight => (Height + 1) >> 1;
|
||||||
public int UvStride => Frame->linesize[1];
|
public int UvStride => Frame->linesize[1];
|
||||||
|
|
||||||
public Surface(int width, int height)
|
public Surface(int width, int height)
|
||||||
|
@ -486,8 +486,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
|
|||||||
Idct8(tempIn, tempOut);
|
Idct8(tempIn, tempOut);
|
||||||
for (j = 0; j < 8; ++j)
|
for (j = 0; j < 8; ++j)
|
||||||
{
|
{
|
||||||
dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i],
|
dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 5));
|
||||||
BitUtils.RoundPowerOfTwo(tempOut[j], 5));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
|
|||||||
public unsafe Plane UPlane => new Plane((IntPtr)UBuffer.ToPointer(), UBuffer.Length);
|
public unsafe Plane UPlane => new Plane((IntPtr)UBuffer.ToPointer(), UBuffer.Length);
|
||||||
public unsafe Plane VPlane => new Plane((IntPtr)VBuffer.ToPointer(), VBuffer.Length);
|
public unsafe Plane VPlane => new Plane((IntPtr)VBuffer.ToPointer(), VBuffer.Length);
|
||||||
|
|
||||||
|
public FrameField Field => FrameField.Progressive;
|
||||||
|
|
||||||
public int Width { get; }
|
public int Width { get; }
|
||||||
public int Height { get; }
|
public int Height { get; }
|
||||||
public int AlignedWidth { get; }
|
public int AlignedWidth { get; }
|
||||||
|
@ -31,7 +31,24 @@ namespace Ryujinx.Graphics.Nvdec
|
|||||||
|
|
||||||
if (decoder.Decode(ref info, outputSurface, bitstream))
|
if (decoder.Decode(ref info, outputSurface, bitstream))
|
||||||
{
|
{
|
||||||
SurfaceWriter.Write(rm.Gmm, outputSurface, lumaOffset, chromaOffset);
|
if (outputSurface.Field == FrameField.Progressive)
|
||||||
|
{
|
||||||
|
SurfaceWriter.Write(
|
||||||
|
rm.Gmm,
|
||||||
|
outputSurface,
|
||||||
|
lumaOffset + pictureInfo.LumaFrameOffset,
|
||||||
|
chromaOffset + pictureInfo.ChromaFrameOffset);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SurfaceWriter.WriteInterlaced(
|
||||||
|
rm.Gmm,
|
||||||
|
outputSurface,
|
||||||
|
lumaOffset + pictureInfo.LumaTopFieldOffset,
|
||||||
|
chromaOffset + pictureInfo.ChromaTopFieldOffset,
|
||||||
|
lumaOffset + pictureInfo.LumaBottomFieldOffset,
|
||||||
|
chromaOffset + pictureInfo.ChromaBottomFieldOffset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rm.Cache.Put(outputSurface);
|
rm.Cache.Put(outputSurface);
|
||||||
|
@ -38,6 +38,55 @@ namespace Ryujinx.Graphics.Nvdec.Image
|
|||||||
surface.UvHeight);
|
surface.UvHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void WriteInterlaced(
|
||||||
|
MemoryManager gmm,
|
||||||
|
ISurface surface,
|
||||||
|
uint lumaTopOffset,
|
||||||
|
uint chromaTopOffset,
|
||||||
|
uint lumaBottomOffset,
|
||||||
|
uint chromaBottomOffset)
|
||||||
|
{
|
||||||
|
int lumaSize = GetBlockLinearSize(surface.Width, surface.Height / 2, 1);
|
||||||
|
|
||||||
|
using var lumaTop = gmm.GetWritableRegion(ExtendOffset(lumaTopOffset), lumaSize);
|
||||||
|
using var lumaBottom = gmm.GetWritableRegion(ExtendOffset(lumaBottomOffset), lumaSize);
|
||||||
|
|
||||||
|
WriteLuma(
|
||||||
|
lumaTop.Memory.Span,
|
||||||
|
surface.YPlane.AsSpan(),
|
||||||
|
surface.Stride * 2,
|
||||||
|
surface.Width,
|
||||||
|
surface.Height / 2);
|
||||||
|
|
||||||
|
WriteLuma(
|
||||||
|
lumaBottom.Memory.Span,
|
||||||
|
surface.YPlane.AsSpan().Slice(surface.Stride),
|
||||||
|
surface.Stride * 2,
|
||||||
|
surface.Width,
|
||||||
|
surface.Height / 2);
|
||||||
|
|
||||||
|
int chromaSize = GetBlockLinearSize(surface.UvWidth, surface.UvHeight / 2, 2);
|
||||||
|
|
||||||
|
using var chromaTop = gmm.GetWritableRegion(ExtendOffset(chromaTopOffset), chromaSize);
|
||||||
|
using var chromaBottom = gmm.GetWritableRegion(ExtendOffset(chromaBottomOffset), chromaSize);
|
||||||
|
|
||||||
|
WriteChroma(
|
||||||
|
chromaTop.Memory.Span,
|
||||||
|
surface.UPlane.AsSpan(),
|
||||||
|
surface.VPlane.AsSpan(),
|
||||||
|
surface.UvStride * 2,
|
||||||
|
surface.UvWidth,
|
||||||
|
surface.UvHeight / 2);
|
||||||
|
|
||||||
|
WriteChroma(
|
||||||
|
chromaBottom.Memory.Span,
|
||||||
|
surface.UPlane.AsSpan().Slice(surface.UvStride),
|
||||||
|
surface.VPlane.AsSpan().Slice(surface.UvStride),
|
||||||
|
surface.UvStride * 2,
|
||||||
|
surface.UvWidth,
|
||||||
|
surface.UvHeight / 2);
|
||||||
|
}
|
||||||
|
|
||||||
private static void WriteLuma(Span<byte> dst, ReadOnlySpan<byte> src, int srcStride, int width, int height)
|
private static void WriteLuma(Span<byte> dst, ReadOnlySpan<byte> src, int srcStride, int width, int height)
|
||||||
{
|
{
|
||||||
LayoutConverter.ConvertLinearToBlockLinear(dst, width, height, srcStride, 1, 2, src);
|
LayoutConverter.ConvertLinearToBlockLinear(dst, width, height, srcStride, 1, 2, src);
|
||||||
|
@ -26,10 +26,10 @@ namespace Ryujinx.Graphics.Nvdec.Types.H264
|
|||||||
public uint Transform8x8ModeFlag;
|
public uint Transform8x8ModeFlag;
|
||||||
public uint LumaPitch;
|
public uint LumaPitch;
|
||||||
public uint ChromaPitch;
|
public uint ChromaPitch;
|
||||||
public uint LumaTopOffset;
|
public uint LumaTopFieldOffset;
|
||||||
public uint LumaBottomOffset;
|
public uint LumaBottomFieldOffset;
|
||||||
public uint LumaFrameOffset;
|
public uint LumaFrameOffset;
|
||||||
public uint ChromaTopOffset;
|
public uint ChromaTopFieldOffset;
|
||||||
public uint ChromaBottomFieldOffset;
|
public uint ChromaBottomFieldOffset;
|
||||||
public uint ChromaFrameOffset;
|
public uint ChromaFrameOffset;
|
||||||
public uint HistBufferSize;
|
public uint HistBufferSize;
|
||||||
|
@ -444,8 +444,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
return TextureTarget.Texture2DArray;
|
return TextureTarget.Texture2DArray;
|
||||||
case Target.Texture2DMultisample:
|
case Target.Texture2DMultisample:
|
||||||
return TextureTarget.Texture2DMultisample;
|
return TextureTarget.Texture2DMultisample;
|
||||||
case Target.Rectangle:
|
case Target.Texture2DMultisampleArray:
|
||||||
return TextureTarget.TextureRectangle;
|
return TextureTarget.Texture2DMultisampleArray;
|
||||||
case Target.Cubemap:
|
case Target.Cubemap:
|
||||||
return TextureTarget.TextureCubeMap;
|
return TextureTarget.TextureCubeMap;
|
||||||
case Target.CubemapArray:
|
case Target.CubemapArray:
|
||||||
|
@ -612,7 +612,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_program?.Bind();
|
_program?.Bind();
|
||||||
_unit0Sampler?.Bind(0);
|
_unit0Sampler?.Bind(0);
|
||||||
|
|
||||||
GL.ViewportArray(0, 1, _viewportArray);
|
RestoreViewport0();
|
||||||
|
|
||||||
Enable(EnableCap.CullFace, _cullEnable);
|
Enable(EnableCap.CullFace, _cullEnable);
|
||||||
Enable(EnableCap.StencilTest, _stencilTestEnable);
|
Enable(EnableCap.StencilTest, _stencilTestEnable);
|
||||||
@ -1501,7 +1501,10 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
public void RestoreViewport0()
|
public void RestoreViewport0()
|
||||||
{
|
{
|
||||||
GL.ViewportArray(0, 1, _viewportArray);
|
if (_viewportArray.Length > 0)
|
||||||
|
{
|
||||||
|
GL.ViewportArray(0, 1, _viewportArray);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
|
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
|
||||||
|
@ -155,6 +155,12 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
_pipeline.Initialize(this);
|
_pipeline.Initialize(this);
|
||||||
_counters.Initialize();
|
_counters.Initialize();
|
||||||
|
|
||||||
|
// This is required to disable [0, 1] clamping for SNorm outputs on compatibility profiles.
|
||||||
|
// This call is expected to fail if we're running with a core profile,
|
||||||
|
// as this clamp target was deprecated, but that's fine as a core profile
|
||||||
|
// should already have the desired behaviour were outputs are not clamped.
|
||||||
|
GL.ClampColor(ClampColorTarget.ClampFragmentColor, ClampColorMode.False);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PrintGpuInformation()
|
private void PrintGpuInformation()
|
||||||
|
@ -70,6 +70,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
AppendLine("}" + suffix);
|
AppendLine("}" + suffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public (TextureDescriptor, int) FindTextureDescriptor(AstTextureOperation texOp)
|
||||||
|
{
|
||||||
|
TextureDescriptor[] descriptors = Config.GetTextureDescriptors();
|
||||||
|
|
||||||
|
for (int i = 0; i < descriptors.Length; i++)
|
||||||
|
{
|
||||||
|
var descriptor = descriptors[i];
|
||||||
|
|
||||||
|
if (descriptor.CbufSlot == texOp.CbufSlot &&
|
||||||
|
descriptor.HandleIndex == texOp.Handle &&
|
||||||
|
descriptor.Format == texOp.Format)
|
||||||
|
{
|
||||||
|
return (descriptor, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (default, -1);
|
||||||
|
}
|
||||||
|
|
||||||
private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp)
|
private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < array.Length; i++)
|
for (int i = 0; i < array.Length; i++)
|
||||||
|
@ -756,27 +756,34 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
|
|
||||||
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
|
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
|
||||||
|
|
||||||
int lodSrcIndex = isBindless || isIndexed ? 1 : 0;
|
|
||||||
|
|
||||||
IAstNode lod = operation.GetSource(lodSrcIndex);
|
|
||||||
|
|
||||||
string lodExpr = GetSoureExpr(context, lod, GetSrcVarType(operation.Inst, lodSrcIndex));
|
|
||||||
|
|
||||||
if (texOp.Index == 3)
|
if (texOp.Index == 3)
|
||||||
{
|
{
|
||||||
return $"textureQueryLevels({samplerName})";
|
return $"textureQueryLevels({samplerName})";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string texCall = $"textureSize({samplerName}, {lodExpr}){GetMask(texOp.Index)}";
|
(TextureDescriptor descriptor, int descriptorIndex) = context.FindTextureDescriptor(texOp);
|
||||||
|
bool hasLod = !descriptor.Type.HasFlag(SamplerType.Multisample) && descriptor.Type != SamplerType.TextureBuffer;
|
||||||
|
string texCall;
|
||||||
|
|
||||||
|
if (hasLod)
|
||||||
|
{
|
||||||
|
int lodSrcIndex = isBindless || isIndexed ? 1 : 0;
|
||||||
|
IAstNode lod = operation.GetSource(lodSrcIndex);
|
||||||
|
string lodExpr = GetSoureExpr(context, lod, GetSrcVarType(operation.Inst, lodSrcIndex));
|
||||||
|
|
||||||
|
texCall = $"textureSize({samplerName}, {lodExpr}){GetMask(texOp.Index)}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
texCall = $"textureSize({samplerName}){GetMask(texOp.Index)}";
|
||||||
|
}
|
||||||
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
if (context.Config.Stage.SupportsRenderScale() &&
|
||||||
!isBindless &&
|
!isBindless &&
|
||||||
!isIndexed)
|
!isIndexed)
|
||||||
{
|
{
|
||||||
int index = context.FindTextureDescriptorIndex(texOp);
|
texCall = $"Helper_TextureSizeUnscale({texCall}, {descriptorIndex})";
|
||||||
|
|
||||||
texCall = "Helper_TextureSizeUnscale(" + texCall + ", " + index + ")";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return texCall;
|
return texCall;
|
||||||
|
@ -48,38 +48,10 @@ namespace Ryujinx.Graphics.Vic
|
|||||||
|
|
||||||
int one = 1 << (mtx.MatrixRShift + 8);
|
int one = 1 << (mtx.MatrixRShift + 8);
|
||||||
|
|
||||||
|
Vector128<int> col1 = Vector128.Create(mtx.MatrixCoeff00, mtx.MatrixCoeff10, mtx.MatrixCoeff20, 0);
|
||||||
// NOTE: This is buggy on .NET 5.0.100, we use a workaround for now (see https://github.com/dotnet/runtime/issues/44704)
|
Vector128<int> col2 = Vector128.Create(mtx.MatrixCoeff01, mtx.MatrixCoeff11, mtx.MatrixCoeff21, 0);
|
||||||
// TODO: Uncomment this when fixed.
|
Vector128<int> col3 = Vector128.Create(mtx.MatrixCoeff02, mtx.MatrixCoeff12, mtx.MatrixCoeff22, one);
|
||||||
//Vector128<int> col1 = Vector128.Create(mtx.MatrixCoeff00, mtx.MatrixCoeff10, mtx.MatrixCoeff20, 0);
|
Vector128<int> col4 = Vector128.Create(mtx.MatrixCoeff03, mtx.MatrixCoeff13, mtx.MatrixCoeff23, 0);
|
||||||
//Vector128<int> col2 = Vector128.Create(mtx.MatrixCoeff01, mtx.MatrixCoeff11, mtx.MatrixCoeff21, 0);
|
|
||||||
//Vector128<int> col3 = Vector128.Create(mtx.MatrixCoeff02, mtx.MatrixCoeff12, mtx.MatrixCoeff22, one);
|
|
||||||
//Vector128<int> col4 = Vector128.Create(mtx.MatrixCoeff03, mtx.MatrixCoeff13, mtx.MatrixCoeff23, 0);
|
|
||||||
|
|
||||||
Vector128<int> col1 = new Vector128<int>();
|
|
||||||
Vector128<int> col2 = new Vector128<int>();
|
|
||||||
Vector128<int> col3 = new Vector128<int>();
|
|
||||||
Vector128<int> col4 = new Vector128<int>();
|
|
||||||
|
|
||||||
col1 = Sse41.Insert(col1, mtx.MatrixCoeff00, 0);
|
|
||||||
col1 = Sse41.Insert(col1, mtx.MatrixCoeff10, 1);
|
|
||||||
col1 = Sse41.Insert(col1, mtx.MatrixCoeff20, 2);
|
|
||||||
col1 = Sse41.Insert(col1, 0, 3);
|
|
||||||
|
|
||||||
col2 = Sse41.Insert(col2, mtx.MatrixCoeff01, 0);
|
|
||||||
col2 = Sse41.Insert(col2, mtx.MatrixCoeff11, 1);
|
|
||||||
col2 = Sse41.Insert(col2, mtx.MatrixCoeff21, 2);
|
|
||||||
col2 = Sse41.Insert(col2, 0, 3);
|
|
||||||
|
|
||||||
col3 = Sse41.Insert(col3, mtx.MatrixCoeff02, 0);
|
|
||||||
col3 = Sse41.Insert(col3, mtx.MatrixCoeff12, 1);
|
|
||||||
col3 = Sse41.Insert(col3, mtx.MatrixCoeff22, 2);
|
|
||||||
col3 = Sse41.Insert(col3, one, 3);
|
|
||||||
|
|
||||||
col4 = Sse41.Insert(col4, mtx.MatrixCoeff03, 0);
|
|
||||||
col4 = Sse41.Insert(col4, mtx.MatrixCoeff13, 1);
|
|
||||||
col4 = Sse41.Insert(col4, mtx.MatrixCoeff23, 2);
|
|
||||||
col4 = Sse41.Insert(col4, 0, 3);
|
|
||||||
|
|
||||||
Vector128<int> rShift = Vector128.CreateScalar(mtx.MatrixRShift);
|
Vector128<int> rShift = Vector128.CreateScalar(mtx.MatrixRShift);
|
||||||
Vector128<ushort> clMin = Vector128.Create((ushort)slot.SlotConfig.SoftClampLow);
|
Vector128<ushort> clMin = Vector128.Create((ushort)slot.SlotConfig.SoftClampLow);
|
||||||
|
@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Vic.Image
|
|||||||
/// If the required buffer is larger than this, it won't be
|
/// If the required buffer is larger than this, it won't be
|
||||||
/// added to the pool to avoid long term high memory usage.
|
/// added to the pool to avoid long term high memory usage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int MaxBufferSize = 2048 * 1280;
|
private const int MaxBufferSize = 2048 * 2048;
|
||||||
|
|
||||||
private struct PoolItem
|
private struct PoolItem
|
||||||
{
|
{
|
||||||
|
@ -2,16 +2,85 @@
|
|||||||
|
|
||||||
namespace Ryujinx.Graphics.Vic.Image
|
namespace Ryujinx.Graphics.Vic.Image
|
||||||
{
|
{
|
||||||
|
ref struct RentedBuffer
|
||||||
|
{
|
||||||
|
public static RentedBuffer Empty => new RentedBuffer(Span<byte>.Empty, -1);
|
||||||
|
|
||||||
|
public Span<byte> Data;
|
||||||
|
public int Index;
|
||||||
|
|
||||||
|
public RentedBuffer(Span<byte> data, int index)
|
||||||
|
{
|
||||||
|
Data = data;
|
||||||
|
Index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Return(BufferPool<byte> pool)
|
||||||
|
{
|
||||||
|
if (Index != -1)
|
||||||
|
{
|
||||||
|
pool.Return(Index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ref struct InputSurface
|
ref struct InputSurface
|
||||||
{
|
{
|
||||||
public ReadOnlySpan<byte> Buffer0;
|
public ReadOnlySpan<byte> Buffer0;
|
||||||
public ReadOnlySpan<byte> Buffer1;
|
public ReadOnlySpan<byte> Buffer1;
|
||||||
public ReadOnlySpan<byte> Buffer2;
|
public ReadOnlySpan<byte> Buffer2;
|
||||||
|
|
||||||
|
public int Buffer0Index;
|
||||||
|
public int Buffer1Index;
|
||||||
|
public int Buffer2Index;
|
||||||
|
|
||||||
public int Width;
|
public int Width;
|
||||||
public int Height;
|
public int Height;
|
||||||
|
|
||||||
public int UvWidth;
|
public int UvWidth;
|
||||||
public int UvHeight;
|
public int UvHeight;
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
Buffer0Index = -1;
|
||||||
|
Buffer1Index = -1;
|
||||||
|
Buffer2Index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBuffer0(RentedBuffer buffer)
|
||||||
|
{
|
||||||
|
Buffer0 = buffer.Data;
|
||||||
|
Buffer0Index = buffer.Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBuffer1(RentedBuffer buffer)
|
||||||
|
{
|
||||||
|
Buffer1 = buffer.Data;
|
||||||
|
Buffer1Index = buffer.Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBuffer2(RentedBuffer buffer)
|
||||||
|
{
|
||||||
|
Buffer2 = buffer.Data;
|
||||||
|
Buffer2Index = buffer.Index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Return(BufferPool<byte> pool)
|
||||||
|
{
|
||||||
|
if (Buffer0Index != -1)
|
||||||
|
{
|
||||||
|
pool.Return(Buffer0Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Buffer1Index != -1)
|
||||||
|
{
|
||||||
|
pool.Return(Buffer1Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Buffer2Index != -1)
|
||||||
|
{
|
||||||
|
pool.Return(Buffer2Index);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Gpu.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Ryujinx.Graphics.Texture;
|
using Ryujinx.Graphics.Texture;
|
||||||
using Ryujinx.Graphics.Vic.Types;
|
using Ryujinx.Graphics.Vic.Types;
|
||||||
using System;
|
using System;
|
||||||
@ -12,24 +12,32 @@ namespace Ryujinx.Graphics.Vic.Image
|
|||||||
{
|
{
|
||||||
static class SurfaceReader
|
static class SurfaceReader
|
||||||
{
|
{
|
||||||
public static Surface Read(ResourceManager rm, ref SlotSurfaceConfig config, ref PlaneOffsets offsets)
|
public static Surface Read(
|
||||||
|
ResourceManager rm,
|
||||||
|
ref SlotConfig config,
|
||||||
|
ref SlotSurfaceConfig surfaceConfig,
|
||||||
|
ref Array8<PlaneOffsets> offsets)
|
||||||
{
|
{
|
||||||
switch (config.SlotPixelFormat)
|
switch (surfaceConfig.SlotPixelFormat)
|
||||||
{
|
{
|
||||||
case PixelFormat.Y8___V8U8_N420: return ReadNv12(rm, ref config, ref offsets);
|
case PixelFormat.Y8___V8U8_N420: return ReadNv12(rm, ref config, ref surfaceConfig, ref offsets);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.Error?.Print(LogClass.Vic, $"Unsupported pixel format \"{config.SlotPixelFormat}\".");
|
Logger.Error?.Print(LogClass.Vic, $"Unsupported pixel format \"{surfaceConfig.SlotPixelFormat}\".");
|
||||||
|
|
||||||
int lw = config.SlotLumaWidth + 1;
|
int lw = surfaceConfig.SlotLumaWidth + 1;
|
||||||
int lh = config.SlotLumaHeight + 1;
|
int lh = surfaceConfig.SlotLumaHeight + 1;
|
||||||
|
|
||||||
return new Surface(rm.SurfacePool, lw, lh);
|
return new Surface(rm.SurfacePool, lw, lh);
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe static Surface ReadNv12(ResourceManager rm, ref SlotSurfaceConfig config, ref PlaneOffsets offsets)
|
private unsafe static Surface ReadNv12(
|
||||||
|
ResourceManager rm,
|
||||||
|
ref SlotConfig config,
|
||||||
|
ref SlotSurfaceConfig surfaceConfig,
|
||||||
|
ref Array8<PlaneOffsets> offsets)
|
||||||
{
|
{
|
||||||
InputSurface input = ReadSurface(rm.Gmm, ref config, ref offsets, 1, 2);
|
InputSurface input = ReadSurface(rm, ref config, ref surfaceConfig, ref offsets, 1, 2);
|
||||||
|
|
||||||
int width = input.Width;
|
int width = input.Width;
|
||||||
int height = input.Height;
|
int height = input.Height;
|
||||||
@ -160,6 +168,8 @@ namespace Ryujinx.Graphics.Vic.Image
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.Return(rm.BufferPool);
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,84 +180,227 @@ namespace Ryujinx.Graphics.Vic.Image
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static InputSurface ReadSurface(
|
private static InputSurface ReadSurface(
|
||||||
MemoryManager gmm,
|
ResourceManager rm,
|
||||||
ref SlotSurfaceConfig config,
|
ref SlotConfig config,
|
||||||
ref PlaneOffsets offsets,
|
ref SlotSurfaceConfig surfaceConfig,
|
||||||
|
ref Array8<PlaneOffsets> offsets,
|
||||||
int bytesPerPixel,
|
int bytesPerPixel,
|
||||||
int planes)
|
int planes)
|
||||||
{
|
{
|
||||||
InputSurface surface = new InputSurface();
|
InputSurface surface = new InputSurface();
|
||||||
|
|
||||||
int gobBlocksInY = 1 << config.SlotBlkHeight;
|
surface.Initialize();
|
||||||
|
|
||||||
bool linear = config.SlotBlkKind == 0;
|
int gobBlocksInY = 1 << surfaceConfig.SlotBlkHeight;
|
||||||
|
|
||||||
int lw = config.SlotLumaWidth + 1;
|
bool linear = surfaceConfig.SlotBlkKind == 0;
|
||||||
int lh = config.SlotLumaHeight + 1;
|
|
||||||
|
|
||||||
int cw = config.SlotChromaWidth + 1;
|
int lw = surfaceConfig.SlotLumaWidth + 1;
|
||||||
int ch = config.SlotChromaHeight + 1;
|
int lh = surfaceConfig.SlotLumaHeight + 1;
|
||||||
|
|
||||||
|
int cw = surfaceConfig.SlotChromaWidth + 1;
|
||||||
|
int ch = surfaceConfig.SlotChromaHeight + 1;
|
||||||
|
|
||||||
|
// Interlaced inputs have double the height when deinterlaced.
|
||||||
|
int heightShift = config.FrameFormat.IsField() ? 1 : 0;
|
||||||
|
|
||||||
surface.Width = lw;
|
surface.Width = lw;
|
||||||
surface.Height = lh;
|
surface.Height = lh << heightShift;
|
||||||
surface.UvWidth = cw;
|
surface.UvWidth = cw;
|
||||||
surface.UvHeight = ch;
|
surface.UvHeight = ch << heightShift;
|
||||||
|
|
||||||
if (planes > 0)
|
if (planes > 0)
|
||||||
{
|
{
|
||||||
surface.Buffer0 = ReadBuffer(gmm, offsets.LumaOffset, linear, lw, lh, bytesPerPixel, gobBlocksInY);
|
surface.SetBuffer0(ReadBuffer(rm, ref config, ref offsets, linear, 0, lw, lh, bytesPerPixel, gobBlocksInY));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (planes > 1)
|
if (planes > 1)
|
||||||
{
|
{
|
||||||
surface.Buffer1 = ReadBuffer(gmm, offsets.ChromaUOffset, linear, cw, ch, planes == 2 ? 2 : 1, gobBlocksInY);
|
surface.SetBuffer1(ReadBuffer(rm, ref config, ref offsets, linear, 1, cw, ch, planes == 2 ? 2 : 1, gobBlocksInY));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (planes > 2)
|
if (planes > 2)
|
||||||
{
|
{
|
||||||
surface.Buffer2 = ReadBuffer(gmm, offsets.ChromaVOffset, linear, cw, ch, 1, gobBlocksInY);
|
surface.SetBuffer2(ReadBuffer(rm, ref config, ref offsets, linear, 2, cw, ch, 1, gobBlocksInY));
|
||||||
}
|
}
|
||||||
|
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> ReadBuffer(
|
private static RentedBuffer ReadBuffer(
|
||||||
MemoryManager gmm,
|
ResourceManager rm,
|
||||||
uint offset,
|
ref SlotConfig config,
|
||||||
|
ref Array8<PlaneOffsets> offsets,
|
||||||
bool linear,
|
bool linear,
|
||||||
|
int plane,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int bytesPerPixel,
|
||||||
|
int gobBlocksInY)
|
||||||
|
{
|
||||||
|
FrameFormat frameFormat = config.FrameFormat;
|
||||||
|
bool isLuma = plane == 0;
|
||||||
|
bool isField = frameFormat.IsField();
|
||||||
|
bool isTopField = frameFormat.IsTopField(isLuma);
|
||||||
|
int stride = GetPitch(width, bytesPerPixel);
|
||||||
|
uint offset = GetOffset(ref offsets[0], plane);
|
||||||
|
|
||||||
|
int dstStart = 0;
|
||||||
|
int dstStride = stride;
|
||||||
|
|
||||||
|
if (isField)
|
||||||
|
{
|
||||||
|
dstStart = isTopField ? 0 : stride;
|
||||||
|
dstStride = stride * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
RentedBuffer buffer;
|
||||||
|
|
||||||
|
if (linear)
|
||||||
|
{
|
||||||
|
buffer = ReadBufferLinear(rm, offset, width, height, dstStart, dstStride, bytesPerPixel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer = ReadBufferBlockLinear(rm, offset, width, height, dstStart, dstStride, bytesPerPixel, gobBlocksInY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isField || frameFormat.IsInterlaced())
|
||||||
|
{
|
||||||
|
RentedBuffer prevBuffer = RentedBuffer.Empty;
|
||||||
|
RentedBuffer nextBuffer = RentedBuffer.Empty;
|
||||||
|
|
||||||
|
if (config.PrevFieldEnable)
|
||||||
|
{
|
||||||
|
prevBuffer = ReadBufferNoDeinterlace(rm, ref offsets[1], linear, plane, width, height, bytesPerPixel, gobBlocksInY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.NextFieldEnable)
|
||||||
|
{
|
||||||
|
nextBuffer = ReadBufferNoDeinterlace(rm, ref offsets[2], linear, plane, width, height, bytesPerPixel, gobBlocksInY);
|
||||||
|
}
|
||||||
|
|
||||||
|
int w = width * bytesPerPixel;
|
||||||
|
|
||||||
|
switch (config.DeinterlaceMode)
|
||||||
|
{
|
||||||
|
case DeinterlaceMode.Weave:
|
||||||
|
Scaler.DeinterlaceWeave(buffer.Data, prevBuffer.Data, w, stride, isTopField);
|
||||||
|
break;
|
||||||
|
case DeinterlaceMode.BobField:
|
||||||
|
Scaler.DeinterlaceBob(buffer.Data, w, stride, isTopField);
|
||||||
|
break;
|
||||||
|
case DeinterlaceMode.Bob:
|
||||||
|
bool isCurrentTop = isLuma ? config.IsEven : config.ChromaEven;
|
||||||
|
Scaler.DeinterlaceBob(buffer.Data, w, stride, isCurrentTop ^ frameFormat.IsInterlacedBottomFirst());
|
||||||
|
break;
|
||||||
|
case DeinterlaceMode.NewBob:
|
||||||
|
case DeinterlaceMode.Disi1:
|
||||||
|
Scaler.DeinterlaceMotionAdaptive(buffer.Data, prevBuffer.Data, nextBuffer.Data, w, stride, isTopField);
|
||||||
|
break;
|
||||||
|
case DeinterlaceMode.WeaveLumaBobFieldChroma:
|
||||||
|
if (isLuma)
|
||||||
|
{
|
||||||
|
Scaler.DeinterlaceWeave(buffer.Data, prevBuffer.Data, w, stride, isTopField);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Scaler.DeinterlaceBob(buffer.Data, w, stride, isTopField);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Logger.Error?.Print(LogClass.Vic, $"Unsupported deinterlace mode \"{config.DeinterlaceMode}\".");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevBuffer.Return(rm.BufferPool);
|
||||||
|
nextBuffer.Return(rm.BufferPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static uint GetOffset(ref PlaneOffsets offsets, int plane)
|
||||||
|
{
|
||||||
|
return plane switch
|
||||||
|
{
|
||||||
|
0 => offsets.LumaOffset,
|
||||||
|
1 => offsets.ChromaUOffset,
|
||||||
|
2 => offsets.ChromaVOffset,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(plane))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RentedBuffer ReadBufferNoDeinterlace(
|
||||||
|
ResourceManager rm,
|
||||||
|
ref PlaneOffsets offsets,
|
||||||
|
bool linear,
|
||||||
|
int plane,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
int bytesPerPixel,
|
int bytesPerPixel,
|
||||||
int gobBlocksInY)
|
int gobBlocksInY)
|
||||||
{
|
{
|
||||||
int stride = GetPitch(width, bytesPerPixel);
|
int stride = GetPitch(width, bytesPerPixel);
|
||||||
|
uint offset = GetOffset(ref offsets, plane);
|
||||||
|
|
||||||
if (linear)
|
if (linear)
|
||||||
{
|
{
|
||||||
return gmm.GetSpan(ExtendOffset(offset), stride * height);
|
return ReadBufferLinear(rm, offset, width, height, 0, stride, bytesPerPixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ReadBuffer(gmm, offset, width, height, stride, bytesPerPixel, gobBlocksInY);
|
return ReadBufferBlockLinear(rm, offset, width, height, 0, stride, bytesPerPixel, gobBlocksInY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ReadOnlySpan<byte> ReadBuffer(
|
private static RentedBuffer ReadBufferLinear(
|
||||||
MemoryManager gmm,
|
ResourceManager rm,
|
||||||
uint offset,
|
uint offset,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
|
int dstStart,
|
||||||
|
int dstStride,
|
||||||
|
int bytesPerPixel)
|
||||||
|
{
|
||||||
|
int srcStride = GetPitch(width, bytesPerPixel);
|
||||||
|
int inSize = srcStride * height;
|
||||||
|
|
||||||
|
ReadOnlySpan<byte> src = rm.Gmm.GetSpan(ExtendOffset(offset), inSize);
|
||||||
|
|
||||||
|
int outSize = dstStride * height;
|
||||||
|
int bufferIndex = rm.BufferPool.RentMinimum(outSize, out byte[] buffer);
|
||||||
|
Span<byte> dst = buffer;
|
||||||
|
dst = dst.Slice(0, outSize);
|
||||||
|
|
||||||
|
for (int y = 0; y < height; y++)
|
||||||
|
{
|
||||||
|
src.Slice(y * srcStride, srcStride).CopyTo(dst.Slice(dstStart + y * dstStride, srcStride));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RentedBuffer(dst, bufferIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RentedBuffer ReadBufferBlockLinear(
|
||||||
|
ResourceManager rm,
|
||||||
|
uint offset,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int dstStart,
|
||||||
int dstStride,
|
int dstStride,
|
||||||
int bytesPerPixel,
|
int bytesPerPixel,
|
||||||
int gobBlocksInY)
|
int gobBlocksInY)
|
||||||
{
|
{
|
||||||
int inSize = GetBlockLinearSize(width, height, bytesPerPixel, gobBlocksInY);
|
int inSize = GetBlockLinearSize(width, height, bytesPerPixel, gobBlocksInY);
|
||||||
|
|
||||||
ReadOnlySpan<byte> src = gmm.GetSpan(ExtendOffset(offset), inSize);
|
ReadOnlySpan<byte> src = rm.Gmm.GetSpan(ExtendOffset(offset), inSize);
|
||||||
|
|
||||||
Span<byte> dst = new byte[dstStride * height];
|
int outSize = dstStride * height;
|
||||||
|
int bufferIndex = rm.BufferPool.RentMinimum(outSize, out byte[] buffer);
|
||||||
|
Span<byte> dst = buffer;
|
||||||
|
dst = dst.Slice(0, outSize);
|
||||||
|
|
||||||
LayoutConverter.ConvertBlockLinearToLinear(dst, width, height, dstStride, bytesPerPixel, gobBlocksInY, src);
|
LayoutConverter.ConvertBlockLinearToLinear(dst.Slice(dstStart), width, height, dstStride, bytesPerPixel, gobBlocksInY, src);
|
||||||
|
|
||||||
return dst;
|
return new RentedBuffer(dst, bufferIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Vic.Image
|
|||||||
switch (config.OutPixelFormat)
|
switch (config.OutPixelFormat)
|
||||||
{
|
{
|
||||||
case PixelFormat.A8B8G8R8:
|
case PixelFormat.A8B8G8R8:
|
||||||
case PixelFormat.X8B8G8R8:
|
case PixelFormat.X8B8G8R8:
|
||||||
WriteA8B8G8R8(rm, input, ref config, ref offsets);
|
WriteA8B8G8R8(rm, input, ref config, ref offsets);
|
||||||
break;
|
break;
|
||||||
case PixelFormat.A8R8G8B8:
|
case PixelFormat.A8R8G8B8:
|
||||||
@ -433,7 +433,7 @@ namespace Ryujinx.Graphics.Vic.Image
|
|||||||
{
|
{
|
||||||
if (linear)
|
if (linear)
|
||||||
{
|
{
|
||||||
rm.Gmm.Write(ExtendOffset(offset), src);
|
rm.Gmm.WriteMapped(ExtendOffset(offset), src);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -456,7 +456,7 @@ namespace Ryujinx.Graphics.Vic.Image
|
|||||||
|
|
||||||
LayoutConverter.ConvertLinearToBlockLinear(dst, width, height, dstStride, bytesPerPixel, gobBlocksInY, src);
|
LayoutConverter.ConvertLinearToBlockLinear(dst, width, height, dstStride, bytesPerPixel, gobBlocksInY, src);
|
||||||
|
|
||||||
rm.Gmm.Write(ExtendOffset(offset), dst);
|
rm.Gmm.WriteMapped(ExtendOffset(offset), dst);
|
||||||
|
|
||||||
rm.BufferPool.Return(dstIndex);
|
rm.BufferPool.Return(dstIndex);
|
||||||
}
|
}
|
||||||
|
124
Ryujinx.Graphics.Vic/Scaler.cs
Normal file
124
Ryujinx.Graphics.Vic/Scaler.cs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.Intrinsics;
|
||||||
|
using System.Runtime.Intrinsics.X86;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vic
|
||||||
|
{
|
||||||
|
static class Scaler
|
||||||
|
{
|
||||||
|
public static void DeinterlaceWeave(Span<byte> data, ReadOnlySpan<byte> prevData, int width, int fieldSize, bool isTopField)
|
||||||
|
{
|
||||||
|
// Prev I Curr I Curr P
|
||||||
|
// TTTTTTTT BBBBBBBB TTTTTTTT
|
||||||
|
// -------- -------- BBBBBBBB
|
||||||
|
|
||||||
|
if (isTopField)
|
||||||
|
{
|
||||||
|
for (int offset = 0; offset < data.Length; offset += fieldSize * 2)
|
||||||
|
{
|
||||||
|
prevData.Slice(offset >> 1, width).CopyTo(data.Slice(offset + fieldSize, width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int offset = 0; offset < data.Length; offset += fieldSize * 2)
|
||||||
|
{
|
||||||
|
prevData.Slice(offset >> 1, width).CopyTo(data.Slice(offset, width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void DeinterlaceBob(Span<byte> data, int width, int fieldSize, bool isTopField)
|
||||||
|
{
|
||||||
|
// Curr I Curr P
|
||||||
|
// TTTTTTTT TTTTTTTT
|
||||||
|
// -------- TTTTTTTT
|
||||||
|
|
||||||
|
if (isTopField)
|
||||||
|
{
|
||||||
|
for (int offset = 0; offset < data.Length; offset += fieldSize * 2)
|
||||||
|
{
|
||||||
|
data.Slice(offset, width).CopyTo(data.Slice(offset + fieldSize, width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int offset = 0; offset < data.Length; offset += fieldSize * 2)
|
||||||
|
{
|
||||||
|
data.Slice(offset + fieldSize, width).CopyTo(data.Slice(offset, width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe static void DeinterlaceMotionAdaptive(
|
||||||
|
Span<byte> data,
|
||||||
|
ReadOnlySpan<byte> prevData,
|
||||||
|
ReadOnlySpan<byte> nextData,
|
||||||
|
int width,
|
||||||
|
int fieldSize,
|
||||||
|
bool isTopField)
|
||||||
|
{
|
||||||
|
// Very simple motion adaptive algorithm.
|
||||||
|
// If the pixel changed between previous and next frame, use Bob, otherwise use Weave.
|
||||||
|
//
|
||||||
|
// Example pseudo code:
|
||||||
|
// C_even = (P_even == N_even) ? P_even : C_odd
|
||||||
|
// Where: C is current frame, P is previous frame and N is next frame, and even/odd are the fields.
|
||||||
|
//
|
||||||
|
// Note: This does not fully match the hardware algorithm.
|
||||||
|
// The motion adaptive deinterlacing implemented on hardware is considerably more complex,
|
||||||
|
// and hard to implement accurately without proper documentation as for example, the
|
||||||
|
// method used for motion estimation is unknown.
|
||||||
|
|
||||||
|
int start = isTopField ? fieldSize : 0;
|
||||||
|
int otherFieldOffset = isTopField ? -fieldSize : fieldSize;
|
||||||
|
|
||||||
|
fixed (byte* pData = data, pPrevData = prevData, pNextData = nextData)
|
||||||
|
{
|
||||||
|
for (int offset = start; offset < data.Length; offset += fieldSize * 2)
|
||||||
|
{
|
||||||
|
int refOffset = (offset - start) >> 1;
|
||||||
|
int x = 0;
|
||||||
|
|
||||||
|
if (Avx2.IsSupported)
|
||||||
|
{
|
||||||
|
for (; x < (width & ~0x1f); x += 32)
|
||||||
|
{
|
||||||
|
Vector256<byte> prevPixels = Avx.LoadVector256(pPrevData + refOffset + x);
|
||||||
|
Vector256<byte> nextPixels = Avx.LoadVector256(pNextData + refOffset + x);
|
||||||
|
Vector256<byte> bob = Avx.LoadVector256(pData + offset + otherFieldOffset + x);
|
||||||
|
Vector256<byte> diff = Avx2.CompareEqual(prevPixels, nextPixels);
|
||||||
|
Avx.Store(pData + offset + x, Avx2.BlendVariable(bob, prevPixels, diff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Sse41.IsSupported)
|
||||||
|
{
|
||||||
|
for (; x < (width & ~0xf); x += 16)
|
||||||
|
{
|
||||||
|
Vector128<byte> prevPixels = Sse2.LoadVector128(pPrevData + refOffset + x);
|
||||||
|
Vector128<byte> nextPixels = Sse2.LoadVector128(pNextData + refOffset + x);
|
||||||
|
Vector128<byte> bob = Sse2.LoadVector128(pData + offset + otherFieldOffset + x);
|
||||||
|
Vector128<byte> diff = Sse2.CompareEqual(prevPixels, nextPixels);
|
||||||
|
Sse2.Store(pData + offset + x, Sse41.BlendVariable(bob, prevPixels, diff));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; x < width; x++)
|
||||||
|
{
|
||||||
|
byte prevPixel = prevData[refOffset + x];
|
||||||
|
byte nextPixel = nextData[refOffset + x];
|
||||||
|
|
||||||
|
if (nextPixel != prevPixel)
|
||||||
|
{
|
||||||
|
data[offset + x] = data[offset + otherFieldOffset + x];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data[offset + x] = prevPixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs
Normal file
12
Ryujinx.Graphics.Vic/Types/DeinterlaceMode.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Ryujinx.Graphics.Vic.Types
|
||||||
|
{
|
||||||
|
enum DeinterlaceMode
|
||||||
|
{
|
||||||
|
Weave,
|
||||||
|
BobField,
|
||||||
|
Bob,
|
||||||
|
NewBob,
|
||||||
|
Disi1,
|
||||||
|
WeaveLumaBobFieldChroma
|
||||||
|
}
|
||||||
|
}
|
79
Ryujinx.Graphics.Vic/Types/FrameFormat.cs
Normal file
79
Ryujinx.Graphics.Vic/Types/FrameFormat.cs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
namespace Ryujinx.Graphics.Vic.Types
|
||||||
|
{
|
||||||
|
enum FrameFormat
|
||||||
|
{
|
||||||
|
Progressive,
|
||||||
|
InterlacedTopFieldFirst,
|
||||||
|
InterlacedBottomFieldFirst,
|
||||||
|
TopField,
|
||||||
|
BottomField,
|
||||||
|
SubPicProgressive,
|
||||||
|
SubPicInterlacedTopFieldFirst,
|
||||||
|
SubPicInterlacedBottomFieldFirst,
|
||||||
|
SubPicTopField,
|
||||||
|
SubPicBottomField,
|
||||||
|
TopFieldChromaBottom,
|
||||||
|
BottomFieldChromaTop,
|
||||||
|
SubPicTopFieldChromaBottom,
|
||||||
|
SubPicBottomFieldChromaTop
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FrameFormatExtensions
|
||||||
|
{
|
||||||
|
public static bool IsField(this FrameFormat frameFormat)
|
||||||
|
{
|
||||||
|
switch (frameFormat)
|
||||||
|
{
|
||||||
|
case FrameFormat.TopField:
|
||||||
|
case FrameFormat.BottomField:
|
||||||
|
case FrameFormat.SubPicTopField:
|
||||||
|
case FrameFormat.SubPicBottomField:
|
||||||
|
case FrameFormat.TopFieldChromaBottom:
|
||||||
|
case FrameFormat.BottomFieldChromaTop:
|
||||||
|
case FrameFormat.SubPicTopFieldChromaBottom:
|
||||||
|
case FrameFormat.SubPicBottomFieldChromaTop:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsInterlaced(this FrameFormat frameFormat)
|
||||||
|
{
|
||||||
|
switch (frameFormat)
|
||||||
|
{
|
||||||
|
case FrameFormat.InterlacedTopFieldFirst:
|
||||||
|
case FrameFormat.InterlacedBottomFieldFirst:
|
||||||
|
case FrameFormat.SubPicInterlacedTopFieldFirst:
|
||||||
|
case FrameFormat.SubPicInterlacedBottomFieldFirst:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsInterlacedBottomFirst(this FrameFormat frameFormat)
|
||||||
|
{
|
||||||
|
return frameFormat == FrameFormat.InterlacedBottomFieldFirst ||
|
||||||
|
frameFormat == FrameFormat.SubPicInterlacedBottomFieldFirst;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsTopField(this FrameFormat frameFormat, bool isLuma)
|
||||||
|
{
|
||||||
|
switch (frameFormat)
|
||||||
|
{
|
||||||
|
case FrameFormat.TopField:
|
||||||
|
case FrameFormat.SubPicTopField:
|
||||||
|
return true;
|
||||||
|
case FrameFormat.TopFieldChromaBottom:
|
||||||
|
case FrameFormat.SubPicTopFieldChromaBottom:
|
||||||
|
return isLuma;
|
||||||
|
case FrameFormat.BottomFieldChromaTop:
|
||||||
|
case FrameFormat.SubPicBottomFieldChromaTop:
|
||||||
|
return !isLuma;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,7 +27,7 @@
|
|||||||
public bool PrevMotionFieldEnable => _word0.Extract(13);
|
public bool PrevMotionFieldEnable => _word0.Extract(13);
|
||||||
public bool PpMotionFieldEnable => _word0.Extract(14);
|
public bool PpMotionFieldEnable => _word0.Extract(14);
|
||||||
public bool CombMotionFieldEnable => _word0.Extract(15);
|
public bool CombMotionFieldEnable => _word0.Extract(15);
|
||||||
public int FrameFormat => _word0.Extract(16, 4);
|
public FrameFormat FrameFormat => (FrameFormat)_word0.Extract(16, 4);
|
||||||
public int FilterLengthY => _word0.Extract(20, 2);
|
public int FilterLengthY => _word0.Extract(20, 2);
|
||||||
public int FilterLengthX => _word0.Extract(22, 2);
|
public int FilterLengthX => _word0.Extract(22, 2);
|
||||||
public int Panoramic => _word0.Extract(24, 12);
|
public int Panoramic => _word0.Extract(24, 12);
|
||||||
@ -36,7 +36,7 @@
|
|||||||
public int FilterDetail => _word1.Extract(74, 10);
|
public int FilterDetail => _word1.Extract(74, 10);
|
||||||
public int ChromaNoise => _word1.Extract(84, 10);
|
public int ChromaNoise => _word1.Extract(84, 10);
|
||||||
public int ChromaDetail => _word1.Extract(94, 10);
|
public int ChromaDetail => _word1.Extract(94, 10);
|
||||||
public int DeinterlaceMode => _word1.Extract(104, 4);
|
public DeinterlaceMode DeinterlaceMode => (DeinterlaceMode)_word1.Extract(104, 4);
|
||||||
public int MotionAccumWeight => _word1.Extract(108, 3);
|
public int MotionAccumWeight => _word1.Extract(108, 3);
|
||||||
public int NoiseIir => _word1.Extract(111, 11);
|
public int NoiseIir => _word1.Extract(111, 11);
|
||||||
public int LightLevel => _word1.Extract(122, 4);
|
public int LightLevel => _word1.Extract(122, 4);
|
||||||
|
@ -43,9 +43,9 @@ namespace Ryujinx.Graphics.Vic
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var offsets = _state.State.SetSurfacexSlotx[i][0];
|
ref var offsets = ref _state.State.SetSurfacexSlotx[i];
|
||||||
|
|
||||||
using Surface src = SurfaceReader.Read(_rm, ref slot.SlotSurfaceConfig, ref offsets);
|
using Surface src = SurfaceReader.Read(_rm, ref slot.SlotConfig, ref slot.SlotSurfaceConfig, ref offsets);
|
||||||
|
|
||||||
Blender.BlendOne(output, src, ref slot);
|
Blender.BlendOne(output, src, ref slot);
|
||||||
}
|
}
|
||||||
|
8
Ryujinx.Graphics.Video/FrameField.cs
Normal file
8
Ryujinx.Graphics.Video/FrameField.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Ryujinx.Graphics.Video
|
||||||
|
{
|
||||||
|
public enum FrameField
|
||||||
|
{
|
||||||
|
Progressive,
|
||||||
|
Interlaced
|
||||||
|
}
|
||||||
|
}
|
@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Video
|
|||||||
Plane UPlane { get; }
|
Plane UPlane { get; }
|
||||||
Plane VPlane { get; }
|
Plane VPlane { get; }
|
||||||
|
|
||||||
|
FrameField Field { get; }
|
||||||
|
|
||||||
int Width { get; }
|
int Width { get; }
|
||||||
int Height { get; }
|
int Height { get; }
|
||||||
int Stride { get; }
|
int Stride { get; }
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.FileSystem.Content
|
|
||||||
{
|
|
||||||
static class ContentPath
|
|
||||||
{
|
|
||||||
public const string SystemContent = "@SystemContent";
|
|
||||||
public const string UserContent = "@UserContent";
|
|
||||||
public const string SdCardContent = "@SdCardContent";
|
|
||||||
public const string SdCard = "@SdCard";
|
|
||||||
public const string CalibFile = "@CalibFile";
|
|
||||||
public const string Safe = "@Safe";
|
|
||||||
public const string User = "@User";
|
|
||||||
public const string System = "@System";
|
|
||||||
public const string Host = "@Host";
|
|
||||||
public const string GamecardApp = "@GcApp";
|
|
||||||
public const string GamecardContents = "@GcS00000001";
|
|
||||||
public const string GamecardUpdate = "@upp";
|
|
||||||
public const string RegisteredUpdate = "@RegUpdate";
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
|
|
||||||
using static Ryujinx.HLE.FileSystem.VirtualFileSystem;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem.Content
|
|
||||||
{
|
|
||||||
internal static class LocationHelper
|
|
||||||
{
|
|
||||||
public static string GetRealPath(VirtualFileSystem fileSystem, string switchContentPath)
|
|
||||||
{
|
|
||||||
string basePath = fileSystem.GetBasePath();
|
|
||||||
|
|
||||||
switch (switchContentPath)
|
|
||||||
{
|
|
||||||
case ContentPath.SystemContent:
|
|
||||||
return Path.Combine(basePath, SystemNandPath, "Contents");
|
|
||||||
case ContentPath.UserContent:
|
|
||||||
return Path.Combine(basePath, UserNandPath, "Contents");
|
|
||||||
case ContentPath.SdCardContent:
|
|
||||||
return Path.Combine(fileSystem.GetSdCardPath(), "Nintendo", "Contents");
|
|
||||||
case ContentPath.System:
|
|
||||||
return Path.Combine(basePath, SystemNandPath);
|
|
||||||
case ContentPath.User:
|
|
||||||
return Path.Combine(basePath, UserNandPath);
|
|
||||||
default:
|
|
||||||
throw new NotSupportedException($"Content Path `{switchContentPath}` is not supported.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetContentPath(ContentStorageId contentStorageId)
|
|
||||||
{
|
|
||||||
switch (contentStorageId)
|
|
||||||
{
|
|
||||||
case ContentStorageId.NandSystem:
|
|
||||||
return ContentPath.SystemContent;
|
|
||||||
case ContentStorageId.NandUser:
|
|
||||||
return ContentPath.UserContent;
|
|
||||||
case ContentStorageId.SdCard:
|
|
||||||
return ContentPath.SdCardContent;
|
|
||||||
default:
|
|
||||||
throw new NotSupportedException($"Content Storage `{contentStorageId}` is not supported.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetContentRoot(StorageId storageId)
|
|
||||||
{
|
|
||||||
switch (storageId)
|
|
||||||
{
|
|
||||||
case StorageId.NandSystem:
|
|
||||||
return ContentPath.SystemContent;
|
|
||||||
case StorageId.NandUser:
|
|
||||||
return ContentPath.UserContent;
|
|
||||||
case StorageId.SdCard:
|
|
||||||
return ContentPath.SdCardContent;
|
|
||||||
default:
|
|
||||||
throw new NotSupportedException($"Storage Id `{storageId}` is not supported.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static StorageId GetStorageId(string contentPathString)
|
|
||||||
{
|
|
||||||
string cleanedPath = contentPathString.Split(':')[0];
|
|
||||||
|
|
||||||
switch (cleanedPath)
|
|
||||||
{
|
|
||||||
case ContentPath.SystemContent:
|
|
||||||
case ContentPath.System:
|
|
||||||
return StorageId.NandSystem;
|
|
||||||
|
|
||||||
case ContentPath.UserContent:
|
|
||||||
case ContentPath.User:
|
|
||||||
return StorageId.NandUser;
|
|
||||||
|
|
||||||
case ContentPath.SdCardContent:
|
|
||||||
return StorageId.SdCard;
|
|
||||||
|
|
||||||
case ContentPath.Host:
|
|
||||||
return StorageId.Host;
|
|
||||||
|
|
||||||
case ContentPath.GamecardApp:
|
|
||||||
case ContentPath.GamecardContents:
|
|
||||||
case ContentPath.GamecardUpdate:
|
|
||||||
return StorageId.GameCard;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return StorageId.None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.FileSystem.Content
|
|
||||||
{
|
|
||||||
public enum ContentStorageId
|
|
||||||
{
|
|
||||||
NandSystem,
|
|
||||||
NandUser,
|
|
||||||
SdCard
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.FileSystem.Content
|
|
||||||
{
|
|
||||||
enum TitleType
|
|
||||||
{
|
|
||||||
SystemPrograms = 0x01,
|
|
||||||
SystemDataArchive = 0x02,
|
|
||||||
SystemUpdate = 0x03,
|
|
||||||
FirmwarePackageA = 0x04,
|
|
||||||
FirmwarePackageB = 0x05,
|
|
||||||
RegularApplication = 0x80,
|
|
||||||
Update = 0x81,
|
|
||||||
AddOnContent = 0x82,
|
|
||||||
DeltaTitle = 0x83
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,7 +20,7 @@ using System.IO.Compression;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem.Content
|
namespace Ryujinx.HLE.FileSystem
|
||||||
{
|
{
|
||||||
public class ContentManager
|
public class ContentManager
|
||||||
{
|
{
|
||||||
@ -110,8 +110,8 @@ namespace Ryujinx.HLE.FileSystem.Content
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
contentPathString = LocationHelper.GetContentRoot(storageId);
|
contentPathString = ContentPath.GetContentPath(storageId);
|
||||||
contentDirectory = LocationHelper.GetRealPath(_virtualFileSystem, contentPathString);
|
contentDirectory = ContentPath.GetRealPath(_virtualFileSystem, contentPathString);
|
||||||
registeredDirectory = Path.Combine(contentDirectory, "registered");
|
registeredDirectory = Path.Combine(contentDirectory, "registered");
|
||||||
}
|
}
|
||||||
catch (NotSupportedException)
|
catch (NotSupportedException)
|
||||||
@ -367,8 +367,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
|||||||
{
|
{
|
||||||
LocationEntry locationEntry = GetLocation(titleId, contentType, storageId);
|
LocationEntry locationEntry = GetLocation(titleId, contentType, storageId);
|
||||||
|
|
||||||
return locationEntry.ContentPath != null ?
|
return locationEntry.ContentPath != null ? ContentPath.GetStorageId(locationEntry.ContentPath) : StorageId.None;
|
||||||
LocationHelper.GetStorageId(locationEntry.ContentPath) : StorageId.None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -493,8 +492,8 @@ namespace Ryujinx.HLE.FileSystem.Content
|
|||||||
|
|
||||||
public void InstallFirmware(string firmwareSource)
|
public void InstallFirmware(string firmwareSource)
|
||||||
{
|
{
|
||||||
string contentPathString = LocationHelper.GetContentRoot(StorageId.NandSystem);
|
string contentPathString = ContentPath.GetContentPath(StorageId.BuiltInSystem);
|
||||||
string contentDirectory = LocationHelper.GetRealPath(_virtualFileSystem, contentPathString);
|
string contentDirectory = ContentPath.GetRealPath(_virtualFileSystem, contentPathString);
|
||||||
string registeredDirectory = Path.Combine(contentDirectory, "registered");
|
string registeredDirectory = Path.Combine(contentDirectory, "registered");
|
||||||
string temporaryDirectory = Path.Combine(contentDirectory, "temp");
|
string temporaryDirectory = Path.Combine(contentDirectory, "temp");
|
||||||
|
|
||||||
@ -998,9 +997,9 @@ namespace Ryujinx.HLE.FileSystem.Content
|
|||||||
|
|
||||||
foreach (var entry in updateNcas)
|
foreach (var entry in updateNcas)
|
||||||
{
|
{
|
||||||
foreach (var nca in entry.Value)
|
foreach (var (type, path) in entry.Value)
|
||||||
{
|
{
|
||||||
extraNcas += nca.path + Environment.NewLine;
|
extraNcas += path + Environment.NewLine;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1019,7 +1018,7 @@ namespace Ryujinx.HLE.FileSystem.Content
|
|||||||
|
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
var locationEnties = _locationEntries[StorageId.NandSystem];
|
var locationEnties = _locationEntries[StorageId.BuiltInSystem];
|
||||||
|
|
||||||
foreach (var entry in locationEnties)
|
foreach (var entry in locationEnties)
|
||||||
{
|
{
|
82
Ryujinx.HLE/FileSystem/ContentPath.cs
Normal file
82
Ryujinx.HLE/FileSystem/ContentPath.cs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
using LibHac.Fs;
|
||||||
|
using LibHac.Ncm;
|
||||||
|
using Ryujinx.Common.Configuration;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
using static Ryujinx.HLE.FileSystem.VirtualFileSystem;
|
||||||
|
using Path = System.IO.Path;
|
||||||
|
|
||||||
|
namespace Ryujinx.HLE.FileSystem
|
||||||
|
{
|
||||||
|
internal static class ContentPath
|
||||||
|
{
|
||||||
|
public const string SystemContent = "@SystemContent";
|
||||||
|
public const string UserContent = "@UserContent";
|
||||||
|
public const string SdCardContent = "@SdCardContent";
|
||||||
|
public const string SdCard = "@Sdcard";
|
||||||
|
public const string CalibFile = "@CalibFile";
|
||||||
|
public const string Safe = "@Safe";
|
||||||
|
public const string User = "@User";
|
||||||
|
public const string System = "@System";
|
||||||
|
public const string Host = "@Host";
|
||||||
|
public const string GamecardApp = "@GcApp";
|
||||||
|
public const string GamecardContents = "@GcS00000001";
|
||||||
|
public const string GamecardUpdate = "@upp";
|
||||||
|
public const string RegisteredUpdate = "@RegUpdate";
|
||||||
|
|
||||||
|
public const string Nintendo = "Nintendo";
|
||||||
|
public const string Contents = "Contents";
|
||||||
|
|
||||||
|
public static string GetRealPath(VirtualFileSystem fileSystem, string switchContentPath)
|
||||||
|
{
|
||||||
|
return switchContentPath switch
|
||||||
|
{
|
||||||
|
SystemContent => Path.Combine(AppDataManager.BaseDirPath, SystemNandPath, Contents),
|
||||||
|
UserContent => Path.Combine(AppDataManager.BaseDirPath, UserNandPath, Contents),
|
||||||
|
SdCardContent => Path.Combine(fileSystem.GetSdCardPath(), Nintendo, Contents),
|
||||||
|
System => Path.Combine(AppDataManager.BaseDirPath, SystemNandPath),
|
||||||
|
User => Path.Combine(AppDataManager.BaseDirPath, UserNandPath),
|
||||||
|
_ => throw new NotSupportedException($"Content Path \"`{switchContentPath}`\" is not supported.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetContentPath(ContentStorageId contentStorageId)
|
||||||
|
{
|
||||||
|
return contentStorageId switch
|
||||||
|
{
|
||||||
|
ContentStorageId.System => SystemContent,
|
||||||
|
ContentStorageId.User => UserContent,
|
||||||
|
ContentStorageId.SdCard => SdCardContent,
|
||||||
|
_ => throw new NotSupportedException($"Content Storage Id \"`{contentStorageId}`\" is not supported.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string GetContentPath(StorageId storageId)
|
||||||
|
{
|
||||||
|
return storageId switch
|
||||||
|
{
|
||||||
|
StorageId.BuiltInSystem => SystemContent,
|
||||||
|
StorageId.BuiltInUser => UserContent,
|
||||||
|
StorageId.SdCard => SdCardContent,
|
||||||
|
_ => throw new NotSupportedException($"Storage Id \"`{storageId}`\" is not supported.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StorageId GetStorageId(string contentPathString)
|
||||||
|
{
|
||||||
|
return contentPathString.Split(':')[0] switch
|
||||||
|
{
|
||||||
|
SystemContent or
|
||||||
|
System => StorageId.BuiltInSystem,
|
||||||
|
UserContent or
|
||||||
|
User => StorageId.BuiltInUser,
|
||||||
|
SdCardContent => StorageId.SdCard,
|
||||||
|
Host => StorageId.Host,
|
||||||
|
GamecardApp or
|
||||||
|
GamecardContents or
|
||||||
|
GamecardUpdate => StorageId.GameCard,
|
||||||
|
_ => StorageId.None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,6 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
{
|
{
|
||||||
public class EncryptedFileSystemCreator : IEncryptedFileSystemCreator
|
public class EncryptedFileSystemCreator : IEncryptedFileSystemCreator
|
||||||
{
|
{
|
||||||
|
|
||||||
public Result Create(ref SharedRef<IFileSystem> outEncryptedFileSystem,
|
public Result Create(ref SharedRef<IFileSystem> outEncryptedFileSystem,
|
||||||
ref SharedRef<IFileSystem> baseFileSystem, IEncryptedFileSystemCreator.KeyId idIndex,
|
ref SharedRef<IFileSystem> baseFileSystem, IEncryptedFileSystemCreator.KeyId idIndex,
|
||||||
in EncryptionSeed encryptionSeed)
|
in EncryptionSeed encryptionSeed)
|
||||||
@ -18,10 +17,10 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
return ResultFs.InvalidArgument.Log();
|
return ResultFs.InvalidArgument.Log();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Todo: Reenable when AesXtsFileSystem is fixed
|
// TODO: Reenable when AesXtsFileSystem is fixed.
|
||||||
outEncryptedFileSystem = SharedRef<IFileSystem>.CreateMove(ref baseFileSystem);
|
outEncryptedFileSystem = SharedRef<IFileSystem>.CreateMove(ref baseFileSystem);
|
||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem.Content
|
namespace Ryujinx.HLE.FileSystem
|
||||||
{
|
{
|
||||||
public struct LocationEntry
|
public struct LocationEntry
|
||||||
{
|
{
|
@ -1,12 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.FileSystem
|
|
||||||
{
|
|
||||||
enum SaveDataType : byte
|
|
||||||
{
|
|
||||||
SystemSaveData,
|
|
||||||
SaveData,
|
|
||||||
BcatDeliveryCacheStorage,
|
|
||||||
DeviceSaveData,
|
|
||||||
TemporaryStorage,
|
|
||||||
CacheStorage
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem
|
|
||||||
{
|
|
||||||
struct SaveInfo
|
|
||||||
{
|
|
||||||
public ulong TitleId { get; private set; }
|
|
||||||
public long SaveId { get; private set; }
|
|
||||||
public SaveDataType SaveDataType { get; private set; }
|
|
||||||
public SaveSpaceId SaveSpaceId { get; private set; }
|
|
||||||
public UserId UserId { get; private set; }
|
|
||||||
|
|
||||||
public SaveInfo(
|
|
||||||
ulong titleId,
|
|
||||||
long saveId,
|
|
||||||
SaveDataType saveDataType,
|
|
||||||
SaveSpaceId saveSpaceId,
|
|
||||||
UserId userId = new UserId())
|
|
||||||
{
|
|
||||||
TitleId = titleId;
|
|
||||||
SaveId = saveId;
|
|
||||||
SaveDataType = saveDataType;
|
|
||||||
SaveSpaceId = saveSpaceId;
|
|
||||||
UserId = userId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.FileSystem
|
|
||||||
{
|
|
||||||
enum SaveSpaceId
|
|
||||||
{
|
|
||||||
NandSystem,
|
|
||||||
NandUser,
|
|
||||||
SdCard,
|
|
||||||
TemporaryStorage
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
namespace Ryujinx.HLE.FileSystem
|
|
||||||
{
|
|
||||||
public enum StorageId
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Host,
|
|
||||||
GameCard,
|
|
||||||
NandSystem,
|
|
||||||
NandUser,
|
|
||||||
SdCard
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,7 @@
|
|||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.FileSystem.Content
|
namespace Ryujinx.HLE.FileSystem
|
||||||
{
|
{
|
||||||
public class SystemVersion
|
public class SystemVersion
|
||||||
{
|
{
|
@ -13,7 +13,6 @@ using LibHac.Tools.Fs;
|
|||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers.Text;
|
using System.Buffers.Text;
|
||||||
@ -28,20 +27,29 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
{
|
{
|
||||||
public class VirtualFileSystem : IDisposable
|
public class VirtualFileSystem : IDisposable
|
||||||
{
|
{
|
||||||
public const string NandPath = AppDataManager.DefaultNandDir;
|
public static string SafeNandPath = Path.Combine(AppDataManager.DefaultNandDir, "safe");
|
||||||
public const string SdCardPath = AppDataManager.DefaultSdcardDir;
|
public static string SystemNandPath = Path.Combine(AppDataManager.DefaultNandDir, "system");
|
||||||
|
public static string UserNandPath = Path.Combine(AppDataManager.DefaultNandDir, "user");
|
||||||
|
|
||||||
public static string SafeNandPath = Path.Combine(NandPath, "safe");
|
public KeySet KeySet { get; private set; }
|
||||||
public static string SystemNandPath = Path.Combine(NandPath, "system");
|
public EmulatedGameCard GameCard { get; private set; }
|
||||||
public static string UserNandPath = Path.Combine(NandPath, "user");
|
public EmulatedSdCard SdCard { get; private set; }
|
||||||
|
public ModLoader ModLoader { get; private set; }
|
||||||
|
public Stream RomFs { get; private set; }
|
||||||
|
|
||||||
private static bool _isInitialized = false;
|
private static bool _isInitialized = false;
|
||||||
|
|
||||||
public KeySet KeySet { get; private set; }
|
public static VirtualFileSystem CreateInstance()
|
||||||
public EmulatedGameCard GameCard { get; private set; }
|
{
|
||||||
public EmulatedSdCard SdCard { get; private set; }
|
if (_isInitialized)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("VirtualFileSystem can only be instantiated once!");
|
||||||
|
}
|
||||||
|
|
||||||
public ModLoader ModLoader { get; private set; }
|
_isInitialized = true;
|
||||||
|
|
||||||
|
return new VirtualFileSystem();
|
||||||
|
}
|
||||||
|
|
||||||
private VirtualFileSystem()
|
private VirtualFileSystem()
|
||||||
{
|
{
|
||||||
@ -49,8 +57,6 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
ModLoader = new ModLoader(); // Should only be created once
|
ModLoader = new ModLoader(); // Should only be created once
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream RomFs { get; private set; }
|
|
||||||
|
|
||||||
public void LoadRomFs(string fileName)
|
public void LoadRomFs(string fileName)
|
||||||
{
|
{
|
||||||
RomFs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
|
RomFs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
|
||||||
@ -79,7 +85,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
string fullPath = Path.GetFullPath(Path.Combine(basePath, fileName));
|
string fullPath = Path.GetFullPath(Path.Combine(basePath, fileName));
|
||||||
|
|
||||||
if (!fullPath.StartsWith(GetBasePath()))
|
if (!fullPath.StartsWith(AppDataManager.BaseDirPath))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -87,14 +93,8 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
return fullPath;
|
return fullPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal string GetBasePath() => AppDataManager.BaseDirPath;
|
internal string GetSdCardPath() => MakeFullPath(AppDataManager.DefaultSdcardDir);
|
||||||
internal string GetSdCardPath() => MakeFullPath(SdCardPath);
|
public string GetNandPath() => MakeFullPath(AppDataManager.DefaultNandDir);
|
||||||
public string GetNandPath() => MakeFullPath(NandPath);
|
|
||||||
|
|
||||||
public string GetFullPartitionPath(string partitionPath)
|
|
||||||
{
|
|
||||||
return MakeFullPath(partitionPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string SwitchPathToSystemPath(string switchPath)
|
public string SwitchPathToSystemPath(string switchPath)
|
||||||
{
|
{
|
||||||
@ -110,7 +110,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
public string SystemPathToSwitchPath(string systemPath)
|
public string SystemPathToSwitchPath(string systemPath)
|
||||||
{
|
{
|
||||||
string baseSystemPath = GetBasePath() + Path.DirectorySeparatorChar;
|
string baseSystemPath = AppDataManager.BaseDirPath + Path.DirectorySeparatorChar;
|
||||||
|
|
||||||
if (systemPath.StartsWith(baseSystemPath))
|
if (systemPath.StartsWith(baseSystemPath))
|
||||||
{
|
{
|
||||||
@ -136,8 +136,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
switch (path)
|
switch (path)
|
||||||
{
|
{
|
||||||
case ContentPath.SdCard:
|
case ContentPath.SdCard:
|
||||||
case "@Sdcard":
|
path = AppDataManager.DefaultSdcardDir;
|
||||||
path = SdCardPath;
|
|
||||||
break;
|
break;
|
||||||
case ContentPath.User:
|
case ContentPath.User:
|
||||||
path = UserNandPath;
|
path = UserNandPath;
|
||||||
@ -146,7 +145,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
path = SystemNandPath;
|
path = SystemNandPath;
|
||||||
break;
|
break;
|
||||||
case ContentPath.SdCardContent:
|
case ContentPath.SdCardContent:
|
||||||
path = Path.Combine(SdCardPath, "Nintendo", "Contents");
|
path = Path.Combine(AppDataManager.DefaultSdcardDir, "Nintendo", "Contents");
|
||||||
break;
|
break;
|
||||||
case ContentPath.UserContent:
|
case ContentPath.UserContent:
|
||||||
path = Path.Combine(UserNandPath, "Contents");
|
path = Path.Combine(UserNandPath, "Contents");
|
||||||
@ -156,27 +155,19 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
string fullPath = Path.Combine(GetBasePath(), path);
|
string fullPath = Path.Combine(AppDataManager.BaseDirPath, path);
|
||||||
|
|
||||||
if (isDirectory)
|
if (isDirectory && !Directory.Exists(fullPath))
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(fullPath))
|
Directory.CreateDirectory(fullPath);
|
||||||
{
|
|
||||||
Directory.CreateDirectory(fullPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return fullPath;
|
return fullPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DriveInfo GetDrive()
|
|
||||||
{
|
|
||||||
return new DriveInfo(Path.GetPathRoot(GetBasePath()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InitializeFsServer(LibHac.Horizon horizon, out HorizonClient fsServerClient)
|
public void InitializeFsServer(LibHac.Horizon horizon, out HorizonClient fsServerClient)
|
||||||
{
|
{
|
||||||
LocalFileSystem serverBaseFs = new LocalFileSystem(GetBasePath());
|
LocalFileSystem serverBaseFs = new LocalFileSystem(AppDataManager.BaseDirPath);
|
||||||
|
|
||||||
fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
||||||
var fsServer = new FileSystemServer(fsServerClient);
|
var fsServer = new FileSystemServer(fsServerClient);
|
||||||
@ -505,7 +496,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
bool canFixBySaveDataId = extraData.Attribute.StaticSaveDataId == 0 && info.StaticSaveDataId != 0;
|
bool canFixBySaveDataId = extraData.Attribute.StaticSaveDataId == 0 && info.StaticSaveDataId != 0;
|
||||||
|
|
||||||
bool hasEmptyOwnerId = extraData.OwnerId == 0 && info.Type != LibHac.Fs.SaveDataType.System;
|
bool hasEmptyOwnerId = extraData.OwnerId == 0 && info.Type != SaveDataType.System;
|
||||||
|
|
||||||
if (!canFixByProgramId && !canFixBySaveDataId && !hasEmptyOwnerId)
|
if (!canFixByProgramId && !canFixBySaveDataId && !hasEmptyOwnerId)
|
||||||
{
|
{
|
||||||
@ -523,7 +514,7 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
|
|
||||||
// The rest of the extra data can't be created from the save data info.
|
// The rest of the extra data can't be created from the save data info.
|
||||||
// On user saves the owner ID will almost certainly be the same as the program ID.
|
// On user saves the owner ID will almost certainly be the same as the program ID.
|
||||||
if (info.Type != LibHac.Fs.SaveDataType.System)
|
if (info.Type != SaveDataType.System)
|
||||||
{
|
{
|
||||||
extraData.OwnerId = info.ProgramId.Value;
|
extraData.OwnerId = info.ProgramId.Value;
|
||||||
}
|
}
|
||||||
@ -580,11 +571,6 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public void Unload()
|
|
||||||
{
|
|
||||||
RomFs?.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
@ -594,20 +580,8 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
Unload();
|
RomFs?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static VirtualFileSystem CreateInstance()
|
|
||||||
{
|
|
||||||
if (_isInitialized)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("VirtualFileSystem can only be instantiated once!");
|
|
||||||
}
|
|
||||||
|
|
||||||
_isInitialized = true;
|
|
||||||
|
|
||||||
return new VirtualFileSystem();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,6 @@ using Ryujinx.Audio.Integration;
|
|||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Ncm;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.FileSystem;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using System;
|
using System;
|
||||||
@ -106,7 +106,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
|||||||
|
|
||||||
private string GetMessageText(uint module, uint description, string key)
|
private string GetMessageText(uint module, uint description, string key)
|
||||||
{
|
{
|
||||||
string binaryTitleContentPath = _horizon.ContentManager.GetInstalledContentPath(ErrorMessageBinaryTitleId, StorageId.NandSystem, NcaContentType.Data);
|
string binaryTitleContentPath = _horizon.ContentManager.GetInstalledContentPath(ErrorMessageBinaryTitleId, StorageId.BuiltInSystem, NcaContentType.Data);
|
||||||
|
|
||||||
using (LibHac.Fs.IStorage ncaFileStream = new LocalStorage(_horizon.Device.FileSystem.SwitchPathToSystemPath(binaryTitleContentPath), FileAccess.Read, FileMode.Open))
|
using (LibHac.Fs.IStorage ncaFileStream = new LocalStorage(_horizon.Device.FileSystem.SwitchPathToSystemPath(binaryTitleContentPath), FileAccess.Read, FileMode.Open))
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,7 @@ using Ryujinx.Audio.Integration;
|
|||||||
using Ryujinx.Audio.Output;
|
using Ryujinx.Audio.Output;
|
||||||
using Ryujinx.Audio.Renderer.Device;
|
using Ryujinx.Audio.Renderer.Device;
|
||||||
using Ryujinx.Audio.Renderer.Server;
|
using Ryujinx.Audio.Renderer.Server;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
@ -238,6 +238,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
SurfaceFlinger = new SurfaceFlinger(device);
|
SurfaceFlinger = new SurfaceFlinger(device);
|
||||||
|
|
||||||
InitializeAudioRenderer();
|
InitializeAudioRenderer();
|
||||||
|
InitializeServices();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeAudioRenderer()
|
private void InitializeAudioRenderer()
|
||||||
@ -288,7 +289,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
AudioManager.Start();
|
AudioManager.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitializeServices()
|
private void InitializeServices()
|
||||||
{
|
{
|
||||||
SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext));
|
SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext));
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Ryujinx.HLE.FileSystem;
|
using LibHac.Ncm;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Arp
|
namespace Ryujinx.HLE.HOS.Services.Arp
|
||||||
{
|
{
|
||||||
@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Arp
|
|||||||
{
|
{
|
||||||
TitleId = 0x00,
|
TitleId = 0x00,
|
||||||
Version = 0x00,
|
Version = 0x00,
|
||||||
BaseGameStorageId = (byte)StorageId.NandSystem,
|
BaseGameStorageId = (byte)StorageId.BuiltInSystem,
|
||||||
UpdateGameStorageId = (byte)StorageId.None
|
UpdateGameStorageId = (byte)StorageId.None
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Arp
|
|||||||
{
|
{
|
||||||
TitleId = context.Device.Application.TitleId,
|
TitleId = context.Device.Application.TitleId,
|
||||||
Version = 0x00,
|
Version = 0x00,
|
||||||
BaseGameStorageId = (byte)StorageId.NandSystem,
|
BaseGameStorageId = (byte)StorageId.BuiltInSystem,
|
||||||
UpdateGameStorageId = (byte)StorageId.None
|
UpdateGameStorageId = (byte)StorageId.None
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ using LibHac;
|
|||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Shim;
|
using LibHac.Fs.Shim;
|
||||||
using LibHac.FsSrv;
|
|
||||||
using LibHac.FsSrv.Impl;
|
using LibHac.FsSrv.Impl;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.Ncm;
|
using LibHac.Ncm;
|
||||||
@ -19,7 +18,6 @@ using static Ryujinx.HLE.Utilities.StringUtils;
|
|||||||
using IFileSystem = LibHac.FsSrv.Sf.IFileSystem;
|
using IFileSystem = LibHac.FsSrv.Sf.IFileSystem;
|
||||||
using IStorage = LibHac.FsSrv.Sf.IStorage;
|
using IStorage = LibHac.FsSrv.Sf.IStorage;
|
||||||
using RightsId = LibHac.Fs.RightsId;
|
using RightsId = LibHac.Fs.RightsId;
|
||||||
using StorageId = Ryujinx.HLE.FileSystem.StorageId;
|
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Fs
|
namespace Ryujinx.HLE.HOS.Services.Fs
|
||||||
{
|
{
|
||||||
|
@ -55,9 +55,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
_storage = storage;
|
_storage = storage;
|
||||||
|
|
||||||
SharedMemory = SharedMemory.Create();
|
SharedMemory = SharedMemory.Create();
|
||||||
|
|
||||||
|
InitDevices();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitDevices()
|
private void InitDevices()
|
||||||
{
|
{
|
||||||
DebugPad = new DebugPadDevice(_device, true);
|
DebugPad = new DebugPadDevice(_device, true);
|
||||||
Touchscreen = new TouchDevice(_device, true);
|
Touchscreen = new TouchDevice(_device, true);
|
||||||
|
@ -767,6 +767,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
|
|||||||
throw new InvalidOperationException("Out of handles!");
|
throw new InvalidOperationException("Out of handles!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Games expect this event to be signaled after calling this function
|
||||||
|
evnt.ReadableEvent.Signal();
|
||||||
|
|
||||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
|
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle);
|
||||||
|
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, npadId, npadStyleSet });
|
Logger.Stub?.PrintStub(LogClass.ServiceHid, new { appletResourceUserId, npadId, npadStyleSet });
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
using Ryujinx.HLE.FileSystem;
|
using LibHac.Ncm;
|
||||||
using Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager;
|
using Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.Ncm.Lr
|
namespace Ryujinx.HLE.HOS.Services.Ncm.Lr
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Ncm;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
using static Ryujinx.HLE.Utilities.StringUtils;
|
using static Ryujinx.HLE.Utilities.StringUtils;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using LibHac.Ncm;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.Arp;
|
using Ryujinx.HLE.HOS.Services.Arp;
|
||||||
using Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface;
|
using Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface;
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@ using LibHac.Common;
|
|||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Ncm;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Services.Sdb.Pl.Types;
|
using Ryujinx.HLE.HOS.Services.Sdb.Pl.Types;
|
||||||
using System;
|
using System;
|
||||||
@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
|
|||||||
{
|
{
|
||||||
if (contentManager.TryGetFontTitle(name, out ulong fontTitle) && contentManager.TryGetFontFilename(name, out string fontFilename))
|
if (contentManager.TryGetFontTitle(name, out ulong fontTitle) && contentManager.TryGetFontFilename(name, out string fontFilename))
|
||||||
{
|
{
|
||||||
string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.NandSystem, NcaContentType.Data);
|
string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.BuiltInSystem, NcaContentType.Data);
|
||||||
string fontPath = _device.FileSystem.SwitchPathToSystemPath(contentPath);
|
string fontPath = _device.FileSystem.SwitchPathToSystemPath(contentPath);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(fontPath))
|
if (!string.IsNullOrWhiteSpace(fontPath))
|
||||||
|
@ -3,9 +3,9 @@ using LibHac.Common;
|
|||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Ncm;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.FileSystem;
|
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System;
|
using System;
|
||||||
@ -290,7 +290,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
|
|||||||
{
|
{
|
||||||
const ulong SystemVersionTitleId = 0x0100000000000809;
|
const ulong SystemVersionTitleId = 0x0100000000000809;
|
||||||
|
|
||||||
string contentPath = device.System.ContentManager.GetInstalledContentPath(SystemVersionTitleId, StorageId.NandSystem, NcaContentType.Data);
|
string contentPath = device.System.ContentManager.GetInstalledContentPath(SystemVersionTitleId, StorageId.BuiltInSystem, NcaContentType.Data);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(contentPath))
|
if (string.IsNullOrWhiteSpace(contentPath))
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
|
||||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||||
using Ryujinx.HLE.HOS.Services.Spl.Types;
|
using Ryujinx.HLE.HOS.Services.Spl.Types;
|
||||||
|
|
||||||
|
@ -3,13 +3,13 @@ using LibHac.Common;
|
|||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Ncm;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Ssl.Types;
|
using Ryujinx.HLE.HOS.Services.Ssl.Types;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -82,7 +82,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
|
|||||||
|
|
||||||
public string GetCertStoreTitleContentPath()
|
public string GetCertStoreTitleContentPath()
|
||||||
{
|
{
|
||||||
return _contentManager.GetInstalledContentPath(CertStoreTitleId, StorageId.NandSystem, NcaContentType.Data);
|
return _contentManager.GetInstalledContentPath(CertStoreTitleId, StorageId.BuiltInSystem, NcaContentType.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasCertStoreTitle()
|
public bool HasCertStoreTitle()
|
||||||
|
@ -3,12 +3,12 @@ using LibHac.Common;
|
|||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Ncm;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.Exceptions;
|
using Ryujinx.HLE.Exceptions;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
using Ryujinx.HLE.Utilities;
|
using Ryujinx.HLE.Utilities;
|
||||||
using System;
|
using System;
|
||||||
@ -241,7 +241,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
|
|||||||
|
|
||||||
public string GetTimeZoneBinaryTitleContentPath()
|
public string GetTimeZoneBinaryTitleContentPath()
|
||||||
{
|
{
|
||||||
return _contentManager.GetInstalledContentPath(TimeZoneBinaryTitleId, StorageId.NandSystem, NcaContentType.Data);
|
return _contentManager.GetInstalledContentPath(TimeZoneBinaryTitleId, StorageId.BuiltInSystem, NcaContentType.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasTimeZoneBinaryTitle()
|
public bool HasTimeZoneBinaryTitle()
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Concentus" Version="1.1.7" />
|
<PackageReference Include="Concentus" Version="1.1.7" />
|
||||||
<PackageReference Include="LibHac" Version="0.16.0" />
|
<PackageReference Include="LibHac" Version="0.16.1" />
|
||||||
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
|
||||||
|
@ -13,27 +13,17 @@ namespace Ryujinx.HLE
|
|||||||
{
|
{
|
||||||
public class Switch : IDisposable
|
public class Switch : IDisposable
|
||||||
{
|
{
|
||||||
public HLEConfiguration Configuration { get; }
|
public HLEConfiguration Configuration { get; }
|
||||||
|
|
||||||
public IHardwareDeviceDriver AudioDeviceDriver { get; }
|
public IHardwareDeviceDriver AudioDeviceDriver { get; }
|
||||||
|
public MemoryBlock Memory { get; }
|
||||||
internal MemoryBlock Memory { get; }
|
public GpuContext Gpu { get; }
|
||||||
|
public VirtualFileSystem FileSystem { get; }
|
||||||
public GpuContext Gpu { get; }
|
public Horizon System { get; }
|
||||||
|
public ApplicationLoader Application { get; }
|
||||||
public VirtualFileSystem FileSystem => Configuration.VirtualFileSystem;
|
public PerformanceStatistics Statistics { get; }
|
||||||
|
public Hid Hid { get; }
|
||||||
public Horizon System { get; }
|
public TamperMachine TamperMachine { get; }
|
||||||
|
public IHostUiHandler UiHandler { get; }
|
||||||
public ApplicationLoader Application { get; }
|
|
||||||
|
|
||||||
public PerformanceStatistics Statistics { get; }
|
|
||||||
|
|
||||||
public Hid Hid { get; }
|
|
||||||
|
|
||||||
public TamperMachine TamperMachine { get; }
|
|
||||||
|
|
||||||
public IHostUiHandler UiHandler { get; }
|
|
||||||
|
|
||||||
public bool EnableDeviceVsync { get; set; } = true;
|
public bool EnableDeviceVsync { get; set; } = true;
|
||||||
|
|
||||||
@ -55,47 +45,27 @@ namespace Ryujinx.HLE
|
|||||||
}
|
}
|
||||||
|
|
||||||
Configuration = configuration;
|
Configuration = configuration;
|
||||||
|
FileSystem = Configuration.VirtualFileSystem;
|
||||||
|
UiHandler = Configuration.HostUiHandler;
|
||||||
|
|
||||||
UiHandler = configuration.HostUiHandler;
|
AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver);
|
||||||
|
Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), MemoryAllocationFlags.Reserve);
|
||||||
|
Gpu = new GpuContext(Configuration.GpuRenderer);
|
||||||
|
System = new Horizon(this);
|
||||||
|
Statistics = new PerformanceStatistics();
|
||||||
|
Hid = new Hid(this, System.HidStorage);
|
||||||
|
Application = new ApplicationLoader(this);
|
||||||
|
TamperMachine = new TamperMachine();
|
||||||
|
|
||||||
AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(configuration.AudioDeviceDriver);
|
|
||||||
|
|
||||||
Memory = new MemoryBlock(configuration.MemoryConfiguration.ToDramSize(), MemoryAllocationFlags.Reserve);
|
|
||||||
|
|
||||||
Gpu = new GpuContext(configuration.GpuRenderer);
|
|
||||||
|
|
||||||
System = new Horizon(this);
|
|
||||||
System.InitializeServices();
|
|
||||||
|
|
||||||
Statistics = new PerformanceStatistics();
|
|
||||||
|
|
||||||
Hid = new Hid(this, System.HidStorage);
|
|
||||||
Hid.InitDevices();
|
|
||||||
|
|
||||||
Application = new ApplicationLoader(this);
|
|
||||||
|
|
||||||
TamperMachine = new TamperMachine();
|
|
||||||
|
|
||||||
Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Initialize()
|
|
||||||
{
|
|
||||||
System.State.SetLanguage(Configuration.SystemLanguage);
|
System.State.SetLanguage(Configuration.SystemLanguage);
|
||||||
|
|
||||||
System.State.SetRegion(Configuration.Region);
|
System.State.SetRegion(Configuration.Region);
|
||||||
|
|
||||||
EnableDeviceVsync = Configuration.EnableVsync;
|
EnableDeviceVsync = Configuration.EnableVsync;
|
||||||
|
System.State.DockedMode = Configuration.EnableDockedMode;
|
||||||
System.State.DockedMode = Configuration.EnableDockedMode;
|
|
||||||
|
|
||||||
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
||||||
|
System.EnablePtc = Configuration.EnablePtc;
|
||||||
System.EnablePtc = Configuration.EnablePtc;
|
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
|
||||||
|
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
|
||||||
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
|
|
||||||
|
|
||||||
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadCart(string exeFsDir, string romFsFile = null)
|
public void LoadCart(string exeFsDir, string romFsFile = null)
|
||||||
@ -132,7 +102,6 @@ namespace Ryujinx.HLE
|
|||||||
{
|
{
|
||||||
Gpu.ProcessShaderCacheQueue();
|
Gpu.ProcessShaderCacheQueue();
|
||||||
Gpu.Renderer.PreFrame();
|
Gpu.Renderer.PreFrame();
|
||||||
|
|
||||||
Gpu.GPFifo.DispatchCalls();
|
Gpu.GPFifo.DispatchCalls();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,9 +151,9 @@ namespace Ryujinx.HLE
|
|||||||
{
|
{
|
||||||
System.Dispose();
|
System.Dispose();
|
||||||
AudioDeviceDriver.Dispose();
|
AudioDeviceDriver.Dispose();
|
||||||
FileSystem.Unload();
|
FileSystem.Dispose();
|
||||||
Memory.Dispose();
|
Memory.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,7 +20,6 @@ using Ryujinx.Graphics.OpenGL;
|
|||||||
using Ryujinx.Headless.SDL2.OpenGL;
|
using Ryujinx.Headless.SDL2.OpenGL;
|
||||||
using Ryujinx.HLE;
|
using Ryujinx.HLE;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
@ -29,7 +28,6 @@ using Ryujinx.Input.SDL2;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ namespace Ryujinx.Memory.Tests
|
|||||||
Assert.False(alignedAfterTriggers);
|
Assert.False(alignedAfterTriggers);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test, Timeout(1000)]
|
[Test, Explicit, Timeout(1000)]
|
||||||
public void Multithreading()
|
public void Multithreading()
|
||||||
{
|
{
|
||||||
// Multithreading sanity test
|
// Multithreading sanity test
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.Ui.Widgets;
|
using Ryujinx.Ui.Widgets;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -1,18 +1,10 @@
|
|||||||
using System;
|
using ARMeilleure.Translation;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using ARMeilleure.Translation;
|
|
||||||
using ARMeilleure.Translation.PTC;
|
using ARMeilleure.Translation.PTC;
|
||||||
|
|
||||||
using Gtk;
|
using Gtk;
|
||||||
|
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Common.Keys;
|
using LibHac.Common.Keys;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Ncm;
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using Ryujinx.Audio.Backends.Dummy;
|
using Ryujinx.Audio.Backends.Dummy;
|
||||||
@ -29,7 +21,6 @@ using Ryujinx.Graphics.GAL;
|
|||||||
using Ryujinx.Graphics.GAL.Multithreading;
|
using Ryujinx.Graphics.GAL.Multithreading;
|
||||||
using Ryujinx.Graphics.OpenGL;
|
using Ryujinx.Graphics.OpenGL;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
@ -42,9 +33,14 @@ using Ryujinx.Ui.Applet;
|
|||||||
using Ryujinx.Ui.Helper;
|
using Ryujinx.Ui.Helper;
|
||||||
using Ryujinx.Ui.Widgets;
|
using Ryujinx.Ui.Widgets;
|
||||||
using Ryujinx.Ui.Windows;
|
using Ryujinx.Ui.Windows;
|
||||||
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using GUI = Gtk.Builder.ObjectAttribute;
|
using GUI = Gtk.Builder.ObjectAttribute;
|
||||||
|
|
||||||
using PtcLoadingState = ARMeilleure.Translation.PTC.PtcLoadingState;
|
using PtcLoadingState = ARMeilleure.Translation.PTC.PtcLoadingState;
|
||||||
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
|
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
|
||||||
|
|
||||||
@ -1283,7 +1279,7 @@ namespace Ryujinx.Ui
|
|||||||
|
|
||||||
private void Load_Mii_Edit_Applet(object sender, EventArgs args)
|
private void Load_Mii_Edit_Applet(object sender, EventArgs args)
|
||||||
{
|
{
|
||||||
string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.NandSystem, NcaContentType.Program);
|
string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||||
|
|
||||||
LoadApplication(contentPath);
|
LoadApplication(contentPath);
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@ using LibHac.Common;
|
|||||||
using LibHac.Fs;
|
using LibHac.Fs;
|
||||||
using LibHac.Fs.Fsa;
|
using LibHac.Fs.Fsa;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
|
using LibHac.Ncm;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
using LibHac.Tools.FsSystem.NcaUtils;
|
using LibHac.Tools.FsSystem.NcaUtils;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
using SixLabors.ImageSharp.Formats.Png;
|
using SixLabors.ImageSharp.Formats.Png;
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
using SixLabors.ImageSharp.PixelFormats;
|
||||||
@ -115,7 +115,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.NandSystem, NcaContentType.Data);
|
string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, NcaContentType.Data);
|
||||||
string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath);
|
string avatarPath = virtualFileSystem.SwitchPathToSystemPath(contentPath);
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(avatarPath))
|
if (!string.IsNullOrWhiteSpace(avatarPath))
|
||||||
|
@ -105,9 +105,9 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
#pragma warning restore CS0649, IDE0044
|
#pragma warning restore CS0649, IDE0044
|
||||||
|
|
||||||
public SettingsWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, HLE.FileSystem.Content.ContentManager contentManager) : this(parent, new Builder("Ryujinx.Ui.Windows.SettingsWindow.glade"), virtualFileSystem, contentManager) { }
|
public SettingsWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(parent, new Builder("Ryujinx.Ui.Windows.SettingsWindow.glade"), virtualFileSystem, contentManager) { }
|
||||||
|
|
||||||
private SettingsWindow(MainWindow parent, Builder builder, VirtualFileSystem virtualFileSystem, HLE.FileSystem.Content.ContentManager contentManager) : base(builder.GetObject("_settingsWin").Handle)
|
private SettingsWindow(MainWindow parent, Builder builder, VirtualFileSystem virtualFileSystem, ContentManager contentManager) : base(builder.GetObject("_settingsWin").Handle)
|
||||||
{
|
{
|
||||||
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Logo_Ryujinx.png");
|
Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Logo_Ryujinx.png");
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using Gtk;
|
using Gtk;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.FileSystem.Content;
|
|
||||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||||
using Ryujinx.Ui.Widgets;
|
using Ryujinx.Ui.Widgets;
|
||||||
using SixLabors.ImageSharp;
|
using SixLabors.ImageSharp;
|
||||||
@ -12,7 +11,7 @@ using System.Reflection;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Image = SixLabors.ImageSharp.Image;
|
using Image = SixLabors.ImageSharp.Image;
|
||||||
using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
|
using UserId = Ryujinx.HLE.HOS.Services.Account.Acc.UserId;
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Windows
|
namespace Ryujinx.Ui.Windows
|
||||||
|
Reference in New Issue
Block a user