Compare commits

...

18 commits

Author SHA1 Message Date
GreemDev
c95f48f834 Merge branch 'feature/turbo-mode' into 'master'
feature: Turbo Mode

See merge request ryubing/ryujinx!5
2025-03-17 03:54:06 +00:00
GreemDev
17f3540ac9 create CHANGELOG.md 2025-03-17 03:51:03 +00:00
GreemDev
9bfac9a0de wrong branch 2025-03-15 18:24:03 -05:00
GreemDev
a7585340ff compat: X-Out Resurfaced: playable 2025-03-15 18:22:36 -05:00
GreemDev
dbf0f149ab Merge branch 'master' into feature/turbo-mode 2025-03-14 23:54:25 -05:00
Evan Husted
709f0dd59c
Merge branch 'master' into feature/turbo-mode 2025-03-12 13:38:41 -05:00
Evan Husted
aa4a983056
Merge branch 'master' into feature/turbo-mode 2025-03-11 01:54:47 -05:00
Evan Husted
51f3554f82 cap turbo to 300% 2025-03-10 01:27:24 -05:00
Evan Husted
8e285579ae Merge branch 'master' into feature/turbo-mode 2025-03-10 01:24:40 -05:00
Evan Husted
3644e3fd92 Merge branch 'master' into feature/turbo-mode 2025-03-10 01:10:22 -05:00
Evan Husted
3abee2a0be
Merge branch 'master' into feature/turbo-mode 2025-03-04 02:57:30 -06:00
Evan Husted
94f34a9ed1 properly merge + move hleconfig tick scalar parameter 2025-03-04 01:19:18 -06:00
Evan Husted
776c0cb5cf merge 2025-03-04 01:08:39 -06:00
Evan Husted
ef1529a2d9 localize Turbo Mode for French 2025-03-03 13:38:52 -06:00
Evan Husted
768406cb67 Improve description of Turbo Mode in the UI and localize FPS/Turbo indicator on status bar 2025-03-03 12:42:00 -06:00
Evan Husted
ed5cb82aa8 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
2025-03-03 02:33:28 -06:00
Evan Husted
c48a2e6ba0 join assignment and declaration for this local variable in ipc service 2025-03-03 02:30:26 -06:00
Evan Husted
90f2b089eb fix nullref when switching vsync mode with turbo mode 2025-03-02 23:51:56 -06:00
24 changed files with 730 additions and 41 deletions

255
CHANGELOG.md Normal file
View file

