From ed5cb82aa8bbca5a1f2cfe947ef23253c53dfa90 Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 3 Mar 2025 02:33:28 -0600 Subject: [PATCH] feature: Turbo Mode Adds an elapsed tick multiplier feature which speeds up games which are built upon delta time. More information: https://web.archive.org/web/20240713135029/https://github.com/Ryujinx/Ryujinx/pull/6456 --- .../Configuration/Hid/KeyboardHotkeys.cs | 2 + src/Ryujinx.Cpu/ITickSource.cs | 5 + src/Ryujinx.Cpu/TickSource.cs | 29 +++- src/Ryujinx.HLE/HLEConfiguration.cs | 7 + .../Services/SurfaceFlinger/SurfaceFlinger.cs | 3 +- src/Ryujinx.HLE/PerformanceStatistics.cs | 11 +- src/Ryujinx.HLE/Switch.cs | 21 ++- src/Ryujinx/AppHost.cs | 19 ++- src/Ryujinx/Assets/locales.json | 150 ++++++++++++++++++ src/Ryujinx/Common/KeyboardHotkeyState.cs | 1 + src/Ryujinx/Headless/HeadlessRyujinx.Init.cs | 3 +- src/Ryujinx/UI/Models/Input/HotkeyConfig.cs | 8 + .../UI/ViewModels/SettingsViewModel.cs | 22 +++ .../UI/Views/Settings/SettingsCPUView.axaml | 51 ++++++ .../Views/Settings/SettingsHotkeysView.axaml | 32 ++-- .../Settings/SettingsHotkeysView.axaml.cs | 3 + .../Configuration/ConfigurationFileFormat.cs | 7 +- .../ConfigurationState.Migration.cs | 23 ++- .../Configuration/ConfigurationState.Model.cs | 14 ++ .../Configuration/ConfigurationState.cs | 9 +- 20 files changed, 394 insertions(+), 26 deletions(-) diff --git a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs index 6b8152b9d..efdb422e7 100644 --- a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs +++ b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs @@ -13,5 +13,7 @@ namespace Ryujinx.Common.Configuration.Hid public Key VolumeDown { get; set; } public Key CustomVSyncIntervalIncrement { get; set; } public Key CustomVSyncIntervalDecrement { get; set; } + public Key TurboMode { get; set; } + public bool TurboModeWhileHeld { get; set; } } } diff --git a/src/Ryujinx.Cpu/ITickSource.cs b/src/Ryujinx.Cpu/ITickSource.cs index e65e99e26..c352df85a 100644 --- a/src/Ryujinx.Cpu/ITickSource.cs +++ b/src/Ryujinx.Cpu/ITickSource.cs @@ -12,6 +12,11 @@ namespace Ryujinx.Cpu /// Time elapsed since the counter was created. /// TimeSpan ElapsedTime { get; } + + /// + /// Clock tick scalar, in percent points (100 = 1.0). + /// + long TickScalar { get; set; } /// /// Time elapsed since the counter was created, in seconds. diff --git a/src/Ryujinx.Cpu/TickSource.cs b/src/Ryujinx.Cpu/TickSource.cs index eee83fc62..3bc01d6b9 100644 --- a/src/Ryujinx.Cpu/TickSource.cs +++ b/src/Ryujinx.Cpu/TickSource.cs @@ -14,12 +14,37 @@ namespace Ryujinx.Cpu /// public ulong Counter => (ulong)(ElapsedSeconds * Frequency); + + + public long TickScalar { get; set; } + + + private static long _acumElapsedTicks; + + + private static long _lastElapsedTicks; + + + private long ElapsedTicks + { + get + { + long elapsedTicks = _tickCounter.ElapsedTicks; + + _acumElapsedTicks += (elapsedTicks - _lastElapsedTicks) * TickScalar / 100; + + _lastElapsedTicks = elapsedTicks; + + return _acumElapsedTicks; + } + } /// - public TimeSpan ElapsedTime => _tickCounter.Elapsed; + + public TimeSpan ElapsedTime => Stopwatch.GetElapsedTime(0, ElapsedTicks); /// - public double ElapsedSeconds => _tickCounter.ElapsedTicks * _hostTickFreq; + public double ElapsedSeconds => ElapsedTicks * _hostTickFreq; public TickSource(ulong frequency) { diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HLEConfiguration.cs index 0b7ae3974..e2132bb5a 100644 --- a/src/Ryujinx.HLE/HLEConfiguration.cs +++ b/src/Ryujinx.HLE/HLEConfiguration.cs @@ -102,6 +102,11 @@ namespace Ryujinx.HLE /// Control if the Profiled Translation Cache (PTC) should be used. /// internal readonly bool EnablePtc; + + /// + /// Control the arbitrary scalar applied to emulated CPU tick timing. + /// + public long TickScalar { get; set; } /// /// Control if the guest application should be told that there is a Internet connection available. @@ -225,6 +230,7 @@ namespace Ryujinx.HLE string multiplayerLdnPassphrase, string multiplayerLdnServer, int customVSyncInterval, + long tickScalar, EnabledDirtyHack[] dirtyHacks = null) { VirtualFileSystem = virtualFileSystem; @@ -257,6 +263,7 @@ namespace Ryujinx.HLE MultiplayerDisableP2p = multiplayerDisableP2p; MultiplayerLdnPassphrase = multiplayerLdnPassphrase; MultiplayerLdnServer = multiplayerLdnServer; + TickScalar = tickScalar; Hacks = dirtyHacks ?? []; } } diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index 935e9895e..294192363 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -2,6 +2,7 @@ using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Logging; using Ryujinx.Common.PreciseSleep; +using Ryujinx.Cpu; using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; @@ -89,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } else { - _ticksPerFrame = Stopwatch.Frequency / _device.TargetVSyncInterval; + _ticksPerFrame = ((Stopwatch.Frequency / _device.TargetVSyncInterval) * 100) / _device.TickScalar; _targetVSyncInterval = _device.TargetVSyncInterval; } } diff --git a/src/Ryujinx.HLE/PerformanceStatistics.cs b/src/Ryujinx.HLE/PerformanceStatistics.cs index e80faa7d2..48f4a0575 100644 --- a/src/Ryujinx.HLE/PerformanceStatistics.cs +++ b/src/Ryujinx.HLE/PerformanceStatistics.cs @@ -6,6 +6,8 @@ namespace Ryujinx.HLE { public class PerformanceStatistics { + private readonly Switch _device; + private const int FrameTypeGame = 0; private const int PercentTypeFifo = 0; @@ -28,8 +30,10 @@ namespace Ryujinx.HLE private readonly System.Timers.Timer _resetTimer; - public PerformanceStatistics() + public PerformanceStatistics(Switch device) { + _device = device; + _frameRate = new double[1]; _accumulatedFrameTime = new double[1]; _previousFrameTime = new double[1]; @@ -166,8 +170,11 @@ namespace Ryujinx.HLE { double frameRate = GetGameFrameRate(); double frameTime = GetGameFrameTime(); + string turboSuffix = _device.TurboMode + ? $" Turbo ({_device.TickScalar}%)" + : string.Empty; - return $"{frameRate:00.00} FPS ({frameTime:00.00}ms)"; + return $"{frameRate:00.00} FPS ({frameTime:00.00}ms){turboSuffix}"; } public string FormatFifoPercent() diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 86b04061e..dfa81b342 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -26,18 +26,26 @@ namespace Ryujinx.HLE public GpuContext Gpu { get; } public VirtualFileSystem FileSystem { get; } public HOS.Horizon System { get; } + + public bool TurboMode = false; + + public long TickScalar + { + get => System?.TickSource?.TickScalar ?? 100; + set => System.TickSource.TickScalar = value; + } + public ProcessLoader Processes { get; } public PerformanceStatistics Statistics { get; } public Hid Hid { get; } public TamperMachine TamperMachine { get; } public IHostUIHandler UIHandler { get; } - public int CpuCoresCount = 4; //Switch 1 has 4 cores + public int CpuCoresCount = 4; // Switch has a quad-core Tegra X1 SoC public VSyncMode VSyncMode { get; set; } public bool CustomVSyncIntervalEnabled { get; set; } public int CustomVSyncInterval { get; set; } - public long TargetVSyncInterval { get; set; } = 60; public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable; @@ -64,7 +72,7 @@ namespace Ryujinx.HLE Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags); Gpu = new GpuContext(Configuration.GpuRenderer, DirtyHacks); System = new HOS.Horizon(this); - Statistics = new PerformanceStatistics(); + Statistics = new PerformanceStatistics(this); Hid = new Hid(this, System.HidStorage); Processes = new ProcessLoader(this); TamperMachine = new TamperMachine(); @@ -75,6 +83,7 @@ namespace Ryujinx.HLE VSyncMode = Configuration.VSyncMode; CustomVSyncInterval = Configuration.CustomVSyncInterval; + TickScalar = TurboMode ? Configuration.TickScalar : 100; System.State.DockedMode = Configuration.EnableDockedMode; System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default; System.EnablePtc = Configuration.EnablePtc; @@ -122,6 +131,12 @@ namespace Ryujinx.HLE } } + public void ToggleTurbo() + { + TurboMode = !TurboMode; + TickScalar = TurboMode ? Configuration.TickScalar : 100; + } + public bool LoadCart(string exeFsDir, string romFsFile = null) => Processes.LoadUnpackedNca(exeFsDir, romFsFile); public bool LoadXci(string xciFile, ulong applicationId = 0) => Processes.LoadXci(xciFile, applicationId); public bool LoadNca(string ncaFile, BlitStruct? customNacpData = null) => Processes.LoadNca(ncaFile, customNacpData); diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index b741eb977..36716a0cb 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -947,7 +947,8 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Multiplayer.DisableP2p, ConfigurationState.Instance.Multiplayer.LdnPassphrase, ConfigurationState.Instance.Multiplayer.GetLdnServer(), - ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value, + ConfigurationState.Instance.Graphics.CustomVSyncInterval, + ConfigurationState.Instance.System.TickScalar, ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null)); } @@ -1249,6 +1250,12 @@ namespace Ryujinx.Ava if (currentHotkeyState != _prevHotkeyState) { + if (ConfigurationState.Instance.Hid.Hotkeys.Value.TurboModeWhileHeld && + _keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.TurboMode) != Device.TurboMode) + { + Device.ToggleTurbo(); + } + switch (currentHotkeyState) { case KeyboardHotkeyState.ToggleVSyncMode: @@ -1262,6 +1269,12 @@ namespace Ryujinx.Ava Device.IncrementCustomVSyncInterval(); _viewModel.CustomVSyncInterval += 1; break; + case KeyboardHotkeyState.TurboMode: + if (!ConfigurationState.Instance.Hid.Hotkeys.Value.TurboModeWhileHeld) + { + Device.ToggleTurbo(); + } + break; case KeyboardHotkeyState.Screenshot: ScreenshotRequested = true; break; @@ -1391,6 +1404,10 @@ namespace Ryujinx.Ava { state = KeyboardHotkeyState.CustomVSyncIntervalDecrement; } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.TurboMode)) + { + state = KeyboardHotkeyState.TurboMode; + } return state; } diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index d69c3ee9b..659f03f41 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -4897,6 +4897,81 @@ "zh_TW": "低功耗 PPTC" } }, + { + "ID": "SettingsTabSystemTurboMultiplierValue", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Turbo multiplier:", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, + { + "ID": "SettingsTabSystemTurboMultiplierSliderToolTip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "The Turbo mode multiplier target value.\n\nLeave at 100 if unsure.", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, + { + "ID": "SettingsTabSystemTurboMultiplierValueToolTip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "The Turbo mode multiplier, as a percentage of the normal Switch clock speed.\n\nLeave at 100 if unsure.", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "SettingsTabSystemEnableFsIntegrityChecks", "Translations": { @@ -5097,6 +5172,31 @@ "zh_TW": "可能導致模擬器不穩定" } }, + { + "ID": "SettingsTabCpuHacksNote", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Can and will cause crashing, slow/too fast games, etc. Use with caution.", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "SettingsTabSystemDramSize", "Translations": { @@ -23797,6 +23897,56 @@ "zh_TW": "降低自訂的重新整理頻率" } }, + { + "ID": "SettingsTabHotkeysTurboMode", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Turbo mode:", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, + { + "ID": "SettingsTabHotkeysOnlyWhilePressed", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Only while pressed", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "CompatibilityListLastUpdated", "Translations": { diff --git a/src/Ryujinx/Common/KeyboardHotkeyState.cs b/src/Ryujinx/Common/KeyboardHotkeyState.cs index 060c678d2..b6fb02f04 100644 --- a/src/Ryujinx/Common/KeyboardHotkeyState.cs +++ b/src/Ryujinx/Common/KeyboardHotkeyState.cs @@ -14,5 +14,6 @@ namespace Ryujinx.Ava.Common VolumeDown, CustomVSyncIntervalIncrement, CustomVSyncIntervalDecrement, + TurboMode, } } diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index 3ebfee751..b676b104e 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -352,7 +352,8 @@ namespace Ryujinx.Headless false, string.Empty, string.Empty, - options.CustomVSyncInterval); + options.CustomVSyncInterval, + 100); return new Switch(configuration); } diff --git a/src/Ryujinx/UI/Models/Input/HotkeyConfig.cs b/src/Ryujinx/UI/Models/Input/HotkeyConfig.cs index 40f53c673..9e557d7b1 100644 --- a/src/Ryujinx/UI/Models/Input/HotkeyConfig.cs +++ b/src/Ryujinx/UI/Models/Input/HotkeyConfig.cs @@ -28,6 +28,10 @@ namespace Ryujinx.Ava.UI.Models.Input [ObservableProperty] private Key _customVSyncIntervalDecrement; + [ObservableProperty] private Key _turboMode; + + [ObservableProperty] private bool _turboModeWhileHeld; + public HotkeyConfig(KeyboardHotkeys config) { if (config == null) @@ -44,6 +48,8 @@ namespace Ryujinx.Ava.UI.Models.Input VolumeDown = config.VolumeDown; CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement; CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement; + TurboMode = config.TurboMode; + TurboModeWhileHeld = config.TurboModeWhileHeld; } public KeyboardHotkeys GetConfig() => @@ -60,6 +66,8 @@ namespace Ryujinx.Ava.UI.Models.Input VolumeDown = VolumeDown, CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement, CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement, + TurboMode = TurboMode, + TurboModeWhileHeld = TurboModeWhileHeld }; } } diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index d0a6c6d8a..e1ab57709 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -60,6 +60,7 @@ namespace Ryujinx.Ava.UI.ViewModels private bool _enableCustomVSyncInterval; private int _customVSyncIntervalPercentageProxy; private VSyncMode _vSyncMode; + private long _turboModeMultiplier; public event Action CloseWindow; public event Action SaveSettingsEvent; @@ -207,6 +208,25 @@ namespace Ryujinx.Ava.UI.ViewModels } public bool EnablePptc { get; set; } public bool EnableLowPowerPptc { get; set; } + + + public long TurboMultiplier + { + get => _turboModeMultiplier; + set + { + if (_turboModeMultiplier != value) + { + _turboModeMultiplier = value; + + OnPropertyChanged(); + OnPropertyChanged((nameof(TurboMultiplierPercentageText))); + } + } + } + + public string TurboMultiplierPercentageText => $"{TurboMultiplier}%"; + public bool EnableInternetAccess { get; set; } public bool EnableFsIntegrityChecks { get; set; } public bool IgnoreMissingServices { get; set; } @@ -594,6 +614,7 @@ namespace Ryujinx.Ava.UI.ViewModels EnableLowPowerPptc = config.System.EnableLowPowerPtc; MemoryMode = (int)config.System.MemoryManagerMode.Value; UseHypervisor = config.System.UseHypervisor; + TurboMultiplier = config.System.TickScalar; // Graphics GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value; @@ -697,6 +718,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.System.EnableLowPowerPtc.Value = EnableLowPowerPptc; config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode; config.System.UseHypervisor.Value = UseHypervisor; + config.System.TickScalar.Value = TurboMultiplier; // Graphics config.Graphics.VSyncMode.Value = VSyncMode; diff --git a/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml index 62f087510..976309995 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml @@ -7,6 +7,7 @@ xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common" + xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" mc:Ignorable="d" x:DataType="viewModels:SettingsViewModel"> @@ -76,6 +77,56 @@ ToolTip.Tip="{ext:Locale UseHypervisorTooltip}" /> + + + + + + + + + + + + + diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml index 87b6dda7d..40121c2e1 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml @@ -19,7 +19,7 @@ - @@ -47,71 +47,79 @@ Classes="h1" Text="{ext:Locale SettingsTabHotkeysHotkeys}" /> - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + diff --git a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs index d3d1537e0..17b413b5c 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs +++ b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs @@ -116,6 +116,9 @@ namespace Ryujinx.Ava.UI.Views.Settings case "CustomVSyncIntervalDecrement": viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = buttonValue.AsHidType(); break; + case "TurboMode": + viewModel.KeyboardHotkey.TurboMode = buttonValue.AsHidType(); + break; } } }; diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs index 814a48e53..5d70ff5e1 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Ava.Utilities.Configuration /// /// The current version of the file format /// - public const int CurrentVersion = 67; + public const int CurrentVersion = 68; /// /// Version of the configuration file format @@ -258,6 +258,11 @@ namespace Ryujinx.Ava.Utilities.Configuration /// Enables or disables low-power profiled translation cache persistency loading /// public bool EnableLowPowerPtc { get; set; } + + /// + /// Clock tick scalar, in percent points (100 = 1.0). + /// + public long TickScalar { get; set; } /// /// Enables or disables guest Internet access diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs index 8a0ddb560..7e693152e 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs @@ -94,6 +94,7 @@ namespace Ryujinx.Ava.Utilities.Configuration System.EnableDockedMode.Value = cff.DockedMode; System.EnablePtc.Value = cff.EnablePtc; System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc; + System.TickScalar.Value = cff.TickScalar; System.EnableInternetAccess.Value = cff.EnableInternetAccess; System.EnableFsIntegrityChecks.Value = cff.EnableFsIntegrityChecks; System.FsGlobalAccessLogMode.Value = cff.FsGlobalAccessLogMode; @@ -441,7 +442,27 @@ namespace Ryujinx.Ava.Utilities.Configuration (64, static cff => cff.LoggingEnableAvalonia = false), (65, static cff => cff.UpdateCheckerType = cff.CheckUpdatesOnStart ? UpdaterType.PromptAtStartup : UpdaterType.Off), (66, static cff => cff.DisableInputWhenOutOfFocus = false), - (67, static cff => cff.FocusLostActionType = cff.DisableInputWhenOutOfFocus ? FocusLostType.BlockInput : FocusLostType.DoNothing) + (67, static cff => cff.FocusLostActionType = cff.DisableInputWhenOutOfFocus ? FocusLostType.BlockInput : FocusLostType.DoNothing), + (68, static cff => + { + cff.TickScalar = 200; + cff.Hotkeys = new KeyboardHotkeys + { + ToggleVSyncMode = cff.Hotkeys.ToggleVSyncMode, + Screenshot = cff.Hotkeys.Screenshot, + ShowUI = cff.Hotkeys.ShowUI, + Pause = cff.Hotkeys.Pause, + ToggleMute = cff.Hotkeys.ToggleMute, + ResScaleUp = cff.Hotkeys.ResScaleUp, + ResScaleDown = cff.Hotkeys.ResScaleDown, + VolumeUp = cff.Hotkeys.VolumeUp, + VolumeDown = cff.Hotkeys.VolumeDown, + CustomVSyncIntervalIncrement = cff.Hotkeys.CustomVSyncIntervalIncrement, + CustomVSyncIntervalDecrement = cff.Hotkeys.CustomVSyncIntervalDecrement, + TurboMode = Key.Unbound, + TurboModeWhileHeld = false + }; + }) ); } } diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs index ead99fbac..b27d27492 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs @@ -335,6 +335,11 @@ namespace Ryujinx.Ava.Utilities.Configuration /// Enables or disables persistent profiled translation cache /// public ReactiveObject EnablePtc { get; private set; } + + /// + /// Clock tick scalar, in percent points (100 = 1.0). + /// + public ReactiveObject TickScalar { get; set; } /// /// Enables or disables low-power persistent profiled translation cache loading @@ -411,6 +416,15 @@ namespace Ryujinx.Ava.Utilities.Configuration EnableLowPowerPtc.LogChangesToValue(nameof(EnableLowPowerPtc)); EnableLowPowerPtc.Event += (_, evnt) => Optimizations.LowPower = evnt.NewValue; + TickScalar = new ReactiveObject(); + TickScalar.LogChangesToValue(nameof(TickScalar)); + TickScalar.Event += (_, evnt) => + { + if (Switch.Shared is null) + return; + + Switch.Shared.Configuration.TickScalar = evnt.NewValue; + }; EnableInternetAccess = new ReactiveObject(); EnableInternetAccess.LogChangesToValue(nameof(EnableInternetAccess)); EnableFsIntegrityChecks = new ReactiveObject(); diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs index 4fdf7c4f0..44396aef4 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs @@ -73,6 +73,7 @@ namespace Ryujinx.Ava.Utilities.Configuration EnableColorSpacePassthrough = Graphics.EnableColorSpacePassthrough, EnablePtc = System.EnablePtc, EnableLowPowerPtc = System.EnableLowPowerPtc, + TickScalar = System.TickScalar, EnableInternetAccess = System.EnableInternetAccess, EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, FsGlobalAccessLogMode = System.FsGlobalAccessLogMode, @@ -261,6 +262,10 @@ namespace Ryujinx.Ava.Utilities.Configuration ResScaleDown = Key.Unbound, VolumeUp = Key.Unbound, VolumeDown = Key.Unbound, + CustomVSyncIntervalIncrement = Key.Unbound, + CustomVSyncIntervalDecrement = Key.Unbound, + TurboMode = Key.Unbound, + TurboModeWhileHeld = false }; Hid.RainbowSpeed.Value = 1f; Hid.InputConfig.Value = @@ -327,5 +332,5 @@ namespace Ryujinx.Ava.Utilities.Configuration return GraphicsBackend.OpenGl; } - } - } + } +}