From a1ddaa2736b188de928564af56aa787a25831ff7 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Thu, 24 Nov 2022 15:08:27 +0100 Subject: [PATCH] ui: Fixes disposing on GTK/Avalonia and Firmware Messages on Avalonia (#3885) * ui: Only wait on _exitEvent when MainLoop is active under GTK This fixes a dispose issue under Horizon/GTK, we don't check if the ApplicationClient is null so it throw NCE. We don't check if the main loop is active and waiting an event which is set in the main loop... So that could lead to a freeze. Everything works fine in GTK now. Related issue: https://github.com/Ryujinx/Ryujinx/issues/3873 As a side note, same kind of issue appear in Avalonia UI too. Firmware's popup doesn't show anything and the emulator just freeze. * TSRBerry's change Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> * Fix Avalonia crashing/freezing * Add Avalonia OpenGL fixes * Fix firmware popup on windows * Fixes everything * Add _initialized bool to VulkanRenderer and OpenGL Window Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com> --- Ryujinx.Ava/AppHost.cs | 40 +++++++++---------- .../Ui/Controls/OpenGLEmbeddedWindow.cs | 6 +-- Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs | 39 ++++++++++-------- .../Multithreading/ThreadedRenderer.cs | 8 +++- Ryujinx.Graphics.OpenGL/Queries/Counters.cs | 2 +- Ryujinx.Graphics.OpenGL/Window.cs | 10 ++++- Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 11 ++++- Ryujinx.HLE/HOS/Horizon.cs | 5 ++- Ryujinx/Ui/MainWindow.cs | 3 -- Ryujinx/Ui/RendererWidgetBase.cs | 10 +++-- Ryujinx/Ui/VKRenderer.cs | 3 +- 11 files changed, 83 insertions(+), 54 deletions(-) diff --git a/Ryujinx.Ava/AppHost.cs b/Ryujinx.Ava/AppHost.cs index 3d4c6cde86..a016ebd5a5 100644 --- a/Ryujinx.Ava/AppHost.cs +++ b/Ryujinx.Ava/AppHost.cs @@ -60,7 +60,7 @@ namespace Ryujinx.Ava private const float VolumeDelta = 0.05f; - private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None); + private static readonly Cursor InvisibleCursor = new Cursor(StandardCursorType.None); private readonly long _ticksPerFrame; private readonly Stopwatch _chrono; @@ -349,7 +349,10 @@ namespace Ryujinx.Ava _isActive = false; - _renderingThread.Join(); + if (_renderingThread.IsAlive) + { + _renderingThread.Join(); + } DisplaySleep.Restore(); @@ -378,7 +381,7 @@ namespace Ryujinx.Ava _gpuCancellationTokenSource.Cancel(); _gpuCancellationTokenSource.Dispose(); - + _chrono.Stop(); } @@ -393,7 +396,7 @@ namespace Ryujinx.Ava Renderer?.MakeCurrent(); Device.DisposeGpu(); - + Renderer?.MakeCurrent(null); } @@ -417,7 +420,6 @@ namespace Ryujinx.Ava public async Task LoadGuestApplication() { InitializeSwitchInstance(); - MainWindow.UpdateGraphicsConfig(); SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion(); @@ -428,17 +430,16 @@ namespace Ryujinx.Ava { if (userError == UserError.NoFirmware) { - string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"], - firmwareVersion.VersionString); - UserResult result = await ContentDialogHelper.CreateConfirmationDialog( - LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"], message, - LocaleManager.Instance["InputDialogYes"], LocaleManager.Instance["InputDialogNo"], ""); + LocaleManager.Instance["DialogFirmwareNoFirmwareInstalledMessage"], + string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"], firmwareVersion.VersionString), + LocaleManager.Instance["InputDialogYes"], + LocaleManager.Instance["InputDialogNo"], + ""); if (result != UserResult.Yes) { - Dispatcher.UIThread.Post(async () => await - UserErrorDialog.ShowUserErrorDialog(userError, _parent)); + await UserErrorDialog.ShowUserErrorDialog(userError, _parent); Device.Dispose(); return false; @@ -447,8 +448,7 @@ namespace Ryujinx.Ava if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _)) { - Dispatcher.UIThread.Post(async () => await - UserErrorDialog.ShowUserErrorDialog(userError, _parent)); + await UserErrorDialog.ShowUserErrorDialog(userError, _parent); Device.Dispose(); return false; @@ -461,11 +461,9 @@ namespace Ryujinx.Ava _parent.RefreshFirmwareStatus(); - string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString); - await ContentDialogHelper.CreateInfoDialog( string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString), - message, + string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString), LocaleManager.Instance["InputDialogOk"], "", LocaleManager.Instance["RyujinxInfo"]); @@ -473,9 +471,7 @@ namespace Ryujinx.Ava } else { - Dispatcher.UIThread.Post(async () => await - UserErrorDialog.ShowUserErrorDialog(userError, _parent)); - + await UserErrorDialog.ShowUserErrorDialog(userError, _parent); Device.Dispose(); return false; @@ -514,7 +510,7 @@ namespace Ryujinx.Ava } else if (File.Exists(ApplicationPath)) { - switch (System.IO.Path.GetExtension(ApplicationPath).ToLowerInvariant()) + switch (Path.GetExtension(ApplicationPath).ToLowerInvariant()) { case ".xci": { @@ -602,7 +598,7 @@ namespace Ryujinx.Ava if (Renderer.IsVulkan) { string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value; - + renderer = new VulkanRenderer(Renderer.CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu); } else diff --git a/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs b/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs index ce579fdfde..f32bf04153 100644 --- a/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs +++ b/Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs @@ -49,7 +49,7 @@ namespace Ryujinx.Ava.Ui.Controls { throw new PlatformNotSupportedException(); } - + var flags = OpenGLContextFlags.Compat; if (_graphicsDebugLevel != GraphicsDebugLevel.None) { @@ -69,12 +69,12 @@ namespace Ryujinx.Ava.Ui.Controls public void MakeCurrent() { - Context.MakeCurrent(_window); + Context?.MakeCurrent(_window); } public void MakeCurrent(NativeWindowBase window) { - Context.MakeCurrent(window); + Context?.MakeCurrent(window); } public void SwapBuffers() diff --git a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs index 0e03803ba6..be4517c86c 100644 --- a/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs +++ b/Ryujinx.Ava/Ui/Windows/MainWindow.axaml.cs @@ -251,24 +251,29 @@ namespace Ryujinx.Ava.Ui.Windows AppHost = new AppHost(RendererControl, InputManager, path, VirtualFileSystem, ContentManager, AccountManager, _userChannelPersistence, this); - if (!AppHost.LoadGuestApplication().Result) + Dispatcher.UIThread.Post(async () => { - AppHost.DisposeContext(); + if (!await AppHost.LoadGuestApplication()) + { + AppHost.DisposeContext(); + AppHost = null; - return; - } + return; + } - ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance["LoadingHeading"], AppHost.Device.Application.TitleName) : titleName; - ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName; + ViewModel.LoadHeading = string.IsNullOrWhiteSpace(titleName) ? string.Format(LocaleManager.Instance["LoadingHeading"], AppHost.Device.Application.TitleName) : titleName; + ViewModel.TitleName = string.IsNullOrWhiteSpace(titleName) ? AppHost.Device.Application.TitleName : titleName; - SwitchToGameControl(startFullscreen); + SwitchToGameControl(startFullscreen); - _currentEmulatedGamePath = path; - Thread gameThread = new Thread(InitializeGame) - { - Name = "GUI.WindowThread" - }; - gameThread.Start(); + _currentEmulatedGamePath = path; + + Thread gameThread = new(InitializeGame) + { + Name = "GUI.WindowThread" + }; + gameThread.Start(); + }); } private void InitializeGame() @@ -546,10 +551,12 @@ namespace Ryujinx.Ava.Ui.Windows { ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => { - DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed); - double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; + if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime)) + { + double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; - appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); + appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); + } }); } diff --git a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index e105a09267..7a625af38c 100644 --- a/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -422,7 +422,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading // Stop the GPU thread. _disposed = true; - _gpuThread.Join(); + + if (_gpuThread != null && _gpuThread.IsAlive) + { + _gpuThread.Join(); + } // Dispose the renderer. _baseRenderer.Dispose(); @@ -435,4 +439,4 @@ namespace Ryujinx.Graphics.GAL.Multithreading Sync.Dispose(); } } -} +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Queries/Counters.cs b/Ryujinx.Graphics.OpenGL/Queries/Counters.cs index 582800c8d7..ebfd899c5d 100644 --- a/Ryujinx.Graphics.OpenGL/Queries/Counters.cs +++ b/Ryujinx.Graphics.OpenGL/Queries/Counters.cs @@ -54,4 +54,4 @@ namespace Ryujinx.Graphics.OpenGL.Queries } } } -} +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs index 61b739b119..8f7917f91f 100644 --- a/Ryujinx.Graphics.OpenGL/Window.cs +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.OpenGL private const int TextureCount = 3; private readonly OpenGLRenderer _renderer; + private bool _initialized; + private int _width; private int _height; private int _copyFramebufferHandle; @@ -179,6 +181,7 @@ namespace Ryujinx.Graphics.OpenGL public void InitializeBackgroundContext(IOpenGLContext baseContext) { BackgroundContext = new BackgroundContextWorker(baseContext); + _initialized = true; } public void CaptureFrame(int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY) @@ -193,6 +196,11 @@ namespace Ryujinx.Graphics.OpenGL public void Dispose() { + if (!_initialized) + { + return; + } + BackgroundContext.Dispose(); if (_copyFramebufferHandle != 0) @@ -203,4 +211,4 @@ namespace Ryujinx.Graphics.OpenGL } } } -} +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index 3f8ebe67be..22e3032943 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -22,6 +22,8 @@ namespace Ryujinx.Graphics.Vulkan private Device _device; private WindowBase _window; + private bool _initialized; + internal FormatCapabilities FormatCapabilities { get; private set; } internal HardwareCapabilities Capabilities; @@ -266,6 +268,8 @@ namespace Ryujinx.Graphics.Vulkan LoadFeatures(supportedExtensions, maxQueueCount, queueFamilyIndex); _window = new Window(this, _surface, _physicalDevice, _device); + + _initialized = true; } public BufferHandle CreateBuffer(int size) @@ -573,6 +577,11 @@ namespace Ryujinx.Graphics.Vulkan public unsafe void Dispose() { + if (!_initialized) + { + return; + } + CommandBufferPool.Dispose(); BackgroundResources.Dispose(); _counters.Dispose(); @@ -613,4 +622,4 @@ namespace Ryujinx.Graphics.Vulkan Api.DestroyInstance(_instance, null); } } -} +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index d2716beb6e..288c308a08 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -479,7 +479,10 @@ namespace Ryujinx.HLE.HOS AudioRendererManager.Dispose(); - LibHacHorizonManager.PmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value).ThrowIfFailure(); + if (LibHacHorizonManager.ApplicationClient != null) + { + LibHacHorizonManager.PmClient.Fs.UnregisterProgram(LibHacHorizonManager.ApplicationClient.Os.GetCurrentProcessId().Value).ThrowIfFailure(); + } KernelContext.Dispose(); } diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 3f261f4d0d..9f9016b9f9 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -735,7 +735,6 @@ namespace Ryujinx.Ui _emulationContext.Dispose(); SwitchToGameTable(); - RendererWidget.Dispose(); return; } @@ -747,7 +746,6 @@ namespace Ryujinx.Ui _emulationContext.Dispose(); SwitchToGameTable(); - RendererWidget.Dispose(); return; } @@ -770,7 +768,6 @@ namespace Ryujinx.Ui _emulationContext.Dispose(); SwitchToGameTable(); - RendererWidget.Dispose(); return; } diff --git a/Ryujinx/Ui/RendererWidgetBase.cs b/Ryujinx/Ui/RendererWidgetBase.cs index 7e25ba2d93..576d2d1219 100644 --- a/Ryujinx/Ui/RendererWidgetBase.cs +++ b/Ryujinx/Ui/RendererWidgetBase.cs @@ -519,10 +519,14 @@ namespace Ryujinx.Ui _gpuCancellationTokenSource.Cancel(); _isStopped = true; - _isActive = false; + + if (_isActive) + { + _isActive = false; - _exitEvent.WaitOne(); - _exitEvent.Dispose(); + _exitEvent.WaitOne(); + _exitEvent.Dispose(); + } } private void NVStutterWorkaround() diff --git a/Ryujinx/Ui/VKRenderer.cs b/Ryujinx/Ui/VKRenderer.cs index 49578d2af6..7e02c689d9 100644 --- a/Ryujinx/Ui/VKRenderer.cs +++ b/Ryujinx/Ui/VKRenderer.cs @@ -72,7 +72,8 @@ namespace Ryujinx.Ui protected override void Dispose(bool disposing) { - Device.DisposeGpu(); + Device?.DisposeGpu(); + NpadManager.Dispose(); } }