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

Co-authored-by: KeatonTheBot <keaton@ryujinx.app>
This commit is contained in:
GreemDev 2025-03-25 02:37:22 +00:00 committed by KeatonTheBot
parent 5c6175cebf
commit c56d04a9a9
23 changed files with 325 additions and 43 deletions

View file

@ -13,5 +13,7 @@ namespace Ryujinx.Common.Configuration.Hid
public Key VolumeDown { get; set; } public Key VolumeDown { get; set; }
public Key CustomVSyncIntervalIncrement { get; set; } public Key CustomVSyncIntervalIncrement { get; set; }
public Key CustomVSyncIntervalDecrement { get; set; } public Key CustomVSyncIntervalDecrement { get; set; }
public Key TurboMode { get; set; }
public bool TurboModeWhileHeld { get; set; }
} }
} }

View file

@ -8,11 +8,18 @@ namespace Ryujinx.Cpu
/// </summary> /// </summary>
public interface ITickSource : ICounter public interface ITickSource : ICounter
{ {
public const long RealityTickScalar = 100;
/// <summary> /// <summary>
/// Time elapsed since the counter was created. /// Time elapsed since the counter was created.
/// </summary> /// </summary>
TimeSpan ElapsedTime { get; } TimeSpan ElapsedTime { get; }
/// <summary>
/// Clock tick scalar, in percent points (100 = 1.0).
/// </summary>
long TickScalar { get; set; }
/// <summary> /// <summary>
/// Time elapsed since the counter was created, in seconds. /// Time elapsed since the counter was created, in seconds.
/// </summary> /// </summary>

View file

@ -15,11 +15,36 @@ namespace Ryujinx.Cpu
/// <inheritdoc/> /// <inheritdoc/>
public ulong Counter => (ulong)(ElapsedSeconds * Frequency); public ulong Counter => (ulong)(ElapsedSeconds * Frequency);
/// <inheritdoc/>
public TimeSpan ElapsedTime => _tickCounter.Elapsed; 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;
}
}
/// <inheritdoc/> /// <inheritdoc/>
public double ElapsedSeconds => _tickCounter.ElapsedTicks * _hostTickFreq;
public TimeSpan ElapsedTime => Stopwatch.GetElapsedTime(0, ElapsedTicks);
/// <inheritdoc/>
public double ElapsedSeconds => ElapsedTicks * _hostTickFreq;
public TickSource(ulong frequency) public TickSource(ulong frequency)
{ {

View file

@ -1072,7 +1072,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
private void UpdateIndexBufferState() private void UpdateIndexBufferState()
{ {
var indexBuffer = _state.State.IndexBufferState; var indexBufferNullable = _state?.State.IndexBufferState;
if (!indexBufferNullable.HasValue)
{
return;
}
IndexBufferState indexBuffer = indexBufferNullable.Value;
if (_drawState.IndexCount == 0) if (_drawState.IndexCount == 0)
{ {

View file

@ -104,6 +104,11 @@ namespace Ryujinx.HLE
/// </summary> /// </summary>
internal readonly bool EnablePtc; internal readonly bool EnablePtc;
/// <summary>
/// Control the arbitrary scalar applied to emulated CPU tick timing.
/// </summary>
public long TickScalar { get; set; }
/// <summary> /// <summary>
/// Control if the guest application should be told that there is a Internet connection available. /// Control if the guest application should be told that there is a Internet connection available.
/// </summary> /// </summary>
@ -219,6 +224,7 @@ namespace Ryujinx.HLE
VSyncMode vSyncMode, VSyncMode vSyncMode,
bool enableDockedMode, bool enableDockedMode,
bool enablePtc, bool enablePtc,
long tickScalar,
bool enableInternetAccess, bool enableInternetAccess,
IntegrityCheckLevel fsIntegrityCheckLevel, IntegrityCheckLevel fsIntegrityCheckLevel,
int fsGlobalAccessLogMode, int fsGlobalAccessLogMode,
@ -254,6 +260,7 @@ namespace Ryujinx.HLE
CustomVSyncInterval = customVSyncInterval; CustomVSyncInterval = customVSyncInterval;
EnableDockedMode = enableDockedMode; EnableDockedMode = enableDockedMode;
EnablePtc = enablePtc; EnablePtc = enablePtc;
TickScalar = tickScalar;
EnableInternetAccess = enableInternetAccess; EnableInternetAccess = enableInternetAccess;
FsIntegrityCheckLevel = fsIntegrityCheckLevel; FsIntegrityCheckLevel = fsIntegrityCheckLevel;
FsGlobalAccessLogMode = fsGlobalAccessLogMode; FsGlobalAccessLogMode = fsGlobalAccessLogMode;

View file

@ -127,10 +127,7 @@ namespace Ryujinx.HLE.HOS.Services
} }
else else
{ {
string serviceName; string serviceName = (service is not DummyService dummyService) ? service.GetType().FullName : dummyService.ServiceName;
serviceName = (service is not DummyService dummyService) ? service.GetType().FullName : dummyService.ServiceName;
Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored"); Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored");
} }

View file

@ -2,6 +2,7 @@ using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.PreciseSleep; using Ryujinx.Common.PreciseSleep;
using Ryujinx.Cpu;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
@ -90,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
} }
else else
{ {
_ticksPerFrame = Stopwatch.Frequency / _device.TargetVSyncInterval; _ticksPerFrame = ((Stopwatch.Frequency / _device.TargetVSyncInterval) * 100) / _device.TickScalar;
_targetVSyncInterval = _device.TargetVSyncInterval; _targetVSyncInterval = _device.TargetVSyncInterval;
} }
} }