@ -0,0 +1,255 @@
# Ryujinx Changelog
All updates to this Ryujinx branch will be documented in this file.
## [1.2.86](<https://git.ryujinx.app/ryubing/ryujinx/-/releases/1.2.86>) - 2025-03-13
A list of notable changes can be found on the release linked in the version number above.
## [1.2.82](<https://web.archive.org/web/20250312010534/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.82>) - 2025-02-16
A list of notable changes can be found on the release linked in the version number above.
## [1.2.80-81](<https://web.archive.org/web/20250302064257/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.81>) - 2025-01-22
A list of notable changes can be found on the release linked in the version number above.
## [1.2.78](<https://web.archive.org/web/20250301174537/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.78>) - 2024-12-19
A list of notable changes can be found on the release linked in the version number above.
## [1.2.73-1.2.76](<https://web.archive.org/web/20250209202612/https://github.com/Ryubing/Ryujinx/releases/tag/1.2.76>) - 2024-11-19
A list of notable changes can be found on the release linked in the version number above.
Additionally, 1.2.74 & 75 were fixes for uploading Windows build artifacts.
1.2.76 fixes a rare crash on startup.
## [1.2.72](<https://github.com/GreemDev/Ryujinx/releases/tag/1.2.72>) - 2024-11-03
PRs [#163](<https://github.com/GreemDev/Ryujinx/pull/163>), [#164](<https://github.com/GreemDev/Ryujinx/pull/164>), [#139](<https://github.com/GreemDev/Ryujinx/pull/139>)
### HLE:
- Add DebugMouse HID device.
- Fixes "Clock Tower Rewind" crashing while loading.
### Audio:
- Fix index bounds check in GetCoefficientAtIndex.
- Fixes crashing in Super Mario Party Jamboree.
### misc:
- Update macOS distribution .icns.
## [1.2.69](<https://github.com/GreemDev/Ryujinx/releases/tag/1.2.69>) - 2024-11-01
### Infra:
- Compile the native libraries into the Ryujinx executable.
- Remove `libarmeilleure-jitsupport.dylib` from Windows & Linux releases (dylibs are macOS-only)
### Misc:
- Remove custom themes in config.
- This is a leftover from the GTK UI, as Avalonia does not have custom themes.
- Replace "" with `string.Empty`.
- Code cleanups & simplifications.
## [1.2.67](<https://github.com/GreemDev/Ryujinx/releases/tag/1.2.67>) - 2024-11-01
PRs [#36](<https://github.com/GreemDev/Ryujinx/pull/36>), [#135](<https://github.com/GreemDev/Ryujinx/pull/135>)
### GUI:
- Set UseFloatingWatermark to false when watermark is empty
- Should prevent the text prompt box from having weird jumpy behavior.
### GPU:
- Increase the amount of VRAM cache available for textures based on selected DRAM amount.
### Misc:
- Fix homebrew loading.
## [1.2.64](https://github.com/GreemDev/Ryujinx/releases/tag/1.2.64) - 2024-10-30
PRs [#92](https://github.com/GreemDev/Ryujinx/pull/92), [#96](https://github.com/GreemDev/Ryujinx/pull/96), [#97](https://github.com/GreemDev/Ryujinx/pull/97), [#101](https://github.com/GreemDev/Ryujinx/pull/101), [#103](https://github.com/GreemDev/Ryujinx/pull/103)
### GUI:
- Option to show classic-style title bar. Requires restart of emulator to take effect.
- This is only relevant on Windows. Other Operating Systems default to this being on and not being changeable, because the custom (current) title bar only works on Windows in the first place.
### i18n:
- it_IT:
- Add missing Italian strings.
- pt_BR:
- Add missing Brazilian Portuguese strings.
- fr_FR:
- Fix some French strings.
### MISC:
- Higher-res logo.
## 1.2.59 - 2024-10-27
PRs [#88](https://github.com/GreemDev/Ryujinx/pull/88), [#87](https://github.com/GreemDev/Ryujinx/pull/87)
### i18n:
- fr_FR:
- Add missing translations for new features & fix a couple wrong ones.
- Fix Ignore Missing Services / Ignore Applet tooltip.
## 1.2.57 - 2024-10-27
PRs [#60](https://github.com/GreemDev/Ryujinx/pull/60), [#42](https://github.com/GreemDev/Ryujinx/pull/42)
### GUI:
- Automatically remove invalid DLC & updates as part of autoload.
- Added Thai translation for Ignore Applet hover tooltip.
### INPUT:
- When using multiple gamepads, when reconnecting they will no longer be mixed up between players.
## 1.2.50 - 2024-10-25
### GUI:
- Fix crash when using "delete all" button in mod manager.
### Updater:
- Remove Avalonia migration code.
### MISC:
- Replace references to IntPtr/UIntPtr to nint/nuint.
## 1.2.45 - 2024-10-25
### GUI:
- Added program icon to windows other than the main.
- Reference translations added in the last version.
- Shader compile counter is now translated.
### RPC:
- Added SONIC X SHADOW GENERATIONS asset image.
### MISC:
- Code cleanup.
## 1.2.44 - 2024-10-25
PR [#59](https://github.com/GreemDev/Ryujinx/pull/59)
### GUI:
- Add descriptions for "ignoring applet" translated into other languages.
NOTE: The translation isn't referenced in the code yet, it will be in the next update. These are just the translations.
## Hotfix: 1.2.43 - 2024-10-24
### GUI:
- Do not enable Ignore Applet by default when upgrading config version.
## 1.2.42 - 2024-10-24
Sources:
Init function: https://github.com/MutantAura/Ryujinx/commit/9cef4ceba40d66492ff775af793ff70e6e7551a9
Shader counter: https://github.com/MutantAura/Ryujinx/commit/67b873645fd593e83d042a77bf7ab12e5ec97357
Thanks MutantAura :D
### GUI:
- Implement shader compile counter (currently not translated, will change, need to pull changes.)
- Remove graphics backend / GPU name event logic in favor of a single init function.
## 1.2.41 - 2024-10-24
PR [#54](https://github.com/GreemDev/Ryujinx/pull/54)
Thanks Whitescatz!
### i18n:
- th_TH (Thai): Added missing translations, reduce transliterated words, fix grammar.
## 1.2.40 - 2024-10-23
PR [#40](https://github.com/GreemDev/Ryujinx/pull/40)
Thanks Вова С!
### GUI:
- Add option to ignore controller applet upon start.
*This option is under the hacks section for a reason; it ignores intended behavior. Use with caution.
## 1.2.39 - 2024-10-23
### MISC:
- Null-coalesce autoloaddirs on config load.
- Should prevent crashing on config loads in some circumstances.
## 1.2.38 - 2024-10-23
PR [#51](https://github.com/GreemDev/Ryujinx/pull/51)
### i18n:
- zh_CH (Simplified Chinese): Add some missing translations.
## 1.2.37 - 2024-10-23
PR [#37](https://github.com/GreemDev/Ryujinx/pull/37)
Thanks Last Breath!
### GUI:
- Set the default controller to the Pro Controller.
## 1.2.36 - 2024-10-21
PR [#30](https://github.com/GreemDev/Ryujinx/pull/30)
### GUI:
- Fix repeated dialog popup notifying you of new updates when there aren't any, while having a bundled update inside an XCI and an external update file.
## 1.2.35 - 2024-10-21
PR [#32](https://github.com/GreemDev/Ryujinx/pull/32)
### GUI:
- Replace "expand DRAM" option with a DRAM size dropdown.
- Allows for using mods which require a ridiculous amount of memory to allocate from.
## 1.2.34 - 2024-10-21
PR [#29](https://github.com/GreemDev/Ryujinx/pull/29)
### GUI:
- Fix duplicate controller names when 2 controllers of the same type are connected.
### INPUT:
- Fix invert x, y, and rotate when mapping physical left stick to logical right stick and vice versa.
## 1.2.32-1.2.33 - 2024-10-21
### i18n:
- fr_FR: Added missing strings and general improvements.
- Improve French translation clarity & add missing translations by Nebroc351, helped by Fredy27 in the Discord.
## 1.2.31 - 2024-10-21
### GUI:
- Revert maximized = fullscreen change.
- Fixes fullscreen not hiding the Windows taskbar.
## 1.2.30 - 2024-10-19
### GUI:
- Reload game list on locale change.
- Add keybinds to useful things (namely opening Amiibo scan window (Ctrl + A) and the scan button (Enter)).
- Reset RPC state when AppHost stops.
### MISC:
- XML & code cleanups.
## 1.2.29 - 2024-10-19
### GUI:
- Remove references to ryujinx.org in the localization files.
- Switch from downloading amiibo.ryujinx.org to just referencing a file in the repo & images in the repo, under assets/amiibo.
This fork is now entirely independent of the existing Ryujinx infrastructure, and as such the Amiibo features will continue to work in my version when they break in the mainline version.
## 1.2.28 - 2024-10-17
### GUI:
- Fix dialog popups doubling the window controls and laying text over the menu bar.
## 1.2.26 - 2024-10-17
### I18n:
Added Low-power PPTC mode strings to the translation files.
### GUI:
- Remove OS-provided title bar and put the Ryujinx logo next to "File" in the menu bar.
- What was in the title bar, Ryujinx version & current game information, is still visible by hovering the Ryujinx icon.
- Added icons to many actions in dropdown menus.
### RPC:
- Added Kirby and the Forgotten Land, Elder Scrolls V Skyrim, and Hyrule Warriors: Age of Calamity to the RPC assets.
## 1.2.25 - 2024-10-14
### CPU:
- Add low-power PPTC mode.
- Specifically, this setting causes the core count to get reduced by two-thirds, for lower-power but still fast loading if desired, and for unstable CPUs.
## 1.2.24 - 2024-10-14
### SDL:
- Move Mouse & MouseDriver to Input project, instead of Headless.
## 1.2.22 - 2024-10-12
### GUI/RPC:
- Added RDR, Luigi's Mansion 2 HD & 3 asset images.
### MISC:
- Minor code cleanups & improvements.
- Removed duplicate executable in the release bundle (leftovers from GTK & Avalonia dual releases).
- Removed Avalonia test release bundle, which was kept in Ryujinx for the OG Avalonia testers. That doesn't apply to this fork, so it's removed.
## 1.2.21 - 2024-10-11
### GUI/RPC:
- Add game version string when hovering large image asset.
- Add version information about this fork to the Ryujinx logo (big when in main menu, small when in game) when hovering.
## 1.2.20 - 2024-10-11
### MISC:
- Code cleanups & remove references to Ryujinx Patreon & Twitter.
### GUI:
- Add more Discord presence assets.
## 1.2.1-1.2.19 - 2024-10-08 - 2024-10-11
### GUI/INFRA/MISC:
- Remove GTK UI.
- Autoload DLC/Updates from dir ([#12](https://github.com/GreemDev/Ryujinx/pull/12)).
- Changed executable icon to rainbow logo.
- Extract Data > Logo now also extracts the square thumbnail you see for the game in the UI.
- The "use random UUID hack" checkbox in the Amiibo screen now remembers its last state when you reopen the window in a given session.

View file

@ -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; }
}
}

View file

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

View file

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

View file

@ -1065,7 +1065,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
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)
{

View file

@ -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");
}

View file

@ -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;
}
}

View file

@ -102,6 +102,11 @@ namespace Ryujinx.HLE
/// Control if the Profiled Translation Cache (PTC) should be used.
/// </summary>
internal readonly bool EnablePtc;
/// <summary>
/// Control the arbitrary scalar applied to emulated CPU tick timing.
/// </summary>
public long TickScalar { get; set; }
/// <summary>
/// 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;

View file

@ -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();

View file

@ -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<ApplicationControlProperty>? customNacpData = null) => Processes.LoadNca(ncaFile, customNacpData);

View file

@ -4922,6 +4922,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": {
@ -10505,7 +10580,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": "",
@ -18097,6 +18172,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": {
@ -23822,6 +23947,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": {

View file

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

View file

@ -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

View file

@ -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,

View file

@ -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;
}

View file

@ -258,6 +258,11 @@ namespace Ryujinx.Ava.Systems.Configuration
/// Enables or disables low-power profiled translation cache persistency loading
/// </summary>
public bool EnableLowPowerPtc { get; set; }
/// <summary>
/// Clock tick scalar, in percent points (100 = 1.0).
/// </summary>
public long TickScalar { get; set; }
/// <summary>
/// Enables or disables guest Internet access

View file

@ -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
};
})
);
}
}

View file

@ -335,6 +335,11 @@ namespace Ryujinx.Ava.Systems.Configuration
/// Enables or disables persistent profiled translation cache
/// </summary>
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>
/// 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<long>();
TickScalar.LogChangesToValue(nameof(TickScalar));
TickScalar.Event += (_, evnt) =>
{
if (Switch.Shared is null)
return;
Switch.Shared.Configuration.TickScalar = evnt.NewValue;
};
EnableInternetAccess = new ReactiveObject<bool>();
EnableInternetAccess.LogChangesToValue(nameof(EnableInternetAccess));
EnableFsIntegrityChecks = new ReactiveObject<bool>();
@ -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);
}
}

