From 416ddd0f6e9b5c9e9b3da627cc90c98bb5a3a56b Mon Sep 17 00:00:00 2001 From: Thog Date: Thu, 13 Feb 2020 18:43:29 +0100 Subject: [PATCH] Some fixes for the new GLRenderer (#930) * Some fixes for the new GLRenderer Changelog: - Fix transparency of the window on some games on Windows. - Fix escape key not being able to exit emulation. - Fix inverted logic in fullscreen event handling. - Fix a race condition when stoping emulation causing a hang. - Fix a memory leak of the OpenGL context when stoping emulation (saving ~200MB of RAM when stoping emulation). - Simplify and document behaviours when exiting the emulator while the emulation is running. * Make sure to clear alpha channel when presenting Texture This fix once and for all the transparency issue on Windows. * Enforce footer bar size to avoid gl widget to get resized to 1280x724 * Fix full screen inversion in MainWindow and make sure _listStatusBox don't come back when not needed * Remove previous transparency clear attempt that is useless now * Remove an extra line return --- Ryujinx.Graphics.OpenGL/Window.cs | 40 ++++++++++++------ Ryujinx/Ui/GLRenderer.cs | 69 ++++++++++++++++--------------- Ryujinx/Ui/MainWindow.cs | 54 +++++++++++++----------- Ryujinx/Ui/MainWindow.glade | 6 ++- 4 files changed, 97 insertions(+), 72 deletions(-) diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs index 26fc6a64b8..2689a7c4a6 100644 --- a/Ryujinx.Graphics.OpenGL/Window.cs +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -22,15 +22,30 @@ namespace Ryujinx.Graphics.OpenGL public void Present(ITexture texture, ImageCrop crop) { - TextureView view = (TextureView)texture; - GL.Disable(EnableCap.FramebufferSrgb); + CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop); + + GL.Enable(EnableCap.FramebufferSrgb); + } + + public void SetSize(int width, int height) + { + _width = width; + _height = height; + } + + + private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop) + { + bool[] oldFramebufferColorWritemask = new bool[4]; + int oldReadFramebufferHandle = GL.GetInteger(GetPName.ReadFramebufferBinding); int oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding); + GL.GetBoolean(GetIndexedPName.ColorWritemask, drawFramebuffer, oldFramebufferColorWritemask); - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0); - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetCopyFramebufferHandleLazy()); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer); + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer); GL.FramebufferTexture( FramebufferTarget.ReadFramebuffer, @@ -93,16 +108,17 @@ namespace Ryujinx.Graphics.OpenGL ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear); + // Remove Alpha channel + GL.ColorMask(drawFramebuffer, false, false, false, true); + GL.ClearBuffer(ClearBuffer.Color, 0, new float[] { 0.0f, 0.0f, 0.0f, 1.0f }); + GL.ColorMask(drawFramebuffer, + oldFramebufferColorWritemask[0], + oldFramebufferColorWritemask[1], + oldFramebufferColorWritemask[2], + oldFramebufferColorWritemask[3]); + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); - - GL.Enable(EnableCap.FramebufferSrgb); - } - - public void SetSize(int width, int height) - { - _width = width; - _height = height; } private int GetCopyFramebufferHandleLazy() diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs index 7506821737..737a1fdd74 100644 --- a/Ryujinx/Ui/GLRenderer.cs +++ b/Ryujinx/Ui/GLRenderer.cs @@ -53,7 +53,7 @@ namespace Ryujinx.Ui private Input.NpadController _primaryController; public GLRenderer(Switch device) - : base (new GraphicsMode(new ColorFormat(24)), + : base (new GraphicsMode(new ColorFormat()), 3, 3, GraphicsContextFlags.ForwardCompatible) { @@ -63,6 +63,7 @@ namespace Ryujinx.Ui this.Initialized += GLRenderer_Initialized; this.Destroyed += GLRenderer_Destroyed; + this.ShuttingDown += GLRenderer_ShuttingDown; Initialize(); @@ -81,6 +82,11 @@ namespace Ryujinx.Ui this.Shown += Renderer_Shown; } + private void GLRenderer_ShuttingDown(object sender, EventArgs args) + { + Exit(); + } + private void Parent_FocusOutEvent(object o, Gtk.FocusOutEventArgs args) { IsFocused = false; @@ -93,9 +99,7 @@ namespace Ryujinx.Ui private void GLRenderer_Destroyed(object sender, EventArgs e) { - Exit(); - - this.Dispose(); + Dispose(); } protected void Renderer_Shown(object sender, EventArgs e) @@ -106,41 +110,38 @@ namespace Ryujinx.Ui public void HandleScreenState(KeyboardState keyboard) { bool toggleFullscreen = keyboard.IsKeyDown(OpenTK.Input.Key.F11) - || ((keyboard.IsKeyDown(OpenTK.Input.Key.AltLeft) - || keyboard.IsKeyDown(OpenTK.Input.Key.AltRight)) - && keyboard.IsKeyDown(OpenTK.Input.Key.Enter)); + || ((keyboard.IsKeyDown(OpenTK.Input.Key.AltLeft) + || keyboard.IsKeyDown(OpenTK.Input.Key.AltRight)) + && keyboard.IsKeyDown(OpenTK.Input.Key.Enter)) + || keyboard.IsKeyDown(OpenTK.Input.Key.Escape); - if (toggleFullscreen == _toggleFullscreen) + bool fullScreenToggled = ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen); + + if (toggleFullscreen != _toggleFullscreen) { - return; + if (toggleFullscreen) + { + if (fullScreenToggled) + { + ParentWindow.Unfullscreen(); + (Toplevel as MainWindow)?.ToggleExtraWidgets(true); + } + else + { + if (keyboard.IsKeyDown(OpenTK.Input.Key.Escape)) + { + Exit(); + } + else + { + ParentWindow.Fullscreen(); + (Toplevel as MainWindow)?.ToggleExtraWidgets(false); + } + } + } } _toggleFullscreen = toggleFullscreen; - - Gtk.Application.Invoke(delegate - { - if (this.ParentWindow.State.HasFlag(Gdk.WindowState.Fullscreen)) - { - if (keyboard.IsKeyDown(OpenTK.Input.Key.Escape) || _toggleFullscreen) - { - this.ParentWindow.Unfullscreen(); - (this.Toplevel as MainWindow)?.ToggleExtraWidgets(true); - } - } - else - { - if (keyboard.IsKeyDown(OpenTK.Input.Key.Escape)) - { - Exit(); - } - - if (_toggleFullscreen) - { - this.ParentWindow.Fullscreen(); - (this.Toplevel as MainWindow)?.ToggleExtraWidgets(false); - } - } - }); } private void GLRenderer_Initialized(object sender, EventArgs e) diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 6c771bb96f..8c500b30ee 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -31,7 +31,7 @@ namespace Ryujinx.Ui private static GLRenderer _gLWidget; - private static AutoResetEvent _screenExitStatus = new AutoResetEvent(false); + private static AutoResetEvent _deviceExitStatus = new AutoResetEvent(false); private static ListStore _tableStore; @@ -356,7 +356,7 @@ namespace Ryujinx.Ui _emulationContext = device; - _screenExitStatus.Reset(); + _deviceExitStatus.Reset(); #if MACOS_BUILD CreateGameWindow(device); @@ -391,8 +391,6 @@ namespace Ryujinx.Ui { device.Hid.InitializePrimaryController(ConfigurationState.Instance.Hid.ControllerType); - _gLWidget?.Exit(); - _gLWidget?.Dispose(); _gLWidget = new GLRenderer(_emulationContext); Application.Invoke(delegate @@ -402,13 +400,17 @@ namespace Ryujinx.Ui _viewBox.Child = _gLWidget; _gLWidget.ShowAll(); - _listStatusBox.Hide(); + ClearFooterForGameRender(); }); _gLWidget.WaitEvent.WaitOne(); _gLWidget.Start(); + device.Dispose(); + _deviceExitStatus.Set(); + + // NOTE: Everything that is here will not be executed when you close the UI. Application.Invoke(delegate { _viewBox.Remove(_gLWidget); @@ -419,36 +421,41 @@ namespace Ryujinx.Ui _gLWidget.Window.Dispose(); } + _gLWidget.Dispose(); + _viewBox.Add(_gameTableWindow); _gameTableWindow.Expand = true; this.Window.Title = "Ryujinx"; - _listStatusBox.ShowAll(); + _emulationContext = null; + _gameLoaded = false; + _gLWidget = null; + + DiscordIntegrationModule.SwitchToMainMenu(); + + RecreateFooterForMenu(); UpdateColumns(); UpdateGameTable(); Task.Run(RefreshFirmwareLabel); - }); - device.Dispose(); - - _emulationContext = null; - _gameLoaded = false; - _gLWidget = null; - - DiscordIntegrationModule.SwitchToMainMenu(); - - Application.Invoke(delegate - { _stopEmulation.Sensitive = false; _firmwareInstallFile.Sensitive = true; _firmwareInstallDirectory.Sensitive = true; }); + } - _screenExitStatus.Set(); + private void RecreateFooterForMenu() + { + _footerBox.Add(_listStatusBox); + } + + private void ClearFooterForGameRender() + { + _footerBox.Remove(_listStatusBox); } public void ToggleExtraWidgets(bool show) @@ -469,7 +476,7 @@ namespace Ryujinx.Ui bool fullScreenToggled = this.Window.State.HasFlag(Gdk.WindowState.Fullscreen); - _fullScreen.Label = !fullScreenToggled ? "Exit Fullscreen" : "Enter Fullscreen"; + _fullScreen.Label = fullScreenToggled ? "Exit Fullscreen" : "Enter Fullscreen"; } private static void UpdateGameMetadata(string titleId) @@ -506,8 +513,11 @@ namespace Ryujinx.Ui if (_gLWidget != null) { + // We tell the widget that we are exiting _gLWidget.Exit(); - _screenExitStatus.WaitOne(); + + // Wait for the other thread to dispose the HLE context before exiting. + _deviceExitStatus.WaitOne(); } } @@ -874,16 +884,12 @@ namespace Ryujinx.Ui { Fullscreen(); - _fullScreen.Label = "Exit Fullscreen"; - ToggleExtraWidgets(false); } else { Unfullscreen(); - _fullScreen.Label = "Enter Fullscreen"; - ToggleExtraWidgets(true); } } diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade index 8477f392a4..6eeab7a41e 100644 --- a/Ryujinx/Ui/MainWindow.glade +++ b/Ryujinx/Ui/MainWindow.glade @@ -7,7 +7,7 @@ Ryujinx center 1280 - 750 + 760 @@ -357,7 +357,7 @@ - + True False vertical @@ -403,6 +403,8 @@ True False + 1280 + 19 True