View file

@ -6,6 +6,8 @@ namespace Ryujinx.HLE
{ {
public class PerformanceStatistics public class PerformanceStatistics
{ {
private readonly Switch _device;
private const int FrameTypeGame = 0; private const int FrameTypeGame = 0;
private const int PercentTypeFifo = 0; private const int PercentTypeFifo = 0;
@ -28,8 +30,10 @@ namespace Ryujinx.HLE
private readonly System.Timers.Timer _resetTimer; private readonly System.Timers.Timer _resetTimer;
public PerformanceStatistics() public PerformanceStatistics(Switch device)
{ {
_device = device;
_frameRate = new double[1]; _frameRate = new double[1];
_accumulatedFrameTime = new double[1]; _accumulatedFrameTime = new double[1];
_previousFrameTime = new double[1]; _previousFrameTime = new double[1];

View file

@ -1,6 +1,7 @@
using Ryujinx.Audio.Backends.CompatLayer; using Ryujinx.Audio.Backends.CompatLayer;
using Ryujinx.Audio.Integration; using Ryujinx.Audio.Integration;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Cpu;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
@ -17,12 +18,23 @@ namespace Ryujinx.HLE
{ {
public class Switch : IDisposable public class Switch : IDisposable
{ {
public static Switch Shared { get; private set; }
public HLEConfiguration Configuration { get; } public HLEConfiguration Configuration { get; }
public IHardwareDeviceDriver AudioDeviceDriver { get; } public IHardwareDeviceDriver AudioDeviceDriver { get; }
public MemoryBlock Memory { get; } public MemoryBlock Memory { get; }
public GpuContext Gpu { get; } public GpuContext Gpu { get; }
public VirtualFileSystem FileSystem { get; } public VirtualFileSystem FileSystem { get; }
public HOS.Horizon System { 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 ProcessLoader Processes { get; }
public PerformanceStatistics Statistics { get; } public PerformanceStatistics Statistics { get; }
public Hid Hid { get; } public Hid Hid { get; }
@ -30,12 +42,11 @@ namespace Ryujinx.HLE
public IHostUIHandler UIHandler { get; } public IHostUIHandler UIHandler { get; }
public Debugger.Debugger Debugger { get; } public Debugger.Debugger Debugger { 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; } = VSyncMode.Switch; public VSyncMode VSyncMode { get; set; } = VSyncMode.Switch;
public bool CustomVSyncIntervalEnabled { get; set; } = false; public bool CustomVSyncIntervalEnabled { get; set; } = false;
public int CustomVSyncInterval { get; set; } public int CustomVSyncInterval { get; set; }
public long TargetVSyncInterval { get; set; } = 60; public long TargetVSyncInterval { get; set; } = 60;
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable; public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
@ -60,7 +71,7 @@ namespace Ryujinx.HLE
Gpu = new GpuContext(Configuration.GpuRenderer); Gpu = new GpuContext(Configuration.GpuRenderer);
Debugger = Configuration.EnableGdbStub ? new Debugger.Debugger(this, Configuration.GdbStubPort) : null; Debugger = Configuration.EnableGdbStub ? new Debugger.Debugger(this, Configuration.GdbStubPort) : null;
System = new HOS.Horizon(this); System = new HOS.Horizon(this);
Statistics = new PerformanceStatistics(); Statistics = new PerformanceStatistics(this);
Hid = new Hid(this, System.HidStorage); Hid = new Hid(this, System.HidStorage);
Processes = new ProcessLoader(this); Processes = new ProcessLoader(this);
TamperMachine = new TamperMachine(); TamperMachine = new TamperMachine();
@ -71,6 +82,7 @@ namespace Ryujinx.HLE
VSyncMode = Configuration.VSyncMode; VSyncMode = Configuration.VSyncMode;
CustomVSyncInterval = Configuration.CustomVSyncInterval; CustomVSyncInterval = Configuration.CustomVSyncInterval;
TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar;
System.State.DockedMode = Configuration.EnableDockedMode; System.State.DockedMode = Configuration.EnableDockedMode;
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default; System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
System.EnablePtc = Configuration.EnablePtc; System.EnablePtc = Configuration.EnablePtc;
@ -78,6 +90,8 @@ namespace Ryujinx.HLE
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode; System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
UpdateVSyncInterval(); UpdateVSyncInterval();
#pragma warning restore IDE0055 #pragma warning restore IDE0055
Shared = this;
} }
public bool LoadCart(string exeFsDir, string romFsFile = null) public bool LoadCart(string exeFsDir, string romFsFile = null)
@ -175,6 +189,12 @@ namespace Ryujinx.HLE
} }
} }
public void ToggleTurbo()
{
TurboMode = !TurboMode;
TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar;
}
public void SetVolume(float volume) public void SetVolume(float volume)
{ {
AudioDeviceDriver.Volume = Math.Clamp(volume, 0f, 1f); AudioDeviceDriver.Volume = Math.Clamp(volume, 0f, 1f);
@ -215,6 +235,8 @@ namespace Ryujinx.HLE
FileSystem.Dispose(); FileSystem.Dispose();
Memory.Dispose(); Memory.Dispose();
Debugger?.Dispose(); Debugger?.Dispose();
Shared = null;
} }
} }
} }

View file

@ -240,6 +240,11 @@ namespace Ryujinx.UI.Common.Configuration
/// </summary> /// </summary>
public bool EnableLowPowerPtc { get; set; } public bool EnableLowPowerPtc { get; set; }
/// <summary>
/// Clock tick scalar, in percent points (100 = 1.0).
/// </summary>
public long TickScalar { get; set; }
/// <summary> /// <summary>
/// Enables or disables guest Internet access /// Enables or disables guest Internet access
/// </summary> /// </summary>

View file

@ -12,7 +12,6 @@ using Ryujinx.UI.Common.Configuration.UI;
using Ryujinx.UI.Common.Helper; using Ryujinx.UI.Common.Helper;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization; using System.Globalization;
namespace Ryujinx.UI.Common.Configuration namespace Ryujinx.UI.Common.Configuration
@ -341,6 +340,11 @@ namespace Ryujinx.UI.Common.Configuration
/// </summary> /// </summary>
public ReactiveObject<bool> EnablePtc { get; private set; } public ReactiveObject<bool> EnablePtc { get; private set; }
/// <summary>
/// Clock tick scalar, in percent points (100 = 1.0).
/// </summary>
public ReactiveObject<long> TickScalar { get; set; }
/// <summary> /// <summary>
/// Enables or disables low-power profiled translation cache persistency loading /// Enables or disables low-power profiled translation cache persistency loading
/// </summary> /// </summary>
@ -406,6 +410,15 @@ namespace Ryujinx.UI.Common.Configuration
EnablePtc.Event += static (sender, e) => LogValueChange(e, nameof(EnablePtc)); EnablePtc.Event += static (sender, e) => LogValueChange(e, nameof(EnablePtc));
EnableLowPowerPtc = new ReactiveObject<bool>(); EnableLowPowerPtc = new ReactiveObject<bool>();
EnableLowPowerPtc.Event += static (sender, e) => LogValueChange(e, nameof(EnableLowPowerPtc)); EnableLowPowerPtc.Event += static (sender, e) => LogValueChange(e, nameof(EnableLowPowerPtc));
TickScalar = new ReactiveObject<long>();
TickScalar.Event += static (sender, e) => LogValueChange(e, nameof(TickScalar));
TickScalar.Event += static (sender, e) =>
{
if (Switch.Shared is null)
return;
Switch.Shared.Configuration.TickScalar = e.NewValue;
};
EnableInternetAccess = new ReactiveObject<bool>(); EnableInternetAccess = new ReactiveObject<bool>();
EnableInternetAccess.Event += static (sender, e) => LogValueChange(e, nameof(EnableInternetAccess)); EnableInternetAccess.Event += static (sender, e) => LogValueChange(e, nameof(EnableInternetAccess));
EnableFsIntegrityChecks = new ReactiveObject<bool>(); EnableFsIntegrityChecks = new ReactiveObject<bool>();
@ -813,6 +826,7 @@ namespace Ryujinx.UI.Common.Configuration
EnableColorSpacePassthrough = Graphics.EnableColorSpacePassthrough, EnableColorSpacePassthrough = Graphics.EnableColorSpacePassthrough,
EnablePtc = System.EnablePtc, EnablePtc = System.EnablePtc,
EnableLowPowerPtc = System.EnableLowPowerPtc, EnableLowPowerPtc = System.EnableLowPowerPtc,
TickScalar = System.TickScalar,
EnableInternetAccess = System.EnableInternetAccess, EnableInternetAccess = System.EnableInternetAccess,
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode, FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
@ -1000,6 +1014,10 @@ namespace Ryujinx.UI.Common.Configuration
ResScaleDown = Key.Unbound, ResScaleDown = Key.Unbound,
VolumeUp = Key.Unbound, VolumeUp = Key.Unbound,
VolumeDown = Key.Unbound, VolumeDown = Key.Unbound,
CustomVSyncIntervalIncrement = Key.Unbound,
CustomVSyncIntervalDecrement = Key.Unbound,
TurboMode = Key.Unbound,
TurboModeWhileHeld = false
}; };
Hid.InputConfig.Value = Hid.InputConfig.Value =
[ [
@ -1690,6 +1708,26 @@ namespace Ryujinx.UI.Common.Configuration
configurationFileFormat.MatchSystemTime = false; configurationFileFormat.MatchSystemTime = false;
// Turbo Mode should be version 59 - KeatonTheBot
// TODO: Fix config migration for Turbo Mode
configurationFileFormat.TickScalar = 200;
configurationFileFormat.Hotkeys = new KeyboardHotkeys
{
ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode,
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
Pause = configurationFileFormat.Hotkeys.Pause,
ToggleMute = configurationFileFormat.Hotkeys.ToggleMute,
ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp,
ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown,
VolumeUp = configurationFileFormat.Hotkeys.VolumeUp,
VolumeDown = configurationFileFormat.Hotkeys.VolumeDown,
CustomVSyncIntervalIncrement = configurationFileFormat.Hotkeys.CustomVSyncIntervalIncrement,
CustomVSyncIntervalDecrement = configurationFileFormat.Hotkeys.CustomVSyncIntervalDecrement,
TurboMode = Key.Unbound,
TurboModeWhileHeld = false
};
configurationFileUpdated = true; configurationFileUpdated = true;
} }
@ -1737,6 +1775,7 @@ namespace Ryujinx.UI.Common.Configuration
Graphics.EnableColorSpacePassthrough.Value = configurationFileFormat.EnableColorSpacePassthrough; Graphics.EnableColorSpacePassthrough.Value = configurationFileFormat.EnableColorSpacePassthrough;
System.EnablePtc.Value = configurationFileFormat.EnablePtc; System.EnablePtc.Value = configurationFileFormat.EnablePtc;
System.EnableLowPowerPtc.Value = configurationFileFormat.EnableLowPowerPtc; System.EnableLowPowerPtc.Value = configurationFileFormat.EnableLowPowerPtc;
System.TickScalar.Value = configurationFileFormat.TickScalar;
System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess; System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess;
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks; System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;
System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode; System.FsGlobalAccessLogMode.Value = configurationFileFormat.FsGlobalAccessLogMode;

