From d461d4f68bf64f008336845ae8a25af4f69ec9ed Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sun, 20 Mar 2022 16:37:45 +0000 Subject: [PATCH] Fix OpenGL issues with RTSS overlays and OBS Game Capture (#3217) OpenGL game overlays and hooks tend to make a lot of assumptions about how games present frames to the screen, since presentation in OpenGL kind of sucks and they would like to have info such as the size of the screen, or if the contents are SRGB rather than linear. There are two ways of getting this. OBS hooks swap buffers to get a frame for video capture, but it actually checks the bound framebuffer at the time. I made sure that this matches the output framebuffer (the window) so that the output matches the size. RTSS checks the viewport size by default, but this was actually set to the last used viewport by the game, causing the OSD to fly all across the screen depending on how it was used (or res scale). The viewport is now manually set to match the output framebuffer size. In the case of RTSS, it also loads its resources by destructively setting a pixel pack parameter without regard to what it was set to by the guest application. OpenGL state can be set for a long period of time and is not expected to be set before each call to a method, so randomly changing it isn't great practice. To fix this, I've added a line to set the pixel unpack alignment back to 4 after presentation, which should cover RTSS loading its incredibly ugly font. - RTSS and overlays that use it should no longer cause certain textures to load incorrectly. (mario kart 8, pokemon legends arceus) - OBS Game Capture should no longer crop the game output incorrectly, flicker randomly, or capture with incorrect gamma. This doesn't fix issues with how RTSS reports our frame timings. --- Ryujinx.Graphics.OpenGL/Pipeline.cs | 10 ++++++++++ Ryujinx.Graphics.OpenGL/Window.cs | 16 +++++++++++++--- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index c20ce8a32..35278629b 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -1478,6 +1478,11 @@ namespace Ryujinx.Graphics.OpenGL _currentComponentMasks |= componentMaskAtIndex; } + public void RestoreClipControl() + { + GL.ClipControl(_clipOrigin, _clipDepthMode); + } + public void RestoreScissor0Enable() { if ((_scissorEnables & 1u) != 0) @@ -1494,6 +1499,11 @@ namespace Ryujinx.Graphics.OpenGL } } + public void RestoreViewport0() + { + GL.ViewportArray(0, 1, _viewportArray); + } + public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) { if (value is CounterQueueEvent) diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs index da214553e..f7f75f4ec 100644 --- a/Ryujinx.Graphics.OpenGL/Window.cs +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -27,11 +27,12 @@ namespace Ryujinx.Graphics.OpenGL { GL.Disable(EnableCap.FramebufferSrgb); - CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop); + CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop, swapBuffersCallback); GL.Enable(EnableCap.FramebufferSrgb); - swapBuffersCallback(); + // Restore unpack alignment to 4, as performance overlays such as RTSS may change this to load their resources. + GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4); } public void SetSize(int width, int height) @@ -40,7 +41,7 @@ namespace Ryujinx.Graphics.OpenGL _height = height; } - private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop) + private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action swapBuffersCallback) { (int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers(); @@ -139,11 +140,20 @@ namespace Ryujinx.Graphics.OpenGL ((Pipeline)_renderer.Pipeline).RestoreComponentMask(i); } + // Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture. + GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne); + GL.Viewport(0, 0, _width, _height); + GL.BindFramebuffer(FramebufferTarget.Framebuffer, drawFramebuffer); + + swapBuffersCallback(); + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); + ((Pipeline)_renderer.Pipeline).RestoreClipControl(); ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable(); ((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard(); + ((Pipeline)_renderer.Pipeline).RestoreViewport0(); if (viewConverted != view) {