Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4905101df1 | ||
|
8750b90a7f | ||
|
af01100050 | ||
|
c0821fee1f | ||
|
a5c2aead67 | ||
|
d41c95dcff | ||
|
fbf2b09706 | ||
|
1fc0f569de | ||
|
dff138229c | ||
|
472119c8da | ||
|
1865ea87e5 | ||
|
18b61aff59 | ||
|
cb22629ac1 | ||
|
6f0f99ee2b | ||
|
70f2da8fdf | ||
|
5d3ef7761b | ||
|
476b4683cf | ||
|
5fb5079730 | ||
|
3fbacd0f49 | ||
|
7aa6abc120 | ||
|
548bfd60a2 | ||
|
65778a6b78 | ||
|
f4e879a1e6 | ||
|
a1ddaa2736 | ||
|
008286b79f | ||
|
a0c77f8d11 | ||
|
ece36b274d | ||
|
f3cc2e5703 | ||
|
5a39d3c4a1 | ||
|
cc51a03af9 | ||
|
567c64e149 | ||
|
36f00985d3 | ||
|
748d87adcc | ||
|
0fd47ff490 | ||
|
f088c3d344 | ||
|
905a191e28 | ||
|
ab0491817e |
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: Bug Report
|
name: Bug Report
|
||||||
about: Something doesn't work correctly in Ryujinx. Note that game-specific issues should be instead posted on the Game Compatibility List at https://github.com/Ryujinx/Ryujinx-Games-List, unless it is a provable regression.
|
about: Something doesn't work correctly in Ryujinx. Game-specific issues should be posted at https://github.com/Ryujinx/Ryujinx-Games-List instead, unless it is a provable regression.
|
||||||
#assignees:
|
#assignees:
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
## Compatibility
|
## Compatibility
|
||||||
|
|
||||||
As of October 2022, Ryujinx has been tested on approximately 3,700 titles; over 3,500 boot past menus and into gameplay, with roughly 3,000 of those being considered playable.
|
As of November 2022, Ryujinx has been tested on approximately 3,800 titles; over 3,600 boot past menus and into gameplay, with roughly 3,200 of those being considered playable.
|
||||||
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
You can check out the compatibility list [here](https://github.com/Ryujinx/Ryujinx-Games-List/issues). Anyone is free to submit a new game test or update an existing game test entry; simply follow the new issue template and testing guidelines, or post as a reply to the applicable game issue. Use the search function to see if a game has been tested already!
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -90,7 +90,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
|
|||||||
|
|
||||||
- **GPU**
|
- **GPU**
|
||||||
|
|
||||||
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum) or Vulkan APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI.
|
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI.
|
||||||
|
|
||||||
- **Input**
|
- **Input**
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTK.OpenAL" Version="4.7.2" />
|
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Audio.Renderer.Dsp.State;
|
using Ryujinx.Audio.Renderer.Dsp.State;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -71,6 +72,19 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
return (short)value;
|
return (short)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static short GetCoefficientAtIndex(ReadOnlySpan<short> coefficients, int index)
|
||||||
|
{
|
||||||
|
if ((uint)index > (uint)coefficients.Length)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.AudioRenderer, $"Out of bound read for coefficient at index {index}");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return coefficients[index];
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static int Decode(Span<short> output, ReadOnlySpan<byte> input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan<short> coefficients, ref AdpcmLoopContext loopContext)
|
public static int Decode(Span<short> output, ReadOnlySpan<byte> input, int startSampleOffset, int endSampleOffset, int offset, int count, ReadOnlySpan<short> coefficients, ref AdpcmLoopContext loopContext)
|
||||||
{
|
{
|
||||||
@@ -84,8 +98,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
byte coefficientIndex = (byte)((predScale >> 4) & 0xF);
|
byte coefficientIndex = (byte)((predScale >> 4) & 0xF);
|
||||||
short history0 = loopContext.History0;
|
short history0 = loopContext.History0;
|
||||||
short history1 = loopContext.History1;
|
short history1 = loopContext.History1;
|
||||||
short coefficient0 = coefficients[coefficientIndex * 2 + 0];
|
short coefficient0 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 0);
|
||||||
short coefficient1 = coefficients[coefficientIndex * 2 + 1];
|
short coefficient1 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 1);
|
||||||
|
|
||||||
int decodedCount = Math.Min(count, endSampleOffset - startSampleOffset - offset);
|
int decodedCount = Math.Min(count, endSampleOffset - startSampleOffset - offset);
|
||||||
int nibbles = GetNibblesFromSampleCount(offset + startSampleOffset);
|
int nibbles = GetNibblesFromSampleCount(offset + startSampleOffset);
|
||||||
@@ -109,8 +123,8 @@ namespace Ryujinx.Audio.Renderer.Dsp
|
|||||||
|
|
||||||
coefficientIndex = (byte)((predScale >> 4) & 0xF);
|
coefficientIndex = (byte)((predScale >> 4) & 0xF);
|
||||||
|
|
||||||
coefficient0 = coefficients[coefficientIndex * 2 + 0];
|
coefficient0 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2);
|
||||||
coefficient1 = coefficients[coefficientIndex * 2 + 1];
|
coefficient1 = GetCoefficientAtIndex(coefficients, coefficientIndex * 2 + 1);
|
||||||
|
|
||||||
nibbles += 2;
|
nibbles += 2;
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.AdpcmDataSourceVersion1;
|
public CommandType CommandType => CommandType.AdpcmDataSourceVersion1;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ushort OutputBufferIndex { get; }
|
public ushort OutputBufferIndex { get; }
|
||||||
public uint SampleRate { get; }
|
public uint SampleRate { get; }
|
||||||
|
@@ -16,7 +16,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.AuxiliaryBuffer;
|
public CommandType CommandType => CommandType.AuxiliaryBuffer;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public uint InputBufferIndex { get; }
|
public uint InputBufferIndex { get; }
|
||||||
public uint OutputBufferIndex { get; }
|
public uint OutputBufferIndex { get; }
|
||||||
|
@@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.BiquadFilter;
|
public CommandType CommandType => CommandType.BiquadFilter;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public Memory<BiquadFilterState> BiquadFilterState { get; }
|
public Memory<BiquadFilterState> BiquadFilterState { get; }
|
||||||
public int InputBufferIndex { get; }
|
public int InputBufferIndex { get; }
|
||||||
|
@@ -16,7 +16,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.CaptureBuffer;
|
public CommandType CommandType => CommandType.CaptureBuffer;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public uint InputBufferIndex { get; }
|
public uint InputBufferIndex { get; }
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.CircularBufferSink;
|
public CommandType CommandType => CommandType.CircularBufferSink;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ushort[] Input { get; }
|
public ushort[] Input { get; }
|
||||||
public uint InputCount { get; }
|
public uint InputCount { get; }
|
||||||
|
@@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.ClearMixBuffer;
|
public CommandType CommandType => CommandType.ClearMixBuffer;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ClearMixBufferCommand(int nodeId)
|
public ClearMixBufferCommand(int nodeId)
|
||||||
{
|
{
|
||||||
|
@@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.CopyMixBuffer;
|
public CommandType CommandType => CommandType.CopyMixBuffer;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ushort InputBufferIndex { get; }
|
public ushort InputBufferIndex { get; }
|
||||||
public ushort OutputBufferIndex { get; }
|
public ushort OutputBufferIndex { get; }
|
||||||
|
@@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType { get; }
|
public CommandType CommandType { get; }
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ushort OutputBufferIndex { get; }
|
public ushort OutputBufferIndex { get; }
|
||||||
public uint SampleRate { get; }
|
public uint SampleRate { get; }
|
||||||
|
@@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.Delay;
|
public CommandType CommandType => CommandType.Delay;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public DelayParameter Parameter => _parameter;
|
public DelayParameter Parameter => _parameter;
|
||||||
public Memory<DelayState> State { get; }
|
public Memory<DelayState> State { get; }
|
||||||
@@ -49,15 +49,15 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
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
|
DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, InputBufferIndices);
|
||||||
// TODO: Update delay processing and remove this to use RemapLegacyChannelEffectMappingToChannelResourceMapping.
|
DataSourceHelper.RemapLegacyChannelEffectMappingToChannelResourceMapping(newEffectChannelMappingSupported, OutputBufferIndices);
|
||||||
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, InputBufferIndices);
|
|
||||||
DataSourceHelper.RemapChannelResourceMappingToLegacy(newEffectChannelMappingSupported, OutputBufferIndices);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[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)
|
||||||
{
|
{
|
||||||
|
const ushort channelCount = 1;
|
||||||
|
|
||||||
float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision);
|
float feedbackGain = FixedPointHelper.ToFloat(Parameter.FeedbackGain, FixedPointPrecision);
|
||||||
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);
|
||||||
@@ -70,7 +70,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
float temp = input * inGain + delayLineValue * feedbackGain;
|
float temp = input * inGain + delayLineValue * feedbackGain;
|
||||||
|
|
||||||
state.UpdateLowPassFilter(ref temp, 1);
|
state.UpdateLowPassFilter(ref temp, channelCount);
|
||||||
|
|
||||||
outputBuffer[i] = (input * dryGain + delayLineValue * outGain) / 64;
|
outputBuffer[i] = (input * dryGain + delayLineValue * outGain) / 64;
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
Y = state.DelayLines[1].Read(),
|
Y = state.DelayLines[1].Read(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector2 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain;
|
Vector2 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain;
|
||||||
|
|
||||||
state.UpdateLowPassFilter(ref Unsafe.As<Vector2, float>(ref temp), channelCount);
|
state.UpdateLowPassFilter(ref Unsafe.As<Vector2, float>(ref temp), channelCount);
|
||||||
|
|
||||||
@@ -148,7 +148,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
W = state.DelayLines[3].Read()
|
W = state.DelayLines[3].Read()
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector4 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain;
|
Vector4 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain;
|
||||||
|
|
||||||
state.UpdateLowPassFilter(ref Unsafe.As<Vector4, float>(ref temp), channelCount);
|
state.UpdateLowPassFilter(ref Unsafe.As<Vector4, float>(ref temp), channelCount);
|
||||||
|
|
||||||
@@ -171,12 +171,12 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
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,
|
Matrix6x6 delayFeedback = new Matrix6x6(delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, 0.0f, delayFeedbackCrossGain, 0.0f,
|
||||||
0.0f, delayFeedbackBaseGain, 0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f,
|
0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain,
|
||||||
delayFeedbackCrossGain, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain, 0.0f, 0.0f,
|
delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f, 0.0f,
|
||||||
0.0f, delayFeedbackCrossGain, delayFeedbackCrossGain, delayFeedbackBaseGain, 0.0f, 0.0f,
|
0.0f, 0.0f, 0.0f, feedbackGain, 0.0f, 0.0f,
|
||||||
delayFeedbackCrossGain, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackBaseGain, 0.0f,
|
delayFeedbackCrossGain, 0.0f, 0.0f, 0.0f, delayFeedbackBaseGain, delayFeedbackCrossGain,
|
||||||
0.0f, 0.0f, 0.0f, 0.0f, 0.0f, feedbackGain);
|
0.0f, delayFeedbackCrossGain, 0.0f, 0.0f, delayFeedbackCrossGain, delayFeedbackBaseGain);
|
||||||
|
|
||||||
for (int i = 0; i < sampleCount; i++)
|
for (int i = 0; i < sampleCount; i++)
|
||||||
{
|
{
|
||||||
@@ -200,7 +200,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
U = state.DelayLines[5].Read()
|
U = state.DelayLines[5].Read()
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector6 temp = MatrixHelper.Transform(ref channelInput, ref delayFeedback) + channelInput * inGain;
|
Vector6 temp = MatrixHelper.Transform(ref delayLineValues, ref delayFeedback) + channelInput * inGain;
|
||||||
|
|
||||||
state.UpdateLowPassFilter(ref Unsafe.As<Vector6, float>(ref temp), channelCount);
|
state.UpdateLowPassFilter(ref Unsafe.As<Vector6, float>(ref temp), channelCount);
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.DepopForMixBuffers;
|
public CommandType CommandType => CommandType.DepopForMixBuffers;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public uint MixBufferOffset { get; }
|
public uint MixBufferOffset { get; }
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.DepopPrepare;
|
public CommandType CommandType => CommandType.DepopPrepare;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public uint MixBufferCount { get; }
|
public uint MixBufferCount { get; }
|
||||||
|
|
||||||
|
@@ -14,7 +14,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.DeviceSink;
|
public CommandType CommandType => CommandType.DeviceSink;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public string DeviceName { get; }
|
public string DeviceName { get; }
|
||||||
|
|
||||||
|
@@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.DownMixSurroundToStereo;
|
public CommandType CommandType => CommandType.DownMixSurroundToStereo;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ushort[] InputBufferIndices { get; }
|
public ushort[] InputBufferIndices { get; }
|
||||||
public ushort[] OutputBufferIndices { get; }
|
public ushort[] OutputBufferIndices { get; }
|
||||||
|
@@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.GroupedBiquadFilter;
|
public CommandType CommandType => CommandType.GroupedBiquadFilter;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
private BiquadFilterParameter[] _parameters;
|
private BiquadFilterParameter[] _parameters;
|
||||||
private Memory<BiquadFilterState> _biquadFilterStates;
|
private Memory<BiquadFilterState> _biquadFilterStates;
|
||||||
@@ -47,9 +47,8 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Nintendo also implements a hot path for double biquad filters, but no generic path when the command definition suggests it could be done.
|
// NOTE: Nintendo only implement single and double biquad filters but no generic path when the command definition suggests it could be done.
|
||||||
// As such we currently only implement a generic path for simplicity.
|
// As such we currently only implement a generic path for simplicity for double biquad.
|
||||||
// TODO: Implement double biquad filters fast path.
|
|
||||||
if (_parameters.Length == 1)
|
if (_parameters.Length == 1)
|
||||||
{
|
{
|
||||||
BiquadFilterHelper.ProcessBiquadFilter(ref _parameters[0], ref states[0], outputBuffer, inputBuffer, context.SampleCount);
|
BiquadFilterHelper.ProcessBiquadFilter(ref _parameters[0], ref states[0], outputBuffer, inputBuffer, context.SampleCount);
|
||||||
|
@@ -8,7 +8,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType { get; }
|
public CommandType CommandType { get; }
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; }
|
public uint EstimatedProcessingTime { get; }
|
||||||
|
|
||||||
public void Process(CommandList context);
|
public void Process(CommandList context);
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.LimiterVersion1;
|
public CommandType CommandType => CommandType.LimiterVersion1;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public LimiterParameter Parameter => _parameter;
|
public LimiterParameter Parameter => _parameter;
|
||||||
public Memory<LimiterState> State { get; }
|
public Memory<LimiterState> State { get; }
|
||||||
|
@@ -15,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.LimiterVersion2;
|
public CommandType CommandType => CommandType.LimiterVersion2;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public LimiterParameter Parameter => _parameter;
|
public LimiterParameter Parameter => _parameter;
|
||||||
public Memory<LimiterState> State { get; }
|
public Memory<LimiterState> State { get; }
|
||||||
|
@@ -15,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.Mix;
|
public CommandType CommandType => CommandType.Mix;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ushort InputBufferIndex { get; }
|
public ushort InputBufferIndex { get; }
|
||||||
public ushort OutputBufferIndex { get; }
|
public ushort OutputBufferIndex { get; }
|
||||||
|
@@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.MixRamp;
|
public CommandType CommandType => CommandType.MixRamp;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ushort InputBufferIndex { get; }
|
public ushort InputBufferIndex { get; }
|
||||||
public ushort OutputBufferIndex { get; }
|
public ushort OutputBufferIndex { get; }
|
||||||
|
@@ -12,7 +12,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.MixRampGrouped;
|
public CommandType CommandType => CommandType.MixRampGrouped;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public uint MixBufferCount { get; }
|
public uint MixBufferCount { get; }
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.PcmFloatDataSourceVersion1;
|
public CommandType CommandType => CommandType.PcmFloatDataSourceVersion1;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ushort OutputBufferIndex { get; }
|
public ushort OutputBufferIndex { get; }
|
||||||
public uint SampleRate { get; }
|
public uint SampleRate { get; }
|
||||||
|
@@ -13,7 +13,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.PcmInt16DataSourceVersion1;
|
public CommandType CommandType => CommandType.PcmInt16DataSourceVersion1;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ushort OutputBufferIndex { get; }
|
public ushort OutputBufferIndex { get; }
|
||||||
public uint SampleRate { get; }
|
public uint SampleRate { get; }
|
||||||
|
@@ -17,7 +17,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.Performance;
|
public CommandType CommandType => CommandType.Performance;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public PerformanceEntryAddresses PerformanceEntryAddresses { get; }
|
public PerformanceEntryAddresses PerformanceEntryAddresses { get; }
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.Reverb3d;
|
public CommandType CommandType => CommandType.Reverb3d;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ushort InputBufferIndex { get; }
|
public ushort InputBufferIndex { get; }
|
||||||
public ushort OutputBufferIndex { get; }
|
public ushort OutputBufferIndex { get; }
|
||||||
|
@@ -34,7 +34,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.Reverb;
|
public CommandType CommandType => CommandType.Reverb;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ReverbParameter Parameter => _parameter;
|
public ReverbParameter Parameter => _parameter;
|
||||||
public Memory<ReverbState> State { get; }
|
public Memory<ReverbState> State { get; }
|
||||||
|
@@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.Upsample;
|
public CommandType CommandType => CommandType.Upsample;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public uint BufferCount { get; }
|
public uint BufferCount { get; }
|
||||||
public uint InputBufferIndex { get; }
|
public uint InputBufferIndex { get; }
|
||||||
|
@@ -15,7 +15,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.Volume;
|
public CommandType CommandType => CommandType.Volume;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ushort InputBufferIndex { get; }
|
public ushort InputBufferIndex { get; }
|
||||||
public ushort OutputBufferIndex { get; }
|
public ushort OutputBufferIndex { get; }
|
||||||
|
@@ -11,7 +11,7 @@ namespace Ryujinx.Audio.Renderer.Dsp.Command
|
|||||||
|
|
||||||
public CommandType CommandType => CommandType.VolumeRamp;
|
public CommandType CommandType => CommandType.VolumeRamp;
|
||||||
|
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
public ushort InputBufferIndex { get; }
|
public ushort InputBufferIndex { get; }
|
||||||
public ushort OutputBufferIndex { get; }
|
public ushort OutputBufferIndex { get; }
|
||||||
|
@@ -28,6 +28,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
private object _lock = new object();
|
private object _lock = new object();
|
||||||
|
|
||||||
|
private AudioRendererRenderingDevice _renderingDevice;
|
||||||
private AudioRendererExecutionMode _executionMode;
|
private AudioRendererExecutionMode _executionMode;
|
||||||
private IWritableEvent _systemEvent;
|
private IWritableEvent _systemEvent;
|
||||||
private ManualResetEvent _terminationEvent;
|
private ManualResetEvent _terminationEvent;
|
||||||
@@ -63,6 +64,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
private uint _renderingTimeLimitPercent;
|
private uint _renderingTimeLimitPercent;
|
||||||
private bool _voiceDropEnabled;
|
private bool _voiceDropEnabled;
|
||||||
private uint _voiceDropCount;
|
private uint _voiceDropCount;
|
||||||
|
private float _voiceDropParameter;
|
||||||
private bool _isDspRunningBehind;
|
private bool _isDspRunningBehind;
|
||||||
|
|
||||||
private ICommandProcessingTimeEstimator _commandProcessingTimeEstimator;
|
private ICommandProcessingTimeEstimator _commandProcessingTimeEstimator;
|
||||||
@@ -95,6 +97,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
_totalElapsedTicksUpdating = 0;
|
_totalElapsedTicksUpdating = 0;
|
||||||
_sessionId = 0;
|
_sessionId = 0;
|
||||||
|
_voiceDropParameter = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultCode Initialize(
|
public ResultCode Initialize(
|
||||||
@@ -130,6 +133,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
_upsamplerCount = parameter.SinkCount + parameter.SubMixBufferCount;
|
_upsamplerCount = parameter.SinkCount + parameter.SubMixBufferCount;
|
||||||
_appletResourceId = appletResourceId;
|
_appletResourceId = appletResourceId;
|
||||||
_memoryPoolCount = parameter.EffectCount + parameter.VoiceCount * Constants.VoiceWaveBufferCount;
|
_memoryPoolCount = parameter.EffectCount + parameter.VoiceCount * Constants.VoiceWaveBufferCount;
|
||||||
|
_renderingDevice = parameter.RenderingDevice;
|
||||||
_executionMode = parameter.ExecutionMode;
|
_executionMode = parameter.ExecutionMode;
|
||||||
_sessionId = sessionId;
|
_sessionId = sessionId;
|
||||||
MemoryManager = memoryManager;
|
MemoryManager = memoryManager;
|
||||||
@@ -337,6 +341,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
|
|
||||||
_processHandle = processHandle;
|
_processHandle = processHandle;
|
||||||
_elapsedFrameCount = 0;
|
_elapsedFrameCount = 0;
|
||||||
|
_voiceDropParameter = 1.0f;
|
||||||
|
|
||||||
switch (_behaviourContext.GetCommandProcessingTimeEstimatorVersion())
|
switch (_behaviourContext.GetCommandProcessingTimeEstimatorVersion())
|
||||||
{
|
{
|
||||||
@@ -515,7 +520,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
return (ulong)(_manager.TickSource.ElapsedSeconds * Constants.TargetTimerFrequency);
|
return (ulong)(_manager.TickSource.ElapsedSeconds * Constants.TargetTimerFrequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
private uint ComputeVoiceDrop(CommandBuffer commandBuffer, long voicesEstimatedTime, long deltaTimeDsp)
|
private uint ComputeVoiceDrop(CommandBuffer commandBuffer, uint voicesEstimatedTime, long deltaTimeDsp)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -584,7 +589,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
command.Enabled = false;
|
command.Enabled = false;
|
||||||
|
|
||||||
voicesEstimatedTime -= (long)command.EstimatedProcessingTime;
|
voicesEstimatedTime -= (uint)(_voiceDropParameter * command.EstimatedProcessingTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -618,13 +623,13 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
_voiceContext.Sort();
|
_voiceContext.Sort();
|
||||||
commandGenerator.GenerateVoices();
|
commandGenerator.GenerateVoices();
|
||||||
|
|
||||||
long voicesEstimatedTime = (long)commandBuffer.EstimatedProcessingTime;
|
uint voicesEstimatedTime = (uint)(_voiceDropParameter * commandBuffer.EstimatedProcessingTime);
|
||||||
|
|
||||||
commandGenerator.GenerateSubMixes();
|
commandGenerator.GenerateSubMixes();
|
||||||
commandGenerator.GenerateFinalMixes();
|
commandGenerator.GenerateFinalMixes();
|
||||||
commandGenerator.GenerateSinks();
|
commandGenerator.GenerateSinks();
|
||||||
|
|
||||||
long totalEstimatedTime = (long)commandBuffer.EstimatedProcessingTime;
|
uint totalEstimatedTime = (uint)(_voiceDropParameter * commandBuffer.EstimatedProcessingTime);
|
||||||
|
|
||||||
if (_voiceDropEnabled)
|
if (_voiceDropEnabled)
|
||||||
{
|
{
|
||||||
@@ -856,5 +861,26 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetVoiceDropParameter(float voiceDropParameter)
|
||||||
|
{
|
||||||
|
_voiceDropParameter = Math.Clamp(voiceDropParameter, 0.0f, 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetVoiceDropParameter()
|
||||||
|
{
|
||||||
|
return _voiceDropParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResultCode ExecuteAudioRendererRendering()
|
||||||
|
{
|
||||||
|
if (_executionMode == AudioRendererExecutionMode.Manual && _renderingDevice == AudioRendererRenderingDevice.Cpu)
|
||||||
|
{
|
||||||
|
// NOTE: Here Nintendo aborts with this error code, we don't want that.
|
||||||
|
return ResultCode.InvalidExecutionContextOperation;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ResultCode.UnsupportedOperation;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -94,8 +94,9 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// REV11:
|
/// REV11:
|
||||||
/// The "legacy" effects (Delay, Reverb and Reverb 3D) were updated to match the standard channel mapping used by the audio renderer.
|
/// 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.
|
/// A new version of the command estimator was added to address timing changes caused by the legacy effects changes.
|
||||||
|
/// A voice drop parameter was added in 15.0.0: This allows an application to amplify or attenuate the estimated time of DSP commands.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>This was added in system update 14.0.0</remarks>
|
/// <remarks>This was added in system update 14.0.0 but some changes were made in 15.0.0</remarks>
|
||||||
public const int Revision11 = 11 << 24;
|
public const int Revision11 = 11 << 24;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -25,7 +25,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The estimated total processing time.
|
/// The estimated total processing time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ulong EstimatedProcessingTime { get; set; }
|
public uint EstimatedProcessingTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The command list that is populated by the <see cref="CommandBuffer"/>.
|
/// The command list that is populated by the <see cref="CommandBuffer"/>.
|
||||||
|
@@ -263,12 +263,12 @@ namespace Ryujinx.Audio.Renderer.Server.MemoryPool
|
|||||||
return UpdateResult.Success;
|
return UpdateResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inParameter.CpuAddress == 0 || (inParameter.CpuAddress & (pageSize - 1)) != 0)
|
if (inParameter.CpuAddress == 0 || (inParameter.CpuAddress % pageSize) != 0)
|
||||||
{
|
{
|
||||||
return UpdateResult.InvalidParameter;
|
return UpdateResult.InvalidParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inParameter.Size == 0 || (inParameter.Size & (pageSize - 1)) != 0)
|
if (inParameter.Size == 0 || (inParameter.Size % pageSize) != 0)
|
||||||
{
|
{
|
||||||
return UpdateResult.InvalidParameter;
|
return UpdateResult.InvalidParameter;
|
||||||
}
|
}
|
||||||
|
@@ -17,5 +17,6 @@ namespace Ryujinx.Audio
|
|||||||
InvalidAddressInfo = (42 << ErrorCodeShift) | ModuleId,
|
InvalidAddressInfo = (42 << ErrorCodeShift) | ModuleId,
|
||||||
InvalidMixSorting = (43 << ErrorCodeShift) | ModuleId,
|
InvalidMixSorting = (43 << ErrorCodeShift) | ModuleId,
|
||||||
UnsupportedOperation = (513 << ErrorCodeShift) | ModuleId,
|
UnsupportedOperation = (513 << ErrorCodeShift) | ModuleId,
|
||||||
|
InvalidExecutionContextOperation = (514 << ErrorCodeShift) | ModuleId,
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -21,6 +21,8 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
Name = $"Ryujinx {Program.Version}";
|
||||||
|
|
||||||
AvaloniaXamlLoader.Load(this);
|
AvaloniaXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -349,7 +349,10 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
|
|
||||||
|
if (_renderingThread.IsAlive)
|
||||||
|
{
|
||||||
_renderingThread.Join();
|
_renderingThread.Join();
|
||||||
|
}
|
||||||
|
|
||||||
DisplaySleep.Restore();
|
DisplaySleep.Restore();
|
||||||
|
|
||||||
@@ -417,7 +420,6 @@ namespace Ryujinx.Ava
|
|||||||
public async Task<bool> LoadGuestApplication()
|
public async Task<bool> LoadGuestApplication()
|
||||||
{
|
{
|
||||||
InitializeSwitchInstance();
|
InitializeSwitchInstance();
|
||||||
|
|
||||||
MainWindow.UpdateGraphicsConfig();
|
MainWindow.UpdateGraphicsConfig();
|
||||||
|
|
||||||
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
||||||
@@ -428,17 +430,16 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
if (userError == UserError.NoFirmware)
|
if (userError == UserError.NoFirmware)
|
||||||
{
|
{
|
||||||
string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"],
|
|
||||||
firmwareVersion.VersionString);
|
|
||||||
|
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
|
||||||
LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"], message,
|
LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"],
|
||||||
LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], "");
|
string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"], firmwareVersion.VersionString),
|
||||||
|
LocaleManager.Instance["InputDialogYes"],
|
||||||
|
LocaleManager.Instance["InputDialogNo"],
|
||||||
|
"");
|
||||||
|
|
||||||
if (result != UserResult.Yes)
|
if (result != UserResult.Yes)
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () => await
|
await UserErrorDialog.ShowUserErrorDialog(userError, _parent);
|
||||||
UserErrorDialog.ShowUserErrorDialog(userError, _parent));
|
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -447,8 +448,7 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
|
if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () => await
|
await UserErrorDialog.ShowUserErrorDialog(userError, _parent);
|
||||||
UserErrorDialog.ShowUserErrorDialog(userError, _parent));
|
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -461,11 +461,9 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
_parent.RefreshFirmwareStatus();
|
_parent.RefreshFirmwareStatus();
|
||||||
|
|
||||||
string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString);
|
|
||||||
|
|
||||||
await ContentDialogHelper.CreateInfoDialog(
|
await ContentDialogHelper.CreateInfoDialog(
|
||||||
string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString),
|
string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString),
|
||||||
message,
|
string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString),
|
||||||
LocaleManager.Instance["InputDialogOk"],
|
LocaleManager.Instance["InputDialogOk"],
|
||||||
"",
|
"",
|
||||||
LocaleManager.Instance["RyujinxInfo"]);
|
LocaleManager.Instance["RyujinxInfo"]);
|
||||||
@@ -473,9 +471,7 @@ namespace Ryujinx.Ava
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () => await
|
await UserErrorDialog.ShowUserErrorDialog(userError, _parent);
|
||||||
UserErrorDialog.ShowUserErrorDialog(userError, _parent));
|
|
||||||
|
|
||||||
Device.Dispose();
|
Device.Dispose();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -514,7 +510,7 @@ namespace Ryujinx.Ava
|
|||||||
}
|
}
|
||||||
else if (File.Exists(ApplicationPath))
|
else if (File.Exists(ApplicationPath))
|
||||||
{
|
{
|
||||||
switch (System.IO.Path.GetExtension(ApplicationPath).ToLowerInvariant())
|
switch (Path.GetExtension(ApplicationPath).ToLowerInvariant())
|
||||||
{
|
{
|
||||||
case ".xci":
|
case ".xci":
|
||||||
{
|
{
|
||||||
|
@@ -119,7 +119,7 @@
|
|||||||
"SettingsTabSystemAudioBackendSDL2": "SDL2",
|
"SettingsTabSystemAudioBackendSDL2": "SDL2",
|
||||||
"SettingsTabSystemHacks": "Hacks",
|
"SettingsTabSystemHacks": "Hacks",
|
||||||
"SettingsTabSystemHacksNote": " (may cause instability)",
|
"SettingsTabSystemHacksNote": " (may cause instability)",
|
||||||
"SettingsTabSystemExpandDramSize": "Expand DRAM Size to 6GiB",
|
"SettingsTabSystemExpandDramSize": "Use alternative memory layout (Developers)",
|
||||||
"SettingsTabSystemIgnoreMissingServices": "Ignore Missing Services",
|
"SettingsTabSystemIgnoreMissingServices": "Ignore Missing Services",
|
||||||
"SettingsTabGraphics": "Graphics",
|
"SettingsTabGraphics": "Graphics",
|
||||||
"SettingsTabGraphicsAPI": "Graphics API",
|
"SettingsTabGraphicsAPI": "Graphics API",
|
||||||
@@ -410,6 +410,8 @@
|
|||||||
"DlcManagerTableHeadingContainerPathLabel": "Container Path",
|
"DlcManagerTableHeadingContainerPathLabel": "Container Path",
|
||||||
"DlcManagerTableHeadingFullPathLabel": "Full Path",
|
"DlcManagerTableHeadingFullPathLabel": "Full Path",
|
||||||
"DlcManagerRemoveAllButton": "Remove All",
|
"DlcManagerRemoveAllButton": "Remove All",
|
||||||
|
"DlcManagerEnableAllButton": "Enable All",
|
||||||
|
"DlcManagerDisableAllButton": "Disable All",
|
||||||
"MenuBarOptionsChangeLanguage": "Change Language",
|
"MenuBarOptionsChangeLanguage": "Change Language",
|
||||||
"CommonSort": "Sort",
|
"CommonSort": "Sort",
|
||||||
"CommonShowNames": "Show Names",
|
"CommonShowNames": "Show Names",
|
||||||
@@ -440,7 +442,7 @@
|
|||||||
"MemoryManagerSoftwareTooltip": "Use a software page table for address translation. Highest accuracy but slowest performance.",
|
"MemoryManagerSoftwareTooltip": "Use a software page table for address translation. Highest accuracy but slowest performance.",
|
||||||
"MemoryManagerHostTooltip": "Directly map memory in the host address space. Much faster JIT compilation and execution.",
|
"MemoryManagerHostTooltip": "Directly map memory in the host address space. Much faster JIT compilation and execution.",
|
||||||
"MemoryManagerUnsafeTooltip": "Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode.",
|
"MemoryManagerUnsafeTooltip": "Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode.",
|
||||||
"DRamTooltip": "Increases the amount of memory on the emulated system from 4GiB to 6GiB.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.",
|
"DRamTooltip": "Utilizes an alternative MemoryMode layout to mimic a Switch development model.\n\nThis is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance.\n\nLeave OFF if unsure.",
|
||||||
"IgnoreMissingServicesTooltip": "Ignores unimplemented Horizon OS services. This may help in bypassing crashes when booting certain games.\n\nLeave OFF if unsure.",
|
"IgnoreMissingServicesTooltip": "Ignores unimplemented Horizon OS services. This may help in bypassing crashes when booting certain games.\n\nLeave OFF if unsure.",
|
||||||
"GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
"GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
||||||
"GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
"GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
|
||||||
@@ -562,12 +564,12 @@
|
|||||||
"Writable": "Writable",
|
"Writable": "Writable",
|
||||||
"SelectDlcDialogTitle": "Select DLC files",
|
"SelectDlcDialogTitle": "Select DLC files",
|
||||||
"SelectUpdateDialogTitle": "Select update files",
|
"SelectUpdateDialogTitle": "Select update files",
|
||||||
"UserProfileWindowTitle": "Manage User Profiles",
|
"UserProfileWindowTitle": "User Profiles Manager",
|
||||||
"CheatWindowTitle": "Manage Game Cheats",
|
"CheatWindowTitle": "Cheats Manager",
|
||||||
"DlcWindowTitle": "Manage Game DLC",
|
"DlcWindowTitle": "Downloadable Content Manager",
|
||||||
"UpdateWindowTitle": "Manage Game Updates",
|
"UpdateWindowTitle": "Title Update Manager",
|
||||||
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
|
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
|
||||||
"DlcWindowHeading": "DLC Available for {0} [{1}]",
|
"DlcWindowHeading": "{0} Downloadable Content(s) available for {1} ({2})",
|
||||||
"UserProfilesEditProfile": "Edit Selected",
|
"UserProfilesEditProfile": "Edit Selected",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
"Save": "Save",
|
"Save": "Save",
|
||||||
@@ -575,7 +577,7 @@
|
|||||||
"UserProfilesSetProfileImage": "Set Profile Image",
|
"UserProfilesSetProfileImage": "Set Profile Image",
|
||||||
"UserProfileEmptyNameError": "Name is required",
|
"UserProfileEmptyNameError": "Name is required",
|
||||||
"UserProfileNoImageError": "Profile image must be set",
|
"UserProfileNoImageError": "Profile image must be set",
|
||||||
"GameUpdateWindowHeading": "Updates Available for {0} [{1}]",
|
"GameUpdateWindowHeading": "{0} Update(s) available for {1} ({2})",
|
||||||
"SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:",
|
"SettingsTabHotkeysResScaleUpHotkey": "Increase resolution:",
|
||||||
"SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:",
|
"SettingsTabHotkeysResScaleDownHotkey": "Decrease resolution:",
|
||||||
"UserProfilesName": "Name:",
|
"UserProfilesName": "Name:",
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Ava.Ui.ViewModels;
|
using Ryujinx.Ava.Ui.ViewModels;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -93,7 +94,7 @@ namespace Ryujinx.Ava.Common.Locale
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var strings = JsonSerializer.Deserialize<Dictionary<string, string>>(languageJson);
|
var strings = JsonHelper.Deserialize<Dictionary<string, string>>(languageJson);
|
||||||
|
|
||||||
foreach (var item in strings)
|
foreach (var item in strings)
|
||||||
{
|
{
|
||||||
|
@@ -28,7 +28,6 @@ namespace Ryujinx.Ava
|
|||||||
public static string Version { get; private set; }
|
public static string Version { get; private set; }
|
||||||
public static string ConfigurationPath { get; private set; }
|
public static string ConfigurationPath { get; private set; }
|
||||||
public static bool PreviewerDetached { get; private set; }
|
public static bool PreviewerDetached { get; private set; }
|
||||||
|
|
||||||
public static RenderTimer RenderTimer { get; private set; }
|
public static RenderTimer RenderTimer { get; private set; }
|
||||||
|
|
||||||
[DllImport("user32.dll", SetLastError = true)]
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
@@ -43,7 +42,7 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134))
|
||||||
{
|
{
|
||||||
MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING);
|
_ = MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nStarting on June 1st 2022, Ryujinx will only support Windows 10 1803 and newer.\n", $"Ryujinx {Version}", MB_ICONWARNING);
|
||||||
}
|
}
|
||||||
|
|
||||||
PreviewerDetached = true;
|
PreviewerDetached = true;
|
||||||
@@ -73,7 +72,7 @@ namespace Ryujinx.Ava
|
|||||||
EnableMultitouch = true,
|
EnableMultitouch = true,
|
||||||
UseWgl = false,
|
UseWgl = false,
|
||||||
AllowEglInitialization = false,
|
AllowEglInitialization = false,
|
||||||
CompositionBackdropCornerRadius = 8f,
|
CompositionBackdropCornerRadius = 8.0f,
|
||||||
})
|
})
|
||||||
.UseSkia()
|
.UseSkia()
|
||||||
.AfterSetup(_ =>
|
.AfterSetup(_ =>
|
||||||
@@ -122,12 +121,10 @@ namespace Ryujinx.Ava
|
|||||||
PrintSystemInfo();
|
PrintSystemInfo();
|
||||||
|
|
||||||
// Enable OGL multithreading on the driver, when available.
|
// Enable OGL multithreading on the driver, when available.
|
||||||
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
|
DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
|
||||||
DriverUtilities.ToggleOGLThreading(threadingMode == BackendThreading.Off);
|
|
||||||
|
|
||||||
// Check if keys exists.
|
// Check if keys exists.
|
||||||
bool hasSystemProdKeys = File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys"));
|
if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
|
||||||
if (!hasSystemProdKeys)
|
|
||||||
{
|
{
|
||||||
if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys"))))
|
if (!(AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys"))))
|
||||||
{
|
{
|
||||||
@@ -197,8 +194,7 @@ namespace Ryujinx.Ava
|
|||||||
Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}");
|
Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}");
|
||||||
SystemInfo.Gather().Print();
|
SystemInfo.Gather().Print();
|
||||||
|
|
||||||
var enabledLogs = Logger.GetEnabledLevels();
|
Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(Logger.GetEnabledLevels().Count == 0 ? "<None>" : string.Join(", ", Logger.GetEnabledLevels()))}");
|
||||||
Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(enabledLogs.Count == 0 ? "<None>" : string.Join(", ", enabledLogs))}");
|
|
||||||
|
|
||||||
if (AppDataManager.Mode == AppDataManager.LaunchMode.Custom)
|
if (AppDataManager.Mode == AppDataManager.LaunchMode.Custom)
|
||||||
{
|
{
|
||||||
|
@@ -18,26 +18,26 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Avalonia" Version="0.10.15" />
|
<PackageReference Include="Avalonia" Version="0.10.18" />
|
||||||
<PackageReference Include="Avalonia.Desktop" Version="0.10.15" />
|
<PackageReference Include="Avalonia.Desktop" Version="0.10.18" />
|
||||||
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.15" />
|
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.18" />
|
||||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.15" />
|
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
|
||||||
<PackageReference Include="Avalonia.Markup.Xaml.Loader" Version="0.10.15" />
|
<PackageReference Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
|
||||||
<PackageReference Include="Avalonia.Svg" Version="0.10.14" />
|
<PackageReference Include="Avalonia.Svg" Version="0.10.18" />
|
||||||
<PackageReference Include="Avalonia.Svg.Skia" Version="0.10.14" />
|
<PackageReference Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
||||||
<PackageReference Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
|
<PackageReference Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
|
||||||
<PackageReference Include="DynamicData" Version="7.9.4" />
|
<PackageReference Include="DynamicData" Version="7.12.8" />
|
||||||
<PackageReference Include="FluentAvaloniaUI" Version="1.4.1" />
|
<PackageReference Include="FluentAvaloniaUI" Version="1.4.5" />
|
||||||
<PackageReference Include="XamlNameReferenceGenerator" Version="1.3.4" />
|
<PackageReference Include="XamlNameReferenceGenerator" Version="1.4.2" />
|
||||||
|
|
||||||
<PackageReference Include="OpenTK.Core" Version="4.7.2" />
|
<PackageReference Include="OpenTK.Core" Version="4.7.5" />
|
||||||
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build10" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64'" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan" Version="2.10.1" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.10.1" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.10.1" />
|
||||||
<PackageReference Include="SPB" Version="0.0.4-build28" />
|
<PackageReference Include="SPB" Version="0.0.4-build28" />
|
||||||
<PackageReference Include="SharpZipLib" Version="1.3.3" />
|
<PackageReference Include="SharpZipLib" Version="1.4.1" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
@@ -168,7 +168,7 @@ namespace Ryujinx.Ava.Ui.Applet
|
|||||||
|
|
||||||
object response = await msgDialog.Run();
|
object response = await msgDialog.Run();
|
||||||
|
|
||||||
if (response != null && buttons.Length > 1 && (int)response != buttons.Length - 1)
|
if (response != null && buttons != null && buttons.Length > 1 && (int)response != buttons.Length - 1)
|
||||||
{
|
{
|
||||||
showDetails = true;
|
showDetails = true;
|
||||||
}
|
}
|
||||||
|
@@ -127,9 +127,16 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
contentDialog.PrimaryButtonClick += deferCloseAction;
|
contentDialog.PrimaryButtonClick += deferCloseAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
|
if (useOverlay)
|
||||||
|
{
|
||||||
|
await contentDialog.ShowAsync(overlay, ContentDialogPlacement.Popup);
|
||||||
|
|
||||||
overlay?.Close();
|
overlay!.Close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await contentDialog.ShowAsync(ContentDialogPlacement.Popup);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useOverlay)
|
if (useOverlay)
|
||||||
|
@@ -6,8 +6,8 @@ using SPB.Graphics;
|
|||||||
using SPB.Platform;
|
using SPB.Platform;
|
||||||
using SPB.Platform.GLX;
|
using SPB.Platform.GLX;
|
||||||
using SPB.Platform.X11;
|
using SPB.Platform.X11;
|
||||||
|
using SPB.Windowing;
|
||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -15,12 +15,12 @@ using static Ryujinx.Ava.Ui.Controls.Win32NativeInterop;
|
|||||||
|
|
||||||
namespace Ryujinx.Ava.Ui.Controls
|
namespace Ryujinx.Ava.Ui.Controls
|
||||||
{
|
{
|
||||||
public unsafe class EmbeddedWindow : NativeControlHost
|
public class EmbeddedWindow : NativeControlHost
|
||||||
{
|
{
|
||||||
private WindowProc _wndProcDelegate;
|
private WindowProc _wndProcDelegate;
|
||||||
private string _className;
|
private string _className;
|
||||||
|
|
||||||
protected GLXWindow X11Window { get; private set; }
|
protected GLXWindow X11Window { get; set; }
|
||||||
protected IntPtr WindowHandle { get; set; }
|
protected IntPtr WindowHandle { get; set; }
|
||||||
protected IntPtr X11Display { get; set; }
|
protected IntPtr X11Display { get; set; }
|
||||||
|
|
||||||
@@ -94,19 +94,17 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("linux")]
|
[SupportedOSPlatform("linux")]
|
||||||
IPlatformHandle CreateLinux(IPlatformHandle parent)
|
protected virtual IPlatformHandle CreateLinux(IPlatformHandle parent)
|
||||||
{
|
{
|
||||||
X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow;
|
X11Window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100) as GLXWindow;
|
||||||
|
|
||||||
WindowHandle = X11Window.WindowHandle.RawHandle;
|
WindowHandle = X11Window.WindowHandle.RawHandle;
|
||||||
|
|
||||||
X11Display = X11Window.DisplayHandle.RawHandle;
|
X11Display = X11Window.DisplayHandle.RawHandle;
|
||||||
|
|
||||||
return new PlatformHandle(WindowHandle, "X11");
|
return new PlatformHandle(WindowHandle, "X11");
|
||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
unsafe IPlatformHandle CreateWin32(IPlatformHandle parent)
|
IPlatformHandle CreateWin32(IPlatformHandle parent)
|
||||||
{
|
{
|
||||||
_className = "NativeWindow-" + Guid.NewGuid();
|
_className = "NativeWindow-" + Guid.NewGuid();
|
||||||
_wndProcDelegate = WndProc;
|
_wndProcDelegate = WndProc;
|
||||||
@@ -142,7 +140,7 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
}
|
}
|
||||||
|
|
||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
internal IntPtr WndProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam)
|
IntPtr WndProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam)
|
||||||
{
|
{
|
||||||
var point = new Point((long)lParam & 0xFFFF, ((long)lParam >> 16) & 0xFFFF);
|
var point = new Point((long)lParam & 0xFFFF, ((long)lParam >> 16) & 0xFFFF);
|
||||||
var root = VisualRoot as Window;
|
var root = VisualRoot as Window;
|
||||||
|
@@ -69,12 +69,12 @@ namespace Ryujinx.Ava.Ui.Controls
|
|||||||
|
|
||||||
public void MakeCurrent()
|
public void MakeCurrent()
|
||||||
{
|
{
|
||||||
Context.MakeCurrent(_window);
|
Context?.MakeCurrent(_window);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MakeCurrent(NativeWindowBase window)
|
public void MakeCurrent(NativeWindowBase window)
|
||||||
{
|
{
|
||||||
Context.MakeCurrent(window);
|
Context?.MakeCurrent(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SwapBuffers()
|
public void SwapBuffers()
|
||||||
|
@@ -1,10 +1,13 @@
|
|||||||
|
using Avalonia.Platform;
|
||||||
using Ryujinx.Ava.Ui.Controls;
|
using Ryujinx.Ava.Ui.Controls;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using SPB.Graphics.Vulkan;
|
using SPB.Graphics.Vulkan;
|
||||||
|
using SPB.Platform.GLX;
|
||||||
using SPB.Platform.Win32;
|
using SPB.Platform.Win32;
|
||||||
using SPB.Platform.X11;
|
using SPB.Platform.X11;
|
||||||
using SPB.Windowing;
|
using SPB.Windowing;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.Ui
|
namespace Ryujinx.Ava.Ui
|
||||||
{
|
{
|
||||||
@@ -12,6 +15,18 @@ namespace Ryujinx.Ava.Ui
|
|||||||
{
|
{
|
||||||
private NativeWindowBase _window;
|
private NativeWindowBase _window;
|
||||||
|
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
protected override IPlatformHandle CreateLinux(IPlatformHandle parent)
|
||||||
|
{
|
||||||
|
X11Window = new GLXWindow(new NativeHandle(X11.DefaultDisplay), new NativeHandle(parent.Handle));
|
||||||
|
WindowHandle = X11Window.WindowHandle.RawHandle;
|
||||||
|
X11Display = X11Window.DisplayHandle.RawHandle;
|
||||||
|
|
||||||
|
X11Window.Hide();
|
||||||
|
|
||||||
|
return new PlatformHandle(WindowHandle, "X11");
|
||||||
|
}
|
||||||
|
|
||||||
public SurfaceKHR CreateSurface(Instance instance)
|
public SurfaceKHR CreateSurface(Instance instance)
|
||||||
{
|
{
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
@@ -20,7 +35,7 @@ namespace Ryujinx.Ava.Ui
|
|||||||
}
|
}
|
||||||
else if (OperatingSystem.IsLinux())
|
else if (OperatingSystem.IsLinux())
|
||||||
{
|
{
|
||||||
_window = X11Window;
|
_window = new SimpleX11Window(new NativeHandle(X11Display), new NativeHandle(WindowHandle));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@@ -1,8 +1,22 @@
|
|||||||
namespace Ryujinx.Ava.Ui.Models
|
using Ryujinx.Ava.Ui.ViewModels;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.Ui.Models
|
||||||
{
|
{
|
||||||
public class DownloadableContentModel
|
public class DownloadableContentModel : BaseModel
|
||||||
{
|
{
|
||||||
public bool Enabled { get; set; }
|
private bool _enabled;
|
||||||
|
|
||||||
|
public bool Enabled
|
||||||
|
{
|
||||||
|
get => _enabled;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_enabled = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string TitleId { get; }
|
public string TitleId { get; }
|
||||||
public string ContainerPath { get; }
|
public string ContainerPath { get; }
|
||||||
public string FullPath { get; }
|
public string FullPath { get; }
|
||||||
|
@@ -8,6 +8,7 @@ using Ryujinx.Ava.Ui.Models;
|
|||||||
using Ryujinx.Ava.Ui.Windows;
|
using Ryujinx.Ava.Ui.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
@@ -189,7 +190,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
amiiboJsonString = File.ReadAllText(_amiiboJsonPath);
|
amiiboJsonString = File.ReadAllText(_amiiboJsonPath);
|
||||||
|
|
||||||
if (await NeedsUpdate(JsonSerializer.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).LastUpdated))
|
if (await NeedsUpdate(JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).LastUpdated))
|
||||||
{
|
{
|
||||||
amiiboJsonString = await DownloadAmiiboJson();
|
amiiboJsonString = await DownloadAmiiboJson();
|
||||||
}
|
}
|
||||||
@@ -206,7 +207,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_amiiboList = JsonSerializer.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).Amiibo;
|
_amiiboList = JsonHelper.Deserialize<Amiibo.AmiiboJson>(amiiboJsonString).Amiibo;
|
||||||
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
_amiiboList = _amiiboList.OrderBy(amiibo => amiibo.AmiiboSeries).ToList();
|
||||||
|
|
||||||
ParseAmiiboData();
|
ParseAmiiboData();
|
||||||
|
@@ -460,8 +460,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
|
using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Configuration, $"{GetShortGamepadName(gamepad.Name)} has been connected with ID: {gamepad.Id}");
|
|
||||||
|
|
||||||
if (gamepad != null)
|
if (gamepad != null)
|
||||||
{
|
{
|
||||||
Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)}"));
|
Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)}"));
|
||||||
@@ -472,8 +470,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
|
using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Configuration, $"{GetShortGamepadName(gamepad.Name)} has been connected with ID: {gamepad.Id}");
|
|
||||||
|
|
||||||
if (gamepad != null)
|
if (gamepad != null)
|
||||||
{
|
{
|
||||||
if (Devices.Any(controller => GetShortGamepadId(controller.Id) == GetShortGamepadId(gamepad.Id)))
|
if (Devices.Any(controller => GetShortGamepadId(controller.Id) == GetShortGamepadId(gamepad.Id)))
|
||||||
|
@@ -19,6 +19,7 @@ using Ryujinx.Common.Configuration;
|
|||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE;
|
using Ryujinx.HLE;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
|
using Ryujinx.HLE.HOS;
|
||||||
using Ryujinx.Modules;
|
using Ryujinx.Modules;
|
||||||
using Ryujinx.Ui.App.Common;
|
using Ryujinx.Ui.App.Common;
|
||||||
using Ryujinx.Ui.Common;
|
using Ryujinx.Ui.Common;
|
||||||
@@ -47,6 +48,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
private string _loadHeading;
|
private string _loadHeading;
|
||||||
private string _cacheLoadStatus;
|
private string _cacheLoadStatus;
|
||||||
private string _searchText;
|
private string _searchText;
|
||||||
|
private Timer _searchTimer;
|
||||||
private string _dockedStatusText;
|
private string _dockedStatusText;
|
||||||
private string _fifoStatusText;
|
private string _fifoStatusText;
|
||||||
private string _gameStatusText;
|
private string _gameStatusText;
|
||||||
@@ -115,10 +117,20 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
_searchText = value;
|
_searchText = value;
|
||||||
|
|
||||||
RefreshView();
|
_searchTimer?.Dispose();
|
||||||
|
|
||||||
|
_searchTimer = new Timer(TimerCallback, null, 1000, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TimerCallback(object obj)
|
||||||
|
{
|
||||||
|
RefreshView();
|
||||||
|
|
||||||
|
_searchTimer.Dispose();
|
||||||
|
_searchTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
public ReadOnlyObservableCollection<ApplicationData> AppsObservableList
|
public ReadOnlyObservableCollection<ApplicationData> AppsObservableList
|
||||||
{
|
{
|
||||||
get => _appsObservableList;
|
get => _appsObservableList;
|
||||||
@@ -207,15 +219,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
switch (Glyph)
|
return Glyph switch
|
||||||
{
|
{
|
||||||
case Glyph.List:
|
Glyph.List => _owner.GameList.SelectedApplication,
|
||||||
return _owner.GameList.SelectedApplication;
|
Glyph.Grid => _owner.GameGrid.SelectedApplication,
|
||||||
case Glyph.Grid:
|
_ => null,
|
||||||
return _owner.GameGrid.SelectedApplication;
|
};
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,6 +417,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
_owner.AppHost.Device.SetVolume(_volume);
|
_owner.AppHost.Device.SetVolume(_volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
OnPropertyChanged(nameof(VolumeStatusText));
|
OnPropertyChanged(nameof(VolumeStatusText));
|
||||||
OnPropertyChanged(nameof(VolumeMuted));
|
OnPropertyChanged(nameof(VolumeMuted));
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
@@ -477,38 +487,36 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
internal void Sort(bool isAscending)
|
internal void Sort(bool isAscending)
|
||||||
{
|
{
|
||||||
IsAscending = isAscending;
|
IsAscending = isAscending;
|
||||||
|
|
||||||
RefreshView();
|
RefreshView();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Sort(ApplicationSort sort)
|
internal void Sort(ApplicationSort sort)
|
||||||
{
|
{
|
||||||
SortMode = sort;
|
SortMode = sort;
|
||||||
|
|
||||||
RefreshView();
|
RefreshView();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IComparer<ApplicationData> GetComparer()
|
private IComparer<ApplicationData> GetComparer()
|
||||||
{
|
{
|
||||||
switch (SortMode)
|
return SortMode switch
|
||||||
{
|
{
|
||||||
case ApplicationSort.LastPlayed:
|
ApplicationSort.LastPlayed => new Models.Generic.LastPlayedSortComparer(IsAscending),
|
||||||
return new Models.Generic.LastPlayedSortComparer(IsAscending);
|
ApplicationSort.FileSize => new Models.Generic.FileSizeSortComparer(IsAscending),
|
||||||
case ApplicationSort.FileSize:
|
ApplicationSort.TotalTimePlayed => new Models.Generic.TimePlayedSortComparer(IsAscending),
|
||||||
return new Models.Generic.FileSizeSortComparer(IsAscending);
|
ApplicationSort.Title => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName)
|
||||||
case ApplicationSort.TotalTimePlayed:
|
: SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName),
|
||||||
return new Models.Generic.TimePlayedSortComparer(IsAscending);
|
ApplicationSort.Favorite => !IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Favorite)
|
||||||
case ApplicationSort.Title:
|
: SortExpressionComparer<ApplicationData>.Descending(app => app.Favorite),
|
||||||
return IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.TitleName) : SortExpressionComparer<ApplicationData>.Descending(app => app.TitleName);
|
ApplicationSort.Developer => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer)
|
||||||
case ApplicationSort.Favorite:
|
: SortExpressionComparer<ApplicationData>.Descending(app => app.Developer),
|
||||||
return !IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Favorite) : SortExpressionComparer<ApplicationData>.Descending(app => app.Favorite);
|
ApplicationSort.FileType => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileExtension)
|
||||||
case ApplicationSort.Developer:
|
: SortExpressionComparer<ApplicationData>.Descending(app => app.FileExtension),
|
||||||
return IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Developer) : SortExpressionComparer<ApplicationData>.Descending(app => app.Developer);
|
ApplicationSort.Path => IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path)
|
||||||
case ApplicationSort.FileType:
|
: SortExpressionComparer<ApplicationData>.Descending(app => app.Path),
|
||||||
return IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.FileExtension) : SortExpressionComparer<ApplicationData>.Descending(app => app.FileExtension);
|
_ => null,
|
||||||
case ApplicationSort.Path:
|
};
|
||||||
return IsAscending ? SortExpressionComparer<ApplicationData>.Ascending(app => app.Path) : SortExpressionComparer<ApplicationData>.Descending(app => app.Path);
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RefreshView()
|
private void RefreshView()
|
||||||
@@ -624,27 +632,18 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
switch (SortMode)
|
return SortMode switch
|
||||||
{
|
{
|
||||||
case ApplicationSort.Title:
|
ApplicationSort.Title => LocaleManager.Instance["GameListHeaderApplication"],
|
||||||
return LocaleManager.Instance["GameListHeaderApplication"];
|
ApplicationSort.Developer => LocaleManager.Instance["GameListHeaderDeveloper"],
|
||||||
case ApplicationSort.Developer:
|
ApplicationSort.LastPlayed => LocaleManager.Instance["GameListHeaderLastPlayed"],
|
||||||
return LocaleManager.Instance["GameListHeaderDeveloper"];
|
ApplicationSort.TotalTimePlayed => LocaleManager.Instance["GameListHeaderTimePlayed"],
|
||||||
case ApplicationSort.LastPlayed:
|
ApplicationSort.FileType => LocaleManager.Instance["GameListHeaderFileExtension"],
|
||||||
return LocaleManager.Instance["GameListHeaderLastPlayed"];
|
ApplicationSort.FileSize => LocaleManager.Instance["GameListHeaderFileSize"],
|
||||||
case ApplicationSort.TotalTimePlayed:
|
ApplicationSort.Path => LocaleManager.Instance["GameListHeaderPath"],
|
||||||
return LocaleManager.Instance["GameListHeaderTimePlayed"];
|
ApplicationSort.Favorite => LocaleManager.Instance["CommonFavorite"],
|
||||||
case ApplicationSort.FileType:
|
_ => string.Empty,
|
||||||
return LocaleManager.Instance["GameListHeaderFileExtension"];
|
};
|
||||||
case ApplicationSort.FileSize:
|
|
||||||
return LocaleManager.Instance["GameListHeaderFileSize"];
|
|
||||||
case ApplicationSort.Path:
|
|
||||||
return LocaleManager.Instance["GameListHeaderPath"];
|
|
||||||
case ApplicationSort.Favorite:
|
|
||||||
return LocaleManager.Instance["CommonFavorite"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,6 +667,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
get => KeyGesture.Parse(_showUikey); set
|
get => KeyGesture.Parse(_showUikey); set
|
||||||
{
|
{
|
||||||
_showUikey = value.ToString();
|
_showUikey = value.ToString();
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -677,6 +677,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
get => KeyGesture.Parse(_screenshotkey); set
|
get => KeyGesture.Parse(_screenshotkey); set
|
||||||
{
|
{
|
||||||
_screenshotkey = value.ToString();
|
_screenshotkey = value.ToString();
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -686,6 +687,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
get => KeyGesture.Parse(_pauseKey); set
|
get => KeyGesture.Parse(_pauseKey); set
|
||||||
{
|
{
|
||||||
_pauseKey = value.ToString();
|
_pauseKey = value.ToString();
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -768,6 +770,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
StatusBarProgressValue = e.NumAppsLoaded;
|
StatusBarProgressValue = e.NumAppsLoaded;
|
||||||
StatusBarProgressMaximum = e.NumAppsFound;
|
StatusBarProgressMaximum = e.NumAppsFound;
|
||||||
|
|
||||||
LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", StatusBarProgressValue, StatusBarProgressMaximum);
|
LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", StatusBarProgressValue, StatusBarProgressMaximum);
|
||||||
|
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
@@ -792,9 +795,11 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
Applications.Clear();
|
Applications.Clear();
|
||||||
|
|
||||||
_owner.LoadProgressBar.IsVisible = true;
|
_owner.LoadProgressBar.IsVisible = true;
|
||||||
StatusBarProgressMaximum = 0;
|
StatusBarProgressMaximum = 0;
|
||||||
StatusBarProgressValue = 0;
|
StatusBarProgressValue = 0;
|
||||||
|
|
||||||
LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", 0, 0);
|
LocaleManager.Instance.UpdateDynamicValue("StatusBarGamesLoaded", 0, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -878,10 +883,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
ShowUiKey = new KeyGesture(showUiKey, KeyModifiers.None);
|
ShowUiKey = new KeyGesture(showUiKey, KeyModifiers.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey))
|
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot, out var screenshotKey))
|
||||||
{
|
{
|
||||||
ScreenshotKey = new KeyGesture(screenshotKey, KeyModifiers.None);
|
ScreenshotKey = new KeyGesture(screenshotKey, KeyModifiers.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey))
|
if (AvaloniaMappingHelper.TryGetAvaKey((Ryujinx.Input.Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause, out var pauseKey))
|
||||||
{
|
{
|
||||||
PauseKey = new KeyGesture(pauseKey, KeyModifiers.None);
|
PauseKey = new KeyGesture(pauseKey, KeyModifiers.None);
|
||||||
@@ -941,9 +948,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
_lastFullscreenToggle = Environment.TickCount64;
|
_lastFullscreenToggle = Environment.TickCount64;
|
||||||
|
|
||||||
WindowState state = _owner.WindowState;
|
if (_owner.WindowState == WindowState.FullScreen)
|
||||||
|
|
||||||
if (state == WindowState.FullScreen)
|
|
||||||
{
|
{
|
||||||
_owner.WindowState = WindowState.Normal;
|
_owner.WindowState = WindowState.Normal;
|
||||||
|
|
||||||
@@ -971,8 +976,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
{
|
{
|
||||||
if (IsGameRunning)
|
if (IsGameRunning)
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.System.EnableDockedMode.Value =
|
ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
|
||||||
!ConfigurationState.Instance.System.EnableDockedMode.Value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -985,6 +989,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
else if (IsGameRunning)
|
else if (IsGameRunning)
|
||||||
{
|
{
|
||||||
await Task.Delay(100);
|
await Task.Delay(100);
|
||||||
|
|
||||||
_owner.AppHost?.ShowExitPrompt();
|
_owner.AppHost?.ShowExitPrompt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -994,6 +999,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
_owner.SettingsWindow = new(_owner.VirtualFileSystem, _owner.ContentManager);
|
_owner.SettingsWindow = new(_owner.VirtualFileSystem, _owner.ContentManager);
|
||||||
|
|
||||||
await _owner.SettingsWindow.ShowDialog(_owner);
|
await _owner.SettingsWindow.ShowDialog(_owner);
|
||||||
|
|
||||||
LoadConfigurableHotKeys();
|
LoadConfigurableHotKeys();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1004,9 +1010,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public async void OpenAboutWindow()
|
public async void OpenAboutWindow()
|
||||||
{
|
{
|
||||||
AboutWindow window = new();
|
await new AboutWindow().ShowDialog(_owner);
|
||||||
|
|
||||||
await window.ShowDialog(_owner);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeLanguage(object obj)
|
public void ChangeLanguage(object obj)
|
||||||
@@ -1065,14 +1069,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public void OpenUserSaveDirectory()
|
public void OpenUserSaveDirectory()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
|
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
||||||
out ulong titleIdNumber))
|
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.Post(async () =>
|
||||||
{
|
{
|
||||||
@@ -1082,8 +1084,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var userId = new LibHac.Fs.UserId((ulong)_owner.AccountManager.LastOpenedUser.UserId.High, (ulong)_owner.AccountManager.LastOpenedUser.UserId.Low);
|
UserId userId = new((ulong)_owner.AccountManager.LastOpenedUser.UserId.High, (ulong)_owner.AccountManager.LastOpenedUser.UserId.Low);
|
||||||
var saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default);
|
SaveDataFilter saveDataFilter = SaveDataFilter.Make(titleIdNumber, saveType: default, userId, saveDataId: default, index: default);
|
||||||
OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber);
|
OpenSaveDirectory(in saveDataFilter, selection, titleIdNumber);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1091,8 +1093,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public void ToggleFavorite()
|
public void ToggleFavorite()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
selection.Favorite = !selection.Favorite;
|
selection.Favorite = !selection.Favorite;
|
||||||
@@ -1108,8 +1109,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public void OpenModsDirectory()
|
public void OpenModsDirectory()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
string modsBasePath = _owner.VirtualFileSystem.ModLoader.GetModsBasePath();
|
string modsBasePath = _owner.VirtualFileSystem.ModLoader.GetModsBasePath();
|
||||||
@@ -1121,7 +1121,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public void OpenSdModsDirectory()
|
public void OpenSdModsDirectory()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
@@ -1134,12 +1134,10 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public void OpenPtcDirectory()
|
public void OpenPtcDirectory()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu");
|
string ptcDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu");
|
||||||
|
|
||||||
string mainPath = Path.Combine(ptcDir, "0");
|
string mainPath = Path.Combine(ptcDir, "0");
|
||||||
string backupPath = Path.Combine(ptcDir, "1");
|
string backupPath = Path.Combine(ptcDir, "1");
|
||||||
|
|
||||||
@@ -1156,8 +1154,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public async void PurgePtcCache()
|
public async void PurgePtcCache()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "0"));
|
DirectoryInfo mainDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "cpu", "0"));
|
||||||
@@ -1165,7 +1162,10 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
|
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"],
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"],
|
||||||
string.Format(LocaleManager.Instance["DialogPPTCDeletionMessage"], selection.TitleName), LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]);
|
string.Format(LocaleManager.Instance["DialogPPTCDeletionMessage"], selection.TitleName),
|
||||||
|
LocaleManager.Instance["InputDialogYes"],
|
||||||
|
LocaleManager.Instance["InputDialogNo"],
|
||||||
|
LocaleManager.Instance["RyujinxConfirm"]);
|
||||||
|
|
||||||
List<FileInfo> cacheFiles = new();
|
List<FileInfo> cacheFiles = new();
|
||||||
|
|
||||||
@@ -1198,8 +1198,7 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public void OpenShaderCacheDirectory()
|
public void OpenShaderCacheDirectory()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader");
|
string shaderCacheDir = Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader");
|
||||||
@@ -1220,18 +1219,20 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public async void PurgeShaderCache()
|
public async void PurgeShaderCache()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader"));
|
DirectoryInfo shaderCacheDir = new(Path.Combine(AppDataManager.GamesDirPath, selection.TitleId, "cache", "shader"));
|
||||||
|
|
||||||
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
|
// FIXME: Found a way to reproduce the bold effect on the title name (fork?).
|
||||||
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"],
|
UserResult result = await ContentDialogHelper.CreateConfirmationDialog(LocaleManager.Instance["DialogWarning"],
|
||||||
string.Format(LocaleManager.Instance["DialogShaderDeletionMessage"], selection.TitleName), LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], LocaleManager.Instance["RyujinxConfirm"]);
|
string.Format(LocaleManager.Instance["DialogShaderDeletionMessage"], selection.TitleName),
|
||||||
|
LocaleManager.Instance["InputDialogYes"],
|
||||||
|
LocaleManager.Instance["InputDialogNo"],
|
||||||
|
LocaleManager.Instance["RyujinxConfirm"]);
|
||||||
|
|
||||||
List<DirectoryInfo> oldCacheDirectories = new List<DirectoryInfo>();
|
List<DirectoryInfo> oldCacheDirectories = new();
|
||||||
List<FileInfo> newCacheFiles = new List<FileInfo>();
|
List<FileInfo> newCacheFiles = new();
|
||||||
|
|
||||||
if (shaderCacheDir.Exists)
|
if (shaderCacheDir.Exists)
|
||||||
{
|
{
|
||||||
@@ -1279,38 +1280,28 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public async void OpenTitleUpdateManager()
|
public async void OpenTitleUpdateManager()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
TitleUpdateWindow titleUpdateManager =
|
await new TitleUpdateWindow(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(_owner);
|
||||||
new(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName);
|
|
||||||
|
|
||||||
await titleUpdateManager.ShowDialog(_owner);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenDownloadableContentManager()
|
public async void OpenDownloadableContentManager()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
DownloadableContentManagerWindow downloadableContentManager = new(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName);
|
await new DownloadableContentManagerWindow(_owner.VirtualFileSystem, ulong.Parse(selection.TitleId, NumberStyles.HexNumber), selection.TitleName).ShowDialog(_owner);
|
||||||
|
|
||||||
await downloadableContentManager.ShowDialog(_owner);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void OpenCheatManager()
|
public async void OpenCheatManager()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
CheatWindow cheatManager = new(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName);
|
await new CheatWindow(_owner.VirtualFileSystem, selection.TitleId, selection.TitleName).ShowDialog(_owner);
|
||||||
|
|
||||||
await cheatManager.ShowDialog(_owner);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1321,13 +1312,10 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var application = _owner.AppHost.Device.Application;
|
ApplicationLoader application = _owner.AppHost.Device.Application;
|
||||||
|
|
||||||
if (application != null)
|
if (application != null)
|
||||||
{
|
{
|
||||||
CheatWindow cheatManager = new(_owner.VirtualFileSystem, application.TitleIdText, application.TitleName);
|
await new CheatWindow(_owner.VirtualFileSystem, application.TitleIdText, application.TitleName).ShowDialog(_owner);
|
||||||
|
|
||||||
await cheatManager.ShowDialog(_owner);
|
|
||||||
|
|
||||||
_owner.AppHost.Device.EnableCheats();
|
_owner.AppHost.Device.EnableCheats();
|
||||||
}
|
}
|
||||||
@@ -1335,14 +1323,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public void OpenDeviceSaveDirectory()
|
public void OpenDeviceSaveDirectory()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
|
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
||||||
out ulong titleIdNumber))
|
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.Post(async () =>
|
||||||
{
|
{
|
||||||
@@ -1360,14 +1346,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
public void OpenBcatSaveDirectory()
|
public void OpenBcatSaveDirectory()
|
||||||
{
|
{
|
||||||
var selection = SelectedApplication;
|
ApplicationData selection = SelectedApplication;
|
||||||
|
|
||||||
if (selection != null)
|
if (selection != null)
|
||||||
{
|
{
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture,
|
if (!ulong.TryParse(selection.TitleId, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out ulong titleIdNumber))
|
||||||
out ulong titleIdNumber))
|
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(async () =>
|
Dispatcher.UIThread.Post(async () =>
|
||||||
{
|
{
|
||||||
@@ -1420,12 +1404,10 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
_owner.Close();
|
_owner.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task HandleFirmwareInstallation(string path)
|
private async Task HandleFirmwareInstallation(string filename)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string filename = path;
|
|
||||||
|
|
||||||
SystemVersion firmwareVersion = _owner.ContentManager.VerifyFirmwarePackage(filename);
|
SystemVersion firmwareVersion = _owner.ContentManager.VerifyFirmwarePackage(filename);
|
||||||
|
|
||||||
if (firmwareVersion == null)
|
if (firmwareVersion == null)
|
||||||
@@ -1437,7 +1419,6 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
|
|
||||||
string dialogTitle = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallTitle"], firmwareVersion.VersionString);
|
string dialogTitle = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallTitle"], firmwareVersion.VersionString);
|
||||||
|
|
||||||
|
|
||||||
SystemVersion currentVersion = _owner.ContentManager.GetCurrentFirmwareVersion();
|
SystemVersion currentVersion = _owner.ContentManager.GetCurrentFirmwareVersion();
|
||||||
|
|
||||||
string dialogMessage = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallMessage"], firmwareVersion.VersionString);
|
string dialogMessage = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallMessage"], firmwareVersion.VersionString);
|
||||||
@@ -1480,11 +1461,12 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallSuccessMessage"], firmwareVersion.VersionString);
|
string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallerFirmwareInstallSuccessMessage"], firmwareVersion.VersionString);
|
||||||
|
|
||||||
await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance["InputDialogOk"], "", LocaleManager.Instance["RyujinxInfo"]);
|
await ContentDialogHelper.CreateInfoDialog(dialogTitle, message, LocaleManager.Instance["InputDialogOk"], "", LocaleManager.Instance["RyujinxInfo"]);
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.Application, message);
|
Logger.Info?.Print(LogClass.Application, message);
|
||||||
|
|
||||||
// Purge Applet Cache.
|
// Purge Applet Cache.
|
||||||
|
|
||||||
DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(System.IO.Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache"));
|
DirectoryInfo miiEditorCacheFolder = new DirectoryInfo(Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache"));
|
||||||
|
|
||||||
if (miiEditorCacheFolder.Exists)
|
if (miiEditorCacheFolder.Exists)
|
||||||
{
|
{
|
||||||
@@ -1514,8 +1496,8 @@ namespace Ryujinx.Ava.Ui.ViewModels
|
|||||||
catch (LibHac.Common.Keys.MissingKeyException ex)
|
catch (LibHac.Common.Keys.MissingKeyException ex)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.Application, ex.ToString());
|
Logger.Error?.Print(LogClass.Application, ex.ToString());
|
||||||
Dispatcher.UIThread.Post(async () => await
|
|
||||||
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, _owner));
|
Dispatcher.UIThread.Post(async () => await UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, _owner));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@@ -3,45 +3,92 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
|
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
|
||||||
|
Width="800"
|
||||||
|
Height="500"
|
||||||
|
MinWidth="800"
|
||||||
|
MinHeight="500"
|
||||||
|
MaxWidth="800"
|
||||||
|
MaxHeight="500"
|
||||||
SizeToContent="Height"
|
SizeToContent="Height"
|
||||||
Width="600" MinHeight="500" Height="500"
|
|
||||||
WindowStartupLocation="CenterOwner"
|
WindowStartupLocation="CenterOwner"
|
||||||
MinWidth="600"
|
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<Grid Name="DownloadableContentGrid" Margin="15">
|
<Grid Name="DownloadableContentGrid" Margin="15">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
<RowDefinition Height="*" />
|
<RowDefinition Height="*" />
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
Name="Heading"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
|
MaxWidth="500"
|
||||||
Margin="20,15,20,20"
|
Margin="20,15,20,20"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
MaxWidth="500"
|
|
||||||
LineHeight="18"
|
LineHeight="18"
|
||||||
TextWrapping="Wrap"
|
TextAlignment="Center"
|
||||||
Text="{Binding Heading}"
|
TextWrapping="Wrap" />
|
||||||
TextAlignment="Center" />
|
<DockPanel
|
||||||
<Border
|
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
|
Margin="0"
|
||||||
|
HorizontalAlignment="Left">
|
||||||
|
<Button
|
||||||
|
Name="EnableAllButton"
|
||||||
|
MinWidth="90"
|
||||||
|
Margin="5"
|
||||||
|
Command="{Binding EnableAll}">
|
||||||
|
<TextBlock Text="{locale:Locale DlcManagerEnableAllButton}" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
Name="DisableAllButton"
|
||||||
|
MinWidth="90"
|
||||||
|
Margin="5"
|
||||||
|
Command="{Binding DisableAll}">
|
||||||
|
<TextBlock Text="{locale:Locale DlcManagerDisableAllButton}" />
|
||||||
|
</Button>
|
||||||
|
</DockPanel>
|
||||||
|
<Border
|
||||||
|
Grid.Row="3"
|
||||||
Margin="5"
|
Margin="5"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
BorderBrush="Gray"
|
BorderBrush="Gray"
|
||||||
BorderThickness="1">
|
BorderThickness="1">
|
||||||
|
<ScrollViewer
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalScrollBarVisibility="Auto"
|
||||||
|
VerticalScrollBarVisibility="Auto">
|
||||||
<DataGrid
|
<DataGrid
|
||||||
|
Name="DlcDataGrid"
|
||||||
MinHeight="200"
|
MinHeight="200"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
|
CanUserReorderColumns="False"
|
||||||
|
CanUserResizeColumns="True"
|
||||||
|
CanUserSortColumns="True"
|
||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
Items="{Binding DownloadableContents}"
|
Items="{Binding _downloadableContents}"
|
||||||
|
SelectionMode="Extended"
|
||||||
VerticalScrollBarVisibility="Auto">
|
VerticalScrollBarVisibility="Auto">
|
||||||
|
<DataGrid.Styles>
|
||||||
|
<Styles>
|
||||||
|
<Style Selector="DataGridCell:nth-child(3), DataGridCell:nth-child(4)">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
||||||
|
<Styles>
|
||||||
|
<Style Selector="DataGridCell:nth-child(1)">
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Right" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Right" />
|
||||||
|
</Style>
|
||||||
|
</Styles>
|
||||||
|
</DataGrid.Styles>
|
||||||
<DataGrid.Columns>
|
<DataGrid.Columns>
|
||||||
<DataGridTemplateColumn Width="90">
|
<DataGridTemplateColumn Width="90">
|
||||||
<DataGridTemplateColumn.CellTemplate>
|
<DataGridTemplateColumn.CellTemplate>
|
||||||
@@ -49,7 +96,7 @@
|
|||||||
<CheckBox
|
<CheckBox
|
||||||
Width="50"
|
Width="50"
|
||||||
MinWidth="40"
|
MinWidth="40"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Center"
|
||||||
IsChecked="{Binding Enabled}" />
|
IsChecked="{Binding Enabled}" />
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</DataGridTemplateColumn.CellTemplate>
|
</DataGridTemplateColumn.CellTemplate>
|
||||||
@@ -57,35 +104,27 @@
|
|||||||
<TextBlock Text="{locale:Locale DlcManagerTableHeadingEnabledLabel}" />
|
<TextBlock Text="{locale:Locale DlcManagerTableHeadingEnabledLabel}" />
|
||||||
</DataGridTemplateColumn.Header>
|
</DataGridTemplateColumn.Header>
|
||||||
</DataGridTemplateColumn>
|
</DataGridTemplateColumn>
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn Width="140" Binding="{Binding TitleId}">
|
||||||
Width="190"
|
|
||||||
Binding="{Binding TitleId}"
|
|
||||||
CanUserResize="True">
|
|
||||||
<DataGridTextColumn.Header>
|
<DataGridTextColumn.Header>
|
||||||
<TextBlock Text="{locale:Locale DlcManagerTableHeadingTitleIdLabel}" />
|
<TextBlock Text="{locale:Locale DlcManagerTableHeadingTitleIdLabel}" />
|
||||||
</DataGridTextColumn.Header>
|
</DataGridTextColumn.Header>
|
||||||
</DataGridTextColumn>
|
</DataGridTextColumn>
|
||||||
<DataGridTextColumn
|
<DataGridTextColumn Width="280" Binding="{Binding FullPath}">
|
||||||
Width="*"
|
|
||||||
Binding="{Binding ContainerPath}"
|
|
||||||
CanUserResize="True">
|
|
||||||
<DataGridTextColumn.Header>
|
|
||||||
<TextBlock Text="{locale:Locale DlcManagerTableHeadingContainerPathLabel}" />
|
|
||||||
</DataGridTextColumn.Header>
|
|
||||||
</DataGridTextColumn>
|
|
||||||
<DataGridTextColumn
|
|
||||||
Width="*"
|
|
||||||
Binding="{Binding FullPath}"
|
|
||||||
CanUserResize="True">
|
|
||||||
<DataGridTextColumn.Header>
|
<DataGridTextColumn.Header>
|
||||||
<TextBlock Text="{locale:Locale DlcManagerTableHeadingFullPathLabel}" />
|
<TextBlock Text="{locale:Locale DlcManagerTableHeadingFullPathLabel}" />
|
||||||
</DataGridTextColumn.Header>
|
</DataGridTextColumn.Header>
|
||||||
</DataGridTextColumn>
|
</DataGridTextColumn>
|
||||||
|
<DataGridTextColumn Binding="{Binding ContainerPath}">
|
||||||
|
<DataGridTextColumn.Header>
|
||||||
|
<TextBlock Text="{locale:Locale DlcManagerTableHeadingContainerPathLabel}" />
|
||||||
|
</DataGridTextColumn.Header>
|
||||||
|
</DataGridTextColumn>
|
||||||
</DataGrid.Columns>
|
</DataGrid.Columns>
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
</ScrollViewer>
|
||||||
</Border>
|
</Border>
|
||||||
<DockPanel
|
<DockPanel
|
||||||
Grid.Row="3"
|
Grid.Row="4"
|
||||||
Margin="0"
|
Margin="0"
|
||||||
HorizontalAlignment="Stretch">
|
HorizontalAlignment="Stretch">
|
||||||
<DockPanel Margin="0" HorizontalAlignment="Left">
|
<DockPanel Margin="0" HorizontalAlignment="Left">
|
||||||
|
@@ -18,6 +18,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reactive.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Path = System.IO.Path;
|
using Path = System.IO.Path;
|
||||||
@@ -29,12 +30,11 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
private readonly List<DownloadableContentContainer> _downloadableContentContainerList;
|
private readonly List<DownloadableContentContainer> _downloadableContentContainerList;
|
||||||
private readonly string _downloadableContentJsonPath;
|
private readonly string _downloadableContentJsonPath;
|
||||||
|
|
||||||
public VirtualFileSystem VirtualFileSystem { get; }
|
private VirtualFileSystem _virtualFileSystem { get; }
|
||||||
public AvaloniaList<DownloadableContentModel> DownloadableContents { get; set; } = new AvaloniaList<DownloadableContentModel>();
|
private AvaloniaList<DownloadableContentModel> _downloadableContents { get; set; }
|
||||||
public ulong TitleId { get; }
|
|
||||||
public string TitleName { get; }
|
|
||||||
|
|
||||||
public string Heading => string.Format(LocaleManager.Instance["DlcWindowHeading"], TitleName, TitleId.ToString("X16"));
|
private ulong _titleId { get; }
|
||||||
|
private string _titleName { get; }
|
||||||
|
|
||||||
public DownloadableContentManagerWindow()
|
public DownloadableContentManagerWindow()
|
||||||
{
|
{
|
||||||
@@ -42,14 +42,16 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"];
|
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {_titleName} ({_titleId:X16})";
|
||||||
}
|
}
|
||||||
|
|
||||||
public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
public DownloadableContentManagerWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
||||||
{
|
{
|
||||||
VirtualFileSystem = virtualFileSystem;
|
_virtualFileSystem = virtualFileSystem;
|
||||||
TitleId = titleId;
|
_downloadableContents = new AvaloniaList<DownloadableContentModel>();
|
||||||
TitleName = titleName;
|
|
||||||
|
_titleId = titleId;
|
||||||
|
_titleName = titleName;
|
||||||
|
|
||||||
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
|
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "dlc.json");
|
||||||
|
|
||||||
@@ -66,9 +68,24 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["DlcWindowTitle"];
|
RemoveButton.IsEnabled = false;
|
||||||
|
|
||||||
|
DlcDataGrid.SelectionChanged += DlcDataGrid_SelectionChanged;
|
||||||
|
|
||||||
|
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["DlcWindowTitle"]} - {_titleName} ({_titleId:X16})";
|
||||||
|
|
||||||
LoadDownloadableContents();
|
LoadDownloadableContents();
|
||||||
|
PrintHeading();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DlcDataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
RemoveButton.IsEnabled = (DlcDataGrid.SelectedItems.Count > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintHeading()
|
||||||
|
{
|
||||||
|
Heading.Text = string.Format(LocaleManager.Instance["DlcWindowHeading"], _downloadableContents.Count, _titleName, _titleId.ToString("X16"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadDownloadableContents()
|
private void LoadDownloadableContents()
|
||||||
@@ -79,20 +96,20 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
{
|
{
|
||||||
using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath);
|
using FileStream containerFile = File.OpenRead(downloadableContentContainer.ContainerPath);
|
||||||
|
|
||||||
PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage());
|
PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage());
|
||||||
|
|
||||||
VirtualFileSystem.ImportTickets(pfs);
|
_virtualFileSystem.ImportTickets(partitionFileSystem);
|
||||||
|
|
||||||
foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
|
foreach (DownloadableContentNca downloadableContentNca in downloadableContentContainer.DownloadableContentNcaList)
|
||||||
{
|
{
|
||||||
using var ncaFile = new UniqueRef<IFile>();
|
using UniqueRef<IFile> ncaFile = new();
|
||||||
|
|
||||||
pfs.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
partitionFileSystem.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
|
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
|
||||||
if (nca != null)
|
if (nca != null)
|
||||||
{
|
{
|
||||||
DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"),
|
_downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"),
|
||||||
downloadableContentContainer.ContainerPath,
|
downloadableContentContainer.ContainerPath,
|
||||||
downloadableContentNca.FullPath,
|
downloadableContentNca.FullPath,
|
||||||
downloadableContentNca.Enabled));
|
downloadableContentNca.Enabled));
|
||||||
@@ -105,11 +122,11 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
Save();
|
Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Nca TryCreateNca(IStorage ncaStorage, string containerPath)
|
private Nca TryOpenNca(IStorage ncaStorage, string containerPath)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new Nca(VirtualFileSystem.KeySet, ncaStorage);
|
return new Nca(_virtualFileSystem.KeySet, ncaStorage);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -124,26 +141,25 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
private async Task AddDownloadableContent(string path)
|
private async Task AddDownloadableContent(string path)
|
||||||
{
|
{
|
||||||
if (!File.Exists(path) || DownloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null)
|
if (!File.Exists(path) || _downloadableContents.FirstOrDefault(x => x.ContainerPath == path) != null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (FileStream containerFile = File.OpenRead(path))
|
using FileStream containerFile = File.OpenRead(path);
|
||||||
{
|
|
||||||
PartitionFileSystem pfs = new PartitionFileSystem(containerFile.AsStorage());
|
PartitionFileSystem partitionFileSystem = new(containerFile.AsStorage());
|
||||||
bool containsDownloadableContent = false;
|
bool containsDownloadableContent = false;
|
||||||
|
|
||||||
VirtualFileSystem.ImportTickets(pfs);
|
_virtualFileSystem.ImportTickets(partitionFileSystem);
|
||||||
|
|
||||||
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
|
foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca"))
|
||||||
{
|
{
|
||||||
using var ncaFile = new UniqueRef<IFile>();
|
using var ncaFile = new UniqueRef<IFile>();
|
||||||
|
|
||||||
pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
partitionFileSystem.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
|
|
||||||
Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), path);
|
|
||||||
|
|
||||||
|
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path);
|
||||||
if (nca == null)
|
if (nca == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -151,12 +167,12 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
if (nca.Header.ContentType == NcaContentType.PublicData)
|
if (nca.Header.ContentType == NcaContentType.PublicData)
|
||||||
{
|
{
|
||||||
if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != TitleId)
|
if ((nca.Header.TitleId & 0xFFFFFFFFFFFFE000) != _titleId)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true));
|
_downloadableContents.Add(new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true));
|
||||||
|
|
||||||
containsDownloadableContent = true;
|
containsDownloadableContent = true;
|
||||||
}
|
}
|
||||||
@@ -167,18 +183,31 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]);
|
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance["DialogDlcNoDlcErrorMessage"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveDownloadableContents(bool removeSelectedOnly = false)
|
private void RemoveDownloadableContents(bool removeSelectedOnly = false)
|
||||||
{
|
{
|
||||||
if (removeSelectedOnly)
|
if (removeSelectedOnly)
|
||||||
{
|
{
|
||||||
DownloadableContents.RemoveAll(DownloadableContents.Where(x => x.Enabled).ToList());
|
AvaloniaList<DownloadableContentModel> removedItems = new();
|
||||||
|
|
||||||
|
foreach (var item in DlcDataGrid.SelectedItems)
|
||||||
|
{
|
||||||
|
removedItems.Add(item as DownloadableContentModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
DlcDataGrid.SelectedItems.Clear();
|
||||||
|
|
||||||
|
foreach (var item in removedItems)
|
||||||
|
{
|
||||||
|
_downloadableContents.RemoveAll(_downloadableContents.Where(x => x.TitleId == item.TitleId).ToList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DownloadableContents.Clear();
|
_downloadableContents.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PrintHeading();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveSelected()
|
public void RemoveSelected()
|
||||||
@@ -191,6 +220,22 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
RemoveDownloadableContents();
|
RemoveDownloadableContents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void EnableAll()
|
||||||
|
{
|
||||||
|
foreach(var item in _downloadableContents)
|
||||||
|
{
|
||||||
|
item.Enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DisableAll()
|
||||||
|
{
|
||||||
|
foreach (var item in _downloadableContents)
|
||||||
|
{
|
||||||
|
item.Enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async void Add()
|
public async void Add()
|
||||||
{
|
{
|
||||||
OpenFileDialog dialog = new OpenFileDialog()
|
OpenFileDialog dialog = new OpenFileDialog()
|
||||||
@@ -214,6 +259,8 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
await AddDownloadableContent(file);
|
await AddDownloadableContent(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PrintHeading();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save()
|
public void Save()
|
||||||
@@ -222,7 +269,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
DownloadableContentContainer container = default;
|
DownloadableContentContainer container = default;
|
||||||
|
|
||||||
foreach (DownloadableContentModel downloadableContent in DownloadableContents)
|
foreach (DownloadableContentModel downloadableContent in _downloadableContents)
|
||||||
{
|
{
|
||||||
if (container.ContainerPath != downloadableContent.ContainerPath)
|
if (container.ContainerPath != downloadableContent.ContainerPath)
|
||||||
{
|
{
|
||||||
|
@@ -90,8 +90,8 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
Title = $"Ryujinx {Program.Version}";
|
Title = $"Ryujinx {Program.Version}";
|
||||||
|
|
||||||
Height = Height / Program.WindowScaleFactor;
|
Height /= Program.WindowScaleFactor;
|
||||||
Width = Width / Program.WindowScaleFactor;
|
Width /= Program.WindowScaleFactor;
|
||||||
|
|
||||||
if (Program.PreviewerDetached)
|
if (Program.PreviewerDetached)
|
||||||
{
|
{
|
||||||
@@ -251,9 +251,12 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this);
|
AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this);
|
||||||
|
|
||||||
if (!AppHost.LoadGuestApplication().Result)
|
Dispatcher.UIThread.Post(async () =>
|
||||||
|
{
|
||||||
|
if (!await AppHost.LoadGuestApplication())
|
||||||
{
|
{
|
||||||
AppHost.DisposeContext();
|
AppHost.DisposeContext();
|
||||||
|
AppHost = null;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -264,11 +267,13 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
SwitchToGameControl(startFullscreen);
|
SwitchToGameControl(startFullscreen);
|
||||||
|
|
||||||
_currentEmulatedGamePath = path;
|
_currentEmulatedGamePath = path;
|
||||||
Thread gameThread = new Thread(InitializeGame)
|
|
||||||
|
Thread gameThread = new(InitializeGame)
|
||||||
{
|
{
|
||||||
Name = "GUI.WindowThread"
|
Name = "GUI.WindowThread"
|
||||||
};
|
};
|
||||||
gameThread.Start();
|
gameThread.Start();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeGame()
|
private void InitializeGame()
|
||||||
@@ -518,10 +523,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
public static void UpdateGraphicsConfig()
|
public static void UpdateGraphicsConfig()
|
||||||
{
|
{
|
||||||
int resScale = ConfigurationState.Instance.Graphics.ResScale;
|
GraphicsConfig.ResScale = ConfigurationState.Instance.Graphics.ResScale == -1 ? ConfigurationState.Instance.Graphics.ResScaleCustom : ConfigurationState.Instance.Graphics.ResScale;
|
||||||
float resScaleCustom = ConfigurationState.Instance.Graphics.ResScaleCustom;
|
|
||||||
|
|
||||||
GraphicsConfig.ResScale = resScale == -1 ? resScaleCustom : resScale;
|
|
||||||
GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy;
|
GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy;
|
||||||
GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
|
GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
|
||||||
GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
|
GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
|
||||||
@@ -546,10 +548,12 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
{
|
{
|
||||||
ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
|
ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
|
||||||
{
|
{
|
||||||
DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed);
|
if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime))
|
||||||
|
{
|
||||||
double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds;
|
double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds;
|
||||||
|
|
||||||
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
|
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -75,7 +75,7 @@
|
|||||||
Spacing="10">
|
Spacing="10">
|
||||||
<ListBox
|
<ListBox
|
||||||
Name="GameList"
|
Name="GameList"
|
||||||
MinHeight="150"
|
MinHeight="250"
|
||||||
Items="{Binding GameDirectories}" />
|
Items="{Binding GameDirectories}" />
|
||||||
<Grid HorizontalAlignment="Stretch">
|
<Grid HorizontalAlignment="Stretch">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
|
@@ -16,7 +16,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone;
|
using TimeZone = Ryujinx.Ava.Ui.Models.TimeZone;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.Ui.Windows
|
namespace Ryujinx.Ava.Ui.Windows
|
||||||
@@ -92,6 +91,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
_currentAssigner.Cancel();
|
_currentAssigner.Cancel();
|
||||||
_currentAssigner = null;
|
_currentAssigner = null;
|
||||||
|
|
||||||
button.IsChecked = false;
|
button.IsChecked = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,36 +122,19 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
{
|
{
|
||||||
if (e.SelectedItem is NavigationViewItem navitem)
|
if (e.SelectedItem is NavigationViewItem navitem)
|
||||||
{
|
{
|
||||||
switch (navitem.Tag.ToString())
|
NavPanel.Content = navitem.Tag.ToString() switch
|
||||||
{
|
{
|
||||||
case "UiPage":
|
"UiPage" => UiPage,
|
||||||
NavPanel.Content = UiPage;
|
"InputPage" => InputPage,
|
||||||
break;
|
"HotkeysPage" => HotkeysPage,
|
||||||
case "InputPage":
|
"SystemPage" => SystemPage,
|
||||||
NavPanel.Content = InputPage;
|
"CpuPage" => CpuPage,
|
||||||
break;
|
"GraphicsPage" => GraphicsPage,
|
||||||
case "HotkeysPage":
|
"AudioPage" => AudioPage,
|
||||||
NavPanel.Content = HotkeysPage;
|
"NetworkPage" => NetworkPage,
|
||||||
break;
|
"LoggingPage" => LoggingPage,
|
||||||
case "SystemPage":
|
_ => throw new NotImplementedException()
|
||||||
NavPanel.Content = SystemPage;
|
};
|
||||||
break;
|
|
||||||
case "CpuPage":
|
|
||||||
NavPanel.Content = CpuPage;
|
|
||||||
break;
|
|
||||||
case "GraphicsPage":
|
|
||||||
NavPanel.Content = GraphicsPage;
|
|
||||||
break;
|
|
||||||
case "AudioPage":
|
|
||||||
NavPanel.Content = AudioPage;
|
|
||||||
break;
|
|
||||||
case "NetworkPage":
|
|
||||||
NavPanel.Content = NetworkPage;
|
|
||||||
break;
|
|
||||||
case "LoggingPage":
|
|
||||||
NavPanel.Content = LoggingPage;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,13 +161,18 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
private void RemoveButton_OnClick(object sender, RoutedEventArgs e)
|
private void RemoveButton_OnClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
List<string> selected = new(GameList.SelectedItems.Cast<string>());
|
int oldIndex = GameList.SelectedIndex;
|
||||||
|
|
||||||
foreach (string path in selected)
|
foreach (string path in new List<string>(GameList.SelectedItems.Cast<string>()))
|
||||||
{
|
{
|
||||||
ViewModel.GameDirectories.Remove(path);
|
ViewModel.GameDirectories.Remove(path);
|
||||||
ViewModel.DirectoryChanged = true;
|
ViewModel.DirectoryChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (GameList.ItemCount > 0)
|
||||||
|
{
|
||||||
|
GameList.SelectedIndex = oldIndex < GameList.ItemCount ? oldIndex : 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
private void TimeZoneBox_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
@@ -214,7 +202,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
private void SaveButton_Clicked(object sender, RoutedEventArgs e)
|
private void SaveButton_Clicked(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
SaveSettings();
|
SaveSettings();
|
||||||
|
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,7 +219,6 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
private void SaveSettings()
|
private void SaveSettings()
|
||||||
{
|
{
|
||||||
ViewModel.SaveSettings();
|
ViewModel.SaveSettings();
|
||||||
|
|
||||||
ControllerSettings?.SaveCurrentProfile();
|
ControllerSettings?.SaveCurrentProfile();
|
||||||
|
|
||||||
if (Owner is MainWindow window && ViewModel.DirectoryChanged)
|
if (Owner is MainWindow window && ViewModel.DirectoryChanged)
|
||||||
@@ -246,8 +232,10 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
protected override void OnClosed(EventArgs e)
|
protected override void OnClosed(EventArgs e)
|
||||||
{
|
{
|
||||||
ControllerSettings.Dispose();
|
ControllerSettings.Dispose();
|
||||||
|
|
||||||
_currentAssigner?.Cancel();
|
_currentAssigner?.Cancel();
|
||||||
_currentAssigner = null;
|
_currentAssigner = null;
|
||||||
|
|
||||||
base.OnClosed(e);
|
base.OnClosed(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,13 +3,17 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
|
xmlns:window="clr-namespace:Ryujinx.Ava.Ui.Windows"
|
||||||
SizeToContent="Height"
|
Width="600"
|
||||||
Width="600" MinHeight="500" Height="500"
|
Height="400"
|
||||||
WindowStartupLocation="CenterOwner"
|
|
||||||
MinWidth="600"
|
MinWidth="600"
|
||||||
|
MinHeight="400"
|
||||||
|
MaxWidth="600"
|
||||||
|
MaxHeight="400"
|
||||||
|
SizeToContent="Height"
|
||||||
|
WindowStartupLocation="CenterOwner"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<Grid Margin="15">
|
<Grid Margin="15">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -19,15 +23,15 @@
|
|||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
Name="Heading"
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
|
MaxWidth="500"
|
||||||
Margin="20,15,20,20"
|
Margin="20,15,20,20"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
MaxWidth="500"
|
|
||||||
LineHeight="18"
|
LineHeight="18"
|
||||||
TextWrapping="Wrap"
|
TextAlignment="Center"
|
||||||
Text="{Binding Heading}"
|
TextWrapping="Wrap" />
|
||||||
TextAlignment="Center" />
|
|
||||||
<Border
|
<Border
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
Margin="5"
|
Margin="5"
|
||||||
@@ -36,8 +40,6 @@
|
|||||||
BorderBrush="Gray"
|
BorderBrush="Gray"
|
||||||
BorderThickness="1">
|
BorderThickness="1">
|
||||||
<ScrollViewer
|
<ScrollViewer
|
||||||
Width="550"
|
|
||||||
MinHeight="200"
|
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
HorizontalScrollBarVisibility="Auto"
|
HorizontalScrollBarVisibility="Auto"
|
||||||
VerticalScrollBarVisibility="Auto">
|
VerticalScrollBarVisibility="Auto">
|
||||||
@@ -45,11 +47,19 @@
|
|||||||
Margin="10"
|
Margin="10"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
Items="{Binding TitleUpdates}">
|
Items="{Binding _titleUpdates}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<RadioButton Padding="8, 0" VerticalContentAlignment="Center" GroupName="Update" IsChecked="{Binding IsEnabled, Mode=TwoWay}">
|
<RadioButton
|
||||||
<Label Margin="0" VerticalAlignment="Center" Content="{Binding Label}" />
|
Padding="8,0"
|
||||||
|
VerticalContentAlignment="Center"
|
||||||
|
GroupName="Update"
|
||||||
|
IsChecked="{Binding IsEnabled, Mode=TwoWay}">
|
||||||
|
<Label
|
||||||
|
Margin="0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Content="{Binding Label}"
|
||||||
|
FontSize="12" />
|
||||||
</RadioButton>
|
</RadioButton>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
|
@@ -30,13 +30,11 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
private readonly string _titleUpdateJsonPath;
|
private readonly string _titleUpdateJsonPath;
|
||||||
private TitleUpdateMetadata _titleUpdateWindowData;
|
private TitleUpdateMetadata _titleUpdateWindowData;
|
||||||
|
|
||||||
public VirtualFileSystem VirtualFileSystem { get; }
|
private VirtualFileSystem _virtualFileSystem { get; }
|
||||||
|
private AvaloniaList<TitleUpdateModel> _titleUpdates { get; set; }
|
||||||
|
|
||||||
internal AvaloniaList<TitleUpdateModel> TitleUpdates { get; set; } = new AvaloniaList<TitleUpdateModel>();
|
private ulong _titleId { get; }
|
||||||
public string TitleId { get; }
|
private string _titleName { get; }
|
||||||
public string TitleName { get; }
|
|
||||||
|
|
||||||
public string Heading => string.Format(LocaleManager.Instance["GameUpdateWindowHeading"], TitleName, TitleId.ToUpper());
|
|
||||||
|
|
||||||
public TitleUpdateWindow()
|
public TitleUpdateWindow()
|
||||||
{
|
{
|
||||||
@@ -44,16 +42,18 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["UpdateWindowTitle"];
|
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["UpdateWindowTitle"]} - {_titleName} ({_titleId:X16})";
|
||||||
}
|
}
|
||||||
|
|
||||||
public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName)
|
public TitleUpdateWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName)
|
||||||
{
|
{
|
||||||
VirtualFileSystem = virtualFileSystem;
|
_virtualFileSystem = virtualFileSystem;
|
||||||
TitleId = titleId;
|
_titleUpdates = new AvaloniaList<TitleUpdateModel>();
|
||||||
TitleName = titleName;
|
|
||||||
|
|
||||||
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId, "updates.json");
|
_titleId = titleId;
|
||||||
|
_titleName = titleName;
|
||||||
|
|
||||||
|
_titleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, titleId.ToString("x16"), "updates.json");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -72,14 +72,20 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance["UpdateWindowTitle"];
|
Title = $"Ryujinx {Program.Version} - {LocaleManager.Instance["UpdateWindowTitle"]} - {_titleName} ({_titleId:X16})";
|
||||||
|
|
||||||
LoadUpdates();
|
LoadUpdates();
|
||||||
|
PrintHeading();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PrintHeading()
|
||||||
|
{
|
||||||
|
Heading.Text = string.Format(LocaleManager.Instance["GameUpdateWindowHeading"], _titleUpdates.Count, _titleName, _titleId.ToString("X16"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadUpdates()
|
private void LoadUpdates()
|
||||||
{
|
{
|
||||||
TitleUpdates.Add(new TitleUpdateModel(default, string.Empty, true));
|
_titleUpdates.Add(new TitleUpdateModel(default, string.Empty, true));
|
||||||
|
|
||||||
foreach (string path in _titleUpdateWindowData.Paths)
|
foreach (string path in _titleUpdateWindowData.Paths)
|
||||||
{
|
{
|
||||||
@@ -88,12 +94,12 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
if (_titleUpdateWindowData.Selected == "")
|
if (_titleUpdateWindowData.Selected == "")
|
||||||
{
|
{
|
||||||
TitleUpdates[0].IsEnabled = true;
|
_titleUpdates[0].IsEnabled = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TitleUpdateModel selected = TitleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected);
|
TitleUpdateModel selected = _titleUpdates.FirstOrDefault(x => x.Path == _titleUpdateWindowData.Selected);
|
||||||
List<TitleUpdateModel> enabled = TitleUpdates.Where(x => x.IsEnabled).ToList();
|
List<TitleUpdateModel> enabled = _titleUpdates.Where(x => x.IsEnabled).ToList();
|
||||||
|
|
||||||
foreach (TitleUpdateModel update in enabled)
|
foreach (TitleUpdateModel update in enabled)
|
||||||
{
|
{
|
||||||
@@ -111,26 +117,31 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
private void AddUpdate(string path)
|
private void AddUpdate(string path)
|
||||||
{
|
{
|
||||||
if (File.Exists(path) && !TitleUpdates.Any(x => x.Path == path))
|
if (File.Exists(path) && !_titleUpdates.Any(x => x.Path == path))
|
||||||
{
|
{
|
||||||
using (FileStream file = new(path, FileMode.Open, FileAccess.Read))
|
using FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||||
{
|
|
||||||
PartitionFileSystem nsp = new PartitionFileSystem(file.AsStorage());
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
(Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(VirtualFileSystem, nsp, TitleId, 0);
|
(Nca patchNca, Nca controlNca) = ApplicationLoader.GetGameUpdateDataFromPartition(_virtualFileSystem, new PartitionFileSystem(file.AsStorage()), _titleId.ToString("x16"), 0);
|
||||||
|
|
||||||
if (controlNca != null && patchNca != null)
|
if (controlNca != null && patchNca != null)
|
||||||
{
|
{
|
||||||
ApplicationControlProperty controlData = new ApplicationControlProperty();
|
ApplicationControlProperty controlData = new();
|
||||||
|
|
||||||
using var nacpFile = new UniqueRef<IFile>();
|
using UniqueRef<IFile> nacpFile = new();
|
||||||
|
|
||||||
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
|
||||||
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
|
||||||
|
|
||||||
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
|
_titleUpdates.Add(new TitleUpdateModel(controlData, path));
|
||||||
|
|
||||||
|
foreach (var update in _titleUpdates)
|
||||||
|
{
|
||||||
|
update.IsEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_titleUpdates.Last().IsEnabled = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -149,22 +160,22 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveUpdates(bool removeSelectedOnly = false)
|
private void RemoveUpdates(bool removeSelectedOnly = false)
|
||||||
{
|
{
|
||||||
if (removeSelectedOnly)
|
if (removeSelectedOnly)
|
||||||
{
|
{
|
||||||
TitleUpdates.RemoveAll(TitleUpdates.Where(x => x.IsEnabled && !x.IsNoUpdate).ToList());
|
_titleUpdates.RemoveAll(_titleUpdates.Where(x => x.IsEnabled && !x.IsNoUpdate).ToList());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TitleUpdates.RemoveAll(TitleUpdates.Where(x => !x.IsNoUpdate).ToList());
|
_titleUpdates.RemoveAll(_titleUpdates.Where(x => !x.IsNoUpdate).ToList());
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleUpdates.FirstOrDefault(x => x.IsNoUpdate).IsEnabled = true;
|
_titleUpdates.FirstOrDefault(x => x.IsNoUpdate).IsEnabled = true;
|
||||||
|
|
||||||
SortUpdates();
|
SortUpdates();
|
||||||
|
PrintHeading();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveSelected()
|
public void RemoveSelected()
|
||||||
@@ -179,7 +190,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
public async void Add()
|
public async void Add()
|
||||||
{
|
{
|
||||||
OpenFileDialog dialog = new OpenFileDialog()
|
OpenFileDialog dialog = new()
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance["SelectUpdateDialogTitle"],
|
Title = LocaleManager.Instance["SelectUpdateDialogTitle"],
|
||||||
AllowMultiple = true
|
AllowMultiple = true
|
||||||
@@ -202,11 +213,12 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
SortUpdates();
|
SortUpdates();
|
||||||
|
PrintHeading();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SortUpdates()
|
private void SortUpdates()
|
||||||
{
|
{
|
||||||
var list = TitleUpdates.ToList();
|
var list = _titleUpdates.ToList();
|
||||||
|
|
||||||
list.Sort((first, second) =>
|
list.Sort((first, second) =>
|
||||||
{
|
{
|
||||||
@@ -222,8 +234,8 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
|
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
|
||||||
});
|
});
|
||||||
|
|
||||||
TitleUpdates.Clear();
|
_titleUpdates.Clear();
|
||||||
TitleUpdates.AddRange(list);
|
_titleUpdates.AddRange(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Save()
|
public void Save()
|
||||||
@@ -232,7 +244,7 @@ namespace Ryujinx.Ava.Ui.Windows
|
|||||||
|
|
||||||
_titleUpdateWindowData.Selected = "";
|
_titleUpdateWindowData.Selected = "";
|
||||||
|
|
||||||
foreach (TitleUpdateModel update in TitleUpdates)
|
foreach (TitleUpdateModel update in _titleUpdates)
|
||||||
{
|
{
|
||||||
_titleUpdateWindowData.Paths.Add(update.Path);
|
_titleUpdateWindowData.Paths.Add(update.Path);
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.Common
|
namespace Ryujinx.Common
|
||||||
@@ -7,49 +8,15 @@ namespace Ryujinx.Common
|
|||||||
public static class BinaryReaderExtensions
|
public static class BinaryReaderExtensions
|
||||||
{
|
{
|
||||||
public unsafe static T ReadStruct<T>(this BinaryReader reader)
|
public unsafe static T ReadStruct<T>(this BinaryReader reader)
|
||||||
where T : struct
|
where T : unmanaged
|
||||||
{
|
{
|
||||||
int size = Marshal.SizeOf<T>();
|
return MemoryMarshal.Cast<byte, T>(reader.ReadBytes(Unsafe.SizeOf<T>()))[0];
|
||||||
|
|
||||||
byte[] data = reader.ReadBytes(size);
|
|
||||||
|
|
||||||
fixed (byte* ptr = data)
|
|
||||||
{
|
|
||||||
return Marshal.PtrToStructure<T>((IntPtr)ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe static T[] ReadStructArray<T>(this BinaryReader reader, int count)
|
|
||||||
where T : struct
|
|
||||||
{
|
|
||||||
int size = Marshal.SizeOf<T>();
|
|
||||||
|
|
||||||
T[] result = new T[count];
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
byte[] data = reader.ReadBytes(size);
|
|
||||||
|
|
||||||
fixed (byte* ptr = data)
|
|
||||||
{
|
|
||||||
result[i] = Marshal.PtrToStructure<T>((IntPtr)ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
|
public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
|
||||||
where T : struct
|
where T : unmanaged
|
||||||
{
|
{
|
||||||
long size = Marshal.SizeOf<T>();
|
ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
|
||||||
|
|
||||||
byte[] data = new byte[size];
|
|
||||||
|
|
||||||
fixed (byte* ptr = data)
|
|
||||||
{
|
|
||||||
Marshal.StructureToPtr<T>(value, (IntPtr)ptr, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Write(data);
|
writer.Write(data);
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
|
<PackageReference Include="System.Drawing.Common" Version="6.0.0" />
|
||||||
<PackageReference Include="System.Management" Version="6.0.0" />
|
<PackageReference Include="System.Management" Version="7.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -180,6 +180,37 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
WriteImpl(va, data);
|
WriteImpl(va, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
if (data.Length == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalMemoryTracking(va, (ulong)data.Length, false);
|
||||||
|
|
||||||
|
if (IsContiguousAndMapped(va, data.Length))
|
||||||
|
{
|
||||||
|
var target = _backingMemory.GetSpan(GetPhysicalAddressInternal(va), data.Length);
|
||||||
|
|
||||||
|
bool changed = !data.SequenceEqual(target);
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
data.CopyTo(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteImpl(va, data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Writes data to CPU mapped memory.
|
/// Writes data to CPU mapped memory.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -307,6 +307,34 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SignalMemoryTracking(va, (ulong)data.Length, false);
|
||||||
|
|
||||||
|
Span<byte> target = _addressSpaceMirror.GetSpan(va, data.Length);
|
||||||
|
bool changed = !data.SequenceEqual(target);
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
data.CopyTo(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
catch (InvalidMemoryRegionException)
|
||||||
|
{
|
||||||
|
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
|
public ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false)
|
||||||
{
|
{
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@@ -23,34 +24,18 @@ namespace Ryujinx.Cpu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe static T Read<T>(IVirtualMemoryManager memory, ulong position) where T : struct
|
public unsafe static T Read<T>(IVirtualMemoryManager memory, ulong position) where T : unmanaged
|
||||||
{
|
{
|
||||||
long size = Marshal.SizeOf<T>();
|
return MemoryMarshal.Cast<byte, T>(memory.GetSpan(position, Unsafe.SizeOf<T>()))[0];
|
||||||
|
|
||||||
byte[] data = new byte[size];
|
|
||||||
|
|
||||||
memory.Read(position, data);
|
|
||||||
|
|
||||||
fixed (byte* ptr = data)
|
|
||||||
{
|
|
||||||
return Marshal.PtrToStructure<T>((IntPtr)ptr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe static ulong Write<T>(IVirtualMemoryManager memory, ulong position, T value) where T : struct
|
public unsafe static ulong Write<T>(IVirtualMemoryManager memory, ulong position, T value) where T : unmanaged
|
||||||
{
|
{
|
||||||
long size = Marshal.SizeOf<T>();
|
ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
|
||||||
|
|
||||||
byte[] data = new byte[size];
|
|
||||||
|
|
||||||
fixed (byte* ptr = data)
|
|
||||||
{
|
|
||||||
Marshal.StructureToPtr<T>(value, (IntPtr)ptr, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
memory.Write(position, data);
|
memory.Write(position, data);
|
||||||
|
|
||||||
return (ulong)size;
|
return (ulong)data.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1)
|
public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1)
|
||||||
|
14
Ryujinx.Graphics.GAL/BufferAssignment.cs
Normal file
14
Ryujinx.Graphics.GAL/BufferAssignment.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public struct BufferAssignment
|
||||||
|
{
|
||||||
|
public readonly int Binding;
|
||||||
|
public readonly BufferRange Range;
|
||||||
|
|
||||||
|
public BufferAssignment(int binding, BufferRange range)
|
||||||
|
{
|
||||||
|
Binding = binding;
|
||||||
|
Range = range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -86,12 +86,12 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
|
|
||||||
void SetStencilTest(StencilTestDescriptor stencilTest);
|
void SetStencilTest(StencilTestDescriptor stencilTest);
|
||||||
|
|
||||||
void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers);
|
void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers);
|
||||||
|
|
||||||
void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler);
|
void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler);
|
||||||
|
|
||||||
void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
|
void SetTransformFeedbackBuffers(ReadOnlySpan<BufferRange> buffers);
|
||||||
void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers);
|
void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers);
|
||||||
|
|
||||||
void SetUserClipDistance(int index, bool enableClip);
|
void SetUserClipDistance(int index, bool enableClip);
|
||||||
|
|
||||||
|
@@ -142,6 +142,30 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
return ranges;
|
return ranges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal Span<BufferAssignment> MapBufferRanges(Span<BufferAssignment> ranges)
|
||||||
|
{
|
||||||
|
// Rewrite the buffer ranges to point to the mapped handles.
|
||||||
|
|
||||||
|
lock (_bufferMap)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ranges.Length; i++)
|
||||||
|
{
|
||||||
|
ref BufferAssignment assignment = ref ranges[i];
|
||||||
|
BufferRange range = assignment.Range;
|
||||||
|
BufferHandle result;
|
||||||
|
|
||||||
|
if (!_bufferMap.TryGetValue(range.Handle, out result))
|
||||||
|
{
|
||||||
|
result = BufferHandle.Null;
|
||||||
|
}
|
||||||
|
|
||||||
|
assignment = new BufferAssignment(ranges[i].Binding, new BufferRange(result, range.Offset, range.Size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ranges;
|
||||||
|
}
|
||||||
|
|
||||||
internal Span<VertexBufferDescriptor> MapBufferRanges(Span<VertexBufferDescriptor> ranges)
|
internal Span<VertexBufferDescriptor> MapBufferRanges(Span<VertexBufferDescriptor> ranges)
|
||||||
{
|
{
|
||||||
// Rewrite the buffer ranges to point to the mapped handles.
|
// Rewrite the buffer ranges to point to the mapped handles.
|
||||||
|
@@ -6,19 +6,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
|||||||
struct SetStorageBuffersCommand : IGALCommand
|
struct SetStorageBuffersCommand : IGALCommand
|
||||||
{
|
{
|
||||||
public CommandType CommandType => CommandType.SetStorageBuffers;
|
public CommandType CommandType => CommandType.SetStorageBuffers;
|
||||||
private int _first;
|
private SpanRef<BufferAssignment> _buffers;
|
||||||
private SpanRef<BufferRange> _buffers;
|
|
||||||
|
|
||||||
public void Set(int first, SpanRef<BufferRange> buffers)
|
public void Set(SpanRef<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
_first = first;
|
|
||||||
_buffers = buffers;
|
_buffers = buffers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Run(ref SetStorageBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref SetStorageBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
Span<BufferRange> buffers = command._buffers.Get(threaded);
|
Span<BufferAssignment> buffers = command._buffers.Get(threaded);
|
||||||
renderer.Pipeline.SetStorageBuffers(command._first, threaded.Buffers.MapBufferRanges(buffers));
|
renderer.Pipeline.SetStorageBuffers(threaded.Buffers.MapBufferRanges(buffers));
|
||||||
command._buffers.Dispose(threaded);
|
command._buffers.Dispose(threaded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,19 +6,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
|||||||
struct SetUniformBuffersCommand : IGALCommand
|
struct SetUniformBuffersCommand : IGALCommand
|
||||||
{
|
{
|
||||||
public CommandType CommandType => CommandType.SetUniformBuffers;
|
public CommandType CommandType => CommandType.SetUniformBuffers;
|
||||||
private int _first;
|
private SpanRef<BufferAssignment> _buffers;
|
||||||
private SpanRef<BufferRange> _buffers;
|
|
||||||
|
|
||||||
public void Set(int first, SpanRef<BufferRange> buffers)
|
public void Set(SpanRef<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
_first = first;
|
|
||||||
_buffers = buffers;
|
_buffers = buffers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Run(ref SetUniformBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
public static void Run(ref SetUniformBuffersCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||||
{
|
{
|
||||||
Span<BufferRange> buffers = command._buffers.Get(threaded);
|
Span<BufferAssignment> buffers = command._buffers.Get(threaded);
|
||||||
renderer.Pipeline.SetUniformBuffers(command._first, threaded.Buffers.MapBufferRanges(buffers));
|
renderer.Pipeline.SetUniformBuffers(threaded.Buffers.MapBufferRanges(buffers));
|
||||||
command._buffers.Dispose(threaded);
|
command._buffers.Dispose(threaded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -275,9 +275,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers)
|
public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
_renderer.New<SetStorageBuffersCommand>().Set(first, _renderer.CopySpan(buffers));
|
_renderer.New<SetStorageBuffersCommand>().Set(_renderer.CopySpan(buffers));
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,9 +293,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers)
|
public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
_renderer.New<SetUniformBuffersCommand>().Set(first, _renderer.CopySpan(buffers));
|
_renderer.New<SetUniformBuffersCommand>().Set(_renderer.CopySpan(buffers));
|
||||||
_renderer.QueueCommand();
|
_renderer.QueueCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -422,7 +422,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
|
|
||||||
// Stop the GPU thread.
|
// Stop the GPU thread.
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|
||||||
|
if (_gpuThread != null && _gpuThread.IsAlive)
|
||||||
|
{
|
||||||
_gpuThread.Join();
|
_gpuThread.Join();
|
||||||
|
}
|
||||||
|
|
||||||
// Dispose the renderer.
|
// Dispose the renderer.
|
||||||
_baseRenderer.Dispose();
|
_baseRenderer.Dispose();
|
||||||
|
@@ -51,16 +51,35 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public uint EntryCount;
|
public uint EntryCount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the entries for the command buffer from memory.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">The memory manager used to fetch the data</param>
|
||||||
|
/// <param name="flush">If true, flushes potential GPU written data before reading the command buffer</param>
|
||||||
|
/// <returns>The fetched data</returns>
|
||||||
|
private ReadOnlySpan<int> GetWords(MemoryManager memoryManager, bool flush)
|
||||||
|
{
|
||||||
|
return MemoryMarshal.Cast<byte, int>(memoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, flush));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prefetch the command buffer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">The memory manager used to fetch the data</param>
|
||||||
|
public void Prefetch(MemoryManager memoryManager)
|
||||||
|
{
|
||||||
|
Words = GetWords(memoryManager, true).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fetch the command buffer.
|
/// Fetch the command buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="memoryManager">The memory manager used to fetch the data</param>
|
||||||
/// <param name="flush">If true, flushes potential GPU written data before reading the command buffer</param>
|
/// <param name="flush">If true, flushes potential GPU written data before reading the command buffer</param>
|
||||||
public void Fetch(MemoryManager memoryManager, bool flush = true)
|
/// <returns>The command buffer words</returns>
|
||||||
|
public ReadOnlySpan<int> Fetch(MemoryManager memoryManager, bool flush)
|
||||||
{
|
{
|
||||||
if (Words == null)
|
return Words ?? GetWords(memoryManager, flush);
|
||||||
{
|
|
||||||
Words = MemoryMarshal.Cast<byte, int>(memoryManager.GetSpan(EntryAddress, (int)EntryCount * 4, flush)).ToArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +177,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||||||
|
|
||||||
if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch)
|
if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch)
|
||||||
{
|
{
|
||||||
commandBuffer.Fetch(processor.MemoryManager);
|
commandBuffer.Prefetch(processor.MemoryManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (commandBuffer.Type == CommandBufferType.NoPrefetch)
|
if (commandBuffer.Type == CommandBufferType.NoPrefetch)
|
||||||
@@ -199,7 +218,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||||||
}
|
}
|
||||||
|
|
||||||
_currentCommandBuffer = entry;
|
_currentCommandBuffer = entry;
|
||||||
_currentCommandBuffer.Fetch(entry.Processor.MemoryManager, flushCommandBuffer);
|
ReadOnlySpan<int> words = entry.Fetch(entry.Processor.MemoryManager, flushCommandBuffer);
|
||||||
|
|
||||||
// If we are changing the current channel,
|
// If we are changing the current channel,
|
||||||
// we need to force all the host state to be updated.
|
// we need to force all the host state to be updated.
|
||||||
@@ -209,7 +228,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
|||||||
entry.Processor.ForceAllDirty();
|
entry.Processor.ForceAllDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
entry.Processor.Process(entry.EntryAddress, _currentCommandBuffer.Words);
|
entry.Processor.Process(entry.EntryAddress, words);
|
||||||
}
|
}
|
||||||
|
|
||||||
_interrupt = false;
|
_interrupt = false;
|
||||||
|
@@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
class ConstantBufferUpdater
|
class ConstantBufferUpdater
|
||||||
{
|
{
|
||||||
|
private const int UniformDataCacheSize = 512;
|
||||||
|
|
||||||
private readonly GpuChannel _channel;
|
private readonly GpuChannel _channel;
|
||||||
private readonly DeviceStateWithShadow<ThreedClassState> _state;
|
private readonly DeviceStateWithShadow<ThreedClassState> _state;
|
||||||
|
|
||||||
@@ -16,6 +18,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
private ulong _ubBeginCpuAddress = 0;
|
private ulong _ubBeginCpuAddress = 0;
|
||||||
private ulong _ubFollowUpAddress = 0;
|
private ulong _ubFollowUpAddress = 0;
|
||||||
private ulong _ubByteCount = 0;
|
private ulong _ubByteCount = 0;
|
||||||
|
private int _ubIndex = 0;
|
||||||
|
private int[] _ubData = new int[UniformDataCacheSize];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new instance of the constant buffer updater.
|
/// Creates a new instance of the constant buffer updater.
|
||||||
@@ -108,9 +112,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
if (_ubFollowUpAddress != 0)
|
if (_ubFollowUpAddress != 0)
|
||||||
{
|
{
|
||||||
var memoryManager = _channel.MemoryManager;
|
var memoryManager = _channel.MemoryManager;
|
||||||
|
|
||||||
|
Span<byte> data = MemoryMarshal.Cast<int, byte>(_ubData.AsSpan(0, (int)(_ubByteCount / 4)));
|
||||||
|
|
||||||
|
if (memoryManager.Physical.WriteWithRedundancyCheck(_ubBeginCpuAddress, data))
|
||||||
|
{
|
||||||
memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
|
memoryManager.Physical.BufferCache.ForceDirty(memoryManager, _ubFollowUpAddress - _ubByteCount, _ubByteCount);
|
||||||
|
}
|
||||||
|
|
||||||
_ubFollowUpAddress = 0;
|
_ubFollowUpAddress = 0;
|
||||||
|
_ubIndex = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +135,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
|
ulong address = uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset;
|
||||||
|
|
||||||
if (_ubFollowUpAddress != address)
|
if (_ubFollowUpAddress != address || _ubIndex == _ubData.Length)
|
||||||
{
|
{
|
||||||
FlushUboDirty();
|
FlushUboDirty();
|
||||||
|
|
||||||
@@ -132,8 +143,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
_ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
|
_ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
var byteData = MemoryMarshal.Cast<int, byte>(MemoryMarshal.CreateSpan(ref argument, 1));
|
_ubData[_ubIndex++] = argument;
|
||||||
_channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
|
|
||||||
|
|
||||||
_ubFollowUpAddress = address + 4;
|
_ubFollowUpAddress = address + 4;
|
||||||
_ubByteCount += 4;
|
_ubByteCount += 4;
|
||||||
@@ -153,7 +163,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
ulong size = (ulong)data.Length * 4;
|
ulong size = (ulong)data.Length * 4;
|
||||||
|
|
||||||
if (_ubFollowUpAddress != address)
|
if (_ubFollowUpAddress != address || _ubIndex + data.Length > _ubData.Length)
|
||||||
{
|
{
|
||||||
FlushUboDirty();
|
FlushUboDirty();
|
||||||
|
|
||||||
@@ -161,8 +171,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
_ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
|
_ubBeginCpuAddress = _channel.MemoryManager.Translate(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
var byteData = MemoryMarshal.Cast<int, byte>(data);
|
data.CopyTo(_ubData.AsSpan(_ubIndex));
|
||||||
_channel.MemoryManager.Physical.WriteUntracked(_ubBeginCpuAddress + _ubByteCount, byteData);
|
_ubIndex += data.Length;
|
||||||
|
|
||||||
_ubFollowUpAddress = address + size;
|
_ubFollowUpAddress = address + size;
|
||||||
_ubByteCount += size;
|
_ubByteCount += size;
|
||||||
|
@@ -372,7 +372,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
float srcX1 = ((float)_state.State.DrawTextureDuDx / (1UL << 32)) * dstWidth + srcX0;
|
float srcX1 = ((float)_state.State.DrawTextureDuDx / (1UL << 32)) * dstWidth + srcX0;
|
||||||
float srcY1 = ((float)_state.State.DrawTextureDvDy / (1UL << 32)) * dstHeight + srcY0;
|
float srcY1 = ((float)_state.State.DrawTextureDvDy / (1UL << 32)) * dstHeight + srcY0;
|
||||||
|
|
||||||
engine.UpdateState();
|
engine.UpdateState(ulong.MaxValue & ~(1UL << StateUpdater.ShaderStateIndex));
|
||||||
|
|
||||||
|
_channel.TextureManager.UpdateRenderTargets();
|
||||||
|
|
||||||
int textureId = _state.State.DrawTextureTextureId;
|
int textureId = _state.State.DrawTextureTextureId;
|
||||||
int samplerId = _state.State.DrawTextureSamplerId;
|
int samplerId = _state.State.DrawTextureSamplerId;
|
||||||
|
@@ -37,6 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
private bool _vsUsesDrawParameters;
|
private bool _vsUsesDrawParameters;
|
||||||
private bool _vtgWritesRtLayer;
|
private bool _vtgWritesRtLayer;
|
||||||
private byte _vsClipDistancesWritten;
|
private byte _vsClipDistancesWritten;
|
||||||
|
private uint _vbEnableMask;
|
||||||
|
|
||||||
private bool _prevDrawIndexed;
|
private bool _prevDrawIndexed;
|
||||||
private bool _prevDrawIndirect;
|
private bool _prevDrawIndirect;
|
||||||
@@ -76,6 +77,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
nameof(ThreedClassState.VertexBufferState),
|
nameof(ThreedClassState.VertexBufferState),
|
||||||
nameof(ThreedClassState.VertexBufferEndAddress)),
|
nameof(ThreedClassState.VertexBufferEndAddress)),
|
||||||
|
|
||||||
|
// Must be done after vertex buffer updates.
|
||||||
new StateUpdateCallbackEntry(UpdateVertexAttribState, nameof(ThreedClassState.VertexAttribState)),
|
new StateUpdateCallbackEntry(UpdateVertexAttribState, nameof(ThreedClassState.VertexAttribState)),
|
||||||
|
|
||||||
new StateUpdateCallbackEntry(UpdateBlendState,
|
new StateUpdateCallbackEntry(UpdateBlendState,
|
||||||
@@ -852,12 +854,23 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateVertexAttribState()
|
private void UpdateVertexAttribState()
|
||||||
{
|
{
|
||||||
|
uint vbEnableMask = _vbEnableMask;
|
||||||
|
|
||||||
Span<VertexAttribDescriptor> vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs];
|
Span<VertexAttribDescriptor> vertexAttribs = stackalloc VertexAttribDescriptor[Constants.TotalVertexAttribs];
|
||||||
|
|
||||||
for (int index = 0; index < Constants.TotalVertexAttribs; index++)
|
for (int index = 0; index < Constants.TotalVertexAttribs; index++)
|
||||||
{
|
{
|
||||||
var vertexAttrib = _state.State.VertexAttribState[index];
|
var vertexAttrib = _state.State.VertexAttribState[index];
|
||||||
|
|
||||||
|
int bufferIndex = vertexAttrib.UnpackBufferIndex();
|
||||||
|
|
||||||
|
if ((vbEnableMask & (1u << bufferIndex)) == 0)
|
||||||
|
{
|
||||||
|
// Using a vertex buffer that doesn't exist is invalid, so let's use a dummy attribute for those cases.
|
||||||
|
vertexAttribs[index] = new VertexAttribDescriptor(0, 0, true, Format.R32G32B32A32Float);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format))
|
if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format))
|
||||||
{
|
{
|
||||||
Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}.");
|
Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}.");
|
||||||
@@ -866,7 +879,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
}
|
}
|
||||||
|
|
||||||
vertexAttribs[index] = new VertexAttribDescriptor(
|
vertexAttribs[index] = new VertexAttribDescriptor(
|
||||||
vertexAttrib.UnpackBufferIndex(),
|
bufferIndex,
|
||||||
vertexAttrib.UnpackOffset(),
|
vertexAttrib.UnpackOffset(),
|
||||||
vertexAttrib.UnpackIsConstant(),
|
vertexAttrib.UnpackIsConstant(),
|
||||||
format);
|
format);
|
||||||
@@ -954,6 +967,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
bool drawIndexed = _drawState.DrawIndexed;
|
bool drawIndexed = _drawState.DrawIndexed;
|
||||||
bool drawIndirect = _drawState.DrawIndirect;
|
bool drawIndirect = _drawState.DrawIndirect;
|
||||||
|
uint vbEnableMask = 0;
|
||||||
|
|
||||||
for (int index = 0; index < Constants.TotalVertexBuffers; index++)
|
for (int index = 0; index < Constants.TotalVertexBuffers; index++)
|
||||||
{
|
{
|
||||||
@@ -971,6 +985,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
|
|
||||||
ulong address = vertexBuffer.Address.Pack();
|
ulong address = vertexBuffer.Address.Pack();
|
||||||
|
|
||||||
|
if (_channel.MemoryManager.IsMapped(address))
|
||||||
|
{
|
||||||
|
vbEnableMask |= 1u << index;
|
||||||
|
}
|
||||||
|
|
||||||
int stride = vertexBuffer.UnpackStride();
|
int stride = vertexBuffer.UnpackStride();
|
||||||
|
|
||||||
bool instanced = _state.State.VertexBufferInstanced[index];
|
bool instanced = _state.State.VertexBufferInstanced[index];
|
||||||
@@ -1017,6 +1036,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
_pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(_channel.MemoryManager.IsMapped(address), stride, divisor);
|
_pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(_channel.MemoryManager.IsMapped(address), stride, divisor);
|
||||||
_channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor);
|
_channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_vbEnableMask != vbEnableMask)
|
||||||
|
{
|
||||||
|
_vbEnableMask = vbEnableMask;
|
||||||
|
UpdateVertexAttribState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -22,6 +22,10 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
private readonly GpuContext _context;
|
private readonly GpuContext _context;
|
||||||
private readonly PhysicalMemory _physicalMemory;
|
private readonly PhysicalMemory _physicalMemory;
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// Only modified from the GPU thread. Must lock for add/remove.
|
||||||
|
/// Must lock for any access from other threads.
|
||||||
|
/// </remarks>
|
||||||
private readonly RangeList<Buffer> _buffers;
|
private readonly RangeList<Buffer> _buffers;
|
||||||
|
|
||||||
private Buffer[] _bufferOverlaps;
|
private Buffer[] _bufferOverlaps;
|
||||||
@@ -200,12 +204,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="size">Size in bytes of the buffer</param>
|
/// <param name="size">Size in bytes of the buffer</param>
|
||||||
private void CreateBufferAligned(ulong address, ulong size)
|
private void CreateBufferAligned(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
int overlapsCount;
|
int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
|
||||||
|
|
||||||
lock (_buffers)
|
|
||||||
{
|
|
||||||
overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overlapsCount != 0)
|
if (overlapsCount != 0)
|
||||||
{
|
{
|
||||||
@@ -409,11 +408,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
Buffer buffer;
|
Buffer buffer;
|
||||||
|
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
{
|
|
||||||
lock (_buffers)
|
|
||||||
{
|
{
|
||||||
buffer = _buffers.FindFirstOverlap(address, size);
|
buffer = _buffers.FindFirstOverlap(address, size);
|
||||||
}
|
|
||||||
|
|
||||||
buffer.SynchronizeMemory(address, size);
|
buffer.SynchronizeMemory(address, size);
|
||||||
|
|
||||||
@@ -423,12 +419,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
lock (_buffers)
|
|
||||||
{
|
{
|
||||||
buffer = _buffers.FindFirstOverlap(address, 1);
|
buffer = _buffers.FindFirstOverlap(address, 1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
@@ -442,12 +435,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
{
|
{
|
||||||
if (size != 0)
|
if (size != 0)
|
||||||
{
|
{
|
||||||
Buffer buffer;
|
Buffer buffer = _buffers.FindFirstOverlap(address, size);
|
||||||
|
|
||||||
lock (_buffers)
|
|
||||||
{
|
|
||||||
buffer = _buffers.FindFirstOverlap(address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.SynchronizeMemory(address, size);
|
buffer.SynchronizeMemory(address, size);
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
private readonly VertexBuffer[] _vertexBuffers;
|
private readonly VertexBuffer[] _vertexBuffers;
|
||||||
private readonly BufferBounds[] _transformFeedbackBuffers;
|
private readonly BufferBounds[] _transformFeedbackBuffers;
|
||||||
private readonly List<BufferTextureBinding> _bufferTextures;
|
private readonly List<BufferTextureBinding> _bufferTextures;
|
||||||
private readonly BufferRange[] _ranges;
|
private readonly BufferAssignment[] _ranges;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Holds shader stage buffer state and binding information.
|
/// Holds shader stage buffer state and binding information.
|
||||||
@@ -134,7 +134,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
|
|
||||||
_bufferTextures = new List<BufferTextureBinding>();
|
_bufferTextures = new List<BufferTextureBinding>();
|
||||||
|
|
||||||
_ranges = new BufferRange[Constants.TotalGpUniformBuffers * Constants.ShaderStages];
|
_ranges = new BufferAssignment[Constants.TotalGpUniformBuffers * Constants.ShaderStages];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -618,10 +618,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage)
|
private void BindBuffers(BufferCache bufferCache, BuffersPerStage[] bindings, bool isStorage)
|
||||||
{
|
{
|
||||||
int rangesFirst = 0;
|
|
||||||
int rangesCount = 0;
|
int rangesCount = 0;
|
||||||
|
|
||||||
Span<BufferRange> ranges = _ranges;
|
Span<BufferAssignment> ranges = _ranges;
|
||||||
|
|
||||||
for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
|
for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
|
||||||
{
|
{
|
||||||
@@ -640,25 +639,14 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
|
? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
|
||||||
: bufferCache.GetBufferRange(bounds.Address, bounds.Size);
|
: bufferCache.GetBufferRange(bounds.Address, bounds.Size);
|
||||||
|
|
||||||
if (rangesCount == 0)
|
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
|
||||||
{
|
|
||||||
rangesFirst = bindingInfo.Binding;
|
|
||||||
}
|
|
||||||
else if (bindingInfo.Binding != rangesFirst + rangesCount)
|
|
||||||
{
|
|
||||||
SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
|
|
||||||
rangesFirst = bindingInfo.Binding;
|
|
||||||
rangesCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ranges[rangesCount++] = range;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rangesCount != 0)
|
if (rangesCount != 0)
|
||||||
{
|
{
|
||||||
SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
|
SetHostBuffers(ranges, rangesCount, isStorage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,10 +659,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage)
|
private void BindBuffers(BufferCache bufferCache, BuffersPerStage buffers, bool isStorage)
|
||||||
{
|
{
|
||||||
int rangesFirst = 0;
|
|
||||||
int rangesCount = 0;
|
int rangesCount = 0;
|
||||||
|
|
||||||
Span<BufferRange> ranges = _ranges;
|
Span<BufferAssignment> ranges = _ranges;
|
||||||
|
|
||||||
for (int index = 0; index < buffers.Count; index++)
|
for (int index = 0; index < buffers.Count; index++)
|
||||||
{
|
{
|
||||||
@@ -689,24 +676,13 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
|
? bufferCache.GetBufferRangeTillEnd(bounds.Address, bounds.Size, isWrite)
|
||||||
: bufferCache.GetBufferRange(bounds.Address, bounds.Size);
|
: bufferCache.GetBufferRange(bounds.Address, bounds.Size);
|
||||||
|
|
||||||
if (rangesCount == 0)
|
ranges[rangesCount++] = new BufferAssignment(bindingInfo.Binding, range);
|
||||||
{
|
|
||||||
rangesFirst = bindingInfo.Binding;
|
|
||||||
}
|
|
||||||
else if (bindingInfo.Binding != rangesFirst + rangesCount)
|
|
||||||
{
|
|
||||||
SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
|
|
||||||
rangesFirst = bindingInfo.Binding;
|
|
||||||
rangesCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ranges[rangesCount++] = range;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rangesCount != 0)
|
if (rangesCount != 0)
|
||||||
{
|
{
|
||||||
SetHostBuffers(ranges, rangesFirst, rangesCount, isStorage);
|
SetHostBuffers(ranges, rangesCount, isStorage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -718,15 +694,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="count">Number of bindings</param>
|
/// <param name="count">Number of bindings</param>
|
||||||
/// <param name="isStorage">Indicates if the buffers are storage or uniform buffers</param>
|
/// <param name="isStorage">Indicates if the buffers are storage or uniform buffers</param>
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void SetHostBuffers(ReadOnlySpan<BufferRange> ranges, int first, int count, bool isStorage)
|
private void SetHostBuffers(ReadOnlySpan<BufferAssignment> ranges, int count, bool isStorage)
|
||||||
{
|
{
|
||||||
if (isStorage)
|
if (isStorage)
|
||||||
{
|
{
|
||||||
_context.Renderer.Pipeline.SetStorageBuffers(first, ranges.Slice(0, count));
|
_context.Renderer.Pipeline.SetStorageBuffers(ranges.Slice(0, count));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_context.Renderer.Pipeline.SetUniformBuffers(first, ranges.Slice(0, count));
|
_context.Renderer.Pipeline.SetUniformBuffers(ranges.Slice(0, count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -242,6 +242,19 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
WriteImpl(range, data, _cpuMemory.WriteUntracked);
|
WriteImpl(range, data, _cpuMemory.WriteUntracked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes data to the application process, returning false if the data was not changed.
|
||||||
|
/// This triggers read memory tracking, as a redundancy check would be useless if the data is not up to date.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The memory manager can return that memory has changed when it hasn't to avoid expensive data copies.</remarks>
|
||||||
|
/// <param name="address">Address to write into</param>
|
||||||
|
/// <param name="data">Data to be written</param>
|
||||||
|
/// <returns>True if the data was changed, false otherwise</returns>
|
||||||
|
public bool WriteWithRedundancyCheck(ulong address, ReadOnlySpan<byte> data)
|
||||||
|
{
|
||||||
|
return _cpuMemory.WriteWithRedundancyCheck(address, data);
|
||||||
|
}
|
||||||
|
|
||||||
private delegate void WriteCallback(ulong address, ReadOnlySpan<byte> data);
|
private delegate void WriteCallback(ulong address, ReadOnlySpan<byte> data);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 3868;
|
private const uint CodeGenVersion = 3943;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@@ -331,7 +331,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
case PrimitiveTopology.QuadStrip:
|
case PrimitiveTopology.QuadStrip:
|
||||||
return PrimitiveType.QuadStrip;
|
return PrimitiveType.QuadStrip;
|
||||||
case PrimitiveTopology.Polygon:
|
case PrimitiveTopology.Polygon:
|
||||||
return PrimitiveType.Polygon;
|
return PrimitiveType.TriangleFan;
|
||||||
case PrimitiveTopology.LinesAdjacency:
|
case PrimitiveTopology.LinesAdjacency:
|
||||||
return PrimitiveType.LinesAdjacency;
|
return PrimitiveType.LinesAdjacency;
|
||||||
case PrimitiveTopology.LineStripAdjacency:
|
case PrimitiveTopology.LineStripAdjacency:
|
||||||
|
@@ -1296,9 +1296,9 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_stencilFrontMask = stencilTest.FrontMask;
|
_stencilFrontMask = stencilTest.FrontMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers)
|
public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
SetBuffers(first, buffers, isStorage: true);
|
SetBuffers(buffers, isStorage: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
|
public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler)
|
||||||
@@ -1366,9 +1366,9 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers)
|
public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
SetBuffers(first, buffers, isStorage: false);
|
SetBuffers(buffers, isStorage: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUserClipDistance(int index, bool enableClip)
|
public void SetUserClipDistance(int index, bool enableClip)
|
||||||
@@ -1460,21 +1460,22 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit);
|
GL.MemoryBarrier(MemoryBarrierFlags.TextureFetchBarrierBit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetBuffers(int first, ReadOnlySpan<BufferRange> buffers, bool isStorage)
|
private void SetBuffers(ReadOnlySpan<BufferAssignment> buffers, bool isStorage)
|
||||||
{
|
{
|
||||||
BufferRangeTarget target = isStorage ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer;
|
BufferRangeTarget target = isStorage ? BufferRangeTarget.ShaderStorageBuffer : BufferRangeTarget.UniformBuffer;
|
||||||
|
|
||||||
for (int index = 0; index < buffers.Length; index++)
|
for (int index = 0; index < buffers.Length; index++)
|
||||||
{
|
{
|
||||||
BufferRange buffer = buffers[index];
|
BufferAssignment assignment = buffers[index];
|
||||||
|
BufferRange buffer = assignment.Range;
|
||||||
|
|
||||||
if (buffer.Handle == BufferHandle.Null)
|
if (buffer.Handle == BufferHandle.Null)
|
||||||
{
|
{
|
||||||
GL.BindBufferRange(target, first + index, 0, IntPtr.Zero, 0);
|
GL.BindBufferRange(target, assignment.Binding, 0, IntPtr.Zero, 0);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
GL.BindBufferRange(target, first + index, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
|
GL.BindBufferRange(target, assignment.Binding, buffer.Handle.ToInt32(), (IntPtr)buffer.Offset, buffer.Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTK.Graphics" Version="4.7.2" />
|
<PackageReference Include="OpenTK.Graphics" Version="4.7.5" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@@ -10,13 +10,9 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
public int Handle { get; private set; }
|
public int Handle { get; private set; }
|
||||||
|
|
||||||
private bool _needsAttribsUpdate;
|
|
||||||
|
|
||||||
private readonly VertexAttribDescriptor[] _vertexAttribs;
|
private readonly VertexAttribDescriptor[] _vertexAttribs;
|
||||||
private readonly VertexBufferDescriptor[] _vertexBuffers;
|
private readonly VertexBufferDescriptor[] _vertexBuffers;
|
||||||
|
|
||||||
private int _vertexAttribsCount;
|
|
||||||
private int _vertexBuffersCount;
|
|
||||||
private int _minVertexCount;
|
private int _minVertexCount;
|
||||||
|
|
||||||
private uint _vertexAttribsInUse;
|
private uint _vertexAttribsInUse;
|
||||||
@@ -76,9 +72,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_vertexBuffers[bindingIndex] = vb;
|
_vertexBuffers[bindingIndex] = vb;
|
||||||
}
|
}
|
||||||
|
|
||||||
_vertexBuffersCount = bindingIndex;
|
|
||||||
_minVertexCount = minVertexCount;
|
_minVertexCount = minVertexCount;
|
||||||
_needsAttribsUpdate = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetVertexAttributes(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
public void SetVertexAttributes(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
||||||
@@ -131,8 +125,6 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_vertexAttribs[index] = attrib;
|
_vertexAttribs[index] = attrib;
|
||||||
}
|
}
|
||||||
|
|
||||||
_vertexAttribsCount = index;
|
|
||||||
|
|
||||||
for (; index < Constants.MaxVertexAttribs; index++)
|
for (; index < Constants.MaxVertexAttribs; index++)
|
||||||
{
|
{
|
||||||
DisableVertexAttrib(index);
|
DisableVertexAttrib(index);
|
||||||
@@ -160,13 +152,11 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
public void PreDraw(int vertexCount)
|
public void PreDraw(int vertexCount)
|
||||||
{
|
{
|
||||||
LimitVertexBuffers(vertexCount);
|
LimitVertexBuffers(vertexCount);
|
||||||
Validate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PreDrawVbUnbounded()
|
public void PreDrawVbUnbounded()
|
||||||
{
|
{
|
||||||
UnlimitVertexBuffers();
|
UnlimitVertexBuffers();
|
||||||
Validate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LimitVertexBuffers(int vertexCount)
|
public void LimitVertexBuffers(int vertexCount)
|
||||||
@@ -252,36 +242,6 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
_vertexBuffersLimited = 0;
|
_vertexBuffersLimited = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Validate()
|
|
||||||
{
|
|
||||||
for (int attribIndex = 0; attribIndex < _vertexAttribsCount; attribIndex++)
|
|
||||||
{
|
|
||||||
VertexAttribDescriptor attrib = _vertexAttribs[attribIndex];
|
|
||||||
|
|
||||||
if (!attrib.IsZero)
|
|
||||||
{
|
|
||||||
if ((uint)attrib.BufferIndex >= _vertexBuffersCount)
|
|
||||||
{
|
|
||||||
DisableVertexAttrib(attribIndex);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_vertexBuffers[attrib.BufferIndex].Buffer.Handle == BufferHandle.Null)
|
|
||||||
{
|
|
||||||
DisableVertexAttrib(attribIndex);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_needsAttribsUpdate)
|
|
||||||
{
|
|
||||||
EnableVertexAttrib(attribIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_needsAttribsUpdate = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private void EnableVertexAttrib(int index)
|
private void EnableVertexAttrib(int index)
|
||||||
{
|
{
|
||||||
|
@@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
private const int TextureCount = 3;
|
private const int TextureCount = 3;
|
||||||
private readonly OpenGLRenderer _renderer;
|
private readonly OpenGLRenderer _renderer;
|
||||||
|
|
||||||
|
private bool _initialized;
|
||||||
|
|
||||||
private int _width;
|
private int _width;
|
||||||
private int _height;
|
private int _height;
|
||||||
private int _copyFramebufferHandle;
|
private int _copyFramebufferHandle;
|
||||||
@@ -179,6 +181,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
public void InitializeBackgroundContext(IOpenGLContext baseContext)
|
public void InitializeBackgroundContext(IOpenGLContext baseContext)
|
||||||
{
|
{
|
||||||
BackgroundContext = new BackgroundContextWorker(baseContext);
|
BackgroundContext = new BackgroundContextWorker(baseContext);
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CaptureFrame(int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
|
public void CaptureFrame(int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
|
||||||
@@ -193,6 +196,11 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BackgroundContext.Dispose();
|
BackgroundContext.Dispose();
|
||||||
|
|
||||||
if (_copyFramebufferHandle != 0)
|
if (_copyFramebufferHandle != 0)
|
||||||
|
@@ -234,7 +234,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var source = operation.GetSource(0);
|
var source = operation.GetSource(0);
|
||||||
|
|
||||||
var uvec4Type = context.TypeVector(context.TypeU32(), 4);
|
var uvec4Type = context.TypeVector(context.TypeU32(), 4);
|
||||||
var execution = context.Constant(context.TypeU32(), 3); // Subgroup
|
var execution = context.Constant(context.TypeU32(), Scope.Subgroup);
|
||||||
|
|
||||||
var maskVector = context.GroupNonUniformBallot(uvec4Type, execution, context.Get(AggregateType.Bool, source));
|
var maskVector = context.GroupNonUniformBallot(uvec4Type, execution, context.Get(AggregateType.Bool, source));
|
||||||
var mask = context.CompositeExtract(context.TypeU32(), maskVector, (SpvLiteralInteger)0);
|
var mask = context.CompositeExtract(context.TypeU32(), maskVector, (SpvLiteralInteger)0);
|
||||||
@@ -1233,7 +1233,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
||||||
var srcThreadId = context.BitwiseOr(context.TypeU32(), indexNotSegMask, minThreadId);
|
var srcThreadId = context.BitwiseOr(context.TypeU32(), indexNotSegMask, minThreadId);
|
||||||
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
||||||
var value = context.SubgroupReadInvocationKHR(context.TypeFP32(), x, srcThreadId);
|
var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId);
|
||||||
var result = context.Select(context.TypeFP32(), valid, value, x);
|
var result = context.Select(context.TypeFP32(), valid, value, x);
|
||||||
|
|
||||||
var validLocal = (AstOperand)operation.GetSource(3);
|
var validLocal = (AstOperand)operation.GetSource(3);
|
||||||
@@ -1263,7 +1263,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
||||||
var srcThreadId = context.IAdd(context.TypeU32(), threadId, index);
|
var srcThreadId = context.IAdd(context.TypeU32(), threadId, index);
|
||||||
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
||||||
var value = context.SubgroupReadInvocationKHR(context.TypeFP32(), x, srcThreadId);
|
var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId);
|
||||||
var result = context.Select(context.TypeFP32(), valid, value, x);
|
var result = context.Select(context.TypeFP32(), valid, value, x);
|
||||||
|
|
||||||
var validLocal = (AstOperand)operation.GetSource(3);
|
var validLocal = (AstOperand)operation.GetSource(3);
|
||||||
@@ -1289,7 +1289,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
var minThreadId = context.BitwiseAnd(context.TypeU32(), threadId, segMask);
|
||||||
var srcThreadId = context.ISub(context.TypeU32(), threadId, index);
|
var srcThreadId = context.ISub(context.TypeU32(), threadId, index);
|
||||||
var valid = context.SGreaterThanEqual(context.TypeBool(), srcThreadId, minThreadId);
|
var valid = context.SGreaterThanEqual(context.TypeBool(), srcThreadId, minThreadId);
|
||||||
var value = context.SubgroupReadInvocationKHR(context.TypeFP32(), x, srcThreadId);
|
var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId);
|
||||||
var result = context.Select(context.TypeFP32(), valid, value, x);
|
var result = context.Select(context.TypeFP32(), valid, value, x);
|
||||||
|
|
||||||
var validLocal = (AstOperand)operation.GetSource(3);
|
var validLocal = (AstOperand)operation.GetSource(3);
|
||||||
@@ -1319,7 +1319,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
var maxThreadId = context.BitwiseOr(context.TypeU32(), minThreadId, clampNotSegMask);
|
||||||
var srcThreadId = context.BitwiseXor(context.TypeU32(), threadId, index);
|
var srcThreadId = context.BitwiseXor(context.TypeU32(), threadId, index);
|
||||||
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
var valid = context.ULessThanEqual(context.TypeBool(), srcThreadId, maxThreadId);
|
||||||
var value = context.SubgroupReadInvocationKHR(context.TypeFP32(), x, srcThreadId);
|
var value = context.GroupNonUniformShuffle(context.TypeFP32(), context.Constant(context.TypeU32(), (int)Scope.Subgroup), x, srcThreadId);
|
||||||
var result = context.Select(context.TypeFP32(), valid, value, x);
|
var result = context.Select(context.TypeFP32(), valid, value, x);
|
||||||
|
|
||||||
var validLocal = (AstOperand)operation.GetSource(3);
|
var validLocal = (AstOperand)operation.GetSource(3);
|
||||||
@@ -1861,19 +1861,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
private static OperationResult GenerateVoteAll(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateVoteAll(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var result = context.SubgroupAllKHR(context.TypeBool(), context.Get(AggregateType.Bool, operation.GetSource(0)));
|
var execution = context.Constant(context.TypeU32(), Scope.Subgroup);
|
||||||
|
var result = context.GroupNonUniformAll(context.TypeBool(), execution, context.Get(AggregateType.Bool, operation.GetSource(0)));
|
||||||
return new OperationResult(AggregateType.Bool, result);
|
return new OperationResult(AggregateType.Bool, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateVoteAllEqual(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateVoteAllEqual(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var result = context.SubgroupAllEqualKHR(context.TypeBool(), context.Get(AggregateType.Bool, operation.GetSource(0)));
|
var execution = context.Constant(context.TypeU32(), Scope.Subgroup);
|
||||||
|
var result = context.GroupNonUniformAllEqual(context.TypeBool(), execution, context.Get(AggregateType.Bool, operation.GetSource(0)));
|
||||||
return new OperationResult(AggregateType.Bool, result);
|
return new OperationResult(AggregateType.Bool, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateVoteAny(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateVoteAny(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var result = context.SubgroupAnyKHR(context.TypeBool(), context.Get(AggregateType.Bool, operation.GetSource(0)));
|
var execution = context.Constant(context.TypeU32(), Scope.Subgroup);
|
||||||
|
var result = context.GroupNonUniformAny(context.TypeBool(), execution, context.Get(AggregateType.Bool, operation.GetSource(0)));
|
||||||
return new OperationResult(AggregateType.Bool, result);
|
return new OperationResult(AggregateType.Bool, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -50,12 +50,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
CodeGenContext context = new CodeGenContext(info, config, instPool, integerPool);
|
CodeGenContext context = new CodeGenContext(info, config, instPool, integerPool);
|
||||||
|
|
||||||
context.AddCapability(Capability.GroupNonUniformBallot);
|
context.AddCapability(Capability.GroupNonUniformBallot);
|
||||||
|
context.AddCapability(Capability.GroupNonUniformShuffle);
|
||||||
|
context.AddCapability(Capability.GroupNonUniformVote);
|
||||||
context.AddCapability(Capability.ImageBuffer);
|
context.AddCapability(Capability.ImageBuffer);
|
||||||
context.AddCapability(Capability.ImageGatherExtended);
|
context.AddCapability(Capability.ImageGatherExtended);
|
||||||
context.AddCapability(Capability.ImageQuery);
|
context.AddCapability(Capability.ImageQuery);
|
||||||
context.AddCapability(Capability.SampledBuffer);
|
context.AddCapability(Capability.SampledBuffer);
|
||||||
context.AddCapability(Capability.SubgroupBallotKHR);
|
|
||||||
context.AddCapability(Capability.SubgroupVoteKHR);
|
|
||||||
|
|
||||||
if (config.TransformFeedbackEnabled && config.LastInVertexPipeline)
|
if (config.TransformFeedbackEnabled && config.LastInVertexPipeline)
|
||||||
{
|
{
|
||||||
@@ -94,9 +94,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
context.AddCapability(Capability.DrawParameters);
|
context.AddCapability(Capability.DrawParameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.AddExtension("SPV_KHR_shader_ballot");
|
|
||||||
context.AddExtension("SPV_KHR_subgroup_vote");
|
|
||||||
|
|
||||||
Declarations.DeclareAll(context, info);
|
Declarations.DeclareAll(context, info);
|
||||||
|
|
||||||
if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0)
|
if ((info.HelperFunctionsMask & NeedsInvocationIdMask) != 0)
|
||||||
|
@@ -128,6 +128,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), baseAddressCbOffset) :
|
GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), baseAddressCbOffset) :
|
||||||
(null, 0);
|
(null, 0);
|
||||||
|
|
||||||
|
if (byteOffset != null)
|
||||||
|
{
|
||||||
|
ReplaceAddressAlignment(node.List, addrLow, byteOffset, constantOffset);
|
||||||
|
}
|
||||||
|
|
||||||
if (byteOffset == null)
|
if (byteOffset == null)
|
||||||
{
|
{
|
||||||
Operand baseAddrLow = Cbuf(0, baseAddressCbOffset);
|
Operand baseAddrLow = Cbuf(0, baseAddressCbOffset);
|
||||||
@@ -156,11 +161,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
|||||||
byteOffset = offset;
|
byteOffset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (byteOffset != null)
|
|
||||||
{
|
|
||||||
ReplaceAddressAlignment(node.List, addrLow, byteOffset, constantOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isStg16Or8)
|
if (isStg16Or8)
|
||||||
{
|
{
|
||||||
return byteOffset;
|
return byteOffset;
|
||||||
|
@@ -163,12 +163,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SignalDirty(DirtyFlags.Image);
|
SignalDirty(DirtyFlags.Image);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetStorageBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan<BufferRange> buffers)
|
public void SetStorageBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < buffers.Length; i++)
|
for (int i = 0; i < buffers.Length; i++)
|
||||||
{
|
{
|
||||||
var buffer = buffers[i];
|
var assignment = buffers[i];
|
||||||
int index = first + i;
|
var buffer = assignment.Range;
|
||||||
|
int index = assignment.Binding;
|
||||||
|
|
||||||
Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false);
|
Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false);
|
||||||
ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index];
|
ref Auto<DisposableBuffer> currentVkBuffer = ref _storageBufferRefs[index];
|
||||||
@@ -243,12 +244,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SignalDirty(DirtyFlags.Texture);
|
SignalDirty(DirtyFlags.Texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUniformBuffers(CommandBuffer commandBuffer, int first, ReadOnlySpan<BufferRange> buffers)
|
public void SetUniformBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < buffers.Length; i++)
|
for (int i = 0; i < buffers.Length; i++)
|
||||||
{
|
{
|
||||||
var buffer = buffers[i];
|
var assignment = buffers[i];
|
||||||
int index = first + i;
|
var buffer = assignment.Range;
|
||||||
|
int index = assignment.Binding;
|
||||||
|
|
||||||
Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false);
|
Auto<DisposableBuffer> vkBuffer = _gd.BufferManager.GetBuffer(commandBuffer, buffer.Handle, false);
|
||||||
ref Auto<DisposableBuffer> currentVkBuffer = ref _uniformBufferRefs[index];
|
ref Auto<DisposableBuffer> currentVkBuffer = ref _uniformBufferRefs[index];
|
||||||
|
@@ -180,6 +180,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
GAL.PrimitiveTopology.TrianglesAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleListWithAdjacency,
|
GAL.PrimitiveTopology.TrianglesAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleListWithAdjacency,
|
||||||
GAL.PrimitiveTopology.TriangleStripAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleStripWithAdjacency,
|
GAL.PrimitiveTopology.TriangleStripAdjacency => Silk.NET.Vulkan.PrimitiveTopology.TriangleStripWithAdjacency,
|
||||||
GAL.PrimitiveTopology.Patches => Silk.NET.Vulkan.PrimitiveTopology.PatchList,
|
GAL.PrimitiveTopology.Patches => Silk.NET.Vulkan.PrimitiveTopology.PatchList,
|
||||||
|
GAL.PrimitiveTopology.Polygon => Silk.NET.Vulkan.PrimitiveTopology.TriangleFan,
|
||||||
GAL.PrimitiveTopology.Quads => throw new NotSupportedException("Quad topology is not available in Vulkan."),
|
GAL.PrimitiveTopology.Quads => throw new NotSupportedException("Quad topology is not available in Vulkan."),
|
||||||
GAL.PrimitiveTopology.QuadStrip => throw new NotSupportedException("QuadStrip topology is not available in Vulkan."),
|
GAL.PrimitiveTopology.QuadStrip => throw new NotSupportedException("QuadStrip topology is not available in Vulkan."),
|
||||||
_ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), Silk.NET.Vulkan.PrimitiveTopology.TriangleList)
|
_ => LogInvalidAndReturn(topology, nameof(GAL.PrimitiveTopology), Silk.NET.Vulkan.PrimitiveTopology.TriangleList)
|
||||||
|
@@ -177,7 +177,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||||
|
|
||||||
_pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) });
|
||||||
|
|
||||||
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
||||||
|
|
||||||
@@ -240,7 +240,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, clearColor);
|
||||||
|
|
||||||
_pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, ClearColorBufferSize) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, ClearColorBufferSize)) });
|
||||||
|
|
||||||
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
||||||
|
|
||||||
@@ -302,7 +302,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
gd.BufferManager.SetData<float>(bufferHandle, 0, region);
|
||||||
|
|
||||||
pipeline.SetUniformBuffers(1, stackalloc[] { new BufferRange(bufferHandle, 0, RegionBufferSize) });
|
pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(1, new BufferRange(bufferHandle, 0, RegionBufferSize)) });
|
||||||
|
|
||||||
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
||||||
|
|
||||||
@@ -380,7 +380,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_pipeline.SetCommandBuffer(cbs);
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
|
|
||||||
_pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(bufferHandle, 0, ParamsBufferSize) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
|
||||||
|
|
||||||
Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
|
Span<Auto<DisposableBuffer>> sbRanges = new Auto<DisposableBuffer>[2];
|
||||||
|
|
||||||
@@ -571,7 +571,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
int conversionType = srcIsMs ? src.Info.BytesPerPixel : -src.Info.BytesPerPixel;
|
int conversionType = srcIsMs ? src.Info.BytesPerPixel : -src.Info.BytesPerPixel;
|
||||||
_pipeline.Specialize(conversionType);
|
_pipeline.Specialize(conversionType);
|
||||||
|
|
||||||
_pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(bufferHandle, 0, ParamsBufferSize) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(bufferHandle, 0, ParamsBufferSize)) });
|
||||||
|
|
||||||
if (src.Info.Target == Target.Texture2DMultisampleArray ||
|
if (src.Info.Target == Target.Texture2DMultisampleArray ||
|
||||||
dst.Info.Target == Target.Texture2DMultisampleArray)
|
dst.Info.Target == Target.Texture2DMultisampleArray)
|
||||||
@@ -776,7 +776,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
srcIndirectBufferOffset,
|
srcIndirectBufferOffset,
|
||||||
indirectDataSize);
|
indirectDataSize);
|
||||||
|
|
||||||
_pipeline.SetUniformBuffers(0, stackalloc[] { drawCountBufferAligned });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, drawCountBufferAligned) });
|
||||||
_pipeline.SetStorageBuffers(1, new[] { srcIndirectBuffer.GetBuffer(), dstIndirectBuffer.GetBuffer(), patternBuffer.GetBuffer() });
|
_pipeline.SetStorageBuffers(1, new[] { srcIndirectBuffer.GetBuffer(), dstIndirectBuffer.GetBuffer(), patternBuffer.GetBuffer() });
|
||||||
|
|
||||||
_pipeline.SetProgram(_programConvertIndirectData);
|
_pipeline.SetProgram(_programConvertIndirectData);
|
||||||
@@ -804,7 +804,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
0,
|
0,
|
||||||
convertedCount * outputIndexSize);
|
convertedCount * outputIndexSize);
|
||||||
|
|
||||||
_pipeline.SetUniformBuffers(0, stackalloc[] { new BufferRange(patternBufferHandle, 0, ParamsBufferSize) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(0, new BufferRange(patternBufferHandle, 0, ParamsBufferSize)) });
|
||||||
_pipeline.SetStorageBuffers(1, new[] { srcIndexBuffer.GetBuffer(), dstIndexBuffer.GetBuffer() });
|
_pipeline.SetStorageBuffers(1, new[] { srcIndexBuffer.GetBuffer(), dstIndexBuffer.GetBuffer() });
|
||||||
|
|
||||||
_pipeline.SetProgram(_programConvertIndexBuffer);
|
_pipeline.SetProgram(_programConvertIndexBuffer);
|
||||||
|
@@ -328,7 +328,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
IndexBufferPattern pattern = _topology switch
|
IndexBufferPattern pattern = _topology switch
|
||||||
{
|
{
|
||||||
GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
|
GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
|
||||||
GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern,
|
GAL.PrimitiveTopology.TriangleFan or
|
||||||
|
GAL.PrimitiveTopology.Polygon => TriFanToTrisPattern,
|
||||||
_ => throw new NotSupportedException($"Unsupported topology: {_topology}")
|
_ => throw new NotSupportedException($"Unsupported topology: {_topology}")
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -359,7 +360,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
pattern = _topology switch
|
pattern = _topology switch
|
||||||
{
|
{
|
||||||
GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
|
GAL.PrimitiveTopology.Quads => QuadsToTrisPattern,
|
||||||
GAL.PrimitiveTopology.TriangleFan => TriFanToTrisPattern,
|
GAL.PrimitiveTopology.TriangleFan or
|
||||||
|
GAL.PrimitiveTopology.Polygon => TriFanToTrisPattern,
|
||||||
_ => throw new NotSupportedException($"Unsupported topology: {_topology}")
|
_ => throw new NotSupportedException($"Unsupported topology: {_topology}")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -973,9 +975,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SignalStateChange();
|
SignalStateChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetStorageBuffers(int first, ReadOnlySpan<BufferRange> buffers)
|
public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
_descriptorSetUpdater.SetStorageBuffers(CommandBuffer, first, buffers);
|
_descriptorSetUpdater.SetStorageBuffers(CommandBuffer, buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
|
public void SetStorageBuffers(int first, ReadOnlySpan<Auto<DisposableBuffer>> buffers)
|
||||||
@@ -1013,9 +1015,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUniformBuffers(int first, ReadOnlySpan<BufferRange> buffers)
|
public void SetUniformBuffers(ReadOnlySpan<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
_descriptorSetUpdater.SetUniformBuffers(CommandBuffer, first, buffers);
|
_descriptorSetUpdater.SetUniformBuffers(CommandBuffer, buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUserClipDistance(int index, bool enableClip)
|
public void SetUserClipDistance(int index, bool enableClip)
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.2" />
|
<PackageReference Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.5" />
|
||||||
<PackageReference Include="shaderc.net" Version="0.1.0" />
|
<PackageReference Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan" Version="2.10.1" />
|
||||||
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.10.1" />
|
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.10.1" />
|
||||||
|
@@ -37,7 +37,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public static string[] RequiredExtensions { get; } = new string[]
|
public static string[] RequiredExtensions { get; } = new string[]
|
||||||
{
|
{
|
||||||
KhrSwapchain.ExtensionName,
|
KhrSwapchain.ExtensionName,
|
||||||
"VK_EXT_shader_subgroup_vote",
|
|
||||||
ExtTransformFeedback.ExtensionName
|
ExtTransformFeedback.ExtensionName
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -22,6 +22,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private Device _device;
|
private Device _device;
|
||||||
private WindowBase _window;
|
private WindowBase _window;
|
||||||
|
|
||||||
|
private bool _initialized;
|
||||||
|
|
||||||
internal FormatCapabilities FormatCapabilities { get; private set; }
|
internal FormatCapabilities FormatCapabilities { get; private set; }
|
||||||
internal HardwareCapabilities Capabilities;
|
internal HardwareCapabilities Capabilities;
|
||||||
|
|
||||||
@@ -266,6 +268,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
LoadFeatures(supportedExtensions, maxQueueCount, queueFamilyIndex);
|
LoadFeatures(supportedExtensions, maxQueueCount, queueFamilyIndex);
|
||||||
|
|
||||||
_window = new Window(this, _surface, _physicalDevice, _device);
|
_window = new Window(this, _surface, _physicalDevice, _device);
|
||||||
|
|
||||||
|
_initialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferHandle CreateBuffer(int size)
|
public BufferHandle CreateBuffer(int size)
|
||||||
@@ -573,6 +577,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public unsafe void Dispose()
|
public unsafe void Dispose()
|
||||||
{
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CommandBufferPool.Dispose();
|
CommandBufferPool.Dispose();
|
||||||
BackgroundResources.Dispose();
|
BackgroundResources.Dispose();
|
||||||
_counters.Dispose();
|
_counters.Dispose();
|
||||||
|
@@ -172,9 +172,11 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
fsServerClient = horizon.CreatePrivilegedHorizonClient();
|
||||||
var fsServer = new FileSystemServer(fsServerClient);
|
var fsServer = new FileSystemServer(fsServerClient);
|
||||||
|
|
||||||
DefaultFsServerObjects fsServerObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(serverBaseFs, KeySet, fsServer);
|
RandomDataGenerator randomGenerator = buffer => Random.Shared.NextBytes(buffer);
|
||||||
|
|
||||||
// Use our own encrypted fs creator that always uses all-zero keys
|
DefaultFsServerObjects fsServerObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(serverBaseFs, KeySet, fsServer, randomGenerator);
|
||||||
|
|
||||||
|
// Use our own encrypted fs creator that doesn't actually do any encryption
|
||||||
fsServerObjects.FsCreators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator();
|
fsServerObjects.FsCreators.EncryptedFileSystemCreator = new EncryptedFileSystemCreator();
|
||||||
|
|
||||||
GameCard = fsServerObjects.GameCard;
|
GameCard = fsServerObjects.GameCard;
|
||||||
@@ -186,7 +188,8 @@ namespace Ryujinx.HLE.FileSystem
|
|||||||
{
|
{
|
||||||
DeviceOperator = fsServerObjects.DeviceOperator,
|
DeviceOperator = fsServerObjects.DeviceOperator,
|
||||||
ExternalKeySet = KeySet.ExternalKeySet,
|
ExternalKeySet = KeySet.ExternalKeySet,
|
||||||
FsCreators = fsServerObjects.FsCreators
|
FsCreators = fsServerObjects.FsCreators,
|
||||||
|
RandomGenerator = randomGenerator
|
||||||
};
|
};
|
||||||
|
|
||||||
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, fsServerConfig);
|
FileSystemServerInitializer.InitializeWithConfig(fsServerClient, fsServer, fsServerConfig);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
|
using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
|
||||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||||
@@ -9,6 +10,7 @@ using Ryujinx.Memory;
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@@ -78,13 +80,13 @@ namespace Ryujinx.HLE.HOS.Applets
|
|||||||
var launchParams = _normalSession.Pop();
|
var launchParams = _normalSession.Pop();
|
||||||
var keyboardConfig = _normalSession.Pop();
|
var keyboardConfig = _normalSession.Pop();
|
||||||
|
|
||||||
_isBackground = keyboardConfig.Length == Marshal.SizeOf<SoftwareKeyboardInitialize>();
|
_isBackground = keyboardConfig.Length == Unsafe.SizeOf<SoftwareKeyboardInitialize>();
|
||||||
|
|
||||||
if (_isBackground)
|
if (_isBackground)
|
||||||
{
|
{
|
||||||
// Initialize the keyboard applet in background mode.
|
// Initialize the keyboard applet in background mode.
|
||||||
|
|
||||||
_keyboardBackgroundInitialize = ReadStruct<SoftwareKeyboardInitialize>(keyboardConfig);
|
_keyboardBackgroundInitialize = MemoryMarshal.Read<SoftwareKeyboardInitialize>(keyboardConfig);
|
||||||
_backgroundState = InlineKeyboardState.Uninitialized;
|
_backgroundState = InlineKeyboardState.Uninitialized;
|
||||||
|
|
||||||
if (_device.UiHandler == null)
|
if (_device.UiHandler == null)
|
||||||
@@ -342,7 +344,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
int wordsCount = reader.ReadInt32();
|
int wordsCount = reader.ReadInt32();
|
||||||
int wordSize = Marshal.SizeOf<SoftwareKeyboardUserWord>();
|
int wordSize = Unsafe.SizeOf<SoftwareKeyboardUserWord>();
|
||||||
remaining = stream.Length - stream.Position;
|
remaining = stream.Length - stream.Position;
|
||||||
|
|
||||||
if (wordsCount > MaxUserWords)
|
if (wordsCount > MaxUserWords)
|
||||||
@@ -359,8 +361,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
|||||||
|
|
||||||
for (int word = 0; word < wordsCount; word++)
|
for (int word = 0; word < wordsCount; word++)
|
||||||
{
|
{
|
||||||
byte[] wordData = reader.ReadBytes(wordSize);
|
_keyboardBackgroundUserWords[word] = reader.ReadStruct<SoftwareKeyboardUserWord>();
|
||||||
_keyboardBackgroundUserWords[word] = ReadStruct<SoftwareKeyboardUserWord>(wordData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -369,27 +370,25 @@ namespace Ryujinx.HLE.HOS.Applets
|
|||||||
case InlineKeyboardRequest.SetCustomizeDic:
|
case InlineKeyboardRequest.SetCustomizeDic:
|
||||||
// Read the custom dic data.
|
// Read the custom dic data.
|
||||||
remaining = stream.Length - stream.Position;
|
remaining = stream.Length - stream.Position;
|
||||||
if (remaining != Marshal.SizeOf<SoftwareKeyboardCustomizeDic>())
|
if (remaining != Unsafe.SizeOf<SoftwareKeyboardCustomizeDic>())
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Customize Dic of {remaining} bytes");
|
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Customize Dic of {remaining} bytes");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var keyboardDicData = reader.ReadBytes((int)remaining);
|
_keyboardBackgroundDic = reader.ReadStruct<SoftwareKeyboardCustomizeDic>();
|
||||||
_keyboardBackgroundDic = ReadStruct<SoftwareKeyboardCustomizeDic>(keyboardDicData);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case InlineKeyboardRequest.SetCustomizedDictionaries:
|
case InlineKeyboardRequest.SetCustomizedDictionaries:
|
||||||
// Read the custom dictionaries data.
|
// Read the custom dictionaries data.
|
||||||
remaining = stream.Length - stream.Position;
|
remaining = stream.Length - stream.Position;
|
||||||
if (remaining != Marshal.SizeOf<SoftwareKeyboardDictSet>())
|
if (remaining != Unsafe.SizeOf<SoftwareKeyboardDictSet>())
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard DictSet of {remaining} bytes");
|
Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard DictSet of {remaining} bytes");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var keyboardDictData = reader.ReadBytes((int)remaining);
|
_keyboardBackgroundDictSet = reader.ReadStruct<SoftwareKeyboardDictSet>();
|
||||||
_keyboardBackgroundDictSet = ReadStruct<SoftwareKeyboardDictSet>(keyboardDictData);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case InlineKeyboardRequest.Calc:
|
case InlineKeyboardRequest.Calc:
|
||||||
|
@@ -5,10 +5,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A structure used by SetCustomizeDic request to software keyboard.
|
/// A structure used by SetCustomizeDic request to software keyboard.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
[StructLayout(LayoutKind.Sequential, Size = 0x70)]
|
||||||
struct SoftwareKeyboardCustomizeDic
|
struct SoftwareKeyboardCustomizeDic
|
||||||
{
|
{
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 112)]
|
// Unknown
|
||||||
public byte[] Unknown;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using System.Runtime.InteropServices;
|
using Ryujinx.Common.Memory;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
||||||
{
|
{
|
||||||
@@ -21,8 +22,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Array of word entries in the buffer.
|
/// Array of word entries in the buffer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)]
|
public Array24<ulong> Entries;
|
||||||
public ulong[] Entries;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of used entries in the Entries field.
|
/// Number of used entries in the Entries field.
|
||||||
|
@@ -5,10 +5,9 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A structure used by SetUserWordInfo request to the software keyboard.
|
/// A structure used by SetUserWordInfo request to the software keyboard.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
[StructLayout(LayoutKind.Sequential, Size = 0x64)]
|
||||||
struct SoftwareKeyboardUserWord
|
struct SoftwareKeyboardUserWord
|
||||||
{
|
{
|
||||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 100)]
|
// Unknown
|
||||||
public byte[] Unknown;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -850,7 +850,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
for (int i = 0; i < programCount; i++)
|
for (int i = 0; i < programCount; i++)
|
||||||
{
|
{
|
||||||
mapInfo[i].ProgramId = new ProgramId(applicationId + (uint)i);
|
mapInfo[i].ProgramId = new ProgramId(applicationId + (uint)i);
|
||||||
mapInfo[i].MainProgramId = new ProgramId(applicationId);
|
mapInfo[i].MainProgramId = new ApplicationId(applicationId);
|
||||||
mapInfo[i].ProgramIndex = (byte)i;
|
mapInfo[i].ProgramIndex = (byte)i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user