From 2562ca6c3fe6ef328e0926c9cbcd6bb52abb328f Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 25 Sep 2018 19:55:30 -0300 Subject: [PATCH] Fix multiple rendertargets (#427) * Simplify render target bindings * Implement multiple viewports * Pack glViewportIndexed calls into a single glViewportArray * Use ARB_viewport_array when available * Cache framebuffer attachments * Use get accessors in OGLExtension * Address feedback --- Ryujinx.Graphics/Gal/IGalRenderTarget.cs | 10 +- .../Gal/OpenGL/OGLEnumConverter.cs | 2 +- Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs | 37 +-- .../Gal/OpenGL/OGLRenderTarget.cs | 257 ++++++++++-------- Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs | 2 +- Ryujinx.Graphics/GpuResourceManager.cs | 4 +- Ryujinx.Graphics/NvGpuEngine3d.cs | 26 +- Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs | 2 +- 8 files changed, 185 insertions(+), 155 deletions(-) diff --git a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs index 7ccf0981d..f941ccd58 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs @@ -2,15 +2,17 @@ namespace Ryujinx.Graphics.Gal { public interface IGalRenderTarget { - void BindColor(long Key, int Attachment, GalImage Image); + void Bind(); + + void BindColor(long Key, int Attachment); void UnbindColor(int Attachment); - void BindZeta(long Key, GalImage Image); + void BindZeta(long Key); void UnbindZeta(); - void Set(long Key); + void Present(long Key); void SetMap(int[] Map); @@ -18,7 +20,7 @@ namespace Ryujinx.Graphics.Gal void SetWindowSize(int Width, int Height); - void SetViewport(int X, int Y, int Width, int Height); + void SetViewport(int Attachment, int X, int Y, int Width, int Height); void Render(); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 187d1eece..7f9e9fbe3 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -225,7 +225,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalTextureWrap.Clamp: return TextureWrapMode.Clamp; } - if (OGLExtension.HasTextureMirrorClamp()) + if (OGLExtension.TextureMirrorClamp) { switch (Wrap) { diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs index 5ad422980..11daeb593 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs @@ -1,40 +1,17 @@ using OpenTK.Graphics.OpenGL; +using System; namespace Ryujinx.Graphics.Gal.OpenGL { static class OGLExtension { - private static bool Initialized = false; + private static Lazy s_EnhancedLayouts = new Lazy(() => HasExtension("GL_ARB_enhanced_layouts")); + private static Lazy s_TextureMirrorClamp = new Lazy(() => HasExtension("GL_EXT_texture_mirror_clamp")); + private static Lazy s_ViewportArray = new Lazy(() => HasExtension("GL_ARB_viewport_array")); - private static bool EnhancedLayouts; - - private static bool TextureMirrorClamp; - - public static bool HasEnhancedLayouts() - { - EnsureInitialized(); - - return EnhancedLayouts; - } - - public static bool HasTextureMirrorClamp() - { - EnsureInitialized(); - - return TextureMirrorClamp; - } - - private static void EnsureInitialized() - { - if (Initialized) - { - return; - } - - EnhancedLayouts = HasExtension("GL_ARB_enhanced_layouts"); - - TextureMirrorClamp = HasExtension("GL_EXT_texture_mirror_clamp"); - } + public static bool EnhancedLayouts => s_EnhancedLayouts.Value; + public static bool TextureMirrorClamp => s_TextureMirrorClamp.Value; + public static bool ViewportArray => s_ViewportArray.Value; private static bool HasExtension(string Name) { diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs index ff5dc1b89..78cf5d2fa 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs @@ -22,16 +22,52 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + private class FrameBufferAttachments + { + public long[] Colors; + public long Zeta; + + public int MapCount; + public DrawBuffersEnum[] Map; + + public FrameBufferAttachments() + { + Colors = new long[RenderTargetsCount]; + + Map = new DrawBuffersEnum[RenderTargetsCount]; + } + + public void SetAndClear(FrameBufferAttachments Source) + { + Zeta = Source.Zeta; + MapCount = Source.MapCount; + + Source.Zeta = 0; + Source.MapCount = 0; + + for (int i = 0; i < RenderTargetsCount; i++) + { + Colors[i] = Source.Colors[i]; + Map[i] = Source.Map[i]; + + Source.Colors[i] = 0; + Source.Map[i] = 0; + } + } + } + private const int NativeWidth = 1280; private const int NativeHeight = 720; + private const int RenderTargetsCount = 8; + private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm; private OGLTexture Texture; private ImageHandler ReadTex; - private Rect Viewport; + private float[] Viewports; private Rect Window; private bool FlipX; @@ -50,138 +86,164 @@ namespace Ryujinx.Graphics.Gal.OpenGL private int SrcFb; private int DstFb; - //Holds current attachments, used to avoid unnecesary calls to OpenGL - private int[] ColorAttachments; - - private int DepthAttachment; - private int StencilAttachment; + private FrameBufferAttachments Attachments; + private FrameBufferAttachments OldAttachments; private int CopyPBO; public OGLRenderTarget(OGLTexture Texture) { - ColorAttachments = new int[8]; + Attachments = new FrameBufferAttachments(); + + OldAttachments = new FrameBufferAttachments(); + + Viewports = new float[RenderTargetsCount * 4]; this.Texture = Texture; } - public void BindColor(long Key, int Attachment, GalImage Image) + public void Bind() { - if (Texture.TryGetImageHandler(Key, out ImageHandler CachedImage)) + if (DummyFrameBuffer == 0) { - EnsureFrameBuffer(); - - Attach(ref ColorAttachments[Attachment], CachedImage.Handle, FramebufferAttachment.ColorAttachment0 + Attachment); + DummyFrameBuffer = GL.GenFramebuffer(); } - else + + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer); + + ImageHandler CachedImage; + + for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++) { - UnbindColor(Attachment); - } - } - - public void UnbindColor(int Attachment) - { - EnsureFrameBuffer(); - - Attach(ref ColorAttachments[Attachment], 0, FramebufferAttachment.ColorAttachment0 + Attachment); - } - - public void BindZeta(long Key, GalImage Image) - { - if (Texture.TryGetImageHandler(Key, out ImageHandler CachedImage)) - { - EnsureFrameBuffer(); - - if (CachedImage.HasDepth && CachedImage.HasStencil) + if (Attachments.Colors[Attachment] == OldAttachments.Colors[Attachment]) { - if (DepthAttachment != CachedImage.Handle || - StencilAttachment != CachedImage.Handle) + continue; + } + + int Handle = 0; + + if (Attachments.Colors[Attachment] != 0 && + Texture.TryGetImageHandler(Attachments.Colors[Attachment], out CachedImage)) + { + Handle = CachedImage.Handle; + } + + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.ColorAttachment0 + Attachment, + Handle, + 0); + } + + if (Attachments.Zeta != OldAttachments.Zeta) + { + if (Attachments.Zeta != 0 && Texture.TryGetImageHandler(Attachments.Zeta, out CachedImage)) + { + if (CachedImage.HasDepth && CachedImage.HasStencil) { GL.FramebufferTexture( FramebufferTarget.DrawFramebuffer, FramebufferAttachment.DepthStencilAttachment, CachedImage.Handle, 0); - - DepthAttachment = CachedImage.Handle; - StencilAttachment = CachedImage.Handle; } - } - else if (CachedImage.HasDepth) - { - Attach(ref DepthAttachment, CachedImage.Handle, FramebufferAttachment.DepthAttachment); + else if (CachedImage.HasDepth) + { + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.DepthAttachment, + CachedImage.Handle, + 0); - Attach(ref StencilAttachment, 0, FramebufferAttachment.StencilAttachment); - } - else if (CachedImage.HasStencil) - { - Attach(ref DepthAttachment, 0, FramebufferAttachment.DepthAttachment); - - Attach(ref StencilAttachment, CachedImage.Handle, FramebufferAttachment.StencilAttachment); + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.StencilAttachment, + 0, + 0); + } + else + { + throw new NotImplementedException(); + } } else { - throw new InvalidOperationException(); + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.DepthStencilAttachment, + 0, + 0); } } + + if (OGLExtension.ViewportArray) + { + GL.ViewportArray(0, 8, Viewports); + } else { - UnbindZeta(); + GL.Viewport( + (int)Viewports[0], + (int)Viewports[1], + (int)Viewports[2], + (int)Viewports[3]); } - } - private void Attach(ref int OldHandle, int NewHandle, FramebufferAttachment FbAttachment) - { - if (OldHandle != NewHandle) + if (Attachments.MapCount > 1) { - GL.FramebufferTexture( - FramebufferTarget.DrawFramebuffer, - FbAttachment, - NewHandle, - 0); - - OldHandle = NewHandle; + GL.DrawBuffers(Attachments.MapCount, Attachments.Map); } + else if (Attachments.MapCount == 1) + { + GL.DrawBuffer((DrawBufferMode)Attachments.Map[0]); + } + else + { + GL.DrawBuffer(DrawBufferMode.None); + } + + OldAttachments.SetAndClear(Attachments); } + public void BindColor(long Key, int Attachment) + { + Attachments.Colors[Attachment] = Key; + } + + public void UnbindColor(int Attachment) + { + Attachments.Colors[Attachment] = 0; + } + + public void BindZeta(long Key) + { + Attachments.Zeta = Key; + } + public void UnbindZeta() { - EnsureFrameBuffer(); - - if (DepthAttachment != 0 || StencilAttachment != 0) - { - GL.FramebufferTexture( - FramebufferTarget.DrawFramebuffer, - FramebufferAttachment.DepthStencilAttachment, - 0, - 0); - - DepthAttachment = 0; - StencilAttachment = 0; - } + Attachments.Zeta = 0; } - public void Set(long Key) + public void Present(long Key) { Texture.TryGetImageHandler(Key, out ReadTex); } public void SetMap(int[] Map) { - if (Map != null && Map.Length > 0) + if (Map != null) { - DrawBuffersEnum[] Mode = new DrawBuffersEnum[Map.Length]; + Attachments.MapCount = Map.Length; - for (int i = 0; i < Map.Length; i++) + for (int Attachment = 0; Attachment < Attachments.MapCount; Attachment++) { - Mode[i] = DrawBuffersEnum.ColorAttachment0 + Map[i]; + Attachments.Map[Attachment] = DrawBuffersEnum.ColorAttachment0 + Map[Attachment]; } - - GL.DrawBuffers(Mode.Length, Mode); } else { - GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + Attachments.MapCount = 0; } } @@ -201,20 +263,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL Window = new Rect(0, 0, Width, Height); } - public void SetViewport(int X, int Y, int Width, int Height) + public void SetViewport(int Attachment, int X, int Y, int Width, int Height) { - Viewport = new Rect(X, Y, Width, Height); + int Offset = Attachment * 4; - SetViewport(Viewport); - } - - private void SetViewport(Rect Viewport) - { - GL.Viewport( - Viewport.X, - Viewport.Y, - Viewport.Width, - Viewport.Height); + Viewports[Offset + 0] = X; + Viewports[Offset + 1] = Y; + Viewports[Offset + 2] = Width; + Viewports[Offset + 3] = Height; } public void Render() @@ -276,7 +332,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, ReadTex.Handle, 0); GL.ReadBuffer(ReadBufferMode.ColorAttachment0); - GL.DrawBuffer(DrawBufferMode.ColorAttachment0); GL.Clear(ClearBufferMask.ColorBufferBit); @@ -285,8 +340,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL DstX0, DstY0, DstX1, DstY1, ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear); - - EnsureFrameBuffer(); } public void Copy( @@ -343,8 +396,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.Clear(Mask); GL.BlitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter); - - EnsureFrameBuffer(); } } @@ -419,15 +470,5 @@ namespace Ryujinx.Graphics.Gal.OpenGL (CachedImage.HasDepth ? ClearBufferMask.DepthBufferBit : 0) | (CachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0); } - - private void EnsureFrameBuffer() - { - if (DummyFrameBuffer == 0) - { - DummyFrameBuffer = GL.GenFramebuffer(); - } - - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer); - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index 73d37b879..efcb7e347 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -133,7 +133,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { //Enhanced layouts are required for Geometry shaders //skip this stage if current driver has no ARB_enhanced_layouts - if (!OGLExtension.HasEnhancedLayouts()) + if (!OGLExtension.EnhancedLayouts) { return; } diff --git a/Ryujinx.Graphics/GpuResourceManager.cs b/Ryujinx.Graphics/GpuResourceManager.cs index 0a8d20145..71390a83a 100644 --- a/Ryujinx.Graphics/GpuResourceManager.cs +++ b/Ryujinx.Graphics/GpuResourceManager.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage); } - Gpu.Renderer.RenderTarget.BindColor(Position, Attachment, NewImage); + Gpu.Renderer.RenderTarget.BindColor(Position, Attachment); } public void SendZetaBuffer(NvGpuVmm Vmm, long Position, GalImage NewImage) @@ -60,7 +60,7 @@ namespace Ryujinx.Graphics Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage); } - Gpu.Renderer.RenderTarget.BindZeta(Position, NewImage); + Gpu.Renderer.RenderTarget.BindZeta(Position); } public void SendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage, int TexIndex = -1) diff --git a/Ryujinx.Graphics/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs index b19f3063b..22c093777 100644 --- a/Ryujinx.Graphics/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/NvGpuEngine3d.cs @@ -100,7 +100,10 @@ namespace Ryujinx.Graphics SetAlphaBlending(State); SetPrimitiveRestart(State); - SetFrameBuffer(Vmm, 0); + for (int FbIndex = 0; FbIndex < 8; FbIndex++) + { + SetFrameBuffer(Vmm, FbIndex); + } SetZeta(Vmm); @@ -154,6 +157,10 @@ namespace Ryujinx.Graphics SetZeta(Vmm); + SetRenderTargets(); + + Gpu.Renderer.RenderTarget.Bind(); + Gpu.Renderer.Rasterizer.ClearBuffers( Flags, FbIndex, @@ -204,7 +211,7 @@ namespace Ryujinx.Graphics Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image); - Gpu.Renderer.RenderTarget.SetViewport(VpX, VpY, VpW, VpH); + Gpu.Renderer.RenderTarget.SetViewport(FbIndex, VpX, VpY, VpW, VpH); } private void SetFrameBuffer(GalPipelineState State) @@ -426,14 +433,15 @@ namespace Ryujinx.Graphics private void SetRenderTargets() { - bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData); + //Commercial games do not seem to + //bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData); - if (SeparateFragData) + uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl)); + + uint Count = Control & 0xf; + + if (Count > 0) { - uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl)); - - uint Count = Control & 0xf; - int[] Map = new int[Count]; for (int i = 0; i < Count; i++) @@ -702,6 +710,8 @@ namespace Ryujinx.Graphics Gpu.Renderer.Pipeline.Bind(State); + Gpu.Renderer.RenderTarget.Bind(); + if (IndexCount != 0) { int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs index c5f38211b..191537b1d 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs @@ -328,7 +328,7 @@ namespace Ryujinx.HLE.HOS.Services.Android Context.Device.Gpu.ResourceManager.SendTexture(Vmm, FbAddr, Image); Renderer.RenderTarget.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom); - Renderer.RenderTarget.Set(FbAddr); + Renderer.RenderTarget.Present(FbAddr); ReleaseBuffer(Slot); });