View file

@ -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 =

View file

@ -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
};
}
}

View file

@ -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;

View file

@ -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">
<Design.DataContext>
@ -76,6 +77,57 @@
ToolTip.Tip="{ext:Locale UseHypervisorTooltip}" />
</CheckBox>
</StackPanel>
<Separator Height="1" />
<StackPanel
Orientation="Vertical"
Spacing="5">
<TextBlock
Classes="h1"
Text="{ext:Locale SettingsTabSystemHacks}" />
<TextBlock
Foreground="{DynamicResource SecondaryTextColor}"
TextDecorations="Underline"
Text="{ext: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="{ext:Locale SettingsTabSystemTurboMultiplier}"
ToolTip.Tip="{ext:Locale SettingsTabSystemTurboMultiplierToolTip}"
Width="250" />
<ui:NumberBox ToolTip.Tip="{ext:Locale SettingsTabSystemTurboMultiplierValueToolTip}"
Value="{Binding TurboMultiplier}"
Width="165"
SmallChange="1.0"
LargeChange="10"
SimpleNumberFormat="F0"
SpinButtonPlacementMode="Hidden"
Minimum="50"
Maximum="300" />
<Slider Value="{Binding TurboMultiplier}"
ToolTip.Tip="{ext: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="500" />
<TextBlock Margin="5,0"
Width="40"
Text="{Binding TurboMultiplierPercentageText}"/>
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
</ScrollViewer>

View file

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

View file

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