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..4aff612f0 100644
--- a/src/Ryujinx.Cpu/ITickSource.cs
+++ b/src/Ryujinx.Cpu/ITickSource.cs
@@ -8,10 +8,17 @@ namespace Ryujinx.Cpu
///
public interface ITickSource : ICounter
{
+ public const long RealityTickScalar = 100;
+
///
/// 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.Graphics.Gpu/Engine/Threed/StateUpdater.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
index f50ec852e..4620821cb 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdater.cs
@@ -1065,7 +1065,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
///
private void UpdateIndexBufferState()
{
- IndexBufferState indexBuffer = _state.State.IndexBufferState;
+ IndexBufferState? indexBufferNullable = _state?.State.IndexBufferState;
+
+ if (!indexBufferNullable.HasValue)
+ {
+ return;
+ }
+
+ IndexBufferState indexBuffer = indexBufferNullable.Value;
if (_drawState.IndexCount == 0)
{
diff --git a/src/Ryujinx.HLE/HOS/Services/IpcService.cs b/src/Ryujinx.HLE/HOS/Services/IpcService.cs
index 1b95b6712..f1ac7c5ac 100644
--- a/src/Ryujinx.HLE/HOS/Services/IpcService.cs
+++ b/src/Ryujinx.HLE/HOS/Services/IpcService.cs
@@ -127,10 +127,7 @@ namespace Ryujinx.HLE.HOS.Services
}
else
{
- string serviceName;
-
-
- serviceName = (service is not DummyService dummyService) ? service.GetType().FullName : dummyService.ServiceName;
+ string serviceName = (service is not DummyService dummyService) ? service.GetType().FullName : dummyService.ServiceName;
Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored");
}
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/HleConfiguration.cs b/src/Ryujinx.HLE/HleConfiguration.cs
index 97835033e..39745ff53 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.
@@ -201,6 +206,7 @@ namespace Ryujinx.HLE
VSyncMode vSyncMode,
bool enableDockedMode,
bool enablePtc,
+ long tickScalar,
bool enableInternetAccess,
IntegrityCheckLevel fsIntegrityCheckLevel,
int fsGlobalAccessLogMode,
@@ -226,6 +232,7 @@ namespace Ryujinx.HLE
CustomVSyncInterval = customVSyncInterval;
EnableDockedMode = enableDockedMode;
EnablePtc = enablePtc;
+ TickScalar = tickScalar;
EnableInternetAccess = enableInternetAccess;
FsIntegrityCheckLevel = fsIntegrityCheckLevel;
FsGlobalAccessLogMode = fsGlobalAccessLogMode;
diff --git a/src/Ryujinx.HLE/PerformanceStatistics.cs b/src/Ryujinx.HLE/PerformanceStatistics.cs
index e80faa7d2..9363ff2d3 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];
@@ -162,14 +166,6 @@ namespace Ryujinx.HLE
return 1000 / _frameRate[FrameTypeGame];
}
- public string FormatGameFrameRate()
- {
- double frameRate = GetGameFrameRate();
- double frameTime = GetGameFrameTime();
-
- return $"{frameRate:00.00} FPS ({frameTime:00.00}ms)";
- }
-
public string FormatFifoPercent()
{
double fifoPercent = GetFifoPercent();
diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs
index df5b48103..e52b3df15 100644
--- a/src/Ryujinx.HLE/Switch.cs
+++ b/src/Ryujinx.HLE/Switch.cs
@@ -4,6 +4,7 @@ using Ryujinx.Audio.Backends.CompatLayer;
using Ryujinx.Audio.Integration;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
+using Ryujinx.Cpu;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
@@ -26,18 +27,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 ?? ITickSource.RealityTickScalar;
+ 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 +73,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 +84,7 @@ namespace Ryujinx.HLE
VSyncMode = Configuration.VSyncMode;
CustomVSyncInterval = Configuration.CustomVSyncInterval;
+ TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar;
System.State.DockedMode = Configuration.EnableDockedMode;
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
System.EnablePtc = Configuration.EnablePtc;
@@ -126,6 +136,12 @@ namespace Ryujinx.HLE
}
}
+ public void ToggleTurbo()
+ {
+ TurboMode = !TurboMode;
+ TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar;
+ }
+
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 40c54b6f7..c2fb8b873 100644
--- a/src/Ryujinx/AppHost.cs
+++ b/src/Ryujinx/AppHost.cs
@@ -4,9 +4,9 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input;
using Avalonia.Threading;
using DiscordRPC;
+using Gommon;
using LibHac.Common;
using LibHac.Ns;
-using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.Dummy;
using Ryujinx.Audio.Backends.OpenAL;
using Ryujinx.Audio.Backends.SDL2;
@@ -35,11 +35,9 @@ using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
-using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
-using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.Input;
using Ryujinx.Input.HLE;
using SkiaSharp;
@@ -1118,11 +1116,23 @@ namespace Ryujinx.Ava
LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%",
dockedMode,
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
- Device.Statistics.FormatGameFrameRate(),
+ FormatGameFrameRate(),
Device.Statistics.FormatFifoPercent(),
_displayCount));
}
+ private string FormatGameFrameRate()
+ {
+ string frameRate = Device.Statistics.GetGameFrameRate().ToString("00.00");
+ string frameTime = Device.Statistics.GetGameFrameTime().ToString("00.00");
+
+ return Device.TurboMode
+ ? LocaleManager.GetUnformatted(LocaleKeys.FpsTurboStatusBarText)
+ .Format(frameRate, frameTime, Device.TickScalar)
+ : LocaleManager.GetUnformatted(LocaleKeys.FpsStatusBarText)
+ .Format(frameRate, frameTime);
+ }
+
public async Task ShowExitPrompt()
{
bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit;
@@ -1218,6 +1228,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:
@@ -1229,6 +1245,12 @@ namespace Ryujinx.Ava
case KeyboardHotkeyState.CustomVSyncIntervalIncrement:
_viewModel.CustomVSyncInterval = Device.IncrementCustomVSyncInterval();
break;
+ case KeyboardHotkeyState.TurboMode:
+ if (!ConfigurationState.Instance.Hid.Hotkeys.Value.TurboModeWhileHeld)
+ {
+ Device.ToggleTurbo();
+ }
+ break;
case KeyboardHotkeyState.Screenshot:
ScreenshotRequested = true;
break;
@@ -1358,6 +1380,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..cd6775bae 100644
--- a/src/Ryujinx/Assets/locales.json
+++ b/src/Ryujinx/Assets/locales.json
@@ -4897,6 +4897,81 @@
"zh_TW": "低功耗 PPTC"
}
},
+ {
+ "ID": "SettingsTabSystemTurboMultiplier",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "Turbo Mode multiplier:",
+ "es_ES": "",
+ "fr_FR": "Multiplicateur du Mode Turbo :",
+ "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 target value.\n\nLeave at 200 if unsure.",
+ "es_ES": "",
+ "fr_FR": "La valeur souhaitée du multiplicateur du Mode Turbo.\n\nGarder à 200 si incertain.",
+ "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": "SettingsTabSystemTurboMultiplierToolTip",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "Turbo mode is an emulator feature which effectively causes speed up or slow down when a game is not frame-rate sensitive.\nYou can toggle this feature in-game with a hotkey, configurable in Ryujinx Keyboard Hotkeys settings.\n\nLeave at 200 if unsure.",
+ "es_ES": "",
+ "fr_FR": "Le Mode Turbo est une fonctionnalité de l'émulateur qui accélère ou ralentit le jeu lorsque ce dernier n'est pas sensible au framerate.\nVous pouvez changer cette option en jeu avec un raccourci clavier, configurable dans les paramètres de Raccourcis clavier de Ryujinx.\n\nGarder à 200 si incertain.",
+ "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": {
@@ -10480,7 +10555,7 @@
"el_GR": "",
"en_US": "Unbound",
"es_ES": "",
- "fr_FR": "Pas Attribuée",
+ "fr_FR": "Non Attribuée",
"he_IL": "",
"it_IT": "Non assegnato",
"ja_JP": "",
@@ -18072,6 +18147,56 @@
"zh_TW": "更新已停用!"
}
},
+ {
+ "ID": "FpsStatusBarText",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "{0} FPS ({1}ms)",
+ "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": "FpsTurboStatusBarText",
+ "Translations": {
+ "ar_SA": "{0} FPS ({1}ms), التوربو %{2}",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "{0} FPS ({1}ms), Turbo ({2}%)",
+ "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": "UpdaterBackgroundStatusBarButtonText",
"Translations": {
@@ -23797,6 +23922,81 @@
"zh_TW": "降低自訂的重新整理頻率"
}
},
+ {
+ "ID": "SettingsTabHotkeysTurboMode",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "Turbo mode:",
+ "es_ES": "",
+ "fr_FR": "Mode Turbo :",
+ "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": "SettingsTabHotkeysTurboModeToolTip",
+ "Translations": {
+ "ar_SA": "",
+ "de_DE": "",
+ "el_GR": "",
+ "en_US": "The Turbo mode hotkey.\nConfigure the behavior of Turbo mode in Ryujinx CPU settings.\n\nLeave Unbound if unsure.",
+ "es_ES": "",
+ "fr_FR": "Le raccourci clavier Mode Turbo.\nConfigurez le comportement du Mode Turbo dans les paramètres de CPU de Ryujinx.\n\nLaisser Non Attribuée si incertain.",
+ "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": "Seulement quand le raccourci est maintenu",
+ "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/Common/LocaleManager.cs b/src/Ryujinx/Common/LocaleManager.cs
index f60cff49b..9320985f7 100644
--- a/src/Ryujinx/Common/LocaleManager.cs
+++ b/src/Ryujinx/Common/LocaleManager.cs
@@ -57,6 +57,13 @@ namespace Ryujinx.Ava.Common.Locale
SetDynamicValues(LocaleKeys.RyujinxRebooter, RyujinxApp.FullAppName);
}
+ public static string GetUnformatted(LocaleKeys key) => Instance.Get(key);
+
+ public string Get(LocaleKeys key) =>
+ _localeStrings.TryGetValue(key, out string value)
+ ? value
+ : key.ToString();
+
public string this[LocaleKeys key]
{
get
diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs
index d6a2ed789..beb28e885 100644
--- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs
+++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs
@@ -10,6 +10,7 @@ using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities;
+using Ryujinx.Cpu;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.OpenGL;
@@ -311,7 +312,7 @@ namespace Ryujinx.Headless
return new OpenGLRenderer();
}
-
+
private static Switch InitializeEmulationContext(WindowBase window, IRenderer renderer, Options options) =>
new(
new HleConfiguration(
@@ -321,6 +322,7 @@ namespace Ryujinx.Headless
options.VSyncMode,
!options.DisableDockedMode,
!options.DisablePTC,
+ ITickSource.RealityTickScalar,
options.EnableInternetAccess,
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
options.FsGlobalAccessLogMode,
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..9c2977ba1 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,57 @@
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..917177fb5 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 2ebf5725b..2d839f9b1 100644
--- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs
+++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs
@@ -336,6 +336,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
@@ -412,6 +417,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();
@@ -848,6 +862,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
Graphics.VSyncMode,
System.EnableDockedMode,
System.EnablePtc,
+ System.TickScalar,
System.EnableInternetAccess,
System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
@@ -866,8 +881,8 @@ namespace Ryujinx.Ava.Utilities.Configuration
Multiplayer.Mode,
Multiplayer.DisableP2p,
Multiplayer.LdnPassphrase,
- Instance.Multiplayer.GetLdnServer(),
- Instance.Graphics.CustomVSyncInterval,
- Instance.Hacks.ShowDirtyHacks ? Instance.Hacks.EnabledHacks : null);
+ Multiplayer.GetLdnServer(),
+ Graphics.CustomVSyncInterval,
+ Hacks.ShowDirtyHacks ? Hacks.EnabledHacks : null);
}
}
diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs
index 5fb313cad..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 =