0
0
Fork 0
mirror of https://github.com/GreemDev/Ryujinx.git synced 2025-01-09 03:02:00 +00:00

misc: Overhaul DirtyHacks saving to support storing a value alongside an off/off flag.

This commit is contained in:
Evan Husted 2024-12-29 21:17:01 -06:00
parent f5ce539de9
commit f362bef43d
13 changed files with 190 additions and 27 deletions

View file

@ -0,0 +1,35 @@
namespace Ryujinx.Common
{
public class BitTricks
{
// Never actually written bit packing logic before, so I looked it up.
// This code is from https://gist.github.com/Alan-FGR/04938e93e2bffdf5802ceb218a37c195
public static ulong PackBitFields(uint[] values, byte[] bitFields)
{
ulong retVal = values[0]; //we set the first value right away
for (int f = 1; f < values.Length; f++)
{
retVal <<= bitFields[f]; // we shift the previous value
retVal += values[f];// and add our current value
}
return retVal;
}
public static uint[] UnpackBitFields(ulong packed, byte[] bitFields)
{
int fields = bitFields.Length - 1; // number of fields to unpack
uint[] retArr = new uint[fields + 1]; // init return array
int curPos = 0; // current field bit position (start)
int lastEnd; // position where last field ended
for (int f = fields; f >= 0; f--) // loop from last
{
lastEnd = curPos; // we store where the last value ended
curPos += bitFields[f]; // we get where the current value starts
int leftShift = 64 - curPos; // we figure how much left shift we gotta apply for the other numbers to overflow into oblivion
retArr[f] = (uint)((packed << leftShift) >> leftShift + lastEnd); // we do magic
}
return retArr;
}
}
}

View file

@ -1,11 +1,52 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Common.Configuration namespace Ryujinx.Common.Configuration
{ {
[Flags] [Flags]
public enum DirtyHacks public enum DirtyHacks : byte
{ {
None = 0, Xc2MenuSoftlockFix = 1,
Xc2MenuSoftlockFix = 1 << 10 ShaderCompilationThreadSleep = 2
}
public record EnabledDirtyHack(DirtyHacks Hack, int Value)
{
private static readonly byte[] _packedFormat = [8, 32];
public ulong Pack() => BitTricks.PackBitFields([(uint)Hack, (uint)Value], _packedFormat);
public static EnabledDirtyHack FromPacked(ulong packedHack)
{
var unpackedFields = BitTricks.UnpackBitFields(packedHack, _packedFormat);
if (unpackedFields is not [var hack, var value])
throw new ArgumentException(nameof(packedHack));
return new EnabledDirtyHack((DirtyHacks)hack, (int)value);
}
}
public class DirtyHackCollection : Dictionary<DirtyHacks, int>
{
public DirtyHackCollection(EnabledDirtyHack[] hacks)
{
foreach ((DirtyHacks dirtyHacks, int value) in hacks)
{
Add(dirtyHacks, value);
}
}
public DirtyHackCollection(ulong[] packedHacks)
{
foreach ((DirtyHacks dirtyHacks, int value) in packedHacks.Select(EnabledDirtyHack.FromPacked))
{
Add(dirtyHacks, value);
}
}
public new int this[DirtyHacks hack] => TryGetValue(hack, out var value) ? value : -1;
public bool IsEnabled(DirtyHacks hack) => ContainsKey(hack);
} }
} }

View file

@ -192,7 +192,7 @@ namespace Ryujinx.HLE
/// <summary> /// <summary>
/// The desired hacky workarounds. /// The desired hacky workarounds.
/// </summary> /// </summary>
public DirtyHacks Hacks { internal get; set; } public EnabledDirtyHack[] Hacks { internal get; set; }
public HLEConfiguration(VirtualFileSystem virtualFileSystem, public HLEConfiguration(VirtualFileSystem virtualFileSystem,
LibHacHorizonManager libHacHorizonManager, LibHacHorizonManager libHacHorizonManager,
@ -224,7 +224,7 @@ namespace Ryujinx.HLE
string multiplayerLdnPassphrase, string multiplayerLdnPassphrase,
string multiplayerLdnServer, string multiplayerLdnServer,
int customVSyncInterval, int customVSyncInterval,
DirtyHacks dirtyHacks = DirtyHacks.None) EnabledDirtyHack[] dirtyHacks = null)
{ {
VirtualFileSystem = virtualFileSystem; VirtualFileSystem = virtualFileSystem;
LibHacHorizonManager = libHacHorizonManager; LibHacHorizonManager = libHacHorizonManager;
@ -256,7 +256,7 @@ namespace Ryujinx.HLE
MultiplayerDisableP2p = multiplayerDisableP2p; MultiplayerDisableP2p = multiplayerDisableP2p;
MultiplayerLdnPassphrase = multiplayerLdnPassphrase; MultiplayerLdnPassphrase = multiplayerLdnPassphrase;
MultiplayerLdnServer = multiplayerLdnServer; MultiplayerLdnServer = multiplayerLdnServer;
Hacks = dirtyHacks; Hacks = dirtyHacks ?? [];
} }
} }
} }