View file

@ -3,6 +3,7 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Threading; using Avalonia.Threading;
using Gommon;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using Ryujinx.Audio.Backends.Dummy; using Ryujinx.Audio.Backends.Dummy;
using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Audio.Backends.OpenAL;
@ -939,6 +940,7 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.VSyncMode, ConfigurationState.Instance.Graphics.VSyncMode,
ConfigurationState.Instance.System.EnableDockedMode, ConfigurationState.Instance.System.EnableDockedMode,
ConfigurationState.Instance.System.EnablePtc, ConfigurationState.Instance.System.EnablePtc,
ConfigurationState.Instance.System.TickScalar,
ConfigurationState.Instance.System.EnableInternetAccess, ConfigurationState.Instance.System.EnableInternetAccess,
ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
ConfigurationState.Instance.System.FsGlobalAccessLogMode, ConfigurationState.Instance.System.FsGlobalAccessLogMode,
@ -1162,6 +1164,18 @@ namespace Ryujinx.Ava
_displayCount)); _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() public async Task ShowExitPrompt()
{ {
bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit; bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit;
@ -1272,6 +1286,12 @@ namespace Ryujinx.Ava
if (currentHotkeyState != _prevHotkeyState) 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) switch (currentHotkeyState)
{ {
case KeyboardHotkeyState.ToggleVSyncMode: case KeyboardHotkeyState.ToggleVSyncMode:
@ -1282,9 +1302,14 @@ namespace Ryujinx.Ava
_viewModel.CustomVSyncInterval -= 1; _viewModel.CustomVSyncInterval -= 1;
break; break;
case KeyboardHotkeyState.CustomVSyncIntervalIncrement: case KeyboardHotkeyState.CustomVSyncIntervalIncrement:
Device.IncrementCustomVSyncInterval();
_viewModel.CustomVSyncInterval += 1; _viewModel.CustomVSyncInterval += 1;
break; break;
case KeyboardHotkeyState.TurboMode:
if (!ConfigurationState.Instance.Hid.Hotkeys.Value.TurboModeWhileHeld)
{
Device.ToggleTurbo();
}
break;
case KeyboardHotkeyState.Screenshot: case KeyboardHotkeyState.Screenshot:
ScreenshotRequested = true; ScreenshotRequested = true;
break; break;
@ -1414,6 +1439,10 @@ namespace Ryujinx.Ava
{ {
state = KeyboardHotkeyState.CustomVSyncIntervalDecrement; state = KeyboardHotkeyState.CustomVSyncIntervalDecrement;
} }
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.TurboMode))
{
state = KeyboardHotkeyState.TurboMode;
}
return state; return state;
} }

