From 01667d9a35dcab22df7144427b9be0019c2e9a81 Mon Sep 17 00:00:00 2001
From: Yuri Kunde Schlesner <yuriks@yuriks.net>
Date: Fri, 16 Sep 2016 00:25:28 -0700
Subject: [PATCH] OpenGL: Take cached viewport sub-rect into account for
 scissor

Fixes #1938
---
 .../renderer_opengl/gl_rasterizer.cpp         | 42 +++++++++----------
 .../renderer_opengl/gl_rasterizer.h           |  3 --
 .../renderer_opengl/gl_shader_gen.cpp         |  9 ++--
 3 files changed, 25 insertions(+), 29 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 60c9d91808..62c9af28ce 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -211,6 +211,27 @@ void RasterizerOpenGL::DrawTriangles() {
         uniform_block_data.dirty = true;
     }
 
+    // Scissor checks are window-, not viewport-relative, which means that if the cached texture
+    // sub-rect changes, the scissor bounds also need to be updated.
+    GLint scissor_x1 = rect.left + regs.scissor_test.x1 * color_surface->res_scale_width;
+    GLint scissor_y1 = rect.bottom + regs.scissor_test.y1 * color_surface->res_scale_height;
+    // x2, y2 have +1 added to cover the entire pixel area, otherwise you might get cracks when
+    // scaling or doing multisampling.
+    GLint scissor_x2 = rect.left + (regs.scissor_test.x2 + 1) * color_surface->res_scale_width;
+    GLint scissor_y2 = rect.bottom + (regs.scissor_test.y2 + 1) * color_surface->res_scale_height;
+
+    if (uniform_block_data.data.scissor_x1 != scissor_x1 ||
+        uniform_block_data.data.scissor_x2 != scissor_x2 ||
+        uniform_block_data.data.scissor_y1 != scissor_y1 ||
+        uniform_block_data.data.scissor_y2 != scissor_y2) {
+
+        uniform_block_data.data.scissor_x1 = scissor_x1;
+        uniform_block_data.data.scissor_x2 = scissor_x2;
+        uniform_block_data.data.scissor_y1 = scissor_y1;
+        uniform_block_data.data.scissor_y2 = scissor_y2;
+        uniform_block_data.dirty = true;
+    }
+
     // Sync and bind the texture surfaces
     const auto pica_textures = regs.GetTextures();
     for (unsigned texture_index = 0; texture_index < pica_textures.size(); ++texture_index) {
@@ -374,10 +395,6 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
     case PICA_REG_INDEX(scissor_test.mode):
         shader_dirty = true;
         break;
-    case PICA_REG_INDEX(scissor_test.x1): // and y1
-    case PICA_REG_INDEX(scissor_test.x2): // and y2
-        SyncScissorTest();
-        break;
 
     // Logic op
     case PICA_REG_INDEX(output_merger.logic_op):
@@ -1061,7 +1078,6 @@ void RasterizerOpenGL::SetShader() {
         SyncDepthOffset();
         SyncAlphaTest();
         SyncCombinerColor();
-        SyncScissorTest();
         auto& tev_stages = Pica::g_state.regs.GetTevStages();
         for (int index = 0; index < tev_stages.size(); ++index)
             SyncTevConstColor(index, tev_stages[index]);
@@ -1236,22 +1252,6 @@ void RasterizerOpenGL::SyncDepthTest() {
                                 : GL_ALWAYS;
 }
 
-void RasterizerOpenGL::SyncScissorTest() {
-    const auto& regs = Pica::g_state.regs;
-
-    if (uniform_block_data.data.scissor_x1 != regs.scissor_test.x1 ||
-        uniform_block_data.data.scissor_y1 != regs.scissor_test.y1 ||
-        uniform_block_data.data.scissor_x2 != regs.scissor_test.x2 ||
-        uniform_block_data.data.scissor_y2 != regs.scissor_test.y2) {
-
-        uniform_block_data.data.scissor_x1 = regs.scissor_test.x1;
-        uniform_block_data.data.scissor_y1 = regs.scissor_test.y1;
-        uniform_block_data.data.scissor_x2 = regs.scissor_test.x2;
-        uniform_block_data.data.scissor_y2 = regs.scissor_test.y2;
-        uniform_block_data.dirty = true;
-    }
-}
-
 void RasterizerOpenGL::SyncCombinerColor() {
     auto combiner_color = PicaToGL::ColorRGBA8(Pica::g_state.regs.tev_combiner_buffer_color.raw);
     if (combiner_color != uniform_block_data.data.tev_combiner_buffer_color) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 24fefed1b0..7b4ce2ac5c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -393,9 +393,6 @@ private:
     /// Syncs the depth test states to match the PICA register
     void SyncDepthTest();
 
-    /// Syncs the scissor test state to match the PICA register
-    void SyncScissorTest();
-
     /// Syncs the TEV combiner color buffer to match the PICA register
     void SyncCombinerColor();
 
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp
index 1808ee0a97..8f278722dc 100644
--- a/src/video_core/renderer_opengl/gl_shader_gen.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp
@@ -645,11 +645,10 @@ vec4 secondary_fragment_color = vec4(0.0);
         // Negate the condition if we have to keep only the pixels outside the scissor box
         if (state.scissor_test_mode == Regs::ScissorMode::Include)
             out += "!";
-        // x2,y2 have +1 added to cover the entire pixel area
-        out += "(gl_FragCoord.x >= scissor_x1 * framebuffer_scale.x && "
-               "gl_FragCoord.y >= scissor_y1 * framebuffer_scale.y && "
-               "gl_FragCoord.x < (scissor_x2 + 1) * framebuffer_scale.x && "
-               "gl_FragCoord.y < (scissor_y2 + 1) * framebuffer_scale.y)) discard;\n";
+        out += "(gl_FragCoord.x >= scissor_x1 && "
+               "gl_FragCoord.y >= scissor_y1 && "
+               "gl_FragCoord.x < scissor_x2 && "
+               "gl_FragCoord.y < scissor_y2)) discard;\n";
     }
 
     out += "float z_over_w = 1.0 - gl_FragCoord.z * 2.0;\n";