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.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
index 4fc66c4c0..2ab413848 100644
--- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs
@@ -117,7 +117,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private static string GetDiskCachePath()
{
return GraphicsConfig.EnableShaderCache && GraphicsConfig.TitleId != null
- ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId, "cache", "shader")
+ ? Path.Combine(AppDataManager.GamesDirPath, GraphicsConfig.TitleId.ToLower(), "cache", "shader")
: null;
}
diff --git a/src/Ryujinx.HLE/HOS/Services/IpcService.cs b/src/Ryujinx.HLE/HOS/Services/IpcService.cs
index 1b95b6712..de58bb178 100644
--- a/src/Ryujinx.HLE/HOS/Services/IpcService.cs
+++ b/src/Ryujinx.HLE/HOS/Services/IpcService.cs
@@ -1,8 +1,10 @@
+using Gommon;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Ipc;
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -21,22 +23,43 @@ namespace Ryujinx.HLE.HOS.Services
private int _selfId;
private bool _isDomain;
- public IpcService(ServerBase server = null)
+ public IpcService(ServerBase server = null, bool registerTipc = false)
{
- CmifCommands = GetType().Assembly.GetTypes()
- .Where(type => type == GetType())
- .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
+ Stopwatch sw = Stopwatch.StartNew();
+
+ CmifCommands = GetType()
+ .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)
.SelectMany(methodInfo => methodInfo.GetCustomAttributes()
.Select(command => (command.Id, methodInfo)))
.ToDictionary(command => command.Id, command => command.methodInfo);
+
+ sw.Stop();
+
+ Logger.Debug?.Print(
+ LogClass.Emulation,
+ $"{CmifCommands.Count} Cmif commands loaded in {sw.ElapsedTicks} ticks ({Stopwatch.Frequency} tps).",
+ GetType().AsPrettyString()
+ );
- TipcCommands = GetType().Assembly.GetTypes()
- .Where(type => type == GetType())
- .SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
- .SelectMany(methodInfo => methodInfo.GetCustomAttributes()
- .Select(command => (command.Id, methodInfo)))
- .ToDictionary(command => command.Id, command => command.methodInfo);
+ if (registerTipc)
+ {
+ sw.Start();
+ TipcCommands = GetType()
+ .GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public)
+ .SelectMany(methodInfo => methodInfo.GetCustomAttributes()
+ .Select(command => (command.Id, methodInfo)))
+ .ToDictionary(command => command.Id, command => command.methodInfo);
+
+ sw.Stop();
+
+ Logger.Debug?.Print(
+ LogClass.Emulation,
+ $"{TipcCommands.Count} Tipc commands loaded in {sw.ElapsedTicks} ticks ({Stopwatch.Frequency} tps).",
+ GetType().AsPrettyString()
+ );
+ }
+
Server = server;
_parent = this;
@@ -127,10 +150,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/Sm/IUserInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
index 6d03d8d05..af511af29 100644
--- a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
@@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
private bool _isInitialized;
- public IUserInterface(KernelContext context, SmRegistry registry)
+ public IUserInterface(KernelContext context, SmRegistry registry) : base(registerTipc: true)
{
_commonServer = new ServerBase(context, "CommonServer");
_registry = registry;
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.Input.SDL2/SDL2GamepadDriver.cs b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs
index 67c68d8ec..60120ab58 100644
--- a/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs
+++ b/src/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs
@@ -57,16 +57,19 @@ namespace Ryujinx.Input.SDL2
return null;
}
+ // Remove the first 4 char of the guid (CRC part) to make it stable
+ string guidString = "0000" + guid.ToString().Substring(4);
+
string id;
lock (_lock)
{
int guidIndex = 0;
- id = guidIndex + "-" + guid;
+ id = guidIndex + "-" + guidString;
while (_gamepadsIds.Contains(id))
{
- id = (++guidIndex) + "-" + guid;
+ id = (++guidIndex) + "-" + guidString;
}
}
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 d116fe709..9307532e7 100644
--- a/src/Ryujinx/Common/LocaleManager.cs
+++ b/src/Ryujinx/Common/LocaleManager.cs
@@ -61,6 +61,13 @@ namespace Ryujinx.Ava.Common.Locale
}
}
+ 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 751a86571..a2f5af24c 100644
--- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs
+++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs
@@ -11,6 +11,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.OpenGL;
using Ryujinx.Graphics.Vulkan;
@@ -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/Systems/AppHost.cs b/src/Ryujinx/Systems/AppHost.cs
index 455afaf45..7b07000b4 100644
--- a/src/Ryujinx/Systems/AppHost.cs
+++ b/src/Ryujinx/Systems/AppHost.cs
@@ -4,6 +4,7 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input;
using Avalonia.Threading;
using DiscordRPC;
+using Gommon;
using LibHac.Common;
using LibHac.Ns;
using Ryujinx.Audio.Backends.Dummy;
@@ -1115,11 +1116,23 @@ namespace Ryujinx.Ava.Systems
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;
@@ -1215,6 +1228,12 @@ namespace Ryujinx.Ava.Systems
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:
@@ -1226,6 +1245,12 @@ namespace Ryujinx.Ava.Systems
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;
@@ -1355,6 +1380,10 @@ namespace Ryujinx.Ava.Systems
{
state = KeyboardHotkeyState.CustomVSyncIntervalDecrement;
}
+ else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.TurboMode))
+ {
+ state = KeyboardHotkeyState.TurboMode;
+ }
return state;
}
diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs
index c5315ab12..470749674 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs
@@ -258,6 +258,11 @@ namespace Ryujinx.Ava.Systems.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/Systems/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs
index b10cc3926..20c4c6414 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs
@@ -93,6 +93,7 @@ namespace Ryujinx.Ava.Systems.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;
@@ -438,9 +439,27 @@ namespace Ryujinx.Ava.Systems.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)
- // 68 was the version that added per-game configs; the file structure did not change
- // the version was increased so external tools could know that your Ryujinx version has per-game config capabilities.
+ (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/Systems/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs
index b52c624e3..205054474 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs
@@ -335,6 +335,11 @@ namespace Ryujinx.Ava.Systems.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
@@ -415,6 +420,15 @@ namespace Ryujinx.Ava.Systems.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();
@@ -842,6 +856,7 @@ namespace Ryujinx.Ava.Systems.Configuration
Graphics.VSyncMode,
System.EnableDockedMode,
System.EnablePtc,
+ System.TickScalar,
System.EnableInternetAccess,
System.EnableFsIntegrityChecks
? IntegrityCheckLevel.ErrorOnInvalid
@@ -860,8 +875,8 @@ namespace Ryujinx.Ava.Systems.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/Systems/Configuration/ConfigurationState.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs
index 6fe35c744..65e8e02ce 100644
--- a/src/Ryujinx/Systems/Configuration/ConfigurationState.cs
+++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs
@@ -72,6 +72,7 @@ namespace Ryujinx.Ava.Systems.Configuration
EnableColorSpacePassthrough = Graphics.EnableColorSpacePassthrough,
EnablePtc = System.EnablePtc,
EnableLowPowerPtc = System.EnableLowPowerPtc,
+ TickScalar = System.TickScalar,
EnableInternetAccess = System.EnableInternetAccess,
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
@@ -260,6 +261,10 @@ namespace Ryujinx.Ava.Systems.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 =
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 a092e97f2..0be6ab3fe 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;
@@ -206,6 +207,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; }
@@ -592,6 +612,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;
@@ -694,6 +715,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..b3a4b66f9 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 54ef00c38..82e01b609 100644
--- a/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs
+++ b/src/Ryujinx/UI/Views/Settings/SettingsHotkeysView.axaml.cs
@@ -110,6 +110,9 @@ namespace Ryujinx.Ava.UI.Views.Settings
ViewModel.KeyboardHotkey.CustomVSyncIntervalDecrement =
buttonValue.AsHidType();
break;
+ case "TurboMode":
+ ViewModel.KeyboardHotkey.TurboMode = buttonValue.AsHidType();
+ break;
}
});
}