From ab4867505ec0d3f5a9ec4f042ccd6f7890e3632e Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 29 Mar 2020 00:02:58 -0300 Subject: [PATCH] Implement GPU scissors (#1058) * Implement GPU scissors * Remove unused using * Add missing changes for Clear --- Ryujinx.Graphics.GAL/IPipeline.cs | 5 +-- Ryujinx.Graphics.Gpu/Engine/MethodClear.cs | 6 ++++ Ryujinx.Graphics.Gpu/Engine/Methods.cs | 26 ++++++++++++++++ Ryujinx.Graphics.Gpu/State/GpuStateTable.cs | 1 + Ryujinx.Graphics.Gpu/State/MethodOffset.cs | 1 + Ryujinx.Graphics.Gpu/State/ScissorState.cs | 12 ++++++++ Ryujinx.Graphics.OpenGL/Pipeline.cs | 34 +++++++++++++++++++++ Ryujinx.Graphics.OpenGL/Renderer.cs | 6 ++-- Ryujinx.Graphics.OpenGL/TextureCopy.cs | 11 +++++++ Ryujinx.Graphics.OpenGL/Window.cs | 12 ++++++-- 10 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 Ryujinx.Graphics.Gpu/State/ScissorState.cs diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs index 1a5f1cf02f..fc5cf483db 100644 --- a/Ryujinx.Graphics.GAL/IPipeline.cs +++ b/Ryujinx.Graphics.GAL/IPipeline.cs @@ -29,9 +29,7 @@ namespace Ryujinx.Graphics.GAL void SetBlendColor(ColorF color); void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp); - void SetDepthMode(DepthMode mode); - void SetDepthTest(DepthTestDescriptor depthTest); void SetFaceCulling(bool enable, Face face); @@ -56,6 +54,9 @@ namespace Ryujinx.Graphics.GAL void SetSampler(int index, ShaderStage stage, ISampler sampler); + void SetScissorEnable(int index, bool enable); + void SetScissor(int index, int x, int y, int width, int height); + void SetStencilTest(StencilTestDescriptor stencilTest); void SetStorageBuffer(int index, ShaderStage stage, BufferRange buffer); diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs b/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs index dfa7b12ed3..ff538df332 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs @@ -13,6 +13,12 @@ namespace Ryujinx.Graphics.Gpu.Engine /// Method call argument private void Clear(GpuState state, int argument) { + // Scissor affects clears aswell. + if (state.QueryModified(MethodOffset.ScissorState)) + { + UpdateScissorState(state); + } + UpdateRenderTargetState(state, useControl: false); TextureManager.CommitGraphicsBindings(); diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index a2c9876e42..0b30e61d82 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -117,6 +117,11 @@ namespace Ryujinx.Graphics.Gpu.Engine UpdateRenderTargetState(state, useControl: true); } + if (state.QueryModified(MethodOffset.ScissorState)) + { + UpdateScissorState(state); + } + if (state.QueryModified(MethodOffset.DepthTestEnable, MethodOffset.DepthWriteEnable, MethodOffset.DepthTestFunc)) @@ -321,6 +326,27 @@ namespace Ryujinx.Graphics.Gpu.Engine return colorState.Format != 0 && colorState.WidthOrStride != 0; } + /// + /// Updates host scissor test state based on current GPU state. + /// + /// Current GPU state + private void UpdateScissorState(GpuState state) + { + for (int index = 0; index < Constants.TotalViewports; index++) + { + ScissorState scissor = state.Get(MethodOffset.ScissorState, index); + + bool enable = scissor.Enable && (scissor.X1 != 0 || scissor.Y1 != 0 || scissor.X2 != 0xffff || scissor.Y2 != 0xffff); + + _context.Renderer.Pipeline.SetScissorEnable(index, enable); + + if (enable) + { + _context.Renderer.Pipeline.SetScissor(index, scissor.X1, scissor.Y1, scissor.X2 - scissor.X1, scissor.Y2 - scissor.Y1); + } + } + } + /// /// Updates host depth test state based on current GPU state. /// diff --git a/Ryujinx.Graphics.Gpu/State/GpuStateTable.cs b/Ryujinx.Graphics.Gpu/State/GpuStateTable.cs index db8d141e77..8631efcc1f 100644 --- a/Ryujinx.Graphics.Gpu/State/GpuStateTable.cs +++ b/Ryujinx.Graphics.Gpu/State/GpuStateTable.cs @@ -57,6 +57,7 @@ namespace Ryujinx.Graphics.Gpu.State new TableItem(MethodOffset.ViewportExtents, typeof(ViewportExtents), 8), new TableItem(MethodOffset.VertexBufferDrawState, typeof(VertexBufferDrawState), 1), new TableItem(MethodOffset.DepthBiasState, typeof(DepthBiasState), 1), + new TableItem(MethodOffset.ScissorState, typeof(ScissorState), 8), new TableItem(MethodOffset.StencilBackMasks, typeof(StencilBackMasks), 1), new TableItem(MethodOffset.RtDepthStencilState, typeof(RtDepthStencilState), 1), new TableItem(MethodOffset.VertexAttribState, typeof(VertexAttribState), 16), diff --git a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs index 730ff40afa..0a720b2c48 100644 --- a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs +++ b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs @@ -33,6 +33,7 @@ namespace Ryujinx.Graphics.Gpu.State ClearStencilValue = 0x368, DepthBiasState = 0x370, TextureBarrier = 0x378, + ScissorState = 0x380, StencilBackMasks = 0x3d5, InvalidateTextures = 0x3dd, TextureBarrierTiled = 0x3df, diff --git a/Ryujinx.Graphics.Gpu/State/ScissorState.cs b/Ryujinx.Graphics.Gpu/State/ScissorState.cs new file mode 100644 index 0000000000..0676664070 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/ScissorState.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct ScissorState + { + public Boolean32 Enable; + public ushort X1; + public ushort X2; + public ushort Y1; + public ushort Y2; + public uint Padding; + } +} diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index c2a42370d0..f01e7c7ad5 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -31,10 +31,14 @@ namespace Ryujinx.Graphics.OpenGL private uint[] _componentMasks; + private readonly bool[] _scissorEnable; + internal Pipeline() { _clipOrigin = ClipOrigin.LowerLeft; _clipDepthMode = ClipDepthMode.NegativeOneToOne; + + _scissorEnable = new bool[8]; } public void Barrier() @@ -674,6 +678,25 @@ namespace Ryujinx.Graphics.OpenGL } } + public void SetScissorEnable(int index, bool enable) + { + if (enable) + { + GL.Enable(IndexedEnableCap.ScissorTest, index); + } + else + { + GL.Disable(IndexedEnableCap.ScissorTest, index); + } + + _scissorEnable[index] = enable; + } + + public void SetScissor(int index, int x, int y, int width, int height) + { + GL.ScissorIndexed(index, x, y, width, height); + } + public void SetStencilTest(StencilTestDescriptor stencilTest) { if (!stencilTest.TestEnable) @@ -928,6 +951,17 @@ namespace Ryujinx.Graphics.OpenGL } } + public void RestoreScissorEnable() + { + for (int index = 0; index < 8; index++) + { + if (_scissorEnable[index]) + { + GL.Enable(IndexedEnableCap.ScissorTest, index); + } + } + } + public void Dispose() { _framebuffer?.Dispose(); diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs index 6ff3c36c72..cf2adc45c2 100644 --- a/Ryujinx.Graphics.OpenGL/Renderer.cs +++ b/Ryujinx.Graphics.OpenGL/Renderer.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.OpenGL { public sealed class Renderer : IRenderer { - private Pipeline _pipeline; + private readonly Pipeline _pipeline; public IPipeline Pipeline => _pipeline; @@ -31,9 +31,9 @@ namespace Ryujinx.Graphics.OpenGL _counters = new Counters(); - _window = new Window(); + _window = new Window(this); - TextureCopy = new TextureCopy(); + TextureCopy = new TextureCopy(this); } public IShader CompileShader(ShaderProgram shader) diff --git a/Ryujinx.Graphics.OpenGL/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/TextureCopy.cs index 27f1fd563b..ac9459d4aa 100644 --- a/Ryujinx.Graphics.OpenGL/TextureCopy.cs +++ b/Ryujinx.Graphics.OpenGL/TextureCopy.cs @@ -6,9 +6,16 @@ namespace Ryujinx.Graphics.OpenGL { class TextureCopy : IDisposable { + private readonly Renderer _renderer; + private int _srcFramebuffer; private int _dstFramebuffer; + public TextureCopy(Renderer renderer) + { + _renderer = renderer; + } + public void Copy( TextureView src, TextureView dst, @@ -34,6 +41,8 @@ namespace Ryujinx.Graphics.OpenGL GL.ReadBuffer(ReadBufferMode.ColorAttachment0); GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + GL.Disable(EnableCap.ScissorTest); + GL.BlitFramebuffer( srcRegion.X1, srcRegion.Y1, @@ -48,6 +57,8 @@ namespace Ryujinx.Graphics.OpenGL GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); + + ((Pipeline)_renderer.Pipeline).RestoreScissorEnable(); } private static void Attach(FramebufferTarget target, Format format, int handle) diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs index 2689a7c4a6..c9b7daeb95 100644 --- a/Ryujinx.Graphics.OpenGL/Window.cs +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -9,13 +9,17 @@ namespace Ryujinx.Graphics.OpenGL private const int NativeWidth = 1280; private const int NativeHeight = 720; + private readonly Renderer _renderer; + private int _width; private int _height; private int _copyFramebufferHandle; - public Window() + public Window(Renderer renderer) { + _renderer = renderer; + _width = NativeWidth; _height = NativeHeight; } @@ -35,13 +39,13 @@ namespace Ryujinx.Graphics.OpenGL _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, drawFramebuffer); @@ -55,6 +59,8 @@ namespace Ryujinx.Graphics.OpenGL GL.ReadBuffer(ReadBufferMode.ColorAttachment0); + GL.Disable(EnableCap.ScissorTest); + GL.Clear(ClearBufferMask.ColorBufferBit); int srcX0, srcX1, srcY0, srcY1; @@ -119,6 +125,8 @@ namespace Ryujinx.Graphics.OpenGL GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); + + ((Pipeline)_renderer.Pipeline).RestoreScissorEnable(); } private int GetCopyFramebufferHandleLazy()