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.
This commit is contained in:
riperiperi 2022-03-20 16:37:45 +00:00 committed by GitHub
parent b45d30acf8
commit d461d4f68b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 23 additions and 3 deletions

View file

@ -1478,6 +1478,11 @@ namespace Ryujinx.Graphics.OpenGL
_currentComponentMasks |= componentMaskAtIndex; _currentComponentMasks |= componentMaskAtIndex;
} }
public void RestoreClipControl()
{
GL.ClipControl(_clipOrigin, _clipDepthMode);
}
public void RestoreScissor0Enable() public void RestoreScissor0Enable()
{ {
if ((_scissorEnables & 1u) != 0) 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) public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
{ {
if (value is CounterQueueEvent) if (value is CounterQueueEvent)

View file

@ -27,11 +27,12 @@ namespace Ryujinx.Graphics.OpenGL
{ {
GL.Disable(EnableCap.FramebufferSrgb); GL.Disable(EnableCap.FramebufferSrgb);
CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop); CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop, swapBuffersCallback);
GL.Enable(EnableCap.FramebufferSrgb); 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) public void SetSize(int width, int height)
@ -40,7 +41,7 @@ namespace Ryujinx.Graphics.OpenGL
_height = height; _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(); (int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers();
@ -139,11 +140,20 @@ namespace Ryujinx.Graphics.OpenGL
((Pipeline)_renderer.Pipeline).RestoreComponentMask(i); ((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.ReadFramebuffer, oldReadFramebufferHandle);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle);
((Pipeline)_renderer.Pipeline).RestoreClipControl();
((Pipeline)_renderer.Pipeline).RestoreScissor0Enable(); ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable();
((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard(); ((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard();
((Pipeline)_renderer.Pipeline).RestoreViewport0();
if (viewConverted != view) if (viewConverted != view)
{ {