View file

@ -803,5 +803,6 @@
"MultiplayerMode": "الوضع:", "MultiplayerMode": "الوضع:",
"MultiplayerModeTooltip": "تغيير وضع LDN متعدد اللاعبين.\n\nسوف يقوم LdnMitm بتعديل وظيفة اللعب المحلية/اللاسلكية المحلية في الألعاب لتعمل كما لو كانت شبكة LAN، مما يسمح باتصالات الشبكة المحلية نفسها مع محاكيات ريوجينكس الأخرى وأجهزة نينتندو سويتش المخترقة التي تم تثبيت وحدة ldn_mitm عليها.\n\nيتطلب وضع اللاعبين المتعددين أن يكون جميع اللاعبين على نفس إصدار اللعبة (على سبيل المثال، يتعذر على الإصدار 13.0.1 من سوبر سماش برذرز ألتميت الاتصال بالإصدار 13.0.0).\n\nاتركه معطلا إذا لم تكن متأكدا.", "MultiplayerModeTooltip": "تغيير وضع LDN متعدد اللاعبين.\n\nسوف يقوم LdnMitm بتعديل وظيفة اللعب المحلية/اللاسلكية المحلية في الألعاب لتعمل كما لو كانت شبكة LAN، مما يسمح باتصالات الشبكة المحلية نفسها مع محاكيات ريوجينكس الأخرى وأجهزة نينتندو سويتش المخترقة التي تم تثبيت وحدة ldn_mitm عليها.\n\nيتطلب وضع اللاعبين المتعددين أن يكون جميع اللاعبين على نفس إصدار اللعبة (على سبيل المثال، يتعذر على الإصدار 13.0.1 من سوبر سماش برذرز ألتميت الاتصال بالإصدار 13.0.0).\n\nاتركه معطلا إذا لم تكن متأكدا.",
"MultiplayerModeDisabled": "معطل", "MultiplayerModeDisabled": "معطل",
"MultiplayerModeLdnMitm": "ldn_mitm" "MultiplayerModeLdnMitm": "ldn_mitm",
"FpsTurboStatusBarText": "{0} FPS ({1}ms), التوربو %{2}"
} }