View file

@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true); using var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true);
Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size); Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size);
if (context.Device.DirtyHacks.HasFlag(DirtyHacks.Xc2MenuSoftlockFix) && TitleIDs.CurrentApplication.Value == Xc2TitleId) if (context.Device.DirtyHacks.IsEnabled(DirtyHacks.Xc2MenuSoftlockFix) && TitleIDs.CurrentApplication.Value == Xc2TitleId)
{ {
// Add a load-bearing sleep to avoid XC2 softlock // Add a load-bearing sleep to avoid XC2 softlock
// https://web.archive.org/web/20240728045136/https://github.com/Ryujinx/Ryujinx/issues/2357 // https://web.archive.org/web/20240728045136/https://github.com/Ryujinx/Ryujinx/issues/2357

View file

@ -40,7 +40,7 @@ namespace Ryujinx.HLE
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable; public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
public DirtyHacks DirtyHacks { get; } public DirtyHackCollection DirtyHacks { get; }
public Switch(HLEConfiguration configuration) public Switch(HLEConfiguration configuration)
{ {
@ -77,7 +77,7 @@ namespace Ryujinx.HLE
System.EnablePtc = Configuration.EnablePtc; System.EnablePtc = Configuration.EnablePtc;
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel; System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode; System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
DirtyHacks = Configuration.Hacks; DirtyHacks = new DirtyHackCollection(Configuration.Hacks);
UpdateVSyncInterval(); UpdateVSyncInterval();
#pragma warning restore IDE0055 #pragma warning restore IDE0055

View file

@ -952,7 +952,7 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Multiplayer.LdnPassphrase, ConfigurationState.Instance.Multiplayer.LdnPassphrase,
ConfigurationState.Instance.Multiplayer.LdnServer, ConfigurationState.Instance.Multiplayer.LdnServer,
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value, ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value,
ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : DirtyHacks.None)); ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null));
} }
private static IHardwareDeviceDriver InitializeAudio() private static IHardwareDeviceDriver InitializeAudio()

View file

@ -14,9 +14,6 @@ using Ryujinx.Cpu;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Metal;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Graphics.Vulkan.MoltenVK;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;

View file

@ -66,6 +66,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private string _ldnServer; private string _ldnServer;
private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix; private bool _xc2MenuSoftlockFix = ConfigurationState.Instance.Hacks.Xc2MenuSoftlockFix;
private bool _shaderTranslationThreadSleep = ConfigurationState.Instance.Hacks.EnableShaderCompilationThreadSleep;
private int _shaderTranslationSleepDelay = ConfigurationState.Instance.Hacks.ShaderCompilationThreadSleepDelay;
public int ResolutionScale public int ResolutionScale
{ {
@ -287,6 +289,28 @@ namespace Ryujinx.Ava.UI.ViewModels
OnPropertyChanged(); OnPropertyChanged();
} }
} }
public bool ShaderTranslationDelayEnabled
{
get => _shaderTranslationThreadSleep;
set
{
_shaderTranslationThreadSleep = value;
OnPropertyChanged();
}
}
public int ShaderTranslationDelay
{
get => _shaderTranslationSleepDelay;
set
{
_shaderTranslationSleepDelay = value;
OnPropertyChanged();
}
}
public int Language { get; set; } public int Language { get; set; }
public int Region { get; set; } public int Region { get; set; }
@ -763,6 +787,8 @@ namespace Ryujinx.Ava.UI.ViewModels
// Dirty Hacks // Dirty Hacks
config.Hacks.Xc2MenuSoftlockFix.Value = Xc2MenuSoftlockFixEnabled; config.Hacks.Xc2MenuSoftlockFix.Value = Xc2MenuSoftlockFixEnabled;
config.Hacks.EnableShaderCompilationThreadSleep.Value = ShaderTranslationDelayEnabled;
config.Hacks.ShaderCompilationThreadSleepDelay.Value = ShaderTranslationDelay;
config.ToFileFormat().SaveConfig(Program.ConfigurationPath); config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
@ -809,5 +835,11 @@ namespace Ryujinx.Ava.UI.ViewModels
"there is a low chance that the game will softlock, " + "there is a low chance that the game will softlock, " +
"the submenu won't show up, while background music is still there."); "the submenu won't show up, while background music is still there.");
}); });
public static string ShaderTranslationDelayTooltip { get; } = Lambda.String(sb =>
{
sb.Append(
"This hack applies the delay you specify every time shaders are attempted to be translated.");
});
} }
} }

