diff --git a/assets/Locales/Root.json b/assets/Locales/Root.json index d9139904c..e5de7b412 100644 --- a/assets/Locales/Root.json +++ b/assets/Locales/Root.json @@ -200,6 +200,31 @@ "zh_TW": "使用 Hypervisor" } }, + { + "ID": "SettingsTabSystemGCLowLatency", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Use Low-latency Garbage Collector", + "es_ES": "Usa recolección de basura de baja latencia", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "MenuBarFile", "Translations": { @@ -16550,6 +16575,31 @@ "zh_TW": "變更客體記憶體的映射和存取方式。這會極大地影響模擬 CPU 效能。\n\n如果不確定,請設定為主體略過檢查模式。" } }, + { + "ID": "GCLowLatencyTooltip", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Sets the garbage collector for the CLR to low-latency mode.\n\nThis may decrease stuttering at the cost of performance.\n\nLeave OFF if unsure.", + "es_ES": "", + "fr_FR": "", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "MemoryManagerSoftwareTooltip", "Translations": { diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs index 651f92986..467dc63fe 100644 --- a/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs +++ b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs @@ -133,9 +133,9 @@ namespace Ryujinx.HLE.HOS.Services.Caps using SKBitmap bitmap = new(new SKImageInfo(ScreenshotWidth, ScreenshotHeight, SKColorType.Rgba8888)); - IntPtr pixels = bitmap.GetPixels(); + nint pixels = bitmap.GetPixels(); - if (pixels == IntPtr.Zero) + if (pixels == 0) { return ResultCode.InvalidArgument; } diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 17d941962..35abab811 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -198,6 +198,12 @@ namespace Ryujinx.Ava public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure() .UsePlatformDetect() + + // Vulkan UI rendering performs better, but its unpolished, and as such it lacks effective transparency. + // https://github.com/AvaloniaUI/Avalonia/issues/19378 + // https://github.com/AvaloniaUI/Avalonia/issues/9610 + // X11RenderingMode.Glx && X11RenderingMode.Egl, Win32RenderingMode.Vulkan have these issues. + .With(new X11PlatformOptions { EnableMultiTouch = true, diff --git a/src/Ryujinx/Systems/AppHost.cs b/src/Ryujinx/Systems/AppHost.cs index 358b585f4..627e486d2 100644 --- a/src/Ryujinx/Systems/AppHost.cs +++ b/src/Ryujinx/Systems/AppHost.cs @@ -46,6 +46,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -579,6 +580,12 @@ namespace Ryujinx.Ava.Systems { _isActive = false; _playTimer.Stop(); + + GCSettings.LatencyMode = GCLatencyMode.Interactive; + if (ConfigurationState.Instance.System.GCLowLatency) + { + Logger.Info?.Print(LogClass.Application, "Garbage collector set to interactive mode."); + } } private void Exit() @@ -662,6 +669,12 @@ namespace Ryujinx.Ava.Systems _chrono.Stop(); _playTimer.Stop(); + + GCSettings.LatencyMode = GCLatencyMode.Interactive; + if (ConfigurationState.Instance.System.GCLowLatency) + { + Logger.Info?.Print(LogClass.Application, "Garbage collector set to interactive mode."); + } } public void DisposeGpu() @@ -915,7 +928,14 @@ namespace Ryujinx.Ava.Systems ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => appMetadata.UpdatePreGame() ); + _playTimer.Start(); + + if (ConfigurationState.Instance.System.GCLowLatency) + { + GCSettings.LatencyMode = GCLatencyMode.LowLatency; + Logger.Info?.Print(LogClass.Application, "Garbage collector set to low latency mode."); + } } internal void Resume() @@ -926,6 +946,12 @@ namespace Ryujinx.Ava.Systems _playTimer.Start(); _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI); Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed."); + + if (ConfigurationState.Instance.System.GCLowLatency) + { + GCSettings.LatencyMode = GCLatencyMode.LowLatency; + Logger.Info?.Print(LogClass.Application, "Garbage collector set to low latency mode."); + } } internal void Pause() @@ -936,6 +962,12 @@ namespace Ryujinx.Ava.Systems _playTimer.Stop(); _viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI, LocaleManager.Instance[LocaleKeys.Paused]); Logger.Info?.Print(LogClass.Emulation, "Emulation was paused."); + + GCSettings.LatencyMode = GCLatencyMode.Interactive; + if (ConfigurationState.Instance.System.GCLowLatency) + { + Logger.Info?.Print(LogClass.Application, "Garbage collector set to interactive mode."); + } } private void InitEmulatedSwitch() diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs index 1fe98ee69..55b2d2cac 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationFileFormat.cs @@ -470,6 +470,11 @@ namespace Ryujinx.Ava.Systems.Configuration /// Uses Hypervisor over JIT if available /// public bool UseHypervisor { get; set; } + + /// + /// Enable or disable low-latency mode for garbage collection + /// + public bool GCLowLatency { get; set; } /// /// Enables or disables the GDB stub diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs index 728321985..1b86e4f39 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Migration.cs @@ -112,6 +112,7 @@ namespace Ryujinx.Ava.Systems.Configuration System.IgnoreControllerApplet.Value = cff.IgnoreApplet; System.SkipUserProfilesManager.Value = cff.SkipUserProfiles; System.UseHypervisor.Value = cff.UseHypervisor; + System.GCLowLatency.Value = cff.GCLowLatency; UI.GuiColumns.FavColumn.Value = shouldLoadFromFile ? cff.GuiColumns.FavColumn : UI.GuiColumns.FavColumn.Value; UI.GuiColumns.IconColumn.Value = shouldLoadFromFile ? cff.GuiColumns.IconColumn : UI.GuiColumns.IconColumn.Value; @@ -535,7 +536,8 @@ namespace Ryujinx.Ava.Systems.Configuration { if (cff.AudioBackend is AudioBackend.SDL2) cff.AudioBackend = AudioBackend.SDL3; - }) + }), + (72, static cff => cff.GCLowLatency = false) ); } } diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs index 7775125d4..c66f74ed5 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.Model.cs @@ -417,6 +417,11 @@ namespace Ryujinx.Ava.Systems.Configuration /// Uses Hypervisor over JIT if available /// public ReactiveObject UseHypervisor { get; private set; } + + /// + /// Enable or disable low-latency garbage collection + /// + public ReactiveObject GCLowLatency { get; private set; } public SystemSection() { @@ -471,6 +476,8 @@ namespace Ryujinx.Ava.Systems.Configuration AudioVolume.LogChangesToValue(nameof(AudioVolume)); UseHypervisor = new ReactiveObject(); UseHypervisor.LogChangesToValue(nameof(UseHypervisor)); + GCLowLatency = new ReactiveObject(); + GCLowLatency.LogChangesToValue(nameof(GCLowLatency)); } } diff --git a/src/Ryujinx/Systems/Configuration/ConfigurationState.cs b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs index e4874963d..b4b5e8029 100644 --- a/src/Ryujinx/Systems/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Systems/Configuration/ConfigurationState.cs @@ -87,6 +87,7 @@ namespace Ryujinx.Ava.Systems.Configuration IgnoreApplet = System.IgnoreControllerApplet, SkipUserProfiles = System.SkipUserProfilesManager, UseHypervisor = System.UseHypervisor, + GCLowLatency = System.GCLowLatency, GuiColumns = new GuiColumns { FavColumn = UI.GuiColumns.FavColumn, diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index e488495d6..ffb658cc2 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -2066,7 +2066,8 @@ namespace Ryujinx.Ava.UI.ViewModels // Remove window chrome: WS_OVERLAPPEDWINDOW -> WS_POPUP | WS_VISIBLE Win32NativeInterop.SetWindowLongPtrW(hwnd, Win32NativeInterop.GWL_STYLE, unchecked((nint)(Win32NativeInterop.WS_POPUP | Win32NativeInterop.WS_VISIBLE))); - + + // TODO: why is this nullable Avalonia.Platform.Screen? screen = Window.Screens.ScreenFromVisual(Window); int w = screen?.Bounds.Width ?? 0; int h = screen?.Bounds.Height ?? 0; diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 6904d4ebc..d4532e8ca 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -286,6 +286,7 @@ namespace Ryujinx.Ava.UI.ViewModels public bool IsVulkanSelected => GraphicsBackendIndex == 1 || (GraphicsBackendIndex == 0 && !OperatingSystem.IsMacOS()); public bool UseHypervisor { get; set; } + public bool GCLowLatency { get; set; } public bool DisableP2P { get; set; } public bool ShowDirtyHacks => ConfigurationState.Instance.Hacks.ShowDirtyHacks; @@ -689,6 +690,7 @@ namespace Ryujinx.Ava.UI.ViewModels EnableLowPowerPptc = config.System.EnableLowPowerPtc; MemoryMode = (int)config.System.MemoryManagerMode.Value; UseHypervisor = config.System.UseHypervisor; + GCLowLatency = config.System.GCLowLatency; TurboMultiplier = config.System.TickScalar; // Graphics @@ -800,6 +802,7 @@ namespace Ryujinx.Ava.UI.ViewModels config.System.EnableLowPowerPtc.Value = EnableLowPowerPptc; config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode; config.System.UseHypervisor.Value = UseHypervisor; + config.System.GCLowLatency.Value = GCLowLatency; config.System.TickScalar.Value = TurboMultiplier; // Graphics diff --git a/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml index 2424aca97..c253c1aa0 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsCPUView.axaml @@ -71,6 +71,11 @@ + + +