Просмотр исходного кода

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>
Ac_K 3 лет назад
Родитель
Сommit
a1ddaa2736

+ 18 - 22
Ryujinx.Ava/AppHost.cs

@@ -60,7 +60,7 @@ namespace Ryujinx.Ava
 
 
         private const float VolumeDelta = 0.05f;
         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 long _ticksPerFrame;
         private readonly Stopwatch _chrono;
         private readonly Stopwatch _chrono;
@@ -349,7 +349,10 @@ namespace Ryujinx.Ava
 
 
             _isActive = false;
             _isActive = false;
 
 
-            _renderingThread.Join();
+            if (_renderingThread.IsAlive)
+            {
+                _renderingThread.Join();
+            }
 
 
             DisplaySleep.Restore();
             DisplaySleep.Restore();
 
 
@@ -378,7 +381,7 @@ namespace Ryujinx.Ava
 
 
             _gpuCancellationTokenSource.Cancel();
             _gpuCancellationTokenSource.Cancel();
             _gpuCancellationTokenSource.Dispose();
             _gpuCancellationTokenSource.Dispose();
-            
+
             _chrono.Stop();
             _chrono.Stop();
         }
         }
 
 
@@ -393,7 +396,7 @@ namespace Ryujinx.Ava
             Renderer?.MakeCurrent();
             Renderer?.MakeCurrent();
 
 
             Device.DisposeGpu();
             Device.DisposeGpu();
-            
+
             Renderer?.MakeCurrent(null);
             Renderer?.MakeCurrent(null);
         }
         }
 
 
