diff --git a/Ryujinx.Graphics.GAL/IPipeline.cs b/Ryujinx.Graphics.GAL/IPipeline.cs
index 1a5f1cf02..fc5cf483d 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 dfa7b12ed..ff538df33 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 a2c9876e4..0b30e61d8 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 db8d141e7..8631efcc1 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 730ff40af..0a720b2c4 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 000000000..067666407
--- /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 c2a42370d..f01e7c7ad 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 6ff3c36c7..cf2adc45c 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 27f1fd563..ac9459d4a 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 2689a7c4a..c9b7daeb9 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()