View file

@ -42,6 +42,33 @@
VerticalAlignment="Center" VerticalAlignment="Center"
Text="Xenoblade Chronicles 2 Menu Softlock Fix" /> Text="Xenoblade Chronicles 2 Menu Softlock Fix" />
</StackPanel> </StackPanel>
<Separator/>
<StackPanel
Margin="0,10,0,0"
Orientation="Horizontal"
HorizontalAlignment="Center"
ToolTip.Tip="{Binding ShaderTranslationDelayTooltip}">
<CheckBox
Margin="0"
IsChecked="{Binding ShaderTranslationDelayEnabled}"/>
<TextBlock VerticalAlignment="Center"
Text="Arbitrary Delay on Shader Translation"/>
</StackPanel>
<Slider HorizontalAlignment="Center"
Value="{Binding ShaderTranslationDelay}"
ToolTip.Tip="{Binding ShaderTranslationDelay}"
Width="175"
Margin="0,-3,0,0"
Height="32"
Padding="0,-5"
TickFrequency="1"
IsSnapToTickEnabled="True"
LargeChange="10"
SmallChange="1"
VerticalAlignment="Center"
Minimum="1"
Maximum="1000" />
<Separator/>
</StackPanel> </StackPanel>
</Border> </Border>
</ScrollViewer> </ScrollViewer>

View file

@ -17,7 +17,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
/// <summary> /// <summary>
/// The current version of the file format /// The current version of the file format
/// </summary> /// </summary>
public const int CurrentVersion = 58; public const int CurrentVersion = 59;
/// <summary> /// <summary>
/// Version of the configuration file format /// Version of the configuration file format
@ -436,9 +436,9 @@ namespace Ryujinx.Ava.Utilities.Configuration
public bool ShowDirtyHacks { get; set; } public bool ShowDirtyHacks { get; set; }
/// <summary> /// <summary>
/// The packed value of the enabled dirty hacks. /// The packed values of the enabled dirty hacks.
/// </summary> /// </summary>
public int EnabledDirtyHacks { get; set; } public ulong[] DirtyHacks { get; set; }
/// <summary> /// <summary>
/// Loads a configuration file from disk /// Loads a configuration file from disk

View file