View file

@ -905,5 +905,13 @@
"SettingsTabDebugGDBStubToggleTooltip": "Enables the GDB stub, which makes it possible to debug the running application. For development use only!", "SettingsTabDebugGDBStubToggleTooltip": "Enables the GDB stub, which makes it possible to debug the running application. For development use only!",
"SettingsTabDebugGDBStubPort": "GDB Stub Port:", "SettingsTabDebugGDBStubPort": "GDB Stub Port:",
"SettingsTabDebugSuspendOnStart": "Suspend Application on Start", "SettingsTabDebugSuspendOnStart": "Suspend Application on Start",
"SettingsTabDebugSuspendOnStartTooltip": "Suspends the application before executing the first instruction, allowing for debugging from the earliest point." "SettingsTabDebugSuspendOnStartTooltip": "Suspends the application before executing the first instruction, allowing for debugging from the earliest point.",
"SettingsTabSystemTurboMultiplier": "Turbo Mode multiplier:",
"SettingsTabSystemTurboMultiplierValueToolTip": "The Turbo mode multiplier target value.\\n\\nLeave at 200 if unsure.",
"SettingsTabSystemTurboMultiplierToolTip": "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.",
"FpsStatusBarText": "{0} FPS ({1}ms)",
"FpsTurboStatusBarText": "{0} FPS ({1}ms), Turbo ({2}%)",
"SettingsTabHotkeysTurboMode": "Turbo mode:",
"SettingsTabHotkeysTurboModeToolTip": "The Turbo mode hotkey.\\nConfigure the behavior of Turbo mode in Ryujinx CPU settings.\\n\\nLeave Unbound if unsure.",
"SettingsTabHotkeysOnlyWhilePressed": "Only while pressed"
} }

View file

