diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs index 8630da9cc4..8deb68b48f 100644 --- a/Ryujinx.Graphics/Gal/GalPipelineState.cs +++ b/Ryujinx.Graphics/Gal/GalPipelineState.cs @@ -91,6 +91,13 @@ public GalStencilOp StencilFrontOpZPass; public uint StencilFrontMask; + public int ScissorTestCount; + public bool[] ScissorTestEnabled; + public int[] ScissorTestX; + public int[] ScissorTestY; + public int[] ScissorTestWidth; + public int[] ScissorTestHeight; + public bool BlendIndependent; public BlendState[] Blends; @@ -111,6 +118,12 @@ Blends = new BlendState[RenderTargetsCount]; + ScissorTestEnabled = new bool[RenderTargetsCount]; + ScissorTestY = new int[RenderTargetsCount]; + ScissorTestX = new int[RenderTargetsCount]; + ScissorTestWidth = new int[RenderTargetsCount]; + ScissorTestHeight = new int[RenderTargetsCount]; + ColorMasks = new ColorMaskState[RenderTargetsCount]; } } diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs index eb7f958b92..e9143c19d5 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs @@ -270,6 +270,57 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + + // Scissor Test + bool forceUpdate; + + for (int Index = 0; Index < New.ScissorTestCount; Index++) + { + forceUpdate = false; + + if (New.ScissorTestEnabled[Index] != Old.ScissorTestEnabled[Index]) + { + if (New.ScissorTestEnabled[Index]) + { + // If there is only 1 scissor test geometry shaders are disables so the scissor test applies to all viewports + if (New.ScissorTestCount == 1) + { + GL.Enable(EnableCap.ScissorTest); + } + else + { + GL.Enable(IndexedEnableCap.ScissorTest, Index); + } + forceUpdate = true; + } + else + { + GL.Disable(IndexedEnableCap.ScissorTest, Index); + } + } + + if (New.ScissorTestEnabled[Index] && + (New.ScissorTestX[Index] != Old.ScissorTestX[Index] || + New.ScissorTestY[Index] != Old.ScissorTestY[Index] || + New.ScissorTestWidth[Index] != Old.ScissorTestWidth[Index] || + New.ScissorTestHeight[Index] != Old.ScissorTestHeight[Index] || + forceUpdate)) // Force update intentionally last to reduce if comparisons + { + // If there is only 1 scissor test geometry shaders are disables so the scissor test applies to all viewports + if (New.ScissorTestCount == 1) + { + GL.Scissor(New.ScissorTestX[Index], New.ScissorTestY[Index], + New.ScissorTestWidth[Index], New.ScissorTestHeight[Index]); + } + else + { + GL.ScissorIndexed(Index, New.ScissorTestX[Index], New.ScissorTestY[Index], + New.ScissorTestWidth[Index], New.ScissorTestHeight[Index]); + } + } + } + + if (New.BlendIndependent) { for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs index dc1eec3b6b..9a22c85f42 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs @@ -24,6 +24,9 @@ namespace Ryujinx.Graphics.Graphics3d private ConstBuffer[][] ConstBuffers; + // Height kept for flipping y axis + private int ViewportHeight = 0; + private int CurrentInstance = 0; public NvGpuEngine3d(NvGpu Gpu) @@ -97,6 +100,7 @@ namespace Ryujinx.Graphics.Graphics3d SetCullFace(State); SetDepth(State); SetStencil(State); + SetScissor(State); SetBlending(State); SetColorMask(State); SetPrimitiveRestart(State); @@ -208,6 +212,8 @@ namespace Ryujinx.Graphics.Graphics3d Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image); + ViewportHeight = VpH; + Gpu.Renderer.RenderTarget.SetViewport(FbIndex, VpX, VpY, VpW, VpH); } @@ -407,6 +413,36 @@ namespace Ryujinx.Graphics.Graphics3d } } + private void SetScissor(GalPipelineState State) + { + // FIXME: Stubbed, only the first scissor test is valid without a geometry shader loaded. At time of writing geometry shaders are also stubbed. + // Once geometry shaders are fixed it should be equal to GalPipelineState.RenderTargetCount when shader loaded, otherwise equal to 1 + State.ScissorTestCount = 1; + + for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + { + State.ScissorTestEnabled[Index] = ReadRegisterBool(NvGpuEngine3dReg.ScissorEnable + Index * 4); + + if (State.ScissorTestEnabled[Index]) + { + uint ScissorHorizontal = (uint)ReadRegister(NvGpuEngine3dReg.ScissorHorizontal + Index * 4); + uint ScissorVertical = (uint)ReadRegister(NvGpuEngine3dReg.ScissorVertical + Index * 4); + + State.ScissorTestX[Index] = (int)((ScissorHorizontal & 0xFFFF) * State.FlipX); // X, lower 16 bits + State.ScissorTestWidth[Index] = (int)((ScissorHorizontal >> 16) * State.FlipX) - State.ScissorTestX[Index]; // Width, right side is upper 16 bits + + State.ScissorTestY[Index] = (int)((ScissorVertical & 0xFFFF)); // Y, lower 16 bits + State.ScissorTestHeight[Index] = (int)((ScissorVertical >> 16)) - State.ScissorTestY[Index]; // Height, top side is upper 16 bits + + // Y coordinates may have to be flipped + if ((int)State.FlipY == -1) + { + State.ScissorTestY[Index] = ViewportHeight - State.ScissorTestY[Index] - State.ScissorTestHeight[Index]; + } + } + } + } + private void SetBlending(GalPipelineState State) { bool BlendIndependent = ReadRegisterBool(NvGpuEngine3dReg.BlendIndependent); diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs index 30243c02b6..026b0cd198 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs +++ b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs @@ -22,6 +22,9 @@ namespace Ryujinx.Graphics.Graphics3d ClearNColor = 0x360, ClearDepth = 0x364, ClearStencil = 0x368, + ScissorEnable = 0x380, + ScissorHorizontal = 0x381, + ScissorVertical = 0x382, StencilBackFuncRef = 0x3d5, StencilBackMask = 0x3d6, StencilBackFuncMask = 0x3d7,