@@ -417,7 +420,6 @@ namespace Ryujinx.Ava
         public async Task<bool> LoadGuestApplication()
         public async Task<bool> LoadGuestApplication()
         {
         {
             InitializeSwitchInstance();
             InitializeSwitchInstance();
-
             MainWindow.UpdateGraphicsConfig();
             MainWindow.UpdateGraphicsConfig();
 
 
             SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
             SystemVersion firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
@@ -428,17 +430,16 @@ namespace Ryujinx.Ava
                 {
                 {
                     if (userError == UserError.NoFirmware)
                     if (userError == UserError.NoFirmware)
                     {
                     {
-                        string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedMessage"],
-                            firmwareVersion.VersionString);
-
                         UserResult result = await ContentDialogHelper.CreateConfirmationDialog(
                         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)
                         if (result != UserResult.Yes)
                         {
                         {
-                            Dispatcher.UIThread.Post(async () => await
-                                UserErrorDialog.ShowUserErrorDialog(userError, _parent));
+                            await UserErrorDialog.ShowUserErrorDialog(userError, _parent);
                             Device.Dispose();
                             Device.Dispose();
 
 
                             return false;
                             return false;
@@ -447,8 +448,7 @@ namespace Ryujinx.Ava
 
 
                     if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
                     if (!SetupValidator.TryFixStartApplication(ContentManager, ApplicationPath, userError, out _))
                     {
                     {
-                        Dispatcher.UIThread.Post(async () => await
-                            UserErrorDialog.ShowUserErrorDialog(userError, _parent));
+                        await UserErrorDialog.ShowUserErrorDialog(userError, _parent);
                         Device.Dispose();
                         Device.Dispose();
 
 
                         return false;
                         return false;
@@ -461,11 +461,9 @@ namespace Ryujinx.Ava
 
 
                         _parent.RefreshFirmwareStatus();
                         _parent.RefreshFirmwareStatus();
 
 
-                        string message = string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString);
-
                         await ContentDialogHelper.CreateInfoDialog(
                         await ContentDialogHelper.CreateInfoDialog(
                             string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString),
                             string.Format(LocaleManager.Instance["DialogFirmwareInstalledMessage"], firmwareVersion.VersionString),
-                            message,
+                            string.Format(LocaleManager.Instance["DialogFirmwareInstallEmbeddedSuccessMessage"], firmwareVersion.VersionString),
                             LocaleManager.Instance["InputDialogOk"],
                             LocaleManager.Instance["InputDialogOk"],
                             "",
                             "",
                             LocaleManager.Instance["RyujinxInfo"]);
                             LocaleManager.Instance["RyujinxInfo"]);
@@ -473,9 +471,7 @@ namespace Ryujinx.Ava
                 }
                 }
                 else
                 else
                 {
                 {
-                    Dispatcher.UIThread.Post(async () => await
-                        UserErrorDialog.ShowUserErrorDialog(userError, _parent));
-
+                    await UserErrorDialog.ShowUserErrorDialog(userError, _parent);
                     Device.Dispose();
                     Device.Dispose();
 
 
                     return false;
                     return false;
@@ -514,7 +510,7 @@ namespace Ryujinx.Ava
             }
             }
             else if (File.Exists(ApplicationPath))
             else if (File.Exists(ApplicationPath))
             {
             {
-                switch (System.IO.Path.GetExtension(ApplicationPath).ToLowerInvariant())
+                switch (Path.GetExtension(ApplicationPath).ToLowerInvariant())
                 {
                 {
                     case ".xci":
                     case ".xci":
                         {
                         {
@@ -602,7 +598,7 @@ namespace Ryujinx.Ava
             if (Renderer.IsVulkan)
             if (Renderer.IsVulkan)
             {
             {
                 string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value;
                 string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value;
-                
+
                 renderer = new VulkanRenderer(Renderer.CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu);
                 renderer = new VulkanRenderer(Renderer.CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu);
             }
             }
             else
             else

+ 3 - 3
Ryujinx.Ava/Ui/Controls/OpenGLEmbeddedWindow.cs

@@ -49,7 +49,7 @@ namespace Ryujinx.Ava.Ui.Controls
             {
             {
                 throw new PlatformNotSupportedException();
                 throw new PlatformNotSupportedException();
             }
             }
-            
+
             var flags = OpenGLContextFlags.Compat;
             var flags = OpenGLContextFlags.Compat;
             if (_graphicsDebugLevel != GraphicsDebugLevel.None)
             if (_graphicsDebugLevel != GraphicsDebugLevel.None)
             {
             {
@@ -69,12 +69,12 @@ namespace Ryujinx.Ava.Ui.Controls
 
 
         public void MakeCurrent()
         public void MakeCurrent()
         {
         {
-            Context.MakeCurrent(_window);
+            Context?.MakeCurrent(_window);
         }
         }
 
 
         public void MakeCurrent(NativeWindowBase window)
         public void MakeCurrent(NativeWindowBase window)
         {
         {
-            Context.MakeCurrent(window);
+            Context?.MakeCurrent(window);
         }
         }
 
 
         public void SwapBuffers()
         public void SwapBuffers()

+ 23 - 16
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);
             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()
         private void InitializeGame()
@@ -546,10 +551,12 @@ namespace Ryujinx.Ava.Ui.Windows
         {
         {
             ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
             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);
+                }
             });
             });
         }
         }
 
 

+ 6 - 2
Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs

@@ -422,7 +422,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading
 
 
             // Stop the GPU thread.
             // Stop the GPU thread.
             _disposed = true;
             _disposed = true;
-            _gpuThread.Join();
+
+            if (_gpuThread != null && _gpuThread.IsAlive)
+            {
+                _gpuThread.Join();
+            }
 
 
             // Dispose the renderer.
             // Dispose the renderer.
             _baseRenderer.Dispose();
             _baseRenderer.Dispose();
@@ -435,4 +439,4 @@ namespace Ryujinx.Graphics.GAL.Multithreading
             Sync.Dispose();
             Sync.Dispose();
         }
         }
     }
     }
-}
+}

+ 1 - 1
Ryujinx.Graphics.OpenGL/Queries/Counters.cs

@@ -54,4 +54,4 @@ namespace Ryujinx.Graphics.OpenGL.Queries
             }
             }
         }
         }
     }
     }
-}
+}

+ 9 - 1
Ryujinx.Graphics.OpenGL/Window.cs

@@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.OpenGL
         private const int TextureCount = 3;
         private const int TextureCount = 3;
         private readonly OpenGLRenderer _renderer;
         private readonly OpenGLRenderer _renderer;
 
 
+        private bool _initialized;
+
         private int _width;
         private int _width;
         private int _height;
         private int _height;
         private int _copyFramebufferHandle;
         private int _copyFramebufferHandle;
@@ -179,6 +181,7 @@ namespace Ryujinx.Graphics.OpenGL
         public void InitializeBackgroundContext(IOpenGLContext baseContext)
         public void InitializeBackgroundContext(IOpenGLContext baseContext)
         {
         {
             BackgroundContext = new BackgroundContextWorker(baseContext);
             BackgroundContext = new BackgroundContextWorker(baseContext);
+            _initialized = true;
         }
         }
 
 
         public void CaptureFrame(int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
         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()
         public void Dispose()
         {
         {
+            if (!_initialized)
+            {
+                return;
+            }
+
             BackgroundContext.Dispose();
             BackgroundContext.Dispose();
 
 
             if (_copyFramebufferHandle != 0)
             if (_copyFramebufferHandle != 0)
@@ -203,4 +211,4 @@ namespace Ryujinx.Graphics.OpenGL
             }
             }
         }
         }
     }
     }