@ -814,5 +814,12 @@
"MultiplayerMode": "Mode :", "MultiplayerMode": "Mode :",
"MultiplayerModeTooltip": "Changer le mode multijoueur LDN.\n\nLdnMitm modifiera la fonctionnalité de jeu sans fil local/jeu local dans les jeux pour fonctionner comme s'il s'agissait d'un LAN, permettant des connexions locales sur le même réseau avec d'autres instances de Ryujinx et des consoles Nintendo Switch piratées ayant le module ldn_mitm installé.\n\nLe multijoueur nécessite que tous les joueurs soient sur la même version du jeu (par exemple, Super Smash Bros. Ultimate v13.0.1 ne peut pas se connecter à v13.0.0).\n\nLaissez DÉSACTIVÉ si vous n'êtes pas sûr.", "MultiplayerModeTooltip": "Changer le mode multijoueur LDN.\n\nLdnMitm modifiera la fonctionnalité de jeu sans fil local/jeu local dans les jeux pour fonctionner comme s'il s'agissait d'un LAN, permettant des connexions locales sur le même réseau avec d'autres instances de Ryujinx et des consoles Nintendo Switch piratées ayant le module ldn_mitm installé.\n\nLe multijoueur nécessite que tous les joueurs soient sur la même version du jeu (par exemple, Super Smash Bros. Ultimate v13.0.1 ne peut pas se connecter à v13.0.0).\n\nLaissez DÉSACTIVÉ si vous n'êtes pas sûr.",
"MultiplayerModeDisabled": "Désactivé", "MultiplayerModeDisabled": "Désactivé",
"MultiplayerModeLdnMitm": "ldn_mitm" "MultiplayerModeLdnMitm": "ldn_mitm",
"SettingsTabSystemTurboMultiplier": "Multiplicateur du Mode Turbo :",
"SettingsTabSystemTurboMultiplierValueToolTip": "La valeur souhaitée du multiplicateur du Mode Turbo.\\n\\nGarder à 200 si incertain.",
"SettingsTabSystemTurboMultiplierToolTip": "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.",
"FpsStatusBarText": "{0} FPS ({1}ms)",
"SettingsTabHotkeysTurboMode": "Mode Turbo :",
"SettingsTabHotkeysTurboModeToolTip": "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.",
"SettingsTabHotkeysOnlyWhilePressed": "Seulement quand le raccourci est maintenu"
} }

View file

@ -14,5 +14,6 @@ namespace Ryujinx.Ava.Common
VolumeDown, VolumeDown,
CustomVSyncIntervalIncrement, CustomVSyncIntervalIncrement,
CustomVSyncIntervalDecrement, CustomVSyncIntervalDecrement,
TurboMode,
} }
} }

View file

@ -48,6 +48,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] public string this[LocaleKeys key]
{ {
get get

View file

@ -9,6 +9,7 @@ using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Configuration.Hid.Keyboard; using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Cpu;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Multithreading; using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.OpenGL;
@ -339,6 +340,7 @@ namespace Ryujinx.Headless
options.VSyncMode, options.VSyncMode,
!options.DisableDockedMode, !options.DisableDockedMode,
!options.DisablePTC, !options.DisablePTC,
ITickSource.RealityTickScalar,
options.EnableInternetAccess, options.EnableInternetAccess,
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None, !options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
options.FsGlobalAccessLogMode, options.FsGlobalAccessLogMode,

View file

@ -126,10 +126,33 @@ namespace Ryujinx.Ava.UI.Models.Input
} }
} }
private Key _turboMode;
public Key TurboMode
{
get => _turboMode;
set
{
_turboMode = value;
OnPropertyChanged();
}
}
private bool _turboModeWhileHeld;
public bool TurboModeWhileHeld
{
get => _turboModeWhileHeld;
set
{
_turboModeWhileHeld = value;
OnPropertyChanged();
}
}
public HotkeyConfig(KeyboardHotkeys config) public HotkeyConfig(KeyboardHotkeys config)
{ {
if (config != null) if (config == null)
{ return;
ToggleVSyncMode = config.ToggleVSyncMode; ToggleVSyncMode = config.ToggleVSyncMode;
Screenshot = config.Screenshot; Screenshot = config.Screenshot;
ShowUI = config.ShowUI; ShowUI = config.ShowUI;
@ -141,7 +164,8 @@ namespace Ryujinx.Ava.UI.Models.Input
VolumeDown = config.VolumeDown; VolumeDown = config.VolumeDown;
CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement; CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement;
CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement; CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement;
} TurboMode = config.TurboMode;
TurboModeWhileHeld = config.TurboModeWhileHeld;
} }
public KeyboardHotkeys GetConfig() public KeyboardHotkeys GetConfig()
@ -159,6 +183,8 @@ namespace Ryujinx.Ava.UI.Models.Input
VolumeDown = VolumeDown, VolumeDown = VolumeDown,
CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement, CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement,
CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement, CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement,
TurboMode = TurboMode,
TurboModeWhileHeld = TurboModeWhileHeld
}; };
return config; return config;

