From 0bae0d1da65dbd65e35dc86654f6b1ebf42ca5bd Mon Sep 17 00:00:00 2001 From: jcm Date: Thu, 21 Sep 2023 20:29:54 -0600 Subject: [PATCH 1/4] Add custom refresh interval target --- src/Ryujinx.Ava/AppHost.cs | 100 ++++++++++- src/Ryujinx.Ava/Assets/Locales/en_US.json | 18 +- src/Ryujinx.Ava/Assets/Styles/Themes.xaml | 5 +- src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs | 4 +- .../UI/Models/StatusUpdatedEventArgs.cs | 6 +- .../UI/ViewModels/MainWindowViewModel.cs | 160 +++++++++++++++++- .../UI/ViewModels/SettingsViewModel.cs | 86 +++++++++- .../UI/Views/Main/MainStatusBarView.axaml | 66 +++++++- .../UI/Views/Main/MainStatusBarView.axaml.cs | 8 +- .../Views/Settings/SettingsHotkeysView.axaml | 22 ++- .../Views/Settings/SettingsSystemView.axaml | 66 +++++++- .../UI/Windows/MainWindow.axaml.cs | 1 + .../Configuration/Hid/KeyboardHotkeys.cs | 4 +- .../Configuration/PresentIntervalState.cs | 15 ++ src/Ryujinx.Graphics.GAL/IWindow.cs | 3 +- .../Multithreading/ThreadedWindow.cs | 3 +- .../PresentIntervalState.cs | 9 + src/Ryujinx.Graphics.OpenGL/Window.cs | 2 +- src/Ryujinx.Graphics.Vulkan/Window.cs | 12 +- src/Ryujinx.Graphics.Vulkan/WindowBase.cs | 2 +- src/Ryujinx.HLE/HLEConfiguration.cs | 20 ++- .../Services/SurfaceFlinger/SurfaceFlinger.cs | 32 ++-- src/Ryujinx.HLE/Switch.cs | 58 ++++++- src/Ryujinx.Headless.SDL2/Options.cs | 8 +- src/Ryujinx.Headless.SDL2/Program.cs | 5 +- .../StatusUpdatedEventArgs.cs | 6 +- src/Ryujinx.Headless.SDL2/WindowBase.cs | 2 +- .../Configuration/ConfigurationFileFormat.cs | 20 ++- .../Configuration/ConfigurationState.cs | 76 +++++++-- src/Ryujinx/Ui/MainWindow.cs | 8 +- src/Ryujinx/Ui/RendererWidgetBase.cs | 7 +- src/Ryujinx/Ui/StatusUpdatedEventArgs.cs | 6 +- 32 files changed, 732 insertions(+), 108 deletions(-) create mode 100644 src/Ryujinx.Common/Configuration/PresentIntervalState.cs create mode 100644 src/Ryujinx.Graphics.GAL/PresentIntervalState.cs diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index 2fd9ce00d..dd77a08af 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -58,6 +58,7 @@ using IRenderer = Ryujinx.Graphics.GAL.IRenderer; using Key = Ryujinx.Input.Key; using MouseButton = Ryujinx.Input.MouseButton; +using PresentIntervalState = Ryujinx.Common.Configuration.PresentIntervalState; using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter; using Size = Avalonia.Size; using Switch = Ryujinx.HLE.Switch; @@ -189,6 +190,9 @@ public AppHost( ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough; + ConfigurationState.Instance.Graphics.PresentIntervalState.Event += UpdatePresentIntervalState; + ConfigurationState.Instance.Graphics.CustomPresentInterval.Event += UpdateCustomPresentIntervalValue; + ConfigurationState.Instance.Graphics.EnableCustomPresentInterval.Event += UpdateCustomPresentIntervalEnabled; ConfigurationState.Instance.System.EnableInternetAccess.Event += UpdateEnableInternetAccessState; ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; @@ -236,6 +240,37 @@ private void UpdateColorSpacePassthrough(object sender, ReactiveEventArgs _renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); } + private void UpdatePresentIntervalState(object sender, ReactiveEventArgs e) + { + if (Device != null) + { + Device.PresentIntervalState = e.NewValue; + Device.UpdatePresentInterval(); + } + //vulkan present mode may change in response, so recreate the swapchain + _renderer.Window?.ChangePresentIntervalState((Ryujinx.Graphics.GAL.PresentIntervalState)e.NewValue); + ConfigurationState.Instance.Graphics.PresentIntervalState.Value = e.NewValue; + ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); + } + + private void UpdateCustomPresentIntervalValue(object sender, ReactiveEventArgs e) + { + if (Device != null) + { + Device.TargetPresentInterval = e.NewValue; + Device.UpdatePresentInterval(); + } + } + + private void UpdateCustomPresentIntervalEnabled(object sender, ReactiveEventArgs e) + { + if (Device != null) + { + Device.CustomPresentIntervalEnabled = e.NewValue; + Device.UpdatePresentInterval(); + } + } + private void ShowCursor() { Dispatcher.UIThread.Post(() => @@ -515,6 +550,12 @@ private void HideCursorState_Changed(object sender, ReactiveEventArgs(oldState, presentIntervalState)); + } + public async Task LoadGuestApplication() { InitializeSwitchInstance(); @@ -778,7 +819,7 @@ private void InitializeSwitchInstance() _viewModel.UiHandler, (SystemLanguage)ConfigurationState.Instance.System.Language.Value, (RegionCode)ConfigurationState.Instance.System.Region.Value, - ConfigurationState.Instance.Graphics.EnableVsync, + ConfigurationState.Instance.Graphics.PresentIntervalState, ConfigurationState.Instance.System.EnableDockedMode, ConfigurationState.Instance.System.EnablePtc, ConfigurationState.Instance.System.EnableInternetAccess, @@ -792,7 +833,8 @@ private void InitializeSwitchInstance() ConfigurationState.Instance.System.AudioVolume, ConfigurationState.Instance.System.UseHypervisor, ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, - ConfigurationState.Instance.Multiplayer.Mode); + ConfigurationState.Instance.Multiplayer.Mode, + ConfigurationState.Instance.Graphics.CustomPresentInterval.Value); Device = new Switch(configuration); } @@ -918,7 +960,7 @@ private void RenderLoop() Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); Translator.IsReadyForTranslation.Set(); - _renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); + _renderer.Window.ChangePresentIntervalState((Ryujinx.Graphics.GAL.PresentIntervalState)Device.PresentIntervalState); while (_isActive) { @@ -966,6 +1008,7 @@ public void UpdateStatus() { // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued. string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; + string presentIntervalState = Device.PresentIntervalState.ToString(); if (GraphicsConfig.ResScale != 1) { @@ -973,7 +1016,7 @@ public void UpdateStatus() } StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( - Device.EnableDeviceVsync, + presentIntervalState, LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan ? "Vulkan" : "OpenGL", dockedMode, @@ -1065,9 +1108,40 @@ private bool UpdateFrame() { switch (currentHotkeyState) { - case KeyboardHotkeyState.ToggleVSync: - Device.EnableDeviceVsync = !Device.EnableDeviceVsync; - + //todo default + case KeyboardHotkeyState.TogglePresentIntervalState: + PresentIntervalState oldState = Device.PresentIntervalState; + PresentIntervalState newState; + if (oldState == PresentIntervalState.Switch) + { + newState = PresentIntervalState.Unbounded; + } + else if (oldState == PresentIntervalState.Unbounded) + { + if (ConfigurationState.Instance.Graphics.EnableCustomPresentInterval) + { + newState = PresentIntervalState.Custom; + } + else + { + newState = PresentIntervalState.Switch; + } + } + else + { + newState = PresentIntervalState.Switch; + } + UpdatePresentIntervalState(this, new ReactiveEventArgs(oldState, newState)); + _viewModel.ShowCustomPresentIntervalPicker = + (newState == PresentIntervalState.Custom); + break; + case KeyboardHotkeyState.CustomPresentIntervalDecrement: + Device.DecrementCustomPresentInterval(); + _viewModel.CustomPresentInterval -= 1; + break; + case KeyboardHotkeyState.CustomPresentIntervalIncrement: + Device.IncrementCustomPresentInterval(); + _viewModel.CustomPresentInterval += 1; break; case KeyboardHotkeyState.Screenshot: ScreenshotRequested = true; @@ -1154,9 +1228,9 @@ private KeyboardHotkeyState GetHotkeyState() { KeyboardHotkeyState state = KeyboardHotkeyState.None; - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.PresentIntervalState)) { - state = KeyboardHotkeyState.ToggleVSync; + state = KeyboardHotkeyState.TogglePresentIntervalState; } else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) { @@ -1190,6 +1264,14 @@ private KeyboardHotkeyState GetHotkeyState() { state = KeyboardHotkeyState.VolumeDown; } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CustomPresentIntervalIncrement)) + { + state = KeyboardHotkeyState.CustomPresentIntervalIncrement; + } + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CustomPresentIntervalDecrement)) + { + state = KeyboardHotkeyState.CustomPresentIntervalDecrement; + } return state; } diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index 72b5e8e3c..1b2705b7a 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -124,9 +124,18 @@ "SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Latin American Spanish", "SettingsTabSystemSystemLanguageSimplifiedChinese": "Simplified Chinese", "SettingsTabSystemSystemLanguageTraditionalChinese": "Traditional Chinese", - "SettingsTabSystemSystemTimeZone": "System TimeZone:", + "SettingsTabSystemSystemTimeZone": "System Time Zone:", "SettingsTabSystemSystemTime": "System Time:", - "SettingsTabSystemEnableVsync": "VSync", + "SettingsTabSystemPresentIntervalState": "Present Interval Mode (VSync):", + "SettingsTabSystemEnableCustomPresentInterval": "Enable toggle for custom present interval", + "SettingsTabSystemPresentIntervalStateSwitch": "60 (VSync ON)", + "SettingsTabSystemPresentIntervalStateUnbounded": "Unbounded (VSync OFF)", + "SettingsTabSystemPresentIntervalStateCustom": "Custom", + "SettingsTabSystemPresentIntervalStateTooltip": "Previously VSync. Use the Custom mode to set a specific refresh interval target. In some titles, the custom mode will act as an FPS cap. In others, it may lead to unpredictable behavior or do nothing at all. \n\nLeave at 60 (VSync ON) if unsure.", + "SettingsTabSystemEnableCustomPresentIntervalTooltip": "The present interval mode toggle will also cycle through the custom interval mode.", + "SettingsTabSystemCustomPresentIntervalValueTooltip": "The custom present interval target value.", + "SettingsTabSystemCustomPresentIntervalSliderTooltip": "The custom present interval target, as a percentage of the normal Switch interval.", + "SettingsTabSystemCustomPresentIntervalValue": "Custom Present Interval Value:", "SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableFsIntegrityChecks": "FS Integrity Checks", "SettingsTabSystemAudioBackend": "Audio Backend:", @@ -134,6 +143,7 @@ "SettingsTabSystemAudioBackendOpenAL": "OpenAL", "SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSDL2": "SDL2", + "SettingsTabSystemCustomPresentInterval": "Interval", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": "May cause instability", "SettingsTabSystemExpandDramSize": "Use alternative memory layout (Developers)", @@ -574,11 +584,13 @@ "RyujinxUpdater": "Ryujinx Updater", "SettingsTabHotkeys": "Keyboard Hotkeys", "SettingsTabHotkeysHotkeys": "Keyboard Hotkeys", - "SettingsTabHotkeysToggleVsyncHotkey": "Toggle VSync:", + "SettingsTabHotkeysTogglePresentIntervalStateHotkey": "Toggle Present Interval state:", "SettingsTabHotkeysScreenshotHotkey": "Screenshot:", "SettingsTabHotkeysShowUiHotkey": "Show UI:", "SettingsTabHotkeysPauseHotkey": "Pause:", "SettingsTabHotkeysToggleMuteHotkey": "Mute:", + "SettingsTabHotkeysIncrementCustomPresentIntervalHotkey": "Raise custom refresh interval", + "SettingsTabHotkeysDecrementCustomPresentIntervalHotkey": "Lower custom refresh interval", "ControllerMotionTitle": "Motion Control Settings", "ControllerRumbleTitle": "Rumble Settings", "SettingsSelectThemeFileDialogTitle": "Select Theme File", diff --git a/src/Ryujinx.Ava/Assets/Styles/Themes.xaml b/src/Ryujinx.Ava/Assets/Styles/Themes.xaml index 0f323f84b..056eba228 100644 --- a/src/Ryujinx.Ava/Assets/Styles/Themes.xaml +++ b/src/Ryujinx.Ava/Assets/Styles/Themes.xaml @@ -26,8 +26,9 @@ #b3ffffff #80cccccc #A0000000 - #FF2EEAC9 - #FFFF4554 + #FF2EEAC9 + #FFFF4554 + #6483F5 _vsyncColor; + get => _presentIntervalStateColor; set { - _vsyncColor = value; + _presentIntervalStateColor = value; OnPropertyChanged(); } } + public bool ShowCustomPresentIntervalPicker + { + get + { + if (_isGameRunning) + { + return AppHost.Device.PresentIntervalState == + PresentIntervalState.Custom; + } + else + { + return false; + } + } + set + { + OnPropertyChanged(); + } + } + + public int CustomPresentIntervalPercentageProxy + { + get => _customPresentIntervalPercentageProxy; + set + { + int newInterval = (int)(((decimal)value / 100) * 60); + _customPresentInterval = newInterval; + _customPresentIntervalPercentageProxy = value; + ConfigurationState.Instance.Graphics.CustomPresentInterval.Value = newInterval; + if (_isGameRunning) + { + AppHost.Device.CustomPresentInterval = newInterval; + AppHost.Device.UpdatePresentInterval(); + } + OnPropertyChanged((nameof(CustomPresentInterval))); + OnPropertyChanged((nameof(CustomPresentIntervalPercentageText))); + } + } + + public string CustomPresentIntervalPercentageText + { + get + { + string text = CustomPresentIntervalPercentageProxy.ToString() + "%"; + return text; + } + set + { + + } + } + + public int CustomPresentInterval + { + get => _customPresentInterval; + set + { + _customPresentInterval = value; + int newPercent = (int)(((decimal)value / 60) * 100); + _customPresentIntervalPercentageProxy = newPercent; + ConfigurationState.Instance.Graphics.CustomPresentInterval.Value = value; + if (_isGameRunning) + { + AppHost.Device.CustomPresentInterval = value; + AppHost.Device.UpdatePresentInterval(); + } + OnPropertyChanged(nameof(CustomPresentIntervalPercentageProxy)); + OnPropertyChanged(nameof(CustomPresentIntervalPercentageText)); + OnPropertyChanged(); + } + } + public byte[] SelectedIcon { get => _selectedIcon; @@ -502,6 +581,17 @@ public string BackendText } } + public string PresentIntervalStateText + { + get => _presentIntervalStateText; + set + { + _presentIntervalStateText = value; + + OnPropertyChanged(); + } + } + public string DockedStatusText { get => _dockedStatusText; @@ -1196,17 +1286,18 @@ private void Update_StatusBar(object sender, StatusUpdatedEventArgs args) { Dispatcher.UIThread.InvokeAsync(() => { - Application.Current.Styles.TryGetResource(args.VSyncEnabled - ? "VsyncEnabled" - : "VsyncDisabled", - Application.Current.ActualThemeVariant, + Application.Current.Styles.TryGetResource(args.PresentIntervalState, + Avalonia.Application.Current.ActualThemeVariant, out object color); if (color is not null) { - VsyncColor = new SolidColorBrush((Color)color); + PresentIntervalStateColor = new SolidColorBrush((Color)color); } + PresentIntervalStateText = args.PresentIntervalState; + ShowCustomPresentIntervalPicker = + args.PresentIntervalState == PresentIntervalState.Custom.ToString(); DockedStatusText = args.DockedMode; AspectRatioStatusText = args.AspectRatio; GameStatusText = args.GameStatus; @@ -1365,6 +1456,57 @@ public void ToggleDockMode() } } + public void UpdatePresentIntervalState() + { + PresentIntervalState oldPresentInterval = AppHost.Device.PresentIntervalState; + PresentIntervalState newPresentInterval = PresentIntervalState.Switch; + bool customPresentIntervalEnabled = ConfigurationState.Instance.Graphics.EnableCustomPresentInterval.Value; + + switch (oldPresentInterval) + { + case PresentIntervalState.Switch: + newPresentInterval = PresentIntervalState.Unbounded; + break; + case PresentIntervalState.Unbounded: + if (customPresentIntervalEnabled) + { + newPresentInterval = PresentIntervalState.Custom; + } + else + { + newPresentInterval = PresentIntervalState.Switch; + } + break; + case PresentIntervalState.Custom: + newPresentInterval = PresentIntervalState.Switch; + break; + } + + ConfigurationState.Instance.Graphics.PresentIntervalState.Value = newPresentInterval; + + if (_isGameRunning) + { + AppHost.UpdatePresentInterval(newPresentInterval); + } + + OnPropertyChanged(nameof(ShowCustomPresentIntervalPicker)); + } + + public void PresentIntervalStateSettingChanged() + { + if (_isGameRunning) + { + AppHost.Device.CustomPresentInterval = ConfigurationState.Instance.Graphics.CustomPresentInterval.Value; + AppHost.Device.UpdatePresentInterval(); + } + + CustomPresentInterval = ConfigurationState.Instance.Graphics.CustomPresentInterval.Value; + OnPropertyChanged(nameof(ShowCustomPresentIntervalPicker)); + OnPropertyChanged(nameof(CustomPresentIntervalPercentageProxy)); + OnPropertyChanged(nameof(CustomPresentIntervalPercentageText)); + OnPropertyChanged(nameof(CustomPresentInterval)); + } + public async Task ExitCurrentState() { if (WindowState == WindowState.FullScreen) diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 604e34067..564677bec 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -7,6 +7,7 @@ using Ryujinx.Audio.Backends.SoundIo; using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.UI.Helpers; +using Ryujinx.Ava.UI.Views.Main; using Ryujinx.Ava.UI.Windows; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; @@ -51,6 +52,10 @@ public class SettingsViewModel : BaseModel private string _customThemePath; private int _scalingFilter; private int _scalingFilterLevel; + private int _customPresentInterval; + private bool _enableCustomPresentInterval; + private int _customPresentIntervalPercentageProxy; + private PresentIntervalState _presentIntervalState; public event Action CloseWindow; public event Action SaveSettingsEvent; @@ -137,7 +142,75 @@ public bool DirectoryChanged public bool EnableDockedMode { get; set; } public bool EnableKeyboard { get; set; } public bool EnableMouse { get; set; } - public bool EnableVsync { get; set; } + public PresentIntervalState PresentIntervalState + { + get => _presentIntervalState; + set + { + if (value == PresentIntervalState.Custom) + { + EnableCustomPresentInterval = true; + } + _presentIntervalState = value; + OnPropertyChanged(); + OnPropertyChanged((nameof(EnableCustomPresentInterval))); + } + } + + public int CustomPresentIntervalPercentageProxy + { + get => _customPresentIntervalPercentageProxy; + set + { + int newInterval = (int)(((decimal)value / 100) * 60); + _customPresentInterval = newInterval; + _customPresentIntervalPercentageProxy = value; + OnPropertyChanged((nameof(CustomPresentInterval))); + OnPropertyChanged((nameof(CustomPresentIntervalPercentageText))); + } + } + + public string CustomPresentIntervalPercentageText + { + get + { + string text = CustomPresentIntervalPercentageProxy.ToString() + "%"; + return text; + } + set + { + + } + } + + public bool EnableCustomPresentInterval + { + get => _enableCustomPresentInterval; + set + { + _enableCustomPresentInterval = value; + if (_presentIntervalState == PresentIntervalState.Custom && value == false) + { + PresentIntervalState = PresentIntervalState.Switch; + OnPropertyChanged(nameof(PresentIntervalState)); + } + OnPropertyChanged(); + } + } + + public int CustomPresentInterval + { + get => _customPresentInterval; + set + { + _customPresentInterval = value; + int newPercent = (int)(((decimal)value / 60) * 100); + _customPresentIntervalPercentageProxy = newPercent; + OnPropertyChanged(nameof(CustomPresentIntervalPercentageProxy)); + OnPropertyChanged(nameof(CustomPresentIntervalPercentageText)); + OnPropertyChanged(); + } + } public bool EnablePptc { get; set; } public bool EnableInternetAccess { get; set; } public bool EnableFsIntegrityChecks { get; set; } @@ -448,7 +521,9 @@ public void LoadCurrentConfiguration() CurrentDate = currentDateTime.Date; CurrentTime = currentDateTime.TimeOfDay.Add(TimeSpan.FromSeconds(config.System.SystemTimeOffset)); - EnableVsync = config.Graphics.EnableVsync; + EnableCustomPresentInterval = config.Graphics.EnableCustomPresentInterval.Value; + CustomPresentInterval = config.Graphics.CustomPresentInterval; + PresentIntervalState = config.Graphics.PresentIntervalState; EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks; ExpandDramSize = config.System.ExpandRam; IgnoreMissingServices = config.System.IgnoreMissingServices; @@ -460,7 +535,7 @@ public void LoadCurrentConfiguration() // Graphics GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value; - // Physical devices are queried asynchronously hence the prefered index config value is loaded in LoadAvailableGpus(). + // Physical devices are queried asynchronously hence the preferred index config value is loaded in LoadAvailableGpus(). EnableShaderCache = config.Graphics.EnableShaderCache; EnableTextureRecompression = config.Graphics.EnableTextureRecompression; EnableMacroHLE = config.Graphics.EnableMacroHLE; @@ -537,7 +612,9 @@ public void SaveSettings() } config.System.SystemTimeOffset.Value = Convert.ToInt64((CurrentDate.ToUnixTimeSeconds() + CurrentTime.TotalSeconds) - DateTimeOffset.Now.ToUnixTimeSeconds()); - config.Graphics.EnableVsync.Value = EnableVsync; + config.Graphics.PresentIntervalState.Value = PresentIntervalState; + config.Graphics.EnableCustomPresentInterval.Value = EnableCustomPresentInterval; + config.Graphics.CustomPresentInterval.Value = CustomPresentInterval; config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks; config.System.ExpandRam.Value = ExpandDramSize; config.System.IgnoreMissingServices.Value = IgnoreMissingServices; @@ -603,6 +680,7 @@ public void SaveSettings() config.ToFileFormat().SaveConfig(Program.ConfigurationPath); MainWindow.UpdateGraphicsConfig(); + MainWindow.MainWindowViewModel.PresentIntervalStateSettingChanged(); SaveSettingsEvent?.Invoke(); diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml index f9e192e62..c53281c2a 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml @@ -1,4 +1,4 @@ - + PointerReleased="PresentIntervalState_PointerReleased" + Text="{Binding PresentIntervalStateText}" + TextAlignment="Left"/> + - + @@ -97,7 +97,23 @@ TextAlignment="Center" /> + + + + + + + + + + + + - \ No newline at end of file + diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml index e6f7c6e46..3ded1e384 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml @@ -4,6 +4,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia" xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale" xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels" xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers" @@ -181,10 +182,67 @@ Width="350" ToolTip.Tip="{locale:Locale TimeTooltip}" /> - + + VerticalAlignment="Center" + Text="{locale:Locale SettingsTabSystemPresentIntervalState}" + ToolTip.Tip="{locale:Locale SettingsTabSystemPresentIntervalStateTooltip}" + Width="250" /> + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs index 1aecbd041..5da60d256 100644 --- a/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs +++ b/src/Ryujinx.Ava/UI/Windows/MainWindow.axaml.cs @@ -10,6 +10,7 @@ using Ryujinx.Ava.UI.Applet; using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.ViewModels; +using Ryujinx.Ava.UI.Views.Main; using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.FileSystem; diff --git a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs index b4f2f9468..458ea5ca5 100644 --- a/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs +++ b/src/Ryujinx.Common/Configuration/Hid/KeyboardHotkeys.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Common.Configuration.Hid // This breaks Avalonia's TwoWay binding, which makes us unable to save new KeyboardHotkeys. public class KeyboardHotkeys { - public Key ToggleVsync { get; set; } + public Key PresentIntervalState { get; set; } public Key Screenshot { get; set; } public Key ShowUi { get; set; } public Key Pause { get; set; } @@ -13,5 +13,7 @@ public class KeyboardHotkeys public Key ResScaleDown { get; set; } public Key VolumeUp { get; set; } public Key VolumeDown { get; set; } + public Key CustomPresentIntervalIncrement { get; set; } + public Key CustomPresentIntervalDecrement { get; set; } } } diff --git a/src/Ryujinx.Common/Configuration/PresentIntervalState.cs b/src/Ryujinx.Common/Configuration/PresentIntervalState.cs new file mode 100644 index 000000000..98c3e88c7 --- /dev/null +++ b/src/Ryujinx.Common/Configuration/PresentIntervalState.cs @@ -0,0 +1,15 @@ +using Ryujinx.Common.Utilities; +using System; +using System.Text.Json.Serialization; + +namespace Ryujinx.Common.Configuration +{ + [JsonConverter(typeof(TypedStringEnumConverter))] + [Flags] + public enum PresentIntervalState + { + Switch = 0, + Unbounded = 1 << 0, + Custom = 1 << 1 + } +} diff --git a/src/Ryujinx.Graphics.GAL/IWindow.cs b/src/Ryujinx.Graphics.GAL/IWindow.cs index 83418e709..285aa1d5f 100644 --- a/src/Ryujinx.Graphics.GAL/IWindow.cs +++ b/src/Ryujinx.Graphics.GAL/IWindow.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Configuration; using System; namespace Ryujinx.Graphics.GAL @@ -8,7 +9,7 @@ public interface IWindow void SetSize(int width, int height); - void ChangeVSyncMode(bool vsyncEnabled); + void ChangePresentIntervalState(PresentIntervalState presentIntervalState); void SetAntiAliasing(AntiAliasing antialiasing); void SetScalingFilter(ScalingFilter type); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs index acda37ef3..f506d08c7 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Configuration; using Ryujinx.Graphics.GAL.Multithreading.Commands.Window; using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Resources; @@ -31,7 +32,7 @@ public void SetSize(int width, int height) _impl.Window.SetSize(width, height); } - public void ChangeVSyncMode(bool vsyncEnabled) { } + public void ChangePresentIntervalState(PresentIntervalState presentIntervalState) { } public void SetAntiAliasing(AntiAliasing effect) { } diff --git a/src/Ryujinx.Graphics.GAL/PresentIntervalState.cs b/src/Ryujinx.Graphics.GAL/PresentIntervalState.cs new file mode 100644 index 000000000..8c4cca19e --- /dev/null +++ b/src/Ryujinx.Graphics.GAL/PresentIntervalState.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum PresentIntervalState + { + Switch = 0, + Unbounded = 1 << 0, + Custom = 1 << 1 + } +} diff --git a/src/Ryujinx.Graphics.OpenGL/Window.cs b/src/Ryujinx.Graphics.OpenGL/Window.cs index 6bcfefa4e..c85e29e6a 100644 --- a/src/Ryujinx.Graphics.OpenGL/Window.cs +++ b/src/Ryujinx.Graphics.OpenGL/Window.cs @@ -54,7 +54,7 @@ public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4); } - public void ChangeVSyncMode(bool vsyncEnabled) { } + public void ChangePresentIntervalState(PresentIntervalState presentIntervalState) { } public void SetSize(int width, int height) { diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs index 2c5764a99..7edc804fd 100644 --- a/src/Ryujinx.Graphics.Vulkan/Window.cs +++ b/src/Ryujinx.Graphics.Vulkan/Window.cs @@ -29,7 +29,7 @@ class Window : WindowBase, IDisposable private int _width; private int _height; - private bool _vsyncEnabled; + private PresentIntervalState _presentIntervalState; private bool _swapchainIsDirty; private VkFormat _format; private AntiAliasing _currentAntiAliasing; @@ -139,7 +139,7 @@ private unsafe void CreateSwapchain() ImageArrayLayers = 1, PreTransform = capabilities.CurrentTransform, CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha), - PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled), + PresentMode = ChooseSwapPresentMode(presentModes, _presentIntervalState), Clipped = true, }; @@ -261,9 +261,9 @@ private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKH } } - private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled) + private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, PresentIntervalState presentIntervalState) { - if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr)) + if (presentIntervalState == PresentIntervalState.Unbounded && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr)) { return PresentModeKHR.ImmediateKhr; } @@ -612,9 +612,9 @@ public override void SetSize(int width, int height) // Not needed as we can get the size from the surface. } - public override void ChangeVSyncMode(bool vsyncEnabled) + public override void ChangePresentIntervalState(PresentIntervalState presentIntervalState) { - _vsyncEnabled = vsyncEnabled; + _presentIntervalState = presentIntervalState; _swapchainIsDirty = true; } diff --git a/src/Ryujinx.Graphics.Vulkan/WindowBase.cs b/src/Ryujinx.Graphics.Vulkan/WindowBase.cs index edb9c688c..ecbf4493f 100644 --- a/src/Ryujinx.Graphics.Vulkan/WindowBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/WindowBase.cs @@ -10,7 +10,7 @@ internal abstract class WindowBase : IWindow public abstract void Dispose(); public abstract void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback); public abstract void SetSize(int width, int height); - public abstract void ChangeVSyncMode(bool vsyncEnabled); + public abstract void ChangePresentIntervalState(PresentIntervalState presentIntervalState); public abstract void SetAntiAliasing(AntiAliasing effect); public abstract void SetScalingFilter(ScalingFilter scalerType); public abstract void SetScalingFilterLevel(float scale); diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HLEConfiguration.cs index f589bfdda..07d0fa095 100644 --- a/src/Ryujinx.HLE/HLEConfiguration.cs +++ b/src/Ryujinx.HLE/HLEConfiguration.cs @@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Ui; using System; +using PresentIntervalState = Ryujinx.Common.Configuration.PresentIntervalState; namespace Ryujinx.HLE { @@ -84,9 +85,14 @@ public class HLEConfiguration internal readonly RegionCode Region; /// - /// Control the initial state of the vertical sync in the SurfaceFlinger service. + /// Control the initial state of the present interval in the SurfaceFlinger service (previously Vsync). /// - internal readonly bool EnableVsync; + internal readonly PresentIntervalState PresentIntervalState; + + /// + /// Control the custom present interval, if enabled and active. + /// + internal readonly int CustomPresentInterval; /// /// Control the initial state of the docked mode. @@ -180,7 +186,7 @@ public HLEConfiguration(VirtualFileSystem virtualFileSystem, IHostUiHandler hostUiHandler, SystemLanguage systemLanguage, RegionCode region, - bool enableVsync, + PresentIntervalState presentIntervalState, bool enableDockedMode, bool enablePtc, bool enableInternetAccess, @@ -194,7 +200,9 @@ public HLEConfiguration(VirtualFileSystem virtualFileSystem, float audioVolume, bool useHypervisor, string multiplayerLanInterfaceId, - MultiplayerMode multiplayerMode) + MultiplayerMode multiplayerMode, + //bool enableCustomPresentInterval, + int customPresentInterval) { VirtualFileSystem = virtualFileSystem; LibHacHorizonManager = libHacHorizonManager; @@ -207,7 +215,9 @@ public HLEConfiguration(VirtualFileSystem virtualFileSystem, HostUiHandler = hostUiHandler; SystemLanguage = systemLanguage; Region = region; - EnableVsync = enableVsync; + PresentIntervalState = presentIntervalState; + //EnableCustomPresentInterval = enableCustomPresentInterval; + CustomPresentInterval = customPresentInterval; EnableDockedMode = enableDockedMode; EnablePtc = enablePtc; EnableInternetAccess = enableInternetAccess; diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index fd517b1ae..b79b85306 100644 --- a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -10,13 +10,12 @@ using System.Diagnostics; using System.Linq; using System.Threading; +using PresentIntervalState = Ryujinx.Common.Configuration.PresentIntervalState; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { class SurfaceFlinger : IConsumerListener, IDisposable { - private const int TargetFps = 60; - private readonly Switch _device; private readonly Dictionary _layers; @@ -34,6 +33,7 @@ class SurfaceFlinger : IConsumerListener, IDisposable private int _swapInterval; private int _swapIntervalDelay; + private bool _targetPresentIntervalChanged = true; private readonly object _lock = new(); @@ -58,6 +58,7 @@ private class TextureCallbackInformation public SurfaceFlinger(Switch device) { _device = device; + _device.TargetPresentIntervalChanged += () => _targetPresentIntervalChanged = true; _layers = new Dictionary(); RenderLayerId = 0; @@ -81,14 +82,18 @@ private void UpdateSwapInterval(int swapInterval) _swapInterval = swapInterval; // If the swap interval is 0, Game VSync is disabled. - if (_swapInterval == 0) - { - _nextFrameEvent.Set(); - _ticksPerFrame = 1; - } - else + if (_targetPresentIntervalChanged) { - _ticksPerFrame = Stopwatch.Frequency / TargetFps; + _targetPresentIntervalChanged = false; + if (_swapInterval == 0) + { + _nextFrameEvent.Set(); + _ticksPerFrame = 1; + } + else + { + _ticksPerFrame = Stopwatch.Frequency / (long)_device.TargetPresentInterval; + } } } @@ -371,14 +376,11 @@ public void Compose() if (acquireStatus == Status.Success) { // If device vsync is disabled, reflect the change. - if (!_device.EnableDeviceVsync) + if (_device.PresentIntervalState == PresentIntervalState.Unbounded) { - if (_swapInterval != 0) - { - UpdateSwapInterval(0); - } + UpdateSwapInterval(0); } - else if (item.SwapInterval != _swapInterval) + else { UpdateSwapInterval(item.SwapInterval); } diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index ae063a47d..8ad263115 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -27,7 +27,24 @@ public class Switch : IDisposable public TamperMachine TamperMachine { get; } public IHostUiHandler UiHandler { get; } - public bool EnableDeviceVsync { get; set; } = true; + public PresentIntervalState PresentIntervalState { get; set; } = PresentIntervalState.Switch; + public bool CustomPresentIntervalEnabled { get; set; } = false; + public int CustomPresentInterval { get; set; } + + public long TargetPresentInterval { get; set; } = 60; + public Action TargetPresentIntervalChanged { get; set; } + + public bool EnableDeviceVsync + { + get + { + return PresentIntervalState == PresentIntervalState.Switch; + } + set + { + PresentIntervalState = value ? PresentIntervalState.Switch : PresentIntervalState.Unbounded; + } + } public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable; @@ -58,12 +75,20 @@ public Switch(HLEConfiguration configuration) System.State.SetLanguage(Configuration.SystemLanguage); System.State.SetRegion(Configuration.Region); - EnableDeviceVsync = Configuration.EnableVsync; + EnableDeviceVsync = Configuration.PresentIntervalState == PresentIntervalState.Switch; + PresentIntervalState = Configuration.PresentIntervalState; + CustomPresentInterval = Configuration.CustomPresentInterval; System.State.DockedMode = Configuration.EnableDockedMode; System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default; System.EnablePtc = Configuration.EnablePtc; System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel; System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode; + UpdatePresentInterval(); //todo remove these comments before committing. + // this call seems awkward. maybe this should be part of HOS.Horizon, + // where surfaceflinger is initialized? not sure though since it isn't a genuine + // Switch system setting. either way, if not, we need this call here because + // SurfaceFlinger was initialized with the default target interval + // of 60. #pragma warning restore IDE0055 } @@ -114,6 +139,35 @@ public void PresentFrame(Action swapBuffersCallback) Gpu.Window.Present(swapBuffersCallback); } + public void IncrementCustomPresentInterval() + { + CustomPresentInterval += 1; + UpdatePresentInterval(); + } + + public void DecrementCustomPresentInterval() + { + CustomPresentInterval -= 1; + UpdatePresentInterval(); + } + + public void UpdatePresentInterval() + { + switch (PresentIntervalState) + { + case PresentIntervalState.Custom: + TargetPresentInterval = CustomPresentInterval; + break; + case PresentIntervalState.Switch: + TargetPresentInterval = 60; + break; + case PresentIntervalState.Unbounded: + TargetPresentInterval = 1; + break; + } + TargetPresentIntervalChanged.Invoke(); + } + public void SetVolume(float volume) { System.SetVolume(Math.Clamp(volume, 0, 1)); diff --git a/src/Ryujinx.Headless.SDL2/Options.cs b/src/Ryujinx.Headless.SDL2/Options.cs index ea2063758..f80fe3eeb 100644 --- a/src/Ryujinx.Headless.SDL2/Options.cs +++ b/src/Ryujinx.Headless.SDL2/Options.cs @@ -114,9 +114,15 @@ public class Options [Option("fs-global-access-log-mode", Required = false, Default = 0, HelpText = "Enables FS access log output to the console.")] public int FsGlobalAccessLogMode { get; set; } - [Option("disable-vsync", Required = false, HelpText = "Disables Vertical Sync.")] + [Option("disable-vsync", Required = false, HelpText = "Disables Vertical Sync. Deprecated. Use present-interval-mode instead.")] public bool DisableVSync { get; set; } + [Option("present-interval-mode", Required = false, Default = PresentIntervalState.Switch, HelpText = "Sets the present interval mode (Switch, Unbounded, or Custom).")] + public PresentIntervalState PresentIntervalState { get; set; } + + [Option("custom-present-interval", Required = false, Default = 90, HelpText = "Sets the custom present interval target value (integer).")] + public int CustomPresentInterval { get; set; } + [Option("disable-shader-cache", Required = false, HelpText = "Disables Shader cache.")] public bool DisableShaderCache { get; set; } diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 7e3c79f54..729ca2a52 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -543,7 +543,7 @@ private static Switch InitializeEmulationContext(WindowBase window, IRenderer re window, options.SystemLanguage, options.SystemRegion, - !options.DisableVSync, + options.PresentIntervalState, !options.DisableDockedMode, !options.DisablePTC, options.EnableInternetAccess, @@ -557,7 +557,8 @@ private static Switch InitializeEmulationContext(WindowBase window, IRenderer re options.AudioVolume, options.UseHypervisor ?? true, options.MultiplayerLanInterfaceId, - Common.Configuration.Multiplayer.MultiplayerMode.Disabled); + Common.Configuration.Multiplayer.MultiplayerMode.Disabled, + options.CustomPresentInterval); return new Switch(configuration); } diff --git a/src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs b/src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs index 0b199d128..2e6d9b9f0 100644 --- a/src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs +++ b/src/Ryujinx.Headless.SDL2/StatusUpdatedEventArgs.cs @@ -4,16 +4,16 @@ namespace Ryujinx.Headless.SDL2 { class StatusUpdatedEventArgs : EventArgs { - public bool VSyncEnabled; + public string PresentIntervalState; public string DockedMode; public string AspectRatio; public string GameStatus; public string FifoStatus; public string GpuName; - public StatusUpdatedEventArgs(bool vSyncEnabled, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName) + public StatusUpdatedEventArgs(string presentIntervalState, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName) { - VSyncEnabled = vSyncEnabled; + PresentIntervalState = presentIntervalState; DockedMode = dockedMode; AspectRatio = aspectRatio; GameStatus = gameStatus; diff --git a/src/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx.Headless.SDL2/WindowBase.cs index adab07641..24f25fd82 100644 --- a/src/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/src/Ryujinx.Headless.SDL2/WindowBase.cs @@ -311,7 +311,7 @@ public void Render() } StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( - Device.EnableDeviceVsync, + Device.PresentIntervalState.ToString(), dockedMode, Device.Configuration.AspectRatio.ToText(), $"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs index 8a4db1fe7..46dc50c13 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationFileFormat.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Multiplayer; @@ -15,7 +16,7 @@ public class ConfigurationFileFormat /// /// The current version of the file format /// - public const int CurrentVersion = 48; + public const int CurrentVersion = 49; /// /// Version of the configuration file format @@ -170,8 +171,25 @@ public class ConfigurationFileFormat /// /// Enables or disables Vertical Sync /// + /// Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions) + /// TODO: Remove this when those older versions aren't in use anymore. public bool EnableVsync { get; set; } + /// + /// Current present interval state; 60 (Switch), unbounded ("Vsync off"), or custom + /// + public PresentIntervalState PresentIntervalState { get; set; } + + /// + /// Enables or disables the custom present interval + /// + public bool EnableCustomPresentInterval { get; set; } + + /// + /// The custom present interval value + /// + public int CustomPresentInterval { get; set; } + /// /// Enables or disables Shader cache /// diff --git a/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs index b017d384c..b91a4e8e3 100644 --- a/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.Ui.Common/Configuration/ConfigurationState.cs @@ -468,10 +468,25 @@ public class GraphicsSection public ReactiveObject ShadersDumpPath { get; private set; } /// - /// Enables or disables Vertical Sync + /// Toggles VSync on or off. Deprecated, included for GTK compatibility. /// public ReactiveObject EnableVsync { get; private set; } + /// + /// Toggles the present interval mode. Options are Switch (60Hz), Unbounded (previously Vsync off), and Custom, if enabled. + /// + public ReactiveObject PresentIntervalState { get; private set; } + + /// + /// Enables or disables the custom present interval mode. + /// + public ReactiveObject EnableCustomPresentInterval { get; private set; } + + /// + /// Changes the custom present interval. + /// + public ReactiveObject CustomPresentInterval { get; private set; } + /// /// Enables or disables Shader cache /// @@ -530,8 +545,14 @@ public GraphicsSection() AspectRatio = new ReactiveObject(); AspectRatio.Event += static (sender, e) => LogValueChange(e, nameof(AspectRatio)); ShadersDumpPath = new ReactiveObject(); + PresentIntervalState = new ReactiveObject(); + PresentIntervalState.Event += static (sender, e) => LogValueChange(e, nameof(PresentIntervalState)); + EnableCustomPresentInterval = new ReactiveObject(); + EnableCustomPresentInterval.Event += static (sender, e) => LogValueChange(e, nameof(EnableCustomPresentInterval)); EnableVsync = new ReactiveObject(); EnableVsync.Event += static (sender, e) => LogValueChange(e, nameof(EnableVsync)); + CustomPresentInterval = new ReactiveObject(); + CustomPresentInterval.Event += static (sender, e) => LogValueChange(e, nameof(CustomPresentInterval)); EnableShaderCache = new ReactiveObject(); EnableShaderCache.Event += static (sender, e) => LogValueChange(e, nameof(EnableShaderCache)); EnableTextureRecompression = new ReactiveObject(); @@ -680,6 +701,9 @@ public ConfigurationFileFormat ToFileFormat() ShowConfirmExit = ShowConfirmExit, HideCursor = HideCursor, EnableVsync = Graphics.EnableVsync, + PresentIntervalState = Graphics.PresentIntervalState, + EnableCustomPresentInterval = Graphics.EnableCustomPresentInterval, + CustomPresentInterval = Graphics.CustomPresentInterval, EnableShaderCache = Graphics.EnableShaderCache, EnableTextureRecompression = Graphics.EnableTextureRecompression, EnableMacroHLE = Graphics.EnableMacroHLE, @@ -787,6 +811,9 @@ public void LoadDefault() ShowConfirmExit.Value = true; HideCursor.Value = HideCursorMode.OnIdle; Graphics.EnableVsync.Value = true; + Graphics.PresentIntervalState.Value = PresentIntervalState.Switch; + Graphics.CustomPresentInterval.Value = 120; + Graphics.EnableCustomPresentInterval.Value = false; Graphics.EnableShaderCache.Value = true; Graphics.EnableTextureRecompression.Value = false; Graphics.EnableMacroHLE.Value = true; @@ -845,7 +872,7 @@ public void LoadDefault() Hid.EnableMouse.Value = false; Hid.Hotkeys.Value = new KeyboardHotkeys { - ToggleVsync = Key.F1, + PresentIntervalState = Key.F1, ToggleMute = Key.F2, Screenshot = Key.F8, ShowUi = Key.F4, @@ -976,7 +1003,7 @@ public void Load(ConfigurationFileFormat configurationFileFormat, string configu configurationFileFormat.Hotkeys = new KeyboardHotkeys { - ToggleVsync = Key.F1, + PresentIntervalState = Key.F1, }; configurationFileUpdated = true; @@ -1170,7 +1197,7 @@ public void Load(ConfigurationFileFormat configurationFileFormat, string configu configurationFileFormat.Hotkeys = new KeyboardHotkeys { - ToggleVsync = Key.F1, + PresentIntervalState = Key.F1, Screenshot = Key.F8, }; @@ -1183,7 +1210,7 @@ public void Load(ConfigurationFileFormat configurationFileFormat, string configu configurationFileFormat.Hotkeys = new KeyboardHotkeys { - ToggleVsync = Key.F1, + PresentIntervalState = Key.F1, Screenshot = Key.F8, ShowUi = Key.F4, }; @@ -1226,7 +1253,7 @@ public void Load(ConfigurationFileFormat configurationFileFormat, string configu configurationFileFormat.Hotkeys = new KeyboardHotkeys { - ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync, + PresentIntervalState = Key.F1, Screenshot = configurationFileFormat.Hotkeys.Screenshot, ShowUi = configurationFileFormat.Hotkeys.ShowUi, Pause = Key.F5, @@ -1241,7 +1268,7 @@ public void Load(ConfigurationFileFormat configurationFileFormat, string configu configurationFileFormat.Hotkeys = new KeyboardHotkeys { - ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync, + PresentIntervalState = Key.F1, Screenshot = configurationFileFormat.Hotkeys.Screenshot, ShowUi = configurationFileFormat.Hotkeys.ShowUi, Pause = configurationFileFormat.Hotkeys.Pause, @@ -1315,7 +1342,7 @@ public void Load(ConfigurationFileFormat configurationFileFormat, string configu configurationFileFormat.Hotkeys = new KeyboardHotkeys { - ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync, + PresentIntervalState = Key.F1, Screenshot = configurationFileFormat.Hotkeys.Screenshot, ShowUi = configurationFileFormat.Hotkeys.ShowUi, Pause = configurationFileFormat.Hotkeys.Pause, @@ -1342,7 +1369,7 @@ public void Load(ConfigurationFileFormat configurationFileFormat, string configu configurationFileFormat.Hotkeys = new KeyboardHotkeys { - ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync, + PresentIntervalState = Key.F1, Screenshot = configurationFileFormat.Hotkeys.Screenshot, ShowUi = configurationFileFormat.Hotkeys.ShowUi, Pause = configurationFileFormat.Hotkeys.Pause, @@ -1430,6 +1457,32 @@ public void Load(ConfigurationFileFormat configurationFileFormat, string configu configurationFileUpdated = true; } + if (configurationFileFormat.Version < 49) + { + Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 49."); + + configurationFileFormat.PresentIntervalState = PresentIntervalState.Switch; + configurationFileFormat.EnableCustomPresentInterval = false; + + configurationFileFormat.Hotkeys = new KeyboardHotkeys + { + PresentIntervalState = Key.F1, + 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, + CustomPresentIntervalIncrement = Key.Unbound, + CustomPresentIntervalDecrement = Key.Unbound, + }; + configurationFileFormat.CustomPresentInterval = 90; + + configurationFileUpdated = true; + } + Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; @@ -1461,7 +1514,10 @@ public void Load(ConfigurationFileFormat configurationFileFormat, string configu CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart; ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit; HideCursor.Value = configurationFileFormat.HideCursor; - Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync; + Graphics.EnableVsync.Value = configurationFileFormat.PresentIntervalState == PresentIntervalState.Switch; + Graphics.PresentIntervalState.Value = configurationFileFormat.PresentIntervalState; + Graphics.EnableCustomPresentInterval.Value = configurationFileFormat.EnableCustomPresentInterval; + Graphics.CustomPresentInterval.Value = configurationFileFormat.CustomPresentInterval; Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache; Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression; Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE; diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs index 2a088f561..f33abb395 100644 --- a/src/Ryujinx/Ui/MainWindow.cs +++ b/src/Ryujinx/Ui/MainWindow.cs @@ -44,6 +44,7 @@ using System.Threading; using System.Threading.Tasks; using GUI = Gtk.Builder.ObjectAttribute; +using PresentIntervalState = Ryujinx.Common.Configuration.PresentIntervalState; using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; namespace Ryujinx.Ui @@ -656,7 +657,7 @@ private void InitializeSwitchInstance() _uiHandler, (SystemLanguage)ConfigurationState.Instance.System.Language.Value, (RegionCode)ConfigurationState.Instance.System.Region.Value, - ConfigurationState.Instance.Graphics.EnableVsync, + ConfigurationState.Instance.Graphics.EnableVsync ? PresentIntervalState.Switch : PresentIntervalState.Unbounded, ConfigurationState.Instance.System.EnableDockedMode, ConfigurationState.Instance.System.EnablePtc, ConfigurationState.Instance.System.EnableInternetAccess, @@ -670,7 +671,8 @@ private void InitializeSwitchInstance() ConfigurationState.Instance.System.AudioVolume, ConfigurationState.Instance.System.UseHypervisor, ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, - ConfigurationState.Instance.Multiplayer.Mode); + ConfigurationState.Instance.Multiplayer.Mode, + ConfigurationState.Instance.Graphics.CustomPresentInterval); _emulationContext = new HLE.Switch(configuration); } @@ -1214,7 +1216,7 @@ private void Update_StatusBar(object sender, StatusUpdatedEventArgs args) _gpuBackend.Text = args.GpuBackend; _volumeStatus.Text = GetVolumeLabelText(args.Volume); - if (args.VSyncEnabled) + if (args.PresentIntervalState == PresentIntervalState.Switch.ToString()) { _vSyncStatus.Attributes = new Pango.AttrList(); _vSyncStatus.Attributes.Insert(new Pango.AttrForeground(11822, 60138, 51657)); diff --git a/src/Ryujinx/Ui/RendererWidgetBase.cs b/src/Ryujinx/Ui/RendererWidgetBase.cs index 6ae122a0a..efefaa51b 100644 --- a/src/Ryujinx/Ui/RendererWidgetBase.cs +++ b/src/Ryujinx/Ui/RendererWidgetBase.cs @@ -24,6 +24,7 @@ using System.Threading.Tasks; using Image = SixLabors.ImageSharp.Image; using Key = Ryujinx.Input.Key; +using PresentIntervalState = Ryujinx.Graphics.GAL.PresentIntervalState; using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter; using Switch = Ryujinx.HLE.Switch; @@ -452,7 +453,7 @@ public void Render() Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); Translator.IsReadyForTranslation.Set(); - Renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); + Renderer.Window.ChangePresentIntervalState(Device.EnableDeviceVsync ? PresentIntervalState.Switch : PresentIntervalState.Unbounded); (Toplevel as MainWindow)?.ActivatePauseMenu(); @@ -489,7 +490,7 @@ public void Render() } StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( - Device.EnableDeviceVsync, + Device.EnableDeviceVsync ? PresentIntervalState.Switch.ToString() : PresentIntervalState.Unbounded.ToString(), Device.GetVolume(), _gpuBackendName, dockedMode, @@ -754,7 +755,7 @@ private KeyboardHotkeyState GetHotkeyState() { KeyboardHotkeyState state = KeyboardHotkeyState.None; - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.PresentIntervalState)) { state |= KeyboardHotkeyState.ToggleVSync; } diff --git a/src/Ryujinx/Ui/StatusUpdatedEventArgs.cs b/src/Ryujinx/Ui/StatusUpdatedEventArgs.cs index 72e7d7f5b..0a746768f 100644 --- a/src/Ryujinx/Ui/StatusUpdatedEventArgs.cs +++ b/src/Ryujinx/Ui/StatusUpdatedEventArgs.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Ui { public class StatusUpdatedEventArgs : EventArgs { - public bool VSyncEnabled; + public string PresentIntervalState; public float Volume; public string DockedMode; public string AspectRatio; @@ -13,9 +13,9 @@ public class StatusUpdatedEventArgs : EventArgs public string GpuName; public string GpuBackend; - public StatusUpdatedEventArgs(bool vSyncEnabled, float volume, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName) + public StatusUpdatedEventArgs(string presentIntervalState, float volume, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName) { - VSyncEnabled = vSyncEnabled; + PresentIntervalState = presentIntervalState; Volume = volume; GpuBackend = gpuBackend; DockedMode = dockedMode; From 4089ba99ec23389e6283c5149fb969fc1a1a6621 Mon Sep 17 00:00:00 2001 From: jcm Date: Sat, 7 Oct 2023 21:14:37 -0500 Subject: [PATCH 2/4] revise naming, UX to conform closer to legacy --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 20 +++++++++---------- .../UI/ViewModels/MainWindowViewModel.cs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index 1b2705b7a..b92f3ff6d 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -126,16 +126,16 @@ "SettingsTabSystemSystemLanguageTraditionalChinese": "Traditional Chinese", "SettingsTabSystemSystemTimeZone": "System Time Zone:", "SettingsTabSystemSystemTime": "System Time:", - "SettingsTabSystemPresentIntervalState": "Present Interval Mode (VSync):", - "SettingsTabSystemEnableCustomPresentInterval": "Enable toggle for custom present interval", - "SettingsTabSystemPresentIntervalStateSwitch": "60 (VSync ON)", - "SettingsTabSystemPresentIntervalStateUnbounded": "Unbounded (VSync OFF)", - "SettingsTabSystemPresentIntervalStateCustom": "Custom", - "SettingsTabSystemPresentIntervalStateTooltip": "Previously VSync. Use the Custom mode to set a specific refresh interval target. In some titles, the custom mode will act as an FPS cap. In others, it may lead to unpredictable behavior or do nothing at all. \n\nLeave at 60 (VSync ON) if unsure.", - "SettingsTabSystemEnableCustomPresentIntervalTooltip": "The present interval mode toggle will also cycle through the custom interval mode.", - "SettingsTabSystemCustomPresentIntervalValueTooltip": "The custom present interval target value.", - "SettingsTabSystemCustomPresentIntervalSliderTooltip": "The custom present interval target, as a percentage of the normal Switch interval.", - "SettingsTabSystemCustomPresentIntervalValue": "Custom Present Interval Value:", + "SettingsTabSystemPresentIntervalState": "VSync:", + "SettingsTabSystemEnableCustomPresentInterval": "Enable toggle through custom refresh rate", + "SettingsTabSystemPresentIntervalStateSwitch": "On", + "SettingsTabSystemPresentIntervalStateUnbounded": "Off", + "SettingsTabSystemPresentIntervalStateCustom": "Custom Refresh Rate", + "SettingsTabSystemPresentIntervalStateTooltip": "Emulated Vertical Sync. 'On' emulates the Switch's refresh rate of 60Hz. 'Off' is an unbounded refresh rate. 'Custom' allows the user to specify a custom emulated refresh rate. In some titles, the custom rate will act as an FPS cap. In others, it may lead to unpredictable behavior. \n\nLeave ON if unsure.", + "SettingsTabSystemEnableCustomPresentIntervalTooltip": "The VSync toggle will also cycle through the custom refresh rate mode.", + "SettingsTabSystemCustomPresentIntervalValueTooltip": "The custom refresh rate target value.", + "SettingsTabSystemCustomPresentIntervalSliderTooltip": "The custom refresh rate, as a percentage of the normal Switch refresh rate.", + "SettingsTabSystemCustomPresentIntervalValue": "Custom Refresh Rate Value:", "SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableFsIntegrityChecks": "FS Integrity Checks", "SettingsTabSystemAudioBackend": "Audio Backend:", diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 9bf1561d8..e34eb72b8 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1295,7 +1295,7 @@ private void Update_StatusBar(object sender, StatusUpdatedEventArgs args) PresentIntervalStateColor = new SolidColorBrush((Color)color); } - PresentIntervalStateText = args.PresentIntervalState; + PresentIntervalStateText = args.PresentIntervalState == "Custom" ? "Custom" : "VSync"; ShowCustomPresentIntervalPicker = args.PresentIntervalState == PresentIntervalState.Custom.ToString(); DockedStatusText = args.DockedMode; From e6916ea0febe78949019a48dfbe81b29114ff744 Mon Sep 17 00:00:00 2001 From: jcm Date: Sun, 8 Oct 2023 11:54:14 -0500 Subject: [PATCH 3/4] move setting to 'Hacks', tweak UI and tooltips accordingly --- src/Ryujinx.Ava/Assets/Locales/en_US.json | 7 ++--- .../UI/ViewModels/SettingsViewModel.cs | 15 ++++++----- .../Views/Settings/SettingsSystemView.axaml | 27 ++++++++++++++----- 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index b92f3ff6d..849604577 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -127,12 +127,13 @@ "SettingsTabSystemSystemTimeZone": "System Time Zone:", "SettingsTabSystemSystemTime": "System Time:", "SettingsTabSystemPresentIntervalState": "VSync:", - "SettingsTabSystemEnableCustomPresentInterval": "Enable toggle through custom refresh rate", + "SettingsTabSystemEnableCustomPresentInterval": "Enable custom refresh rate (Experimental)", "SettingsTabSystemPresentIntervalStateSwitch": "On", "SettingsTabSystemPresentIntervalStateUnbounded": "Off", "SettingsTabSystemPresentIntervalStateCustom": "Custom Refresh Rate", - "SettingsTabSystemPresentIntervalStateTooltip": "Emulated Vertical Sync. 'On' emulates the Switch's refresh rate of 60Hz. 'Off' is an unbounded refresh rate. 'Custom' allows the user to specify a custom emulated refresh rate. In some titles, the custom rate will act as an FPS cap. In others, it may lead to unpredictable behavior. \n\nLeave ON if unsure.", - "SettingsTabSystemEnableCustomPresentIntervalTooltip": "The VSync toggle will also cycle through the custom refresh rate mode.", + "SettingsTabSystemPresentIntervalStateTooltip": "Emulated Vertical Sync. 'On' emulates the Switch's refresh rate of 60Hz. 'Off' is an unbounded refresh rate.", + "SettingsTabSystemPresentIntervalStateTooltipCustom": "Emulated Vertical Sync. 'On' emulates the Switch's refresh rate of 60Hz. 'Off' is an unbounded refresh rate. 'Custom' emulates the specified custom refresh rate.", + "SettingsTabSystemEnableCustomPresentIntervalTooltip": "Allows the user to specify an emulated refresh rate. In some titles, this may speed up or slow down the rate of gameplay logic. In other titles, it may allow for capping FPS at some multiple of the refresh rate, or lead to unpredictable behavior. This is an experimental feature, with no guarantees for how gameplay will be affected. \n\nLeave OFF if unsure.", "SettingsTabSystemCustomPresentIntervalValueTooltip": "The custom refresh rate target value.", "SettingsTabSystemCustomPresentIntervalSliderTooltip": "The custom refresh rate, as a percentage of the normal Switch refresh rate.", "SettingsTabSystemCustomPresentIntervalValue": "Custom Refresh Rate Value:", diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 564677bec..5ff1871bd 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -147,13 +147,13 @@ public PresentIntervalState PresentIntervalState get => _presentIntervalState; set { - if (value == PresentIntervalState.Custom) + if (value == PresentIntervalState.Custom || + value == PresentIntervalState.Switch || + value == PresentIntervalState.Unbounded) { - EnableCustomPresentInterval = true; + _presentIntervalState = value; + OnPropertyChanged(); } - _presentIntervalState = value; - OnPropertyChanged(); - OnPropertyChanged((nameof(EnableCustomPresentInterval))); } } @@ -192,7 +192,10 @@ public bool EnableCustomPresentInterval if (_presentIntervalState == PresentIntervalState.Custom && value == false) { PresentIntervalState = PresentIntervalState.Switch; - OnPropertyChanged(nameof(PresentIntervalState)); + } + else if (value) + { + PresentIntervalState = PresentIntervalState.Custom; } OnPropertyChanged(); } diff --git a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml index 3ded1e384..b143c1072 100644 --- a/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Settings/SettingsSystemView.axaml @@ -190,8 +190,9 @@ ToolTip.Tip="{locale:Locale SettingsTabSystemPresentIntervalStateTooltip}" Width="250" /> @@ -204,6 +205,19 @@ + + + + + + + + - - - + + + From 30552cc2caf043fb33227cba4832adbfb15c38ac Mon Sep 17 00:00:00 2001 From: jcm Date: Sat, 13 Jan 2024 11:32:59 -0600 Subject: [PATCH 4/4] Rename to VSyncMode internally, code cleanup --- src/Ryujinx.Ava/AppHost.cs | 130 +++++++++--------- src/Ryujinx.Ava/Assets/Locales/en_US.json | 30 ++-- src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs | 6 +- .../UI/Models/StatusUpdatedEventArgs.cs | 6 +- .../UI/ViewModels/MainWindowViewModel.cs | 130 +++++++----------- .../UI/ViewModels/SettingsViewModel.cs | 74 +++++----- .../UI/Views/Main/MainStatusBarView.axaml | 22 +-- .../UI/Views/Main/MainStatusBarView.axaml.cs | 6 +- .../Views/Settings/SettingsHotkeysView.axaml | 12 +- .../Views/Settings/SettingsSystemView.axaml | 50 +++---- .../Configuration/Hid/KeyboardHotkeys.cs | 6 +- .../{PresentIntervalState.cs => VSyncMode.cs} | 4 +- src/Ryujinx.Graphics.GAL/IWindow.cs | 2 +- .../Multithreading/ThreadedWindow.cs | 2 +- .../{PresentIntervalState.cs => VSyncMode.cs} | 2 +- src/Ryujinx.Graphics.OpenGL/Window.cs | 2 +- src/Ryujinx.Graphics.Vulkan/Window.cs | 12 +- src/Ryujinx.Graphics.Vulkan/WindowBase.cs | 2 +- src/Ryujinx.HLE/HLEConfiguration.cs | 18 ++- .../Services/SurfaceFlinger/SurfaceFlinger.cs | 14 +- src/Ryujinx.HLE/Switch.cs | 52 +++---- src/Ryujinx.Headless.SDL2/Options.cs | 10 +- src/Ryujinx.Headless.SDL2/Program.cs | 4 +- .../StatusUpdatedEventArgs.cs | 6 +- src/Ryujinx.Headless.SDL2/WindowBase.cs | 2 +- .../Configuration/ConfigurationFileFormat.cs | 8 +- .../Configuration/ConfigurationState.cs | 66 ++++----- src/Ryujinx/Ui/MainWindow.cs | 8 +- src/Ryujinx/Ui/RendererWidgetBase.cs | 8 +- src/Ryujinx/Ui/StatusUpdatedEventArgs.cs | 6 +- 30 files changed, 335 insertions(+), 365 deletions(-) rename src/Ryujinx.Common/Configuration/{PresentIntervalState.cs => VSyncMode.cs} (66%) rename src/Ryujinx.Graphics.GAL/{PresentIntervalState.cs => VSyncMode.cs} (76%) diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index dd77a08af..47fa1c32e 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -58,10 +58,10 @@ using IRenderer = Ryujinx.Graphics.GAL.IRenderer; using Key = Ryujinx.Input.Key; using MouseButton = Ryujinx.Input.MouseButton; -using PresentIntervalState = Ryujinx.Common.Configuration.PresentIntervalState; using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter; using Size = Avalonia.Size; using Switch = Ryujinx.HLE.Switch; +using VSyncMode = Ryujinx.Common.Configuration.VSyncMode; namespace Ryujinx.Ava { @@ -190,9 +190,9 @@ public AppHost( ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough; - ConfigurationState.Instance.Graphics.PresentIntervalState.Event += UpdatePresentIntervalState; - ConfigurationState.Instance.Graphics.CustomPresentInterval.Event += UpdateCustomPresentIntervalValue; - ConfigurationState.Instance.Graphics.EnableCustomPresentInterval.Event += UpdateCustomPresentIntervalEnabled; + ConfigurationState.Instance.Graphics.VSyncMode.Event += UpdateVSyncMode; + ConfigurationState.Instance.Graphics.CustomVSyncInterval.Event += UpdateCustomVSyncIntervalValue; + ConfigurationState.Instance.Graphics.EnableCustomVSyncInterval.Event += UpdateCustomVSyncIntervalEnabled; ConfigurationState.Instance.System.EnableInternetAccess.Event += UpdateEnableInternetAccessState; ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; @@ -240,34 +240,66 @@ private void UpdateColorSpacePassthrough(object sender, ReactiveEventArgs _renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value); } - private void UpdatePresentIntervalState(object sender, ReactiveEventArgs e) + public void UpdateVSyncMode(object sender, ReactiveEventArgs e) { if (Device != null) { - Device.PresentIntervalState = e.NewValue; - Device.UpdatePresentInterval(); + Device.VSyncMode = e.NewValue; + Device.UpdateVSyncInterval(); } //vulkan present mode may change in response, so recreate the swapchain - _renderer.Window?.ChangePresentIntervalState((Ryujinx.Graphics.GAL.PresentIntervalState)e.NewValue); - ConfigurationState.Instance.Graphics.PresentIntervalState.Value = e.NewValue; + _renderer.Window?.ChangeVSyncMode((Ryujinx.Graphics.GAL.VSyncMode)e.NewValue); + + _viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom); + + ConfigurationState.Instance.Graphics.VSyncMode.Value = e.NewValue; ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); } - private void UpdateCustomPresentIntervalValue(object sender, ReactiveEventArgs e) + public void VSyncModeToggle() + { + VSyncMode oldVSyncMode = Device.VSyncMode; + VSyncMode newVSyncMode = VSyncMode.Switch; + bool customVSyncIntervalEnabled = ConfigurationState.Instance.Graphics.EnableCustomVSyncInterval.Value; + + switch (oldVSyncMode) + { + case VSyncMode.Switch: + newVSyncMode = VSyncMode.Unbounded; + break; + case VSyncMode.Unbounded: + if (customVSyncIntervalEnabled) + { + newVSyncMode = VSyncMode.Custom; + } + else + { + newVSyncMode = VSyncMode.Switch; + } + + break; + case VSyncMode.Custom: + newVSyncMode = VSyncMode.Switch; + break; + } + UpdateVSyncMode(this, new ReactiveEventArgs(oldVSyncMode, newVSyncMode)); + } + + private void UpdateCustomVSyncIntervalValue(object sender, ReactiveEventArgs e) { if (Device != null) { - Device.TargetPresentInterval = e.NewValue; - Device.UpdatePresentInterval(); + Device.TargetVSyncInterval = e.NewValue; + Device.UpdateVSyncInterval(); } } - private void UpdateCustomPresentIntervalEnabled(object sender, ReactiveEventArgs e) + private void UpdateCustomVSyncIntervalEnabled(object sender, ReactiveEventArgs e) { if (Device != null) { - Device.CustomPresentIntervalEnabled = e.NewValue; - Device.UpdatePresentInterval(); + Device.CustomVSyncIntervalEnabled = e.NewValue; + Device.UpdateVSyncInterval(); } } @@ -550,12 +582,6 @@ private void HideCursorState_Changed(object sender, ReactiveEventArgs(oldState, presentIntervalState)); - } - public async Task LoadGuestApplication() { InitializeSwitchInstance(); @@ -819,7 +845,7 @@ private void InitializeSwitchInstance() _viewModel.UiHandler, (SystemLanguage)ConfigurationState.Instance.System.Language.Value, (RegionCode)ConfigurationState.Instance.System.Region.Value, - ConfigurationState.Instance.Graphics.PresentIntervalState, + ConfigurationState.Instance.Graphics.VSyncMode, ConfigurationState.Instance.System.EnableDockedMode, ConfigurationState.Instance.System.EnablePtc, ConfigurationState.Instance.System.EnableInternetAccess, @@ -834,7 +860,7 @@ private void InitializeSwitchInstance() ConfigurationState.Instance.System.UseHypervisor, ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, ConfigurationState.Instance.Multiplayer.Mode, - ConfigurationState.Instance.Graphics.CustomPresentInterval.Value); + ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value); Device = new Switch(configuration); } @@ -960,7 +986,7 @@ private void RenderLoop() Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); Translator.IsReadyForTranslation.Set(); - _renderer.Window.ChangePresentIntervalState((Ryujinx.Graphics.GAL.PresentIntervalState)Device.PresentIntervalState); + _renderer.Window.ChangeVSyncMode((Ryujinx.Graphics.GAL.VSyncMode)Device.VSyncMode); while (_isActive) { @@ -1008,7 +1034,7 @@ public void UpdateStatus() { // Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued. string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld]; - string presentIntervalState = Device.PresentIntervalState.ToString(); + string vSyncMode = Device.VSyncMode.ToString(); if (GraphicsConfig.ResScale != 1) { @@ -1016,7 +1042,7 @@ public void UpdateStatus() } StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( - presentIntervalState, + vSyncMode, LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%", ConfigurationState.Instance.Graphics.GraphicsBackend.Value == GraphicsBackend.Vulkan ? "Vulkan" : "OpenGL", dockedMode, @@ -1108,40 +1134,16 @@ private bool UpdateFrame() { switch (currentHotkeyState) { - //todo default - case KeyboardHotkeyState.TogglePresentIntervalState: - PresentIntervalState oldState = Device.PresentIntervalState; - PresentIntervalState newState; - if (oldState == PresentIntervalState.Switch) - { - newState = PresentIntervalState.Unbounded; - } - else if (oldState == PresentIntervalState.Unbounded) - { - if (ConfigurationState.Instance.Graphics.EnableCustomPresentInterval) - { - newState = PresentIntervalState.Custom; - } - else - { - newState = PresentIntervalState.Switch; - } - } - else - { - newState = PresentIntervalState.Switch; - } - UpdatePresentIntervalState(this, new ReactiveEventArgs(oldState, newState)); - _viewModel.ShowCustomPresentIntervalPicker = - (newState == PresentIntervalState.Custom); + case KeyboardHotkeyState.ToggleVSyncMode: + VSyncModeToggle(); break; - case KeyboardHotkeyState.CustomPresentIntervalDecrement: - Device.DecrementCustomPresentInterval(); - _viewModel.CustomPresentInterval -= 1; + case KeyboardHotkeyState.CustomVSyncIntervalDecrement: + Device.DecrementCustomVSyncInterval(); + _viewModel.CustomVSyncInterval -= 1; break; - case KeyboardHotkeyState.CustomPresentIntervalIncrement: - Device.IncrementCustomPresentInterval(); - _viewModel.CustomPresentInterval += 1; + case KeyboardHotkeyState.CustomVSyncIntervalIncrement: + Device.IncrementCustomVSyncInterval(); + _viewModel.CustomVSyncInterval += 1; break; case KeyboardHotkeyState.Screenshot: ScreenshotRequested = true; @@ -1228,9 +1230,9 @@ private KeyboardHotkeyState GetHotkeyState() { KeyboardHotkeyState state = KeyboardHotkeyState.None; - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.PresentIntervalState)) + if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VSyncMode)) { - state = KeyboardHotkeyState.TogglePresentIntervalState; + state = KeyboardHotkeyState.ToggleVSyncMode; } else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) { @@ -1264,13 +1266,13 @@ private KeyboardHotkeyState GetHotkeyState() { state = KeyboardHotkeyState.VolumeDown; } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CustomPresentIntervalIncrement)) + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CustomVSyncIntervalIncrement)) { - state = KeyboardHotkeyState.CustomPresentIntervalIncrement; + state = KeyboardHotkeyState.CustomVSyncIntervalIncrement; } - else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CustomPresentIntervalDecrement)) + else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CustomVSyncIntervalDecrement)) { - state = KeyboardHotkeyState.CustomPresentIntervalDecrement; + state = KeyboardHotkeyState.CustomVSyncIntervalDecrement; } return state; diff --git a/src/Ryujinx.Ava/Assets/Locales/en_US.json b/src/Ryujinx.Ava/Assets/Locales/en_US.json index 849604577..1d80f5161 100644 --- a/src/Ryujinx.Ava/Assets/Locales/en_US.json +++ b/src/Ryujinx.Ava/Assets/Locales/en_US.json @@ -126,17 +126,17 @@ "SettingsTabSystemSystemLanguageTraditionalChinese": "Traditional Chinese", "SettingsTabSystemSystemTimeZone": "System Time Zone:", "SettingsTabSystemSystemTime": "System Time:", - "SettingsTabSystemPresentIntervalState": "VSync:", - "SettingsTabSystemEnableCustomPresentInterval": "Enable custom refresh rate (Experimental)", - "SettingsTabSystemPresentIntervalStateSwitch": "On", - "SettingsTabSystemPresentIntervalStateUnbounded": "Off", - "SettingsTabSystemPresentIntervalStateCustom": "Custom Refresh Rate", - "SettingsTabSystemPresentIntervalStateTooltip": "Emulated Vertical Sync. 'On' emulates the Switch's refresh rate of 60Hz. 'Off' is an unbounded refresh rate.", - "SettingsTabSystemPresentIntervalStateTooltipCustom": "Emulated Vertical Sync. 'On' emulates the Switch's refresh rate of 60Hz. 'Off' is an unbounded refresh rate. 'Custom' emulates the specified custom refresh rate.", - "SettingsTabSystemEnableCustomPresentIntervalTooltip": "Allows the user to specify an emulated refresh rate. In some titles, this may speed up or slow down the rate of gameplay logic. In other titles, it may allow for capping FPS at some multiple of the refresh rate, or lead to unpredictable behavior. This is an experimental feature, with no guarantees for how gameplay will be affected. \n\nLeave OFF if unsure.", - "SettingsTabSystemCustomPresentIntervalValueTooltip": "The custom refresh rate target value.", - "SettingsTabSystemCustomPresentIntervalSliderTooltip": "The custom refresh rate, as a percentage of the normal Switch refresh rate.", - "SettingsTabSystemCustomPresentIntervalValue": "Custom Refresh Rate Value:", + "SettingsTabSystemVSyncMode": "VSync:", + "SettingsTabSystemEnableCustomVSyncInterval": "Enable custom refresh rate (Experimental)", + "SettingsTabSystemVSyncModeSwitch": "On", + "SettingsTabSystemVSyncModeUnbounded": "Off", + "SettingsTabSystemVSyncModeCustom": "Custom Refresh Rate", + "SettingsTabSystemVSyncModeTooltip": "Emulated Vertical Sync. 'On' emulates the Switch's refresh rate of 60Hz. 'Off' is an unbounded refresh rate.", + "SettingsTabSystemVSyncModeTooltipCustom": "Emulated Vertical Sync. 'On' emulates the Switch's refresh rate of 60Hz. 'Off' is an unbounded refresh rate. 'Custom' emulates the specified custom refresh rate.", + "SettingsTabSystemEnableCustomVSyncIntervalTooltip": "Allows the user to specify an emulated refresh rate. In some titles, this may speed up or slow down the rate of gameplay logic. In other titles, it may allow for capping FPS at some multiple of the refresh rate, or lead to unpredictable behavior. This is an experimental feature, with no guarantees for how gameplay will be affected. \n\nLeave OFF if unsure.", + "SettingsTabSystemCustomVSyncIntervalValueTooltip": "The custom refresh rate target value.", + "SettingsTabSystemCustomVSyncIntervalSliderTooltip": "The custom refresh rate, as a percentage of the normal Switch refresh rate.", + "SettingsTabSystemCustomVSyncIntervalValue": "Custom Refresh Rate Value:", "SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)", "SettingsTabSystemEnableFsIntegrityChecks": "FS Integrity Checks", "SettingsTabSystemAudioBackend": "Audio Backend:", @@ -144,7 +144,7 @@ "SettingsTabSystemAudioBackendOpenAL": "OpenAL", "SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSDL2": "SDL2", - "SettingsTabSystemCustomPresentInterval": "Interval", + "SettingsTabSystemCustomVSyncInterval": "Interval", "SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacksNote": "May cause instability", "SettingsTabSystemExpandDramSize": "Use alternative memory layout (Developers)", @@ -585,13 +585,13 @@ "RyujinxUpdater": "Ryujinx Updater", "SettingsTabHotkeys": "Keyboard Hotkeys", "SettingsTabHotkeysHotkeys": "Keyboard Hotkeys", - "SettingsTabHotkeysTogglePresentIntervalStateHotkey": "Toggle Present Interval state:", + "SettingsTabHotkeysToggleVSyncModeHotkey": "Toggle VSync mode:", "SettingsTabHotkeysScreenshotHotkey": "Screenshot:", "SettingsTabHotkeysShowUiHotkey": "Show UI:", "SettingsTabHotkeysPauseHotkey": "Pause:", "SettingsTabHotkeysToggleMuteHotkey": "Mute:", - "SettingsTabHotkeysIncrementCustomPresentIntervalHotkey": "Raise custom refresh interval", - "SettingsTabHotkeysDecrementCustomPresentIntervalHotkey": "Lower custom refresh interval", + "SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey": "Raise custom refresh rate", + "SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey": "Lower custom refresh rate", "ControllerMotionTitle": "Motion Control Settings", "ControllerRumbleTitle": "Rumble Settings", "SettingsSelectThemeFileDialogTitle": "Select Theme File", diff --git a/src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs b/src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs index 6429dda05..5c5507ffc 100644 --- a/src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs +++ b/src/Ryujinx.Ava/Common/KeyboardHotkeyState.cs @@ -3,7 +3,7 @@ namespace Ryujinx.Ava.Common public enum KeyboardHotkeyState { None, - TogglePresentIntervalState, + ToggleVSyncMode, Screenshot, ShowUi, Pause, @@ -12,7 +12,7 @@ public enum KeyboardHotkeyState ResScaleDown, VolumeUp, VolumeDown, - CustomPresentIntervalIncrement, - CustomPresentIntervalDecrement, + CustomVSyncIntervalIncrement, + CustomVSyncIntervalDecrement, } } diff --git a/src/Ryujinx.Ava/UI/Models/StatusUpdatedEventArgs.cs b/src/Ryujinx.Ava/UI/Models/StatusUpdatedEventArgs.cs index ce975f8c5..e14bff25e 100644 --- a/src/Ryujinx.Ava/UI/Models/StatusUpdatedEventArgs.cs +++ b/src/Ryujinx.Ava/UI/Models/StatusUpdatedEventArgs.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Ava.UI.Models { internal class StatusUpdatedEventArgs : EventArgs { - public string PresentIntervalState { get; } + public string VSyncMode { get; } public string VolumeStatus { get; } public string GpuBackend { get; } public string AspectRatio { get; } @@ -13,9 +13,9 @@ internal class StatusUpdatedEventArgs : EventArgs public string GameStatus { get; } public string GpuName { get; } - public StatusUpdatedEventArgs(string presentIntervalState, string volumeStatus, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName) + public StatusUpdatedEventArgs(string vSyncMode, string volumeStatus, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName) { - PresentIntervalState = presentIntervalState; + VSyncMode = vSyncMode; VolumeStatus = volumeStatus; GpuBackend = gpuBackend; DockedMode = dockedMode; diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index e34eb72b8..ec0bb3fc5 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -59,7 +59,7 @@ public class MainWindowViewModel : BaseModel private string _searchText; private Timer _searchTimer; private string _dockedStatusText; - private string _presentIntervalStateText; + private string _vSyncModeText; private string _fifoStatusText; private string _gameStatusText; private string _volumeStatusText; @@ -75,7 +75,7 @@ public class MainWindowViewModel : BaseModel private bool _showStatusSeparator; private Brush _progressBarForegroundColor; private Brush _progressBarBackgroundColor; - private Brush _presentIntervalStateColor; + private Brush _vSyncModeColor; private byte[] _selectedIcon; private bool _isAppletMenuActive; private int _statusBarProgressMaximum; @@ -103,8 +103,8 @@ public class MainWindowViewModel : BaseModel private WindowState _windowState; private double _windowWidth; private double _windowHeight; - private int _customPresentInterval; - private int _customPresentIntervalPercentageProxy; + private int _customVSyncInterval; + private int _customVSyncIntervalPercentageProxy; @@ -133,7 +133,7 @@ public MainWindowViewModel() Volume = ConfigurationState.Instance.System.AudioVolume; } - CustomPresentInterval = ConfigurationState.Instance.Graphics.CustomPresentInterval.Value; + CustomVSyncInterval = ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value; } public void Initialize( @@ -410,25 +410,25 @@ public Brush ProgressBarForegroundColor } } - public Brush PresentIntervalStateColor + public Brush VSyncModeColor { - get => _presentIntervalStateColor; + get => _vSyncModeColor; set { - _presentIntervalStateColor = value; + _vSyncModeColor = value; OnPropertyChanged(); } } - public bool ShowCustomPresentIntervalPicker + public bool ShowCustomVSyncIntervalPicker { get { if (_isGameRunning) { - return AppHost.Device.PresentIntervalState == - PresentIntervalState.Custom; + return AppHost.Device.VSyncMode == + VSyncMode.Custom; } else { @@ -441,30 +441,30 @@ public bool ShowCustomPresentIntervalPicker } } - public int CustomPresentIntervalPercentageProxy + public int CustomVSyncIntervalPercentageProxy { - get => _customPresentIntervalPercentageProxy; + get => _customVSyncIntervalPercentageProxy; set { int newInterval = (int)(((decimal)value / 100) * 60); - _customPresentInterval = newInterval; - _customPresentIntervalPercentageProxy = value; - ConfigurationState.Instance.Graphics.CustomPresentInterval.Value = newInterval; + _customVSyncInterval = newInterval; + _customVSyncIntervalPercentageProxy = value; + ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value = newInterval; if (_isGameRunning) { - AppHost.Device.CustomPresentInterval = newInterval; - AppHost.Device.UpdatePresentInterval(); + AppHost.Device.CustomVSyncInterval = newInterval; + AppHost.Device.UpdateVSyncInterval(); } - OnPropertyChanged((nameof(CustomPresentInterval))); - OnPropertyChanged((nameof(CustomPresentIntervalPercentageText))); + OnPropertyChanged((nameof(CustomVSyncInterval))); + OnPropertyChanged((nameof(CustomVSyncIntervalPercentageText))); } } - public string CustomPresentIntervalPercentageText + public string CustomVSyncIntervalPercentageText { get { - string text = CustomPresentIntervalPercentageProxy.ToString() + "%"; + string text = CustomVSyncIntervalPercentageProxy.ToString() + "%"; return text; } set @@ -473,22 +473,22 @@ public string CustomPresentIntervalPercentageText } } - public int CustomPresentInterval + public int CustomVSyncInterval { - get => _customPresentInterval; + get => _customVSyncInterval; set { - _customPresentInterval = value; + _customVSyncInterval = value; int newPercent = (int)(((decimal)value / 60) * 100); - _customPresentIntervalPercentageProxy = newPercent; - ConfigurationState.Instance.Graphics.CustomPresentInterval.Value = value; + _customVSyncIntervalPercentageProxy = newPercent; + ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value = value; if (_isGameRunning) { - AppHost.Device.CustomPresentInterval = value; - AppHost.Device.UpdatePresentInterval(); + AppHost.Device.CustomVSyncInterval = value; + AppHost.Device.UpdateVSyncInterval(); } - OnPropertyChanged(nameof(CustomPresentIntervalPercentageProxy)); - OnPropertyChanged(nameof(CustomPresentIntervalPercentageText)); + OnPropertyChanged(nameof(CustomVSyncIntervalPercentageProxy)); + OnPropertyChanged(nameof(CustomVSyncIntervalPercentageText)); OnPropertyChanged(); } } @@ -581,12 +581,12 @@ public string BackendText } } - public string PresentIntervalStateText + public string VSyncModeText { - get => _presentIntervalStateText; + get => _vSyncModeText; set { - _presentIntervalStateText = value; + _vSyncModeText = value; OnPropertyChanged(); } @@ -1286,18 +1286,18 @@ private void Update_StatusBar(object sender, StatusUpdatedEventArgs args) { Dispatcher.UIThread.InvokeAsync(() => { - Application.Current.Styles.TryGetResource(args.PresentIntervalState, + Application.Current.Styles.TryGetResource(args.VSyncMode, Avalonia.Application.Current.ActualThemeVariant, out object color); if (color is not null) { - PresentIntervalStateColor = new SolidColorBrush((Color)color); + VSyncModeColor = new SolidColorBrush((Color)color); } - PresentIntervalStateText = args.PresentIntervalState == "Custom" ? "Custom" : "VSync"; - ShowCustomPresentIntervalPicker = - args.PresentIntervalState == PresentIntervalState.Custom.ToString(); + VSyncModeText = args.VSyncMode == "Custom" ? "Custom" : "VSync"; + ShowCustomVSyncIntervalPicker = + args.VSyncMode == VSyncMode.Custom.ToString(); DockedStatusText = args.DockedMode; AspectRatioStatusText = args.AspectRatio; GameStatusText = args.GameStatus; @@ -1456,55 +1456,25 @@ public void ToggleDockMode() } } - public void UpdatePresentIntervalState() + public void UpdateVSyncMode() { - PresentIntervalState oldPresentInterval = AppHost.Device.PresentIntervalState; - PresentIntervalState newPresentInterval = PresentIntervalState.Switch; - bool customPresentIntervalEnabled = ConfigurationState.Instance.Graphics.EnableCustomPresentInterval.Value; - - switch (oldPresentInterval) - { - case PresentIntervalState.Switch: - newPresentInterval = PresentIntervalState.Unbounded; - break; - case PresentIntervalState.Unbounded: - if (customPresentIntervalEnabled) - { - newPresentInterval = PresentIntervalState.Custom; - } - else - { - newPresentInterval = PresentIntervalState.Switch; - } - break; - case PresentIntervalState.Custom: - newPresentInterval = PresentIntervalState.Switch; - break; - } - - ConfigurationState.Instance.Graphics.PresentIntervalState.Value = newPresentInterval; - - if (_isGameRunning) - { - AppHost.UpdatePresentInterval(newPresentInterval); - } - - OnPropertyChanged(nameof(ShowCustomPresentIntervalPicker)); + AppHost.VSyncModeToggle(); + OnPropertyChanged(nameof(ShowCustomVSyncIntervalPicker)); } - public void PresentIntervalStateSettingChanged() + public void VSyncModeSettingChanged() { if (_isGameRunning) { - AppHost.Device.CustomPresentInterval = ConfigurationState.Instance.Graphics.CustomPresentInterval.Value; - AppHost.Device.UpdatePresentInterval(); + AppHost.Device.CustomVSyncInterval = ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value; + AppHost.Device.UpdateVSyncInterval(); } - CustomPresentInterval = ConfigurationState.Instance.Graphics.CustomPresentInterval.Value; - OnPropertyChanged(nameof(ShowCustomPresentIntervalPicker)); - OnPropertyChanged(nameof(CustomPresentIntervalPercentageProxy)); - OnPropertyChanged(nameof(CustomPresentIntervalPercentageText)); - OnPropertyChanged(nameof(CustomPresentInterval)); + CustomVSyncInterval = ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value; + OnPropertyChanged(nameof(ShowCustomVSyncIntervalPicker)); + OnPropertyChanged(nameof(CustomVSyncIntervalPercentageProxy)); + OnPropertyChanged(nameof(CustomVSyncIntervalPercentageText)); + OnPropertyChanged(nameof(CustomVSyncInterval)); } public async Task ExitCurrentState() diff --git a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs index 5ff1871bd..4333f4974 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/SettingsViewModel.cs @@ -52,10 +52,10 @@ public class SettingsViewModel : BaseModel private string _customThemePath; private int _scalingFilter; private int _scalingFilterLevel; - private int _customPresentInterval; - private bool _enableCustomPresentInterval; - private int _customPresentIntervalPercentageProxy; - private PresentIntervalState _presentIntervalState; + private int _customVSyncInterval; + private bool _enableCustomVSyncInterval; + private int _customVSyncIntervalPercentageProxy; + private VSyncMode _vSyncMode; public event Action CloseWindow; public event Action SaveSettingsEvent; @@ -142,39 +142,39 @@ public bool DirectoryChanged public bool EnableDockedMode { get; set; } public bool EnableKeyboard { get; set; } public bool EnableMouse { get; set; } - public PresentIntervalState PresentIntervalState + public VSyncMode VSyncMode { - get => _presentIntervalState; + get => _vSyncMode; set { - if (value == PresentIntervalState.Custom || - value == PresentIntervalState.Switch || - value == PresentIntervalState.Unbounded) + if (value == VSyncMode.Custom || + value == VSyncMode.Switch || + value == VSyncMode.Unbounded) { - _presentIntervalState = value; + _vSyncMode = value; OnPropertyChanged(); } } } - public int CustomPresentIntervalPercentageProxy + public int CustomVSyncIntervalPercentageProxy { - get => _customPresentIntervalPercentageProxy; + get => _customVSyncIntervalPercentageProxy; set { int newInterval = (int)(((decimal)value / 100) * 60); - _customPresentInterval = newInterval; - _customPresentIntervalPercentageProxy = value; - OnPropertyChanged((nameof(CustomPresentInterval))); - OnPropertyChanged((nameof(CustomPresentIntervalPercentageText))); + _customVSyncInterval = newInterval; + _customVSyncIntervalPercentageProxy = value; + OnPropertyChanged((nameof(CustomVSyncInterval))); + OnPropertyChanged((nameof(CustomVSyncIntervalPercentageText))); } } - public string CustomPresentIntervalPercentageText + public string CustomVSyncIntervalPercentageText { get { - string text = CustomPresentIntervalPercentageProxy.ToString() + "%"; + string text = CustomVSyncIntervalPercentageProxy.ToString() + "%"; return text; } set @@ -183,34 +183,34 @@ public string CustomPresentIntervalPercentageText } } - public bool EnableCustomPresentInterval + public bool EnableCustomVSyncInterval { - get => _enableCustomPresentInterval; + get => _enableCustomVSyncInterval; set { - _enableCustomPresentInterval = value; - if (_presentIntervalState == PresentIntervalState.Custom && value == false) + _enableCustomVSyncInterval = value; + if (_vSyncMode == VSyncMode.Custom && value == false) { - PresentIntervalState = PresentIntervalState.Switch; + VSyncMode = VSyncMode.Switch; } else if (value) { - PresentIntervalState = PresentIntervalState.Custom; + VSyncMode = VSyncMode.Custom; } OnPropertyChanged(); } } - public int CustomPresentInterval + public int CustomVSyncInterval { - get => _customPresentInterval; + get => _customVSyncInterval; set { - _customPresentInterval = value; + _customVSyncInterval = value; int newPercent = (int)(((decimal)value / 60) * 100); - _customPresentIntervalPercentageProxy = newPercent; - OnPropertyChanged(nameof(CustomPresentIntervalPercentageProxy)); - OnPropertyChanged(nameof(CustomPresentIntervalPercentageText)); + _customVSyncIntervalPercentageProxy = newPercent; + OnPropertyChanged(nameof(CustomVSyncIntervalPercentageProxy)); + OnPropertyChanged(nameof(CustomVSyncIntervalPercentageText)); OnPropertyChanged(); } } @@ -524,9 +524,9 @@ public void LoadCurrentConfiguration() CurrentDate = currentDateTime.Date; CurrentTime = currentDateTime.TimeOfDay.Add(TimeSpan.FromSeconds(config.System.SystemTimeOffset)); - EnableCustomPresentInterval = config.Graphics.EnableCustomPresentInterval.Value; - CustomPresentInterval = config.Graphics.CustomPresentInterval; - PresentIntervalState = config.Graphics.PresentIntervalState; + EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval.Value; + CustomVSyncInterval = config.Graphics.CustomVSyncInterval; + VSyncMode = config.Graphics.VSyncMode; EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks; ExpandDramSize = config.System.ExpandRam; IgnoreMissingServices = config.System.IgnoreMissingServices; @@ -615,9 +615,9 @@ public void SaveSettings() } config.System.SystemTimeOffset.Value = Convert.ToInt64((CurrentDate.ToUnixTimeSeconds() + CurrentTime.TotalSeconds) - DateTimeOffset.Now.ToUnixTimeSeconds()); - config.Graphics.PresentIntervalState.Value = PresentIntervalState; - config.Graphics.EnableCustomPresentInterval.Value = EnableCustomPresentInterval; - config.Graphics.CustomPresentInterval.Value = CustomPresentInterval; + config.Graphics.VSyncMode.Value = VSyncMode; + config.Graphics.EnableCustomVSyncInterval.Value = EnableCustomVSyncInterval; + config.Graphics.CustomVSyncInterval.Value = CustomVSyncInterval; config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks; config.System.ExpandRam.Value = ExpandDramSize; config.System.IgnoreMissingServices.Value = IgnoreMissingServices; @@ -683,7 +683,7 @@ public void SaveSettings() config.ToFileFormat().SaveConfig(Program.ConfigurationPath); MainWindow.UpdateGraphicsConfig(); - MainWindow.MainWindowViewModel.PresentIntervalStateSettingChanged(); + MainWindow.MainWindowViewModel.VSyncModeSettingChanged(); SaveSettingsEvent?.Invoke(); diff --git a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml index c53281c2a..4bc5130f2 100644 --- a/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml +++ b/src/Ryujinx.Ava/UI/Views/Main/MainStatusBarView.axaml @@ -80,18 +80,18 @@ MaxHeight="18" Orientation="Horizontal"> + PointerReleased="VSyncMode_PointerReleased" + Text="{Binding VSyncModeText}" + TextAlignment="Start"/>