-}
+}

+ 10 - 1
Ryujinx.Graphics.Vulkan/VulkanRenderer.cs

@@ -22,6 +22,8 @@ namespace Ryujinx.Graphics.Vulkan
         private Device _device;
         private Device _device;
         private WindowBase _window;
         private WindowBase _window;
 
 
+        private bool _initialized;
+
         internal FormatCapabilities FormatCapabilities { get; private set; }
         internal FormatCapabilities FormatCapabilities { get; private set; }
         internal HardwareCapabilities Capabilities;
         internal HardwareCapabilities Capabilities;
 
 
@@ -266,6 +268,8 @@ namespace Ryujinx.Graphics.Vulkan
             LoadFeatures(supportedExtensions, maxQueueCount, queueFamilyIndex);
             LoadFeatures(supportedExtensions, maxQueueCount, queueFamilyIndex);
 
 
             _window = new Window(this, _surface, _physicalDevice, _device);
             _window = new Window(this, _surface, _physicalDevice, _device);
+
+            _initialized = true;
         }
         }
 
 
         public BufferHandle CreateBuffer(int size)
         public BufferHandle CreateBuffer(int size)
@@ -573,6 +577,11 @@ namespace Ryujinx.Graphics.Vulkan
 
 
         public unsafe void Dispose()
         public unsafe void Dispose()
         {
         {
+            if (!_initialized)
+            {
+                return;
+            }
+
             CommandBufferPool.Dispose();
             CommandBufferPool.Dispose();
             BackgroundResources.Dispose();
             BackgroundResources.Dispose();
             _counters.Dispose();
             _counters.Dispose();
@@ -613,4 +622,4 @@ namespace Ryujinx.Graphics.Vulkan
             Api.DestroyInstance(_instance, null);
             Api.DestroyInstance(_instance, null);
         }
         }
     }
     }
-}
+}

+ 4 - 1
Ryujinx.HLE/HOS/Horizon.cs

@@ -479,7 +479,10 @@ namespace Ryujinx.HLE.HOS
 
 
                 AudioRendererManager.Dispose();
                 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();
                 KernelContext.Dispose();
             }
             }

+ 0 - 3
Ryujinx/Ui/MainWindow.cs

@@ -735,7 +735,6 @@ namespace Ryujinx.Ui
 
 
                                 _emulationContext.Dispose();
                                 _emulationContext.Dispose();
                                 SwitchToGameTable();
                                 SwitchToGameTable();
-                                RendererWidget.Dispose();
 
 
                                 return;
                                 return;
                             }
                             }
@@ -747,7 +746,6 @@ namespace Ryujinx.Ui
 
 
                             _emulationContext.Dispose();
                             _emulationContext.Dispose();
                             SwitchToGameTable();
                             SwitchToGameTable();
-                            RendererWidget.Dispose();
 
 
                             return;
                             return;
                         }
                         }
@@ -770,7 +768,6 @@ namespace Ryujinx.Ui
 
 
                         _emulationContext.Dispose();
                         _emulationContext.Dispose();
                         SwitchToGameTable();
                         SwitchToGameTable();
-                        RendererWidget.Dispose();
 
 
                         return;
                         return;
                     }
                     }

+ 7 - 3
Ryujinx/Ui/RendererWidgetBase.cs

@@ -519,10 +519,14 @@ namespace Ryujinx.Ui
             _gpuCancellationTokenSource.Cancel();
             _gpuCancellationTokenSource.Cancel();
 
 
             _isStopped = true;
             _isStopped = true;
-            _isActive = false;
+            
+            if (_isActive)
+            {
+                _isActive = false;
 
 
-            _exitEvent.WaitOne();
-            _exitEvent.Dispose();
+                _exitEvent.WaitOne();
+                _exitEvent.Dispose();
+            }
         }
         }
 
 
         private void NVStutterWorkaround()
         private void NVStutterWorkaround()

+ 2 - 1
Ryujinx/Ui/VKRenderer.cs

@@ -72,7 +72,8 @@ namespace Ryujinx.Ui
 
 
         protected override void Dispose(bool disposing)
         protected override void Dispose(bool disposing)
         {
         {
-            Device.DisposeGpu();
+            Device?.DisposeGpu();
+
             NpadManager.Dispose();
             NpadManager.Dispose();
         }
         }
     }
     }