View file

@ -56,6 +56,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private bool _enableCustomVSyncInterval; private bool _enableCustomVSyncInterval;
private int _customVSyncIntervalPercentageProxy; private int _customVSyncIntervalPercentageProxy;
private VSyncMode _vSyncMode; private VSyncMode _vSyncMode;
private long _turboModeMultiplier;
public event Action CloseWindow; public event Action CloseWindow;
public event Action SaveSettingsEvent; public event Action SaveSettingsEvent;
@ -232,6 +233,25 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
public bool EnablePptc { get; set; } public bool EnablePptc { get; set; }
public bool EnableLowPowerPptc { 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 EnableInternetAccess { get; set; }
public bool EnableFsIntegrityChecks { get; set; } public bool EnableFsIntegrityChecks { get; set; }
public bool IgnoreMissingServices { get; set; } public bool IgnoreMissingServices { get; set; }
@ -617,6 +637,7 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableLowPowerPptc = config.System.EnableLowPowerPtc; EnableLowPowerPptc = config.System.EnableLowPowerPtc;
MemoryMode = (int)config.System.MemoryManagerMode.Value; MemoryMode = (int)config.System.MemoryManagerMode.Value;
UseHypervisor = config.System.UseHypervisor; UseHypervisor = config.System.UseHypervisor;
TurboMultiplier = config.System.TickScalar;
// Graphics // Graphics
GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value; GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value;
@ -731,6 +752,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.System.EnableLowPowerPtc.Value = EnableLowPowerPptc; config.System.EnableLowPowerPtc.Value = EnableLowPowerPptc;
config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode; config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode;
config.System.UseHypervisor.Value = UseHypervisor; config.System.UseHypervisor.Value = UseHypervisor;
config.System.TickScalar.Value = TurboMultiplier;
// Graphics // Graphics
config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex; config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex;

View file

@ -6,6 +6,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
mc:Ignorable="d" mc:Ignorable="d"
x:DataType="viewModels:SettingsViewModel"> x:DataType="viewModels:SettingsViewModel">
<Design.DataContext> <Design.DataContext>
@ -70,6 +71,57 @@
ToolTip.Tip="{locale:Locale UseHypervisorTooltip}" /> ToolTip.Tip="{locale:Locale UseHypervisorTooltip}" />
</CheckBox> </CheckBox>
</StackPanel> </StackPanel>
<Separator Height="1" />
<StackPanel
Orientation="Vertical"
Spacing="5">
<TextBlock
Classes="h1"
Text="{locale:Locale SettingsTabSystemHacks}" />
<TextBlock
Foreground="{DynamicResource SecondaryTextColor}"
TextDecorations="Underline"
Text="{locale:Locale SettingsTabSystemHacksNote}" />
</StackPanel>
<StackPanel
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Orientation="Vertical">
<StackPanel Margin="0,0,0,10"
Orientation="Horizontal">
<TextBlock
VerticalAlignment="Center"
Background="Transparent"
Text="{locale:Locale SettingsTabSystemTurboMultiplier}"
ToolTip.Tip="{locale:Locale SettingsTabSystemTurboMultiplierToolTip}"
Width="250" />
<ui:NumberBox ToolTip.Tip="{locale:Locale SettingsTabSystemTurboMultiplierValueToolTip}"
Value="{Binding TurboMultiplier}"
Width="165"
SmallChange="1.0"
LargeChange="10"
SimpleNumberFormat="F0"
SpinButtonPlacementMode="Hidden"
Minimum="50"
Maximum="1000" />
<Slider Value="{Binding TurboMultiplier}"
ToolTip.Tip="{locale:Locale SettingsTabSystemTurboMultiplierValueToolTip}"
MinWidth="175"
Margin="10,-3,0,0"
Height="32"
Padding="0,-5"
TickFrequency="1"
IsSnapToTickEnabled="True"
LargeChange="10"
SmallChange="1"
VerticalAlignment="Center"
Minimum="50"
Maximum="1000" />
<TextBlock Margin="5,0"
Width="40"
Text="{Binding TurboMultiplierPercentageText}"/>
</StackPanel>
</StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>
</ScrollViewer> </ScrollViewer>

View file

@ -22,7 +22,7 @@
<Setter Property="Margin" Value="10, 0, 0, 0" /> <Setter Property="Margin" Value="10, 0, 0, 0" />
<Setter Property="Orientation" Value="Horizontal" /> <Setter Property="Orientation" Value="Horizontal" />
</Style> </Style>
<Style Selector="StackPanel > StackPanel > TextBlock"> <Style Selector="StackPanel > StackPanel > TextBlock.settingHeader">
<Setter Property="VerticalAlignment" Value="Center" /> <Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Width" Value="230" /> <Setter Property="Width" Value="230" />
</Style> </Style>
@ -50,71 +50,79 @@
Classes="h1" Classes="h1"
Text="{locale:Locale SettingsTabHotkeysHotkeys}" /> Text="{locale:Locale SettingsTabHotkeysHotkeys}" />
<StackPanel> <StackPanel>
<TextBlock Text="{locale:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" /> <TextBlock Text="{locale:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" Classes="settingHeader" />
<ToggleButton Name="ToggleVSyncMode"> <ToggleButton Name="ToggleVSyncMode">
<TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={StaticResource Key}}" /> <TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" /> <TextBlock Text="{locale:Locale SettingsTabHotkeysScreenshotHotkey}" Classes="settingHeader" />
<ToggleButton Name="Screenshot"> <ToggleButton Name="Screenshot">
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={StaticResource Key}}" /> <TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" /> <TextBlock Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Classes="settingHeader" />
<ToggleButton Name="ShowUI"> <ToggleButton Name="ShowUI">
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={StaticResource Key}}" /> <TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" /> <TextBlock Text="{locale:Locale SettingsTabHotkeysPauseHotkey}" Classes="settingHeader" />
<ToggleButton Name="Pause"> <ToggleButton Name="Pause">
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={StaticResource Key}}" /> <TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" /> <TextBlock Text="{locale:Locale SettingsTabHotkeysToggleMuteHotkey}" Classes="settingHeader" />
<ToggleButton Name="ToggleMute"> <ToggleButton Name="ToggleMute">
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={StaticResource Key}}" /> <TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" /> <TextBlock Text="{locale:Locale SettingsTabHotkeysResScaleUpHotkey}" Classes="settingHeader" />
<ToggleButton Name="ResScaleUp"> <ToggleButton Name="ResScaleUp">
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={StaticResource Key}}" /> <TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" /> <TextBlock Text="{locale:Locale SettingsTabHotkeysResScaleDownHotkey}" Classes="settingHeader" />
<ToggleButton Name="ResScaleDown"> <ToggleButton Name="ResScaleDown">
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={StaticResource Key}}" /> <TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{locale:Locale SettingsTabHotkeysVolumeUpHotkey}" /> <TextBlock Text="{locale:Locale SettingsTabHotkeysVolumeUpHotkey}" Classes="settingHeader" />
<ToggleButton Name="VolumeUp"> <ToggleButton Name="VolumeUp">
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={StaticResource Key}}" /> <TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<TextBlock Text="{locale:Locale SettingsTabHotkeysVolumeDownHotkey}" /> <TextBlock Text="{locale:Locale SettingsTabHotkeysVolumeDownHotkey}" Classes="settingHeader" />
<ToggleButton Name="VolumeDown"> <ToggleButton Name="VolumeDown">
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={StaticResource Key}}" /> <TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal"> <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock Text="{locale:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" /> <TextBlock Text="{locale:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" Classes="settingHeader" />
<ToggleButton Name="CustomVSyncIntervalIncrement"> <ToggleButton Name="CustomVSyncIntervalIncrement">
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={StaticResource Key}}" /> <TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel Margin="10,0,0,0" Orientation="Horizontal"> <StackPanel Margin="10,0,0,0" Orientation="Horizontal">
<TextBlock Text="{locale:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" /> <TextBlock Text="{locale:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" Classes="settingHeader" />
<ToggleButton Name="CustomVSyncIntervalDecrement"> <ToggleButton Name="CustomVSyncIntervalDecrement">
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={StaticResource Key}}" /> <TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={StaticResource Key}}" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{locale:Locale SettingsTabHotkeysTurboMode}" Classes="settingHeader" ToolTip.Tip="{locale:Locale SettingsTabHotkeysTurboModeToolTip}" Background="Transparent" />
<ToggleButton Name="TurboMode">
<TextBlock Text="{Binding KeyboardHotkey.TurboMode, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
</ToggleButton>
<TextBlock Text="{locale:Locale SettingsTabHotkeysOnlyWhilePressed}" Margin="10,0" />
<CheckBox IsChecked="{Binding KeyboardHotkey.TurboModeWhileHeld}" />
</StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>
</ScrollViewer> </ScrollViewer>

View file

@ -106,6 +106,9 @@ namespace Ryujinx.Ava.UI.Views.Settings
case "CustomVSyncIntervalDecrement": case "CustomVSyncIntervalDecrement":
viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = buttonValue.AsHidType<Key>(); viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = buttonValue.AsHidType<Key>();
break; break;
case "TurboMode":
viewModel.KeyboardHotkey.TurboMode = buttonValue.AsHidType<Key>();
break;
} }
} }
} }