@ -9,6 +9,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.HLE; using Ryujinx.HLE;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Ava.Utilities.Configuration namespace Ryujinx.Ava.Utilities.Configuration
{ {
@ -637,6 +638,18 @@ namespace Ryujinx.Ava.Utilities.Configuration
configurationFileUpdated = true; configurationFileUpdated = true;
} }
// 58 migration accidentally got skipped but it worked with no issues somehow lol
if (configurationFileFormat.Version < 59)
{
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 59.");
configurationFileFormat.ShowDirtyHacks = false;
configurationFileFormat.DirtyHacks = [];
configurationFileUpdated = true;
}
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScale.Value = configurationFileFormat.ResScale;
@ -737,7 +750,16 @@ namespace Ryujinx.Ava.Utilities.Configuration
Multiplayer.LdnServer.Value = configurationFileFormat.LdnServer; Multiplayer.LdnServer.Value = configurationFileFormat.LdnServer;
Hacks.ShowDirtyHacks.Value = configurationFileFormat.ShowDirtyHacks; Hacks.ShowDirtyHacks.Value = configurationFileFormat.ShowDirtyHacks;
Hacks.Xc2MenuSoftlockFix.Value = ((DirtyHacks)configurationFileFormat.EnabledDirtyHacks).HasFlag(DirtyHacks.Xc2MenuSoftlockFix);
{
EnabledDirtyHack[] hacks = configurationFileFormat.DirtyHacks.Select(EnabledDirtyHack.FromPacked).ToArray();
Hacks.Xc2MenuSoftlockFix.Value = hacks.Any(it => it.Hack == DirtyHacks.Xc2MenuSoftlockFix);
var shaderCompilationThreadSleep = hacks.FirstOrDefault(it => it.Hack == DirtyHacks.ShaderCompilationThreadSleep);
Hacks.EnableShaderCompilationThreadSleep.Value = shaderCompilationThreadSleep != null;
Hacks.ShaderCompilationThreadSleepDelay.Value = shaderCompilationThreadSleep?.Value ?? 0;
}
if (configurationFileUpdated) if (configurationFileUpdated)
{ {

View file

@ -9,6 +9,7 @@ using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE; using Ryujinx.HLE;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Ava.Utilities.Configuration namespace Ryujinx.Ava.Utilities.Configuration
{ {
@ -626,36 +627,43 @@ namespace Ryujinx.Ava.Utilities.Configuration
public ReactiveObject<bool> ShowDirtyHacks { get; private set; } public ReactiveObject<bool> ShowDirtyHacks { get; private set; }
public ReactiveObject<bool> Xc2MenuSoftlockFix { get; private set; } public ReactiveObject<bool> Xc2MenuSoftlockFix { get; private set; }
public ReactiveObject<bool> EnableShaderCompilationThreadSleep { get; private set; }
public ReactiveObject<int> ShaderCompilationThreadSleepDelay { get; private set; }
public HacksSection() public HacksSection()
{ {
ShowDirtyHacks = new ReactiveObject<bool>(); ShowDirtyHacks = new ReactiveObject<bool>();
Xc2MenuSoftlockFix = new ReactiveObject<bool>(); Xc2MenuSoftlockFix = new ReactiveObject<bool>();
Xc2MenuSoftlockFix.Event += HackChanged; Xc2MenuSoftlockFix.Event += HackChanged;
EnableShaderCompilationThreadSleep = new ReactiveObject<bool>();
EnableShaderCompilationThreadSleep.Event += HackChanged;
ShaderCompilationThreadSleepDelay = new ReactiveObject<int>();
} }
private void HackChanged(object sender, ReactiveEventArgs<bool> rxe) private void HackChanged(object sender, ReactiveEventArgs<bool> rxe)
{ {
Ryujinx.Common.Logging.Logger.Info?.Print(LogClass.Configuration, $"EnabledDirtyHacks set to: {EnabledHacks}", "LogValueChange"); Ryujinx.Common.Logging.Logger.Info?.Print(LogClass.Configuration, $"EnabledDirtyHacks set to: [{EnabledHacks.Select(x => x.Hack).JoinToString(", ")}]", "LogValueChange");
} }
public DirtyHacks EnabledHacks public EnabledDirtyHack[] EnabledHacks
{ {
get get
{ {
DirtyHacks dirtyHacks = DirtyHacks.None; List<EnabledDirtyHack> enabledHacks = [];
if (Xc2MenuSoftlockFix) if (Xc2MenuSoftlockFix)
Apply(DirtyHacks.Xc2MenuSoftlockFix); Apply(DirtyHacks.Xc2MenuSoftlockFix);
return dirtyHacks; if (EnableShaderCompilationThreadSleep)
Apply(DirtyHacks.ShaderCompilationThreadSleep, ShaderCompilationThreadSleepDelay);
return enabledHacks.ToArray();
void Apply(DirtyHacks hack) void Apply(DirtyHacks hack, int value = 0)
{ {
if (dirtyHacks is not DirtyHacks.None) enabledHacks.Add(new EnabledDirtyHack(hack, value));
dirtyHacks |= hack;
else
dirtyHacks = hack;
} }
} }
} }

View file

@ -7,6 +7,7 @@ using Ryujinx.Common.Configuration.Multiplayer;
using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE; using Ryujinx.HLE;
using System; using System;
using System.Linq;
namespace Ryujinx.Ava.Utilities.Configuration namespace Ryujinx.Ava.Utilities.Configuration
{ {
@ -139,7 +140,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
MultiplayerLdnPassphrase = Multiplayer.LdnPassphrase, MultiplayerLdnPassphrase = Multiplayer.LdnPassphrase,
LdnServer = Multiplayer.LdnServer, LdnServer = Multiplayer.LdnServer,
ShowDirtyHacks = Hacks.ShowDirtyHacks, ShowDirtyHacks = Hacks.ShowDirtyHacks,
EnabledDirtyHacks = (int)Hacks.EnabledHacks, DirtyHacks = Hacks.EnabledHacks.Select(it => it.Pack()).ToArray(),
}; };
return configurationFile; return configurationFile;