From c6676007bfdc65724afebac27990c47a5d6aa3dd Mon Sep 17 00:00:00 2001 From: riperiperi Date: Wed, 31 May 2023 22:43:20 +0100 Subject: GPU: Dispose Renderer after running deferred actions (#5144) * GAL: Dispose Renderer after running deferred actions Deferred actions from disposing physical memory instances always dispose the resources in their caches. The renderer can't be disposed before these resources get disposed, otherwise the dispose actions will not actually run, and the ThreadedRenderer may get stuck trying to enqueue too many commands when there is nothing consuming them. This should fix most instances of the emulator freezing on close. * Wait for main render commands to finish, but keep RenderThread alive til dispose * Address some feedback. * No parameterize needed * Set thread name as part of constructor * Port to Ava and SDL2 --- src/Ryujinx.Headless.SDL2/WindowBase.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/Ryujinx.Headless.SDL2/WindowBase.cs') diff --git a/src/Ryujinx.Headless.SDL2/WindowBase.cs b/src/Ryujinx.Headless.SDL2/WindowBase.cs index 7c310153..d163da22 100644 --- a/src/Ryujinx.Headless.SDL2/WindowBase.cs +++ b/src/Ryujinx.Headless.SDL2/WindowBase.cs @@ -62,6 +62,7 @@ namespace Ryujinx.Headless.SDL2 private readonly long _ticksPerFrame; private readonly CancellationTokenSource _gpuCancellationTokenSource; private readonly ManualResetEvent _exitEvent; + private readonly ManualResetEvent _gpuDoneEvent; private long _ticks; private bool _isActive; @@ -91,6 +92,7 @@ namespace Ryujinx.Headless.SDL2 _ticksPerFrame = Stopwatch.Frequency / TargetFps; _gpuCancellationTokenSource = new CancellationTokenSource(); _exitEvent = new ManualResetEvent(false); + _gpuDoneEvent = new ManualResetEvent(false); _aspectRatio = aspectRatio; _enableMouse = enableMouse; HostUiTheme = new HeadlessHostUiTheme(); @@ -275,6 +277,14 @@ namespace Ryujinx.Headless.SDL2 _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame); } } + + // Make sure all commands in the run loop are fully executed before leaving the loop. + if (Device.Gpu.Renderer is ThreadedRenderer threaded) + { + threaded.FlushThreadedCommands(); + } + + _gpuDoneEvent.Set(); }); FinalizeWindowRenderer(); @@ -404,7 +414,10 @@ namespace Ryujinx.Headless.SDL2 MainLoop(); - renderLoopThread.Join(); + // NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose. + // We only need to wait for all commands submitted during the main gpu loop to be processed. + _gpuDoneEvent.WaitOne(); + _gpuDoneEvent.Dispose(); nvStutterWorkaround?.Join(); Exit(); -- cgit v1.2.3