From e38ed26b9887f4fda723cdc42da7b61c0050d74b Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 01:42:31 -0300
Subject: [PATCH 01/76] common/math_util: Support float type rectangles

---
 src/common/math_util.h | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/src/common/math_util.h b/src/common/math_util.h
index d6c35ee899..83ef0201f4 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -24,17 +24,29 @@ struct Rectangle {
         : left(left), top(top), right(right), bottom(bottom) {}
 
     T GetWidth() const {
-        return std::abs(static_cast<std::make_signed_t<T>>(right - left));
+        if constexpr (std::is_floating_point_v<T>) {
+            return std::abs(right - left);
+        } else {
+            return std::abs(static_cast<std::make_signed_t<T>>(right - left));
+        }
     }
+
     T GetHeight() const {
-        return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
+        if constexpr (std::is_floating_point_v<T>) {
+            return std::abs(bottom - top);
+        } else {
+            return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
+        }
     }
+
     Rectangle<T> TranslateX(const T x) const {
         return Rectangle{left + x, top, right + x, bottom};
     }
+
     Rectangle<T> TranslateY(const T y) const {
         return Rectangle{left, top + y, right, bottom + y};
     }
+
     Rectangle<T> Scale(const float s) const {
         return Rectangle{left, top, static_cast<T>(left + GetWidth() * s),
                          static_cast<T>(top + GetHeight() * s)};

From 96ac3d518a9882a2a040f319c47a567467c9266d Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 25 Dec 2019 17:02:17 -0300
Subject: [PATCH 02/76] gl_rasterizer: Remove dirty flags

---
 src/video_core/CMakeLists.txt                 |   2 +
 src/video_core/dma_pusher.cpp                 |   3 -
 src/video_core/engines/kepler_compute.cpp     |   3 -
 src/video_core/engines/kepler_memory.cpp      |   3 -
 src/video_core/engines/maxwell_3d.cpp         | 181 +-----------------
 src/video_core/engines/maxwell_3d.h           |  75 --------
 src/video_core/engines/maxwell_dma.cpp        |   3 -
 .../renderer_opengl/gl_rasterizer.cpp         |  70 +------
 .../renderer_opengl/gl_shader_cache.cpp       |   4 -
 src/video_core/renderer_opengl/gl_state.cpp   |  20 --
 src/video_core/renderer_opengl/gl_state.h     |  31 ---
 .../renderer_opengl/gl_state_tracker.cpp      |   0
 .../renderer_opengl/gl_state_tracker.h        |   0
 .../renderer_opengl/gl_texture_cache.cpp      |   2 -
 .../renderer_opengl/renderer_opengl.cpp       |  14 --
 .../renderer_vulkan/vk_pipeline_cache.cpp     |   5 -
 .../renderer_vulkan/vk_rasterizer.cpp         |  28 +--
 src/video_core/texture_cache/texture_cache.h  |  20 +-
 18 files changed, 7 insertions(+), 457 deletions(-)
 create mode 100644 src/video_core/renderer_opengl/gl_state_tracker.cpp
 create mode 100644 src/video_core/renderer_opengl/gl_state_tracker.h

diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 4b0c6346fe..db65e7bf3f 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -69,6 +69,8 @@ add_library(video_core STATIC
     renderer_opengl/gl_shader_manager.h
     renderer_opengl/gl_shader_util.cpp
     renderer_opengl/gl_shader_util.h
+    renderer_opengl/gl_state_tracker.cpp
+    renderer_opengl/gl_state_tracker.h
     renderer_opengl/gl_state.cpp
     renderer_opengl/gl_state.h
     renderer_opengl/gl_stream_buffer.cpp
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index 0094fd715b..a42d37c810 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -21,9 +21,6 @@ MICROPROFILE_DEFINE(DispatchCalls, "GPU", "Execute command buffer", MP_RGB(128,
 void DmaPusher::DispatchCalls() {
     MICROPROFILE_SCOPE(DispatchCalls);
 
-    // On entering GPU code, assume all memory may be touched by the ARM core.
-    gpu.Maxwell3D().dirty.OnMemoryWrite();
-
     dma_pushbuffer_subindex = 0;
 
     while (Core::System::GetInstance().IsPoweredOn()) {
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 4b824aa4e4..254ad68100 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -38,9 +38,6 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
     case KEPLER_COMPUTE_REG_INDEX(data_upload): {
         const bool is_last_call = method_call.IsLastCall();
         upload_state.ProcessData(method_call.argument, is_last_call);
-        if (is_last_call) {
-            system.GPU().Maxwell3D().dirty.OnMemoryWrite();
-        }
         break;
     }
     case KEPLER_COMPUTE_REG_INDEX(launch):
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index fa4a7c5c13..b504b450ed 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -33,9 +33,6 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) {
     case KEPLERMEMORY_REG_INDEX(data): {
         const bool is_last_call = method_call.IsLastCall();
         upload_state.ProcessData(method_call.argument, is_last_call);
-        if (is_last_call) {
-            system.GPU().Maxwell3D().dirty.OnMemoryWrite();
-        }
         break;
     }
     }
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index b28de1092c..7a6bf764c3 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -26,7 +26,6 @@ Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& raste
                      MemoryManager& memory_manager)
     : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager},
       macro_interpreter{*this}, upload_state{memory_manager, regs.upload} {
-    InitDirtySettings();
     InitializeRegisterDefaults();
 }
 
@@ -103,164 +102,6 @@ void Maxwell3D::InitializeRegisterDefaults() {
     mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true;
 }
 
-#define DIRTY_REGS_POS(field_name) static_cast<u8>(offsetof(Maxwell3D::DirtyRegs, field_name))
-
-void Maxwell3D::InitDirtySettings() {
-    const auto set_block = [this](std::size_t start, std::size_t range, u8 position) {
-        const auto start_itr = dirty_pointers.begin() + start;
-        const auto end_itr = start_itr + range;
-        std::fill(start_itr, end_itr, position);
-    };
-    dirty.regs.fill(true);
-
-    // Init Render Targets
-    constexpr u32 registers_per_rt = sizeof(regs.rt[0]) / sizeof(u32);
-    constexpr u32 rt_start_reg = MAXWELL3D_REG_INDEX(rt);
-    constexpr u32 rt_end_reg = rt_start_reg + registers_per_rt * 8;
-    u8 rt_dirty_reg = DIRTY_REGS_POS(render_target);
-    for (u32 rt_reg = rt_start_reg; rt_reg < rt_end_reg; rt_reg += registers_per_rt) {
-        set_block(rt_reg, registers_per_rt, rt_dirty_reg);
-        ++rt_dirty_reg;
-    }
-    constexpr u32 depth_buffer_flag = DIRTY_REGS_POS(depth_buffer);
-    dirty_pointers[MAXWELL3D_REG_INDEX(zeta_enable)] = depth_buffer_flag;
-    dirty_pointers[MAXWELL3D_REG_INDEX(zeta_width)] = depth_buffer_flag;
-    dirty_pointers[MAXWELL3D_REG_INDEX(zeta_height)] = depth_buffer_flag;
-    constexpr u32 registers_in_zeta = sizeof(regs.zeta) / sizeof(u32);
-    constexpr u32 zeta_reg = MAXWELL3D_REG_INDEX(zeta);
-    set_block(zeta_reg, registers_in_zeta, depth_buffer_flag);
-
-    // Init Vertex Arrays
-    constexpr u32 vertex_array_start = MAXWELL3D_REG_INDEX(vertex_array);
-    constexpr u32 vertex_array_size = sizeof(regs.vertex_array[0]) / sizeof(u32);
-    constexpr u32 vertex_array_end = vertex_array_start + vertex_array_size * Regs::NumVertexArrays;
-    u8 va_dirty_reg = DIRTY_REGS_POS(vertex_array);
-    u8 vi_dirty_reg = DIRTY_REGS_POS(vertex_instance);
-    for (u32 vertex_reg = vertex_array_start; vertex_reg < vertex_array_end;
-         vertex_reg += vertex_array_size) {
-        set_block(vertex_reg, 3, va_dirty_reg);
-        // The divisor concerns vertex array instances
-        dirty_pointers[static_cast<std::size_t>(vertex_reg) + 3] = vi_dirty_reg;
-        ++va_dirty_reg;
-        ++vi_dirty_reg;
-    }
-    constexpr u32 vertex_limit_start = MAXWELL3D_REG_INDEX(vertex_array_limit);
-    constexpr u32 vertex_limit_size = sizeof(regs.vertex_array_limit[0]) / sizeof(u32);
-    constexpr u32 vertex_limit_end = vertex_limit_start + vertex_limit_size * Regs::NumVertexArrays;
-    va_dirty_reg = DIRTY_REGS_POS(vertex_array);
-    for (u32 vertex_reg = vertex_limit_start; vertex_reg < vertex_limit_end;
-         vertex_reg += vertex_limit_size) {
-        set_block(vertex_reg, vertex_limit_size, va_dirty_reg);
-        va_dirty_reg++;
-    }
-    constexpr u32 vertex_instance_start = MAXWELL3D_REG_INDEX(instanced_arrays);
-    constexpr u32 vertex_instance_size =
-        sizeof(regs.instanced_arrays.is_instanced[0]) / sizeof(u32);
-    constexpr u32 vertex_instance_end =
-        vertex_instance_start + vertex_instance_size * Regs::NumVertexArrays;
-    vi_dirty_reg = DIRTY_REGS_POS(vertex_instance);
-    for (u32 vertex_reg = vertex_instance_start; vertex_reg < vertex_instance_end;
-         vertex_reg += vertex_instance_size) {
-        set_block(vertex_reg, vertex_instance_size, vi_dirty_reg);
-        vi_dirty_reg++;
-    }
-    set_block(MAXWELL3D_REG_INDEX(vertex_attrib_format), regs.vertex_attrib_format.size(),
-              DIRTY_REGS_POS(vertex_attrib_format));
-
-    // Init Shaders
-    constexpr u32 shader_registers_count =
-        sizeof(regs.shader_config[0]) * Regs::MaxShaderProgram / sizeof(u32);
-    set_block(MAXWELL3D_REG_INDEX(shader_config[0]), shader_registers_count,
-              DIRTY_REGS_POS(shaders));
-
-    // State
-
-    // Viewport
-    constexpr u8 viewport_dirty_reg = DIRTY_REGS_POS(viewport);
-    constexpr u32 viewport_start = MAXWELL3D_REG_INDEX(viewports);
-    constexpr u32 viewport_size = sizeof(regs.viewports) / sizeof(u32);
-    set_block(viewport_start, viewport_size, viewport_dirty_reg);
-    constexpr u32 view_volume_start = MAXWELL3D_REG_INDEX(view_volume_clip_control);
-    constexpr u32 view_volume_size = sizeof(regs.view_volume_clip_control) / sizeof(u32);
-    set_block(view_volume_start, view_volume_size, viewport_dirty_reg);
-
-    // Viewport transformation
-    constexpr u32 viewport_trans_start = MAXWELL3D_REG_INDEX(viewport_transform);
-    constexpr u32 viewport_trans_size = sizeof(regs.viewport_transform) / sizeof(u32);
-    set_block(viewport_trans_start, viewport_trans_size, DIRTY_REGS_POS(viewport_transform));
-
-    // Cullmode
-    constexpr u32 cull_mode_start = MAXWELL3D_REG_INDEX(cull);
-    constexpr u32 cull_mode_size = sizeof(regs.cull) / sizeof(u32);
-    set_block(cull_mode_start, cull_mode_size, DIRTY_REGS_POS(cull_mode));
-
-    // Screen y control
-    dirty_pointers[MAXWELL3D_REG_INDEX(screen_y_control)] = DIRTY_REGS_POS(screen_y_control);
-
-    // Primitive Restart
-    constexpr u32 primitive_restart_start = MAXWELL3D_REG_INDEX(primitive_restart);
-    constexpr u32 primitive_restart_size = sizeof(regs.primitive_restart) / sizeof(u32);
-    set_block(primitive_restart_start, primitive_restart_size, DIRTY_REGS_POS(primitive_restart));
-
-    // Depth Test
-    constexpr u8 depth_test_dirty_reg = DIRTY_REGS_POS(depth_test);
-    dirty_pointers[MAXWELL3D_REG_INDEX(depth_test_enable)] = depth_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(depth_write_enabled)] = depth_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(depth_test_func)] = depth_test_dirty_reg;
-
-    // Stencil Test
-    constexpr u32 stencil_test_dirty_reg = DIRTY_REGS_POS(stencil_test);
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_enable)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_func)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_ref)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_func_mask)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_fail)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_zfail)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_op_zpass)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_front_mask)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_two_side_enable)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_func)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_ref)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_func_mask)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_fail)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_zfail)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_op_zpass)] = stencil_test_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(stencil_back_mask)] = stencil_test_dirty_reg;
-
-    // Color Mask
-    constexpr u8 color_mask_dirty_reg = DIRTY_REGS_POS(color_mask);
-    dirty_pointers[MAXWELL3D_REG_INDEX(color_mask_common)] = color_mask_dirty_reg;
-    set_block(MAXWELL3D_REG_INDEX(color_mask), sizeof(regs.color_mask) / sizeof(u32),
-              color_mask_dirty_reg);
-    // Blend State
-    constexpr u8 blend_state_dirty_reg = DIRTY_REGS_POS(blend_state);
-    set_block(MAXWELL3D_REG_INDEX(blend_color), sizeof(regs.blend_color) / sizeof(u32),
-              blend_state_dirty_reg);
-    dirty_pointers[MAXWELL3D_REG_INDEX(independent_blend_enable)] = blend_state_dirty_reg;
-    set_block(MAXWELL3D_REG_INDEX(blend), sizeof(regs.blend) / sizeof(u32), blend_state_dirty_reg);
-    set_block(MAXWELL3D_REG_INDEX(independent_blend), sizeof(regs.independent_blend) / sizeof(u32),
-              blend_state_dirty_reg);
-
-    // Scissor State
-    constexpr u8 scissor_test_dirty_reg = DIRTY_REGS_POS(scissor_test);
-    set_block(MAXWELL3D_REG_INDEX(scissor_test), sizeof(regs.scissor_test) / sizeof(u32),
-              scissor_test_dirty_reg);
-
-    // Polygon Offset
-    constexpr u8 polygon_offset_dirty_reg = DIRTY_REGS_POS(polygon_offset);
-    dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_fill_enable)] = polygon_offset_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_line_enable)] = polygon_offset_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_point_enable)] = polygon_offset_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_units)] = polygon_offset_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_factor)] = polygon_offset_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(polygon_offset_clamp)] = polygon_offset_dirty_reg;
-
-    // Depth bounds
-    constexpr u8 depth_bounds_values_dirty_reg = DIRTY_REGS_POS(depth_bounds_values);
-    dirty_pointers[MAXWELL3D_REG_INDEX(depth_bounds[0])] = depth_bounds_values_dirty_reg;
-    dirty_pointers[MAXWELL3D_REG_INDEX(depth_bounds[1])] = depth_bounds_values_dirty_reg;
-}
-
 void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u32* parameters) {
     // Reset the current macro.
     executing_macro = 0;
@@ -317,23 +158,7 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
     ASSERT_MSG(method < Regs::NUM_REGS,
                "Invalid Maxwell3D register, increase the size of the Regs structure");
 
-    if (regs.reg_array[method] != method_call.argument) {
-        regs.reg_array[method] = method_call.argument;
-        const std::size_t dirty_reg = dirty_pointers[method];
-        if (dirty_reg) {
-            dirty.regs[dirty_reg] = true;
-            if (dirty_reg >= DIRTY_REGS_POS(vertex_array) &&
-                dirty_reg < DIRTY_REGS_POS(vertex_array_buffers)) {
-                dirty.vertex_array_buffers = true;
-            } else if (dirty_reg >= DIRTY_REGS_POS(vertex_instance) &&
-                       dirty_reg < DIRTY_REGS_POS(vertex_instances)) {
-                dirty.vertex_instances = true;
-            } else if (dirty_reg >= DIRTY_REGS_POS(render_target) &&
-                       dirty_reg < DIRTY_REGS_POS(render_settings)) {
-                dirty.render_settings = true;
-            }
-        }
-    }
+    regs.reg_array[method] = method_call.argument;
 
     switch (method) {
     case MAXWELL3D_REG_INDEX(macros.data): {
@@ -418,9 +243,6 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
     case MAXWELL3D_REG_INDEX(data_upload): {
         const bool is_last_call = method_call.IsLastCall();
         upload_state.ProcessData(method_call.argument, is_last_call);
-        if (is_last_call) {
-            dirty.OnMemoryWrite();
-        }
         break;
     }
     default:
@@ -727,7 +549,6 @@ void Maxwell3D::FinishCBData() {
 
     const u32 id = cb_data_state.id;
     memory_manager.WriteBlock(address, cb_data_state.buffer[id].data(), size);
-    dirty.OnMemoryWrite();
 
     cb_data_state.id = null_cb_data;
     cb_data_state.current = null_cb_data;
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 6ea7cc6a53..3a641c1820 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1238,79 +1238,6 @@ public:
 
     State state{};
 
-    struct DirtyRegs {
-        static constexpr std::size_t NUM_REGS = 256;
-        static_assert(NUM_REGS - 1 <= std::numeric_limits<u8>::max());
-
-        union {
-            struct {
-                bool null_dirty;
-
-                // Vertex Attributes
-                bool vertex_attrib_format;
-
-                // Vertex Arrays
-                std::array<bool, 32> vertex_array;
-
-                bool vertex_array_buffers;
-
-                // Vertex Instances
-                std::array<bool, 32> vertex_instance;
-
-                bool vertex_instances;
-
-                // Render Targets
-                std::array<bool, 8> render_target;
-                bool depth_buffer;
-
-                bool render_settings;
-
-                // Shaders
-                bool shaders;
-
-                // Rasterizer State
-                bool viewport;
-                bool clip_coefficient;
-                bool cull_mode;
-                bool primitive_restart;
-                bool depth_test;
-                bool stencil_test;
-                bool blend_state;
-                bool scissor_test;
-                bool transform_feedback;
-                bool color_mask;
-                bool polygon_offset;
-                bool depth_bounds_values;
-
-                // Complementary
-                bool viewport_transform;
-                bool screen_y_control;
-
-                bool memory_general;
-            };
-            std::array<bool, NUM_REGS> regs;
-        };
-
-        void ResetVertexArrays() {
-            vertex_array.fill(true);
-            vertex_array_buffers = true;
-        }
-
-        void ResetRenderTargets() {
-            depth_buffer = true;
-            render_target.fill(true);
-            render_settings = true;
-        }
-
-        void OnMemoryWrite() {
-            shaders = true;
-            memory_general = true;
-            ResetRenderTargets();
-            ResetVertexArrays();
-        }
-
-    } dirty{};
-
     /// Reads a register value located at the input method address
     u32 GetRegisterValue(u32 method) const;
 
@@ -1417,8 +1344,6 @@ private:
     /// Retrieves information about a specific TSC entry from the TSC buffer.
     Texture::TSCEntry GetTSCEntry(u32 tsc_index) const;
 
-    void InitDirtySettings();
-
     /**
      * Call a macro on this engine.
      * @param method Method to call
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index ad8453c5fe..ae51765a69 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -56,9 +56,6 @@ void MaxwellDMA::HandleCopy() {
         return;
     }
 
-    // All copies here update the main memory, so mark all rasterizer states as invalid.
-    system.GPU().Maxwell3D().dirty.OnMemoryWrite();
-
     if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
         // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
         // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index e1965fb21d..1d203fd08d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -117,11 +117,6 @@ GLuint RasterizerOpenGL::SetupVertexFormat() {
     auto& gpu = system.GPU().Maxwell3D();
     const auto& regs = gpu.regs;
 
-    if (!gpu.dirty.vertex_attrib_format) {
-        return state.draw.vertex_array;
-    }
-    gpu.dirty.vertex_attrib_format = false;
-
     MICROPROFILE_SCOPE(OpenGL_VAO);
 
     auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format);
@@ -173,30 +168,18 @@ GLuint RasterizerOpenGL::SetupVertexFormat() {
         }
     }
 
-    // Rebinding the VAO invalidates the vertex buffer bindings.
-    gpu.dirty.ResetVertexArrays();
-
     state.draw.vertex_array = vao_entry.handle;
     return vao_entry.handle;
 }
 
 void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
     auto& gpu = system.GPU().Maxwell3D();
-    if (!gpu.dirty.vertex_array_buffers)
-        return;
-    gpu.dirty.vertex_array_buffers = false;
-
     const auto& regs = gpu.regs;
 
     MICROPROFILE_SCOPE(OpenGL_VB);
 
     // Upload all guest vertex arrays sequentially to our buffer
     for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
-        if (!gpu.dirty.vertex_array[index])
-            continue;
-        gpu.dirty.vertex_array[index] = false;
-        gpu.dirty.vertex_instance[index] = false;
-
         const auto& vertex_array = regs.vertex_array[index];
         if (!vertex_array.IsEnabled())
             continue;
@@ -224,19 +207,10 @@ void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
 
 void RasterizerOpenGL::SetupVertexInstances(GLuint vao) {
     auto& gpu = system.GPU().Maxwell3D();
-
-    if (!gpu.dirty.vertex_instances)
-        return;
-    gpu.dirty.vertex_instances = false;
-
     const auto& regs = gpu.regs;
+
     // Upload all guest vertex arrays sequentially to our buffer
-    for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
-        if (!gpu.dirty.vertex_instance[index])
-            continue;
-
-        gpu.dirty.vertex_instance[index] = false;
-
+    for (u32 index = 0; index < 16; ++index) {
         if (regs.instanced_arrays.IsInstancingEnabled(index) &&
             regs.vertex_array[index].divisor != 0) {
             // Enable vertex buffer instancing with the specified divisor.
@@ -334,8 +308,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
     }
 
     SyncClipEnabled(clip_distances);
-
-    gpu.dirty.shaders = false;
 }
 
 std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -371,10 +343,6 @@ void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
 void RasterizerOpenGL::ConfigureFramebuffers() {
     MICROPROFILE_SCOPE(OpenGL_Framebuffer);
     auto& gpu = system.GPU().Maxwell3D();
-    if (!gpu.dirty.render_settings) {
-        return;
-    }
-    gpu.dirty.render_settings = false;
 
     texture_cache.GuardRenderTargets(true);
 
@@ -453,7 +421,6 @@ void RasterizerOpenGL::Clear() {
 
     OpenGLState prev_state{OpenGLState::GetCurState()};
     SCOPE_EXIT({
-        prev_state.AllDirty();
         prev_state.Apply();
     });
 
@@ -528,7 +495,6 @@ void RasterizerOpenGL::Clear() {
         clear_state.EmulateViewportWithScissor();
     }
 
-    clear_state.AllDirty();
     clear_state.Apply();
 
     if (use_color) {
@@ -631,12 +597,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
     bind_ubo_pushbuffer.Bind();
     bind_ssbo_pushbuffer.Bind();
 
-    if (invalidate) {
-        // As all cached buffers are invalidated, we need to recheck their state.
-        gpu.dirty.ResetVertexArrays();
-    }
-    gpu.dirty.memory_general = false;
-
     shader_program_manager->ApplyTo(state);
     state.Apply();
 
@@ -1084,14 +1044,8 @@ void RasterizerOpenGL::SyncDepthTestState() {
 
 void RasterizerOpenGL::SyncStencilTestState() {
     auto& maxwell3d = system.GPU().Maxwell3D();
-    if (!maxwell3d.dirty.stencil_test) {
-        return;
-    }
-    maxwell3d.dirty.stencil_test = false;
-
     const auto& regs = maxwell3d.regs;
     state.stencil.test_enabled = regs.stencil_enable != 0;
-    state.MarkDirtyStencilState();
 
     if (!regs.stencil_enable) {
         return;
@@ -1130,9 +1084,6 @@ void RasterizerOpenGL::SyncRasterizeEnable(OpenGLState& current_state) {
 
 void RasterizerOpenGL::SyncColorMask() {
     auto& maxwell3d = system.GPU().Maxwell3D();
-    if (!maxwell3d.dirty.color_mask) {
-        return;
-    }
     const auto& regs = maxwell3d.regs;
 
     const std::size_t count =
@@ -1145,9 +1096,6 @@ void RasterizerOpenGL::SyncColorMask() {
         dest.blue_enabled = (source.B == 0) ? GL_FALSE : GL_TRUE;
         dest.alpha_enabled = (source.A == 0) ? GL_FALSE : GL_TRUE;
     }
-
-    state.MarkDirtyColorMask();
-    maxwell3d.dirty.color_mask = false;
 }
 
 void RasterizerOpenGL::SyncMultiSampleState() {
@@ -1163,9 +1111,6 @@ void RasterizerOpenGL::SyncFragmentColorClampState() {
 
 void RasterizerOpenGL::SyncBlendState() {
     auto& maxwell3d = system.GPU().Maxwell3D();
-    if (!maxwell3d.dirty.blend_state) {
-        return;
-    }
     const auto& regs = maxwell3d.regs;
 
     state.blend_color.red = regs.blend_color.r;
@@ -1189,8 +1134,6 @@ void RasterizerOpenGL::SyncBlendState() {
         for (std::size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
             state.blend[i].enabled = false;
         }
-        maxwell3d.dirty.blend_state = false;
-        state.MarkDirtyBlendState();
         return;
     }
 
@@ -1207,9 +1150,6 @@ void RasterizerOpenGL::SyncBlendState() {
         blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
         blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
     }
-
-    state.MarkDirtyBlendState();
-    maxwell3d.dirty.blend_state = false;
 }
 
 void RasterizerOpenGL::SyncLogicOpState() {
@@ -1264,9 +1204,6 @@ void RasterizerOpenGL::SyncPointState() {
 
 void RasterizerOpenGL::SyncPolygonOffset() {
     auto& maxwell3d = system.GPU().Maxwell3D();
-    if (!maxwell3d.dirty.polygon_offset) {
-        return;
-    }
     const auto& regs = maxwell3d.regs;
 
     state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0;
@@ -1277,9 +1214,6 @@ void RasterizerOpenGL::SyncPolygonOffset() {
     state.polygon_offset.units = regs.polygon_offset_units / 2.0f;
     state.polygon_offset.factor = regs.polygon_offset_factor;
     state.polygon_offset.clamp = regs.polygon_offset_clamp;
-
-    state.MarkDirtyPolygonOffset();
-    maxwell3d.dirty.polygon_offset = false;
 }
 
 void RasterizerOpenGL::SyncAlphaTest() {
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 489eb143c0..bef141f63b 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -623,10 +623,6 @@ bool ShaderCacheOpenGL::GenerateUnspecializedShaders(
 }
 
 Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
-    if (!system.GPU().Maxwell3D().dirty.shaders) {
-        return last_shaders[static_cast<std::size_t>(program)];
-    }
-
     auto& memory_manager{system.GPU().MemoryManager()};
     const GPUVAddr address{GetShaderAddress(system, program)};
 
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 7d3bc1a1f3..732cb3a3c5 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -189,11 +189,6 @@ void OpenGLState::ApplyRasterizerDiscard() {
 }
 
 void OpenGLState::ApplyColorMask() {
-    if (!dirty.color_mask) {
-        return;
-    }
-    dirty.color_mask = false;
-
     for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
         const auto& updated = color_mask[i];
         auto& current = cur_state.color_mask[i];
@@ -232,11 +227,6 @@ void OpenGLState::ApplyPrimitiveRestart() {
 }
 
 void OpenGLState::ApplyStencilTest() {
-    if (!dirty.stencil_state) {
-        return;
-    }
-    dirty.stencil_state = false;
-
     Enable(GL_STENCIL_TEST, cur_state.stencil.test_enabled, stencil.test_enabled);
 
     const auto ConfigStencil = [](GLenum face, const auto& config, auto& current) {
@@ -351,11 +341,6 @@ void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) {
 }
 
 void OpenGLState::ApplyBlending() {
-    if (!dirty.blend_state) {
-        return;
-    }
-    dirty.blend_state = false;
-
     if (independant_blend.enabled) {
         const bool force = independant_blend.enabled != cur_state.independant_blend.enabled;
         for (std::size_t target = 0; target < Maxwell::NumRenderTargets; ++target) {
@@ -383,11 +368,6 @@ void OpenGLState::ApplyLogicOp() {
 }
 
 void OpenGLState::ApplyPolygonOffset() {
-    if (!dirty.polygon_offset) {
-        return;
-    }
-    dirty.polygon_offset = false;
-
     Enable(GL_POLYGON_OFFSET_FILL, cur_state.polygon_offset.fill_enable,
            polygon_offset.fill_enable);
     Enable(GL_POLYGON_OFFSET_LINE, cur_state.polygon_offset.line_enable,
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index bce662f2ce..5dda9e88f7 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -212,39 +212,8 @@ public:
     /// Viewport does not affects glClearBuffer so emulate viewport using scissor test
     void EmulateViewportWithScissor();
 
-    void MarkDirtyBlendState() {
-        dirty.blend_state = true;
-    }
-
-    void MarkDirtyStencilState() {
-        dirty.stencil_state = true;
-    }
-
-    void MarkDirtyPolygonOffset() {
-        dirty.polygon_offset = true;
-    }
-
-    void MarkDirtyColorMask() {
-        dirty.color_mask = true;
-    }
-
-    void AllDirty() {
-        dirty.blend_state = true;
-        dirty.stencil_state = true;
-        dirty.polygon_offset = true;
-        dirty.color_mask = true;
-    }
-
 private:
     static OpenGLState cur_state;
-
-    struct {
-        bool blend_state;
-        bool stencil_state;
-        bool viewport_state;
-        bool polygon_offset;
-        bool color_mask;
-    } dirty{};
 };
 static_assert(std::is_trivially_copyable_v<OpenGLState>);
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index cf934b0d87..942cc6c0a9 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -522,7 +522,6 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
 
     OpenGLState prev_state{OpenGLState::GetCurState()};
     SCOPE_EXIT({
-        prev_state.AllDirty();
         prev_state.Apply();
     });
 
@@ -530,7 +529,6 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     state.draw.read_framebuffer = src_framebuffer.handle;
     state.draw.draw_framebuffer = dst_framebuffer.handle;
     state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
-    state.AllDirty();
     state.Apply();
 
     u32 buffers{};
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index a4340b5029..f71e23f9e0 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -311,11 +311,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
         return;
     }
 
-    // Maintain the rasterizer's state as a priority
-    OpenGLState prev_state = OpenGLState::GetCurState();
-    state.AllDirty();
-    state.Apply();
-
     PrepareRendertarget(framebuffer);
     RenderScreenshot();
 
@@ -368,10 +363,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
         m_current_frame++;
         rasterizer->TickFrame();
     }
-
-    // Restore the rasterizer state
-    prev_state.AllDirty();
-    prev_state.Apply();
 }
 
 void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
@@ -445,7 +436,6 @@ void RendererOpenGL::InitOpenGLObjects() {
     // Link shaders and get variable locations
     shader.CreateFromSource(vertex_shader, nullptr, fragment_shader);
     state.draw.shader_program = shader.handle;
-    state.AllDirty();
     state.Apply();
 
     // Generate VBO handle for drawing
@@ -580,14 +570,12 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
 
     state.textures[0] = screen_info.display_texture;
     state.framebuffer_srgb.enabled = screen_info.display_srgb;
-    state.AllDirty();
     state.Apply();
     glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
     // Restore default state
     state.framebuffer_srgb.enabled = false;
     state.textures[0] = 0;
-    state.AllDirty();
     state.Apply();
 }
 
@@ -658,7 +646,6 @@ void RendererOpenGL::RenderScreenshot() {
     GLuint old_read_fb = state.draw.read_framebuffer;
     GLuint old_draw_fb = state.draw.draw_framebuffer;
     state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle;
-    state.AllDirty();
     state.Apply();
 
     Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
@@ -678,7 +665,6 @@ void RendererOpenGL::RenderScreenshot() {
     screenshot_framebuffer.Release();
     state.draw.read_framebuffer = old_read_fb;
     state.draw.draw_framebuffer = old_draw_fb;
-    state.AllDirty();
     state.Apply();
     glDeleteRenderbuffers(1, &renderbuffer);
 
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 7ddf7d3ee6..8186942da0 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -172,11 +172,6 @@ VKPipelineCache::~VKPipelineCache() = default;
 
 std::array<Shader, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
     const auto& gpu = system.GPU().Maxwell3D();
-    auto& dirty = system.GPU().Maxwell3D().dirty.shaders;
-    if (!dirty) {
-        return last_shaders;
-    }
-    dirty = false;
 
     std::array<Shader, Maxwell::MaxShaderProgram> shaders;
     for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 3bf86da876..b1be41a218 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -568,9 +568,7 @@ void RasterizerVulkan::FlushWork() {
 
 RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() {
     MICROPROFILE_SCOPE(Vulkan_RenderTargets);
-    auto& dirty = system.GPU().Maxwell3D().dirty;
-    const bool update_rendertargets = dirty.render_settings;
-    dirty.render_settings = false;
+    constexpr bool update_rendertargets = true;
 
     texture_cache.GuardRenderTargets(true);
 
@@ -973,10 +971,6 @@ void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const Ima
 }
 
 void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu) {
-    if (!gpu.dirty.viewport_transform && scheduler.TouchViewports()) {
-        return;
-    }
-    gpu.dirty.viewport_transform = false;
     const auto& regs = gpu.regs;
     const std::array viewports{
         GetViewportState(device, regs, 0),  GetViewportState(device, regs, 1),
@@ -993,10 +987,6 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu) {
 }
 
 void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu) {
-    if (!gpu.dirty.scissor_test && scheduler.TouchScissors()) {
-        return;
-    }
-    gpu.dirty.scissor_test = false;
     const auto& regs = gpu.regs;
     const std::array scissors = {
         GetScissorState(regs, 0),  GetScissorState(regs, 1),  GetScissorState(regs, 2),
@@ -1011,10 +1001,6 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu) {
 }
 
 void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D& gpu) {
-    if (!gpu.dirty.polygon_offset && scheduler.TouchDepthBias()) {
-        return;
-    }
-    gpu.dirty.polygon_offset = false;
     const auto& regs = gpu.regs;
     scheduler.Record([constant = regs.polygon_offset_units, clamp = regs.polygon_offset_clamp,
                       factor = regs.polygon_offset_factor](auto cmdbuf, auto& dld) {
@@ -1023,10 +1009,6 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D& gpu) {
 }
 
 void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D& gpu) {
-    if (!gpu.dirty.blend_state && scheduler.TouchBlendConstants()) {
-        return;
-    }
-    gpu.dirty.blend_state = false;
     const std::array blend_color = {gpu.regs.blend_color.r, gpu.regs.blend_color.g,
                                     gpu.regs.blend_color.b, gpu.regs.blend_color.a};
     scheduler.Record([blend_color](auto cmdbuf, auto& dld) {
@@ -1035,20 +1017,12 @@ void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D& gpu) {
 }
 
 void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D& gpu) {
-    if (!gpu.dirty.depth_bounds_values && scheduler.TouchDepthBounds()) {
-        return;
-    }
-    gpu.dirty.depth_bounds_values = false;
     const auto& regs = gpu.regs;
     scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]](
                          auto cmdbuf, auto& dld) { cmdbuf.setDepthBounds(min, max, dld); });
 }
 
 void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D& gpu) {
-    if (!gpu.dirty.stencil_test && scheduler.TouchStencilValues()) {
-        return;
-    }
-    gpu.dirty.stencil_test = false;
     const auto& regs = gpu.regs;
     if (regs.stencil_two_side_enable) {
         // Separate values per face
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index c70e4aec24..ec6dfa49eb 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -143,11 +143,6 @@ public:
         std::lock_guard lock{mutex};
         auto& maxwell3d = system.GPU().Maxwell3D();
 
-        if (!maxwell3d.dirty.depth_buffer) {
-            return depth_buffer.view;
-        }
-        maxwell3d.dirty.depth_buffer = false;
-
         const auto& regs{maxwell3d.regs};
         const auto gpu_addr{regs.zeta.Address()};
         if (!gpu_addr || !regs.zeta_enable) {
@@ -175,10 +170,6 @@ public:
         std::lock_guard lock{mutex};
         ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
         auto& maxwell3d = system.GPU().Maxwell3D();
-        if (!maxwell3d.dirty.render_target[index]) {
-            return render_targets[index].view;
-        }
-        maxwell3d.dirty.render_target[index] = false;
 
         const auto& regs{maxwell3d.regs};
         if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
@@ -319,16 +310,7 @@ protected:
     // and reading it from a separate buffer.
     virtual void BufferCopy(TSurface& src_surface, TSurface& dst_surface) = 0;
 
-    void ManageRenderTargetUnregister(TSurface& surface) {
-        auto& maxwell3d = system.GPU().Maxwell3D();
-        const u32 index = surface->GetRenderTarget();
-        if (index == DEPTH_RT) {
-            maxwell3d.dirty.depth_buffer = true;
-        } else {
-            maxwell3d.dirty.render_target[index] = true;
-        }
-        maxwell3d.dirty.render_settings = true;
-    }
+    void ManageRenderTargetUnregister([[maybe_unused]] TSurface& surface) {}
 
     void Register(TSurface surface) {
         const GPUVAddr gpu_addr = surface->GetGpuAddr();

From 1698143a1d3af8d743c4204007995f7b26251233 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 00:28:46 -0300
Subject: [PATCH 03/76] gl_rasterizer: Add OpenGL enable/disable helper

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 1d203fd08d..1fc05e0410 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -88,6 +88,10 @@ std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
     return buffer.size;
 }
 
+void oglEnable(GLenum cap, bool state) {
+    (state ? glEnable : glDisable)(cap);
+}
+
 } // Anonymous namespace
 
 RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,

From b95f064b51cf5638e356f455a834809a76778c89 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 01:27:43 -0300
Subject: [PATCH 04/76] gl_rasterizer: Add oglEnablei helper

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 1fc05e0410..7cd522da0f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -92,6 +92,10 @@ void oglEnable(GLenum cap, bool state) {
     (state ? glEnable : glDisable)(cap);
 }
 
+void oglEnablei(GLenum cap, bool state, GLuint index) {
+    (state ? glEnablei : glDisablei)(cap, index);
+}
+
 } // Anonymous namespace
 
 RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,

From d2d55542965e969a73ddd400e8290b5f4f4101a2 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 25 Dec 2019 19:30:05 -0300
Subject: [PATCH 05/76] gl_state: Remove point size tracking

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 10 ++++------
 src/video_core/renderer_opengl/gl_state.cpp      |  9 ---------
 src/video_core/renderer_opengl/gl_state.h        |  7 -------
 3 files changed, 4 insertions(+), 22 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 7cd522da0f..a0b0274fb7 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -428,9 +428,7 @@ void RasterizerOpenGL::Clear() {
     bool use_stencil{};
 
     OpenGLState prev_state{OpenGLState::GetCurState()};
-    SCOPE_EXIT({
-        prev_state.Apply();
-    });
+    SCOPE_EXIT({ prev_state.Apply(); });
 
     OpenGLState clear_state{OpenGLState::GetCurState()};
     clear_state.SetDefaultViewports();
@@ -1205,9 +1203,9 @@ void RasterizerOpenGL::SyncPointState() {
     const auto& regs = system.GPU().Maxwell3D().regs;
     // Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
     // in OpenGL).
-    state.point.program_control = regs.vp_point_size.enable != 0;
-    state.point.sprite = regs.point_sprite_enable != 0;
-    state.point.size = std::max(1.0f, regs.point_size);
+    oglEnable(GL_PROGRAM_POINT_SIZE, regs.vp_point_size.enable);
+    oglEnable(GL_POINT_SPRITE, regs.point_sprite_enable);
+    glPointSize(std::max(1.0f, regs.point_size));
 }
 
 void RasterizerOpenGL::SyncPolygonOffset() {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 732cb3a3c5..02b3455cc4 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -126,14 +126,6 @@ void OpenGLState::ApplyClipDistances() {
     }
 }
 
-void OpenGLState::ApplyPointSize() {
-    Enable(GL_PROGRAM_POINT_SIZE, cur_state.point.program_control, point.program_control);
-    Enable(GL_POINT_SPRITE, cur_state.point.sprite, point.sprite);
-    if (UpdateValue(cur_state.point.size, point.size)) {
-        glPointSize(point.size);
-    }
-}
-
 void OpenGLState::ApplyFragmentColorClamp() {
     if (UpdateValue(cur_state.fragment_color_clamp.enabled, fragment_color_clamp.enabled)) {
         glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
@@ -445,7 +437,6 @@ void OpenGLState::Apply() {
     ApplyShaderProgram();
     ApplyProgramPipeline();
     ApplyClipDistances();
-    ApplyPointSize();
     ApplyFragmentColorClamp();
     ApplyMultisample();
     ApplyRasterizerDiscard();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 5dda9e88f7..acd24c0420 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -130,12 +130,6 @@ public:
     };
     std::array<Viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
 
-    struct {
-        bool program_control = false; // GL_PROGRAM_POINT_SIZE
-        bool sprite = false;          // GL_POINT_SPRITE
-        GLfloat size = 1.0f;          // GL_POINT_SIZE
-    } point;
-
     struct {
         bool point_enable = false;
         bool line_enable = false;
@@ -176,7 +170,6 @@ public:
     void ApplyShaderProgram();
     void ApplyProgramPipeline();
     void ApplyClipDistances();
-    void ApplyPointSize();
     void ApplyFragmentColorClamp();
     void ApplyMultisample();
     void ApplySRgb();

From 925521da5fbc1096f56933eba1683b60c6c19b20 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 25 Dec 2019 19:46:04 -0300
Subject: [PATCH 06/76] gl_state: Remove front face tracking

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp   | 2 +-
 src/video_core/renderer_opengl/gl_state.cpp        | 4 ----
 src/video_core/renderer_opengl/gl_state.h          | 1 -
 src/video_core/renderer_opengl/renderer_opengl.cpp | 4 ++++
 4 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a0b0274fb7..e9e9f9794a 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1025,7 +1025,7 @@ void RasterizerOpenGL::SyncCullMode() {
         state.cull.mode = MaxwellToGL::CullFace(regs.cull.cull_face);
     }
 
-    state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face);
+    glFrontFace(MaxwellToGL::FrontFace(regs.cull.front_face));
 }
 
 void RasterizerOpenGL::SyncPrimitiveRestart() {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 02b3455cc4..8176765051 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -170,10 +170,6 @@ void OpenGLState::ApplyCulling() {
     if (UpdateValue(cur_state.cull.mode, cull.mode)) {
         glCullFace(cull.mode);
     }
-
-    if (UpdateValue(cur_state.cull.front_face, cull.front_face)) {
-        glFrontFace(cull.front_face);
-    }
 }
 
 void OpenGLState::ApplyRasterizerDiscard() {
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index acd24c0420..1b5fde23a6 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -34,7 +34,6 @@ public:
     struct {
         bool enabled = false;       // GL_CULL_FACE
         GLenum mode = GL_BACK;      // GL_CULL_FACE_MODE
-        GLenum front_face = GL_CCW; // GL_FRONT_FACE
     } cull;
 
     struct {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index f71e23f9e0..3110536953 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -571,6 +571,10 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
     state.textures[0] = screen_info.display_texture;
     state.framebuffer_srgb.enabled = screen_info.display_srgb;
     state.Apply();
+
+    // TODO: Signal state tracker about these changes
+    glFrontFace(GL_CW);
+
     glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
     // Restore default state

From c8f5f54a44e9873134fab74eef97e875b9e6c565 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 25 Dec 2019 19:56:17 -0300
Subject: [PATCH 07/76] gl_state: Remove cull mode tracking

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp   | 6 ++----
 src/video_core/renderer_opengl/gl_state.cpp        | 9 ---------
 src/video_core/renderer_opengl/gl_state.h          | 6 ------
 src/video_core/renderer_opengl/renderer_opengl.cpp | 2 ++
 4 files changed, 4 insertions(+), 19 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index e9e9f9794a..a080c3e81e 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1020,10 +1020,8 @@ void RasterizerOpenGL::SyncClipCoef() {
 void RasterizerOpenGL::SyncCullMode() {
     const auto& regs = system.GPU().Maxwell3D().regs;
 
-    state.cull.enabled = regs.cull.enabled != 0;
-    if (state.cull.enabled) {
-        state.cull.mode = MaxwellToGL::CullFace(regs.cull.cull_face);
-    }
+    oglEnable(GL_CULL_FACE, regs.cull.enabled);
+    glCullFace(MaxwellToGL::CullFace(regs.cull.cull_face));
 
     glFrontFace(MaxwellToGL::FrontFace(regs.cull.front_face));
 }
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 8176765051..08e86b5999 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -164,14 +164,6 @@ void OpenGLState::ApplySRgb() {
     }
 }
 
-void OpenGLState::ApplyCulling() {
-    Enable(GL_CULL_FACE, cur_state.cull.enabled, cull.enabled);
-
-    if (UpdateValue(cur_state.cull.mode, cull.mode)) {
-        glCullFace(cull.mode);
-    }
-}
-
 void OpenGLState::ApplyRasterizerDiscard() {
     Enable(GL_RASTERIZER_DISCARD, cur_state.rasterizer_discard, rasterizer_discard);
 }
@@ -441,7 +433,6 @@ void OpenGLState::Apply() {
     ApplyViewport();
     ApplyStencilTest();
     ApplySRgb();
-    ApplyCulling();
     ApplyDepth();
     ApplyPrimitiveRestart();
     ApplyBlending();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 1b5fde23a6..ae30b95655 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -31,11 +31,6 @@ public:
         bool near_plane = false;
     } depth_clamp; // GL_DEPTH_CLAMP
 
-    struct {
-        bool enabled = false;       // GL_CULL_FACE
-        GLenum mode = GL_BACK;      // GL_CULL_FACE_MODE
-    } cull;
-
     struct {
         bool test_enabled = false;      // GL_DEPTH_TEST
         GLboolean write_mask = GL_TRUE; // GL_DEPTH_WRITEMASK
@@ -172,7 +167,6 @@ public:
     void ApplyFragmentColorClamp();
     void ApplyMultisample();
     void ApplySRgb();
-    void ApplyCulling();
     void ApplyRasterizerDiscard();
     void ApplyColorMask();
     void ApplyDepth();
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 3110536953..3d6125dc1f 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -573,6 +573,8 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
     state.Apply();
 
     // TODO: Signal state tracker about these changes
+    glEnable(GL_CULL_FACE);
+    glCullFace(GL_BACK);
     glFrontFace(GL_CW);
 
     glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));

From f646321dd060572f94c2d4f2f3aa2c5307e21b14 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 25 Dec 2019 20:03:40 -0300
Subject: [PATCH 08/76] gl_state: Remove alpha test tracking

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp   | 8 +++-----
 src/video_core/renderer_opengl/gl_state.cpp        | 9 ---------
 src/video_core/renderer_opengl/gl_state.h          | 7 -------
 src/video_core/renderer_opengl/renderer_opengl.cpp | 1 +
 4 files changed, 4 insertions(+), 21 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a080c3e81e..d1034c2a22 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1225,12 +1225,10 @@ void RasterizerOpenGL::SyncAlphaTest() {
     UNIMPLEMENTED_IF_MSG(regs.alpha_test_enabled != 0 && regs.rt_control.count > 1,
                          "Alpha Testing is enabled with more than one rendertarget");
 
-    state.alpha_test.enabled = regs.alpha_test_enabled;
-    if (!state.alpha_test.enabled) {
-        return;
+    oglEnable(GL_ALPHA_TEST, regs.alpha_test_enabled);
+    if (regs.alpha_test_enabled) {
+        glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref);
     }
-    state.alpha_test.func = MaxwellToGL::ComparisonOp(regs.alpha_test_func);
-    state.alpha_test.ref = regs.alpha_test_ref;
 }
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 08e86b5999..59fd8e4219 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -368,14 +368,6 @@ void OpenGLState::ApplyPolygonOffset() {
     }
 }
 
-void OpenGLState::ApplyAlphaTest() {
-    Enable(GL_ALPHA_TEST, cur_state.alpha_test.enabled, alpha_test.enabled);
-    if (UpdateTie(std::tie(cur_state.alpha_test.func, cur_state.alpha_test.ref),
-                  std::tie(alpha_test.func, alpha_test.ref))) {
-        glAlphaFunc(alpha_test.func, alpha_test.ref);
-    }
-}
-
 void OpenGLState::ApplyClipControl() {
     if (UpdateTie(std::tie(cur_state.clip_control.origin, cur_state.clip_control.depth_mode),
                   std::tie(clip_control.origin, clip_control.depth_mode))) {
@@ -441,7 +433,6 @@ void OpenGLState::Apply() {
     ApplySamplers();
     ApplyImages();
     ApplyPolygonOffset();
-    ApplyAlphaTest();
     ApplyClipControl();
     ApplyRenderBuffer();
 }
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index ae30b95655..f0e02e717d 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -133,12 +133,6 @@ public:
         GLfloat clamp = 0.0f;
     } polygon_offset;
 
-    struct {
-        bool enabled = false;    // GL_ALPHA_TEST
-        GLenum func = GL_ALWAYS; // GL_ALPHA_TEST_FUNC
-        GLfloat ref = 0.0f;      // GL_ALPHA_TEST_REF
-    } alpha_test;
-
     std::array<bool, 8> clip_distance = {}; // GL_CLIP_DISTANCE
 
     struct {
@@ -182,7 +176,6 @@ public:
     void ApplyImages();
     void ApplyDepthClamp();
     void ApplyPolygonOffset();
-    void ApplyAlphaTest();
     void ApplyClipControl();
     void ApplyRenderBuffer();
 
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 3d6125dc1f..0879a5fb12 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -573,6 +573,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
     state.Apply();
 
     // TODO: Signal state tracker about these changes
+    glDisable(GL_ALPHA_TEST);
     glEnable(GL_CULL_FACE);
     glCullFace(GL_BACK);
     glFrontFace(GL_CW);

From a0321b984fc03610ad7b20d2ee345fd978103b19 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 00:25:53 -0300
Subject: [PATCH 09/76] gl_state: Remove polygon offset tracking

---
 .../renderer_opengl/gl_rasterizer.cpp         | 11 +++++-----
 src/video_core/renderer_opengl/gl_state.cpp   | 22 -------------------
 src/video_core/renderer_opengl/gl_state.h     | 10 ---------
 .../renderer_opengl/renderer_opengl.cpp       |  3 ++-
 4 files changed, 7 insertions(+), 39 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index d1034c2a22..744892618d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1210,14 +1210,13 @@ void RasterizerOpenGL::SyncPolygonOffset() {
     auto& maxwell3d = system.GPU().Maxwell3D();
     const auto& regs = maxwell3d.regs;
 
-    state.polygon_offset.fill_enable = regs.polygon_offset_fill_enable != 0;
-    state.polygon_offset.line_enable = regs.polygon_offset_line_enable != 0;
-    state.polygon_offset.point_enable = regs.polygon_offset_point_enable != 0;
+    oglEnable(GL_POLYGON_OFFSET_FILL, regs.polygon_offset_fill_enable);
+    oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable);
+    oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable);
 
     // Hardware divides polygon offset units by two
-    state.polygon_offset.units = regs.polygon_offset_units / 2.0f;
-    state.polygon_offset.factor = regs.polygon_offset_factor;
-    state.polygon_offset.clamp = regs.polygon_offset_clamp;
+    glPolygonOffsetClamp(regs.polygon_offset_factor, regs.polygon_offset_units / 2.0f,
+                         regs.polygon_offset_clamp);
 }
 
 void RasterizerOpenGL::SyncAlphaTest() {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 59fd8e4219..05c271ad2f 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -347,27 +347,6 @@ void OpenGLState::ApplyLogicOp() {
     }
 }
 
-void OpenGLState::ApplyPolygonOffset() {
-    Enable(GL_POLYGON_OFFSET_FILL, cur_state.polygon_offset.fill_enable,
-           polygon_offset.fill_enable);
-    Enable(GL_POLYGON_OFFSET_LINE, cur_state.polygon_offset.line_enable,
-           polygon_offset.line_enable);
-    Enable(GL_POLYGON_OFFSET_POINT, cur_state.polygon_offset.point_enable,
-           polygon_offset.point_enable);
-
-    if (UpdateTie(std::tie(cur_state.polygon_offset.factor, cur_state.polygon_offset.units,
-                           cur_state.polygon_offset.clamp),
-                  std::tie(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp))) {
-        if (GLAD_GL_EXT_polygon_offset_clamp && polygon_offset.clamp != 0) {
-            glPolygonOffsetClamp(polygon_offset.factor, polygon_offset.units, polygon_offset.clamp);
-        } else {
-            UNIMPLEMENTED_IF_MSG(polygon_offset.clamp != 0,
-                                 "Unimplemented Depth polygon offset clamp.");
-            glPolygonOffset(polygon_offset.factor, polygon_offset.units);
-        }
-    }
-}
-
 void OpenGLState::ApplyClipControl() {
     if (UpdateTie(std::tie(cur_state.clip_control.origin, cur_state.clip_control.depth_mode),
                   std::tie(clip_control.origin, clip_control.depth_mode))) {
@@ -432,7 +411,6 @@ void OpenGLState::Apply() {
     ApplyTextures();
     ApplySamplers();
     ApplyImages();
-    ApplyPolygonOffset();
     ApplyClipControl();
     ApplyRenderBuffer();
 }
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index f0e02e717d..71a2cad2ef 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -124,15 +124,6 @@ public:
     };
     std::array<Viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
 
-    struct {
-        bool point_enable = false;
-        bool line_enable = false;
-        bool fill_enable = false;
-        GLfloat units = 0.0f;
-        GLfloat factor = 0.0f;
-        GLfloat clamp = 0.0f;
-    } polygon_offset;
-
     std::array<bool, 8> clip_distance = {}; // GL_CLIP_DISTANCE
 
     struct {
@@ -175,7 +166,6 @@ public:
     void ApplySamplers();
     void ApplyImages();
     void ApplyDepthClamp();
-    void ApplyPolygonOffset();
     void ApplyClipControl();
     void ApplyRenderBuffer();
 
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 0879a5fb12..affc6137ac 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -573,8 +573,9 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
     state.Apply();
 
     // TODO: Signal state tracker about these changes
-    glDisable(GL_ALPHA_TEST);
     glEnable(GL_CULL_FACE);
+    glDisable(GL_ALPHA_TEST);
+    glDisable(GL_POLYGON_OFFSET_FILL);
     glCullFace(GL_BACK);
     glFrontFace(GL_CW);
 

From 915d73f3b8871fc31b9b6571d645ced6f766bc43 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 25 Dec 2019 20:13:43 -0300
Subject: [PATCH 10/76] gl_state: Remove blend color tracking

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 5 +----
 src/video_core/renderer_opengl/gl_state.cpp      | 7 -------
 src/video_core/renderer_opengl/gl_state.h        | 7 -------
 3 files changed, 1 insertion(+), 18 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 744892618d..5c5273b5d8 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1117,10 +1117,7 @@ void RasterizerOpenGL::SyncBlendState() {
     auto& maxwell3d = system.GPU().Maxwell3D();
     const auto& regs = maxwell3d.regs;
 
-    state.blend_color.red = regs.blend_color.r;
-    state.blend_color.green = regs.blend_color.g;
-    state.blend_color.blue = regs.blend_color.b;
-    state.blend_color.alpha = regs.blend_color.a;
+    glBlendColor(regs.blend_color.r, regs.blend_color.g, regs.blend_color.b, regs.blend_color.a);
 
     state.independant_blend.enabled = regs.independent_blend_enable;
     if (!state.independant_blend.enabled) {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 05c271ad2f..e8463da7e4 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -330,13 +330,6 @@ void OpenGLState::ApplyBlending() {
         ApplyGlobalBlending();
     }
     cur_state.independant_blend.enabled = independant_blend.enabled;
-
-    if (UpdateTie(
-            std::tie(cur_state.blend_color.red, cur_state.blend_color.green,
-                     cur_state.blend_color.blue, cur_state.blend_color.alpha),
-            std::tie(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha))) {
-        glBlendColor(blend_color.red, blend_color.green, blend_color.blue, blend_color.alpha);
-    }
 }
 
 void OpenGLState::ApplyLogicOp() {
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 71a2cad2ef..b06a88f013 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -81,13 +81,6 @@ public:
         bool enabled = false;
     } independant_blend;
 
-    struct {
-        GLclampf red = 0.0f;
-        GLclampf green = 0.0f;
-        GLclampf blue = 0.0f;
-        GLclampf alpha = 0.0f;
-    } blend_color; // GL_BLEND_COLOR
-
     struct {
         bool enabled = false; // GL_LOGIC_OP_MODE
         GLenum operation = GL_COPY;

From 42708c762edee4f5bf2fa1d15a4ab764525fb044 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 25 Dec 2019 20:21:53 -0300
Subject: [PATCH 11/76] gl_state: Remove logic op tracker

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp   | 13 ++++---------
 src/video_core/renderer_opengl/gl_state.cpp        |  9 ---------
 src/video_core/renderer_opengl/gl_state.h          |  6 ------
 src/video_core/renderer_opengl/renderer_opengl.cpp |  1 +
 4 files changed, 5 insertions(+), 24 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 5c5273b5d8..9d4b351fb4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1156,15 +1156,10 @@ void RasterizerOpenGL::SyncBlendState() {
 void RasterizerOpenGL::SyncLogicOpState() {
     const auto& regs = system.GPU().Maxwell3D().regs;
 
-    state.logic_op.enabled = regs.logic_op.enable != 0;
-
-    if (!state.logic_op.enabled)
-        return;
-
-    ASSERT_MSG(regs.blend.enable[0] == 0,
-               "Blending and logic op can't be enabled at the same time.");
-
-    state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation);
+    oglEnable(GL_COLOR_LOGIC_OP, regs.logic_op.enable);
+    if (regs.logic_op.enable) {
+        glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation));
+    }
 }
 
 void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index e8463da7e4..ddc534eb4c 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -332,14 +332,6 @@ void OpenGLState::ApplyBlending() {
     cur_state.independant_blend.enabled = independant_blend.enabled;
 }
 
-void OpenGLState::ApplyLogicOp() {
-    Enable(GL_COLOR_LOGIC_OP, cur_state.logic_op.enabled, logic_op.enabled);
-
-    if (UpdateValue(cur_state.logic_op.operation, logic_op.operation)) {
-        glLogicOp(logic_op.operation);
-    }
-}
-
 void OpenGLState::ApplyClipControl() {
     if (UpdateTie(std::tie(cur_state.clip_control.origin, cur_state.clip_control.depth_mode),
                   std::tie(clip_control.origin, clip_control.depth_mode))) {
@@ -400,7 +392,6 @@ void OpenGLState::Apply() {
     ApplyDepth();
     ApplyPrimitiveRestart();
     ApplyBlending();
-    ApplyLogicOp();
     ApplyTextures();
     ApplySamplers();
     ApplyImages();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index b06a88f013..958af57714 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -81,11 +81,6 @@ public:
         bool enabled = false;
     } independant_blend;
 
-    struct {
-        bool enabled = false; // GL_LOGIC_OP_MODE
-        GLenum operation = GL_COPY;
-    } logic_op;
-
     static constexpr std::size_t NumSamplers = 32 * 5;
     static constexpr std::size_t NumImages = 8 * 5;
     std::array<GLuint, NumSamplers> textures = {};
@@ -154,7 +149,6 @@ public:
     void ApplyTargetBlending(std::size_t target, bool force);
     void ApplyGlobalBlending();
     void ApplyBlending();
-    void ApplyLogicOp();
     void ApplyTextures();
     void ApplySamplers();
     void ApplyImages();
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index affc6137ac..104b11b0d0 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -574,6 +574,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
 
     // TODO: Signal state tracker about these changes
     glEnable(GL_CULL_FACE);
+    glDisable(GL_COLOR_LOGIC_OP);
     glDisable(GL_ALPHA_TEST);
     glDisable(GL_POLYGON_OFFSET_FILL);
     glCullFace(GL_BACK);

From 0f343d32c41efc411fddb1a66a9f11fb68eebf06 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 25 Dec 2019 21:00:38 -0300
Subject: [PATCH 12/76] gl_state: Remove primitive restart tracking

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp |  4 ++--
 src/video_core/renderer_opengl/gl_state.cpp      | 10 ----------
 src/video_core/renderer_opengl/gl_state.h        |  6 ------
 3 files changed, 2 insertions(+), 18 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 9d4b351fb4..975cd2f129 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1029,8 +1029,8 @@ void RasterizerOpenGL::SyncCullMode() {
 void RasterizerOpenGL::SyncPrimitiveRestart() {
     const auto& regs = system.GPU().Maxwell3D().regs;
 
-    state.primitive_restart.enabled = regs.primitive_restart.enabled;
-    state.primitive_restart.index = regs.primitive_restart.index;
+    oglEnable(GL_PRIMITIVE_RESTART, regs.primitive_restart.enabled);
+    glPrimitiveRestartIndex(regs.primitive_restart.index);
 }
 
 void RasterizerOpenGL::SyncDepthTestState() {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index ddc534eb4c..49a15f82ff 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -197,15 +197,6 @@ void OpenGLState::ApplyDepth() {
     }
 }
 
-void OpenGLState::ApplyPrimitiveRestart() {
-    Enable(GL_PRIMITIVE_RESTART, cur_state.primitive_restart.enabled, primitive_restart.enabled);
-
-    if (cur_state.primitive_restart.index != primitive_restart.index) {
-        cur_state.primitive_restart.index = primitive_restart.index;
-        glPrimitiveRestartIndex(primitive_restart.index);
-    }
-}
-
 void OpenGLState::ApplyStencilTest() {
     Enable(GL_STENCIL_TEST, cur_state.stencil.test_enabled, stencil.test_enabled);
 
@@ -390,7 +381,6 @@ void OpenGLState::Apply() {
     ApplyStencilTest();
     ApplySRgb();
     ApplyDepth();
-    ApplyPrimitiveRestart();
     ApplyBlending();
     ApplyTextures();
     ApplySamplers();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 958af57714..938222d385 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -37,11 +37,6 @@ public:
         GLenum test_func = GL_LESS;     // GL_DEPTH_FUNC
     } depth;
 
-    struct {
-        bool enabled = false;
-        GLuint index = 0;
-    } primitive_restart; // GL_PRIMITIVE_RESTART
-
     bool rasterizer_discard = false; // GL_RASTERIZER_DISCARD
 
     struct ColorMask {
@@ -143,7 +138,6 @@ public:
     void ApplyRasterizerDiscard();
     void ApplyColorMask();
     void ApplyDepth();
-    void ApplyPrimitiveRestart();
     void ApplyStencilTest();
     void ApplyViewport();
     void ApplyTargetBlending(std::size_t target, bool force);

From e1a16a52fa14835efe6ba9b7418be419cdc4e5d5 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 25 Dec 2019 21:52:39 -0300
Subject: [PATCH 13/76] gl_state: Remove depth tracking

---
 .../renderer_opengl/gl_rasterizer.cpp          | 18 ++++++------------
 src/video_core/renderer_opengl/gl_state.cpp    | 15 ---------------
 src/video_core/renderer_opengl/gl_state.h      |  7 -------
 .../renderer_opengl/renderer_opengl.cpp        |  1 +
 4 files changed, 7 insertions(+), 34 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 975cd2f129..6bb6f9f47f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -446,12 +446,8 @@ void RasterizerOpenGL::Clear() {
         ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
         use_depth = true;
 
-        // Always enable the depth write when clearing the depth buffer. The depth write mask is
-        // ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to
-        // true.
-        clear_state.depth.test_enabled = true;
-        clear_state.depth.test_func = GL_ALWAYS;
-        clear_state.depth.write_mask = GL_TRUE;
+        // TODO: Signal state tracker about these changes
+        glDepthMask(GL_TRUE);
     }
     if (regs.clear_buffers.S) {
         ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!");
@@ -1036,14 +1032,12 @@ void RasterizerOpenGL::SyncPrimitiveRestart() {
 void RasterizerOpenGL::SyncDepthTestState() {
     const auto& regs = system.GPU().Maxwell3D().regs;
 
-    state.depth.test_enabled = regs.depth_test_enable != 0;
-    state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE;
+    glDepthMask(regs.depth_write_enabled ? GL_TRUE : GL_FALSE);
 
-    if (!state.depth.test_enabled) {
-        return;
+    oglEnable(GL_DEPTH_TEST, regs.depth_test_enable);
+    if (regs.depth_test_enable) {
+        glDepthFunc(MaxwellToGL::ComparisonOp(regs.depth_test_func));
     }
-
-    state.depth.test_func = MaxwellToGL::ComparisonOp(regs.depth_test_func);
 }
 
 void RasterizerOpenGL::SyncStencilTestState() {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 49a15f82ff..45fa3042de 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -183,20 +183,6 @@ void OpenGLState::ApplyColorMask() {
     }
 }
 
-void OpenGLState::ApplyDepth() {
-    Enable(GL_DEPTH_TEST, cur_state.depth.test_enabled, depth.test_enabled);
-
-    if (cur_state.depth.test_func != depth.test_func) {
-        cur_state.depth.test_func = depth.test_func;
-        glDepthFunc(depth.test_func);
-    }
-
-    if (cur_state.depth.write_mask != depth.write_mask) {
-        cur_state.depth.write_mask = depth.write_mask;
-        glDepthMask(depth.write_mask);
-    }
-}
-
 void OpenGLState::ApplyStencilTest() {
     Enable(GL_STENCIL_TEST, cur_state.stencil.test_enabled, stencil.test_enabled);
 
@@ -380,7 +366,6 @@ void OpenGLState::Apply() {
     ApplyViewport();
     ApplyStencilTest();
     ApplySRgb();
-    ApplyDepth();
     ApplyBlending();
     ApplyTextures();
     ApplySamplers();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 938222d385..036eeae97d 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -31,12 +31,6 @@ public:
         bool near_plane = false;
     } depth_clamp; // GL_DEPTH_CLAMP
 
-    struct {
-        bool test_enabled = false;      // GL_DEPTH_TEST
-        GLboolean write_mask = GL_TRUE; // GL_DEPTH_WRITEMASK
-        GLenum test_func = GL_LESS;     // GL_DEPTH_FUNC
-    } depth;
-
     bool rasterizer_discard = false; // GL_RASTERIZER_DISCARD
 
     struct ColorMask {
@@ -137,7 +131,6 @@ public:
     void ApplySRgb();
     void ApplyRasterizerDiscard();
     void ApplyColorMask();
-    void ApplyDepth();
     void ApplyStencilTest();
     void ApplyViewport();
     void ApplyTargetBlending(std::size_t target, bool force);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 104b11b0d0..9cd67e05e7 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -576,6 +576,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
     glEnable(GL_CULL_FACE);
     glDisable(GL_COLOR_LOGIC_OP);
     glDisable(GL_ALPHA_TEST);
+    glDisable(GL_DEPTH_TEST);
     glDisable(GL_POLYGON_OFFSET_FILL);
     glCullFace(GL_BACK);
     glFrontFace(GL_CW);

From 2a662fea363027817a73a62a5e4a9d0066fb43ee Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 25 Dec 2019 21:57:10 -0300
Subject: [PATCH 14/76] gl_state: Remove depth clamp tracking

---
 .../renderer_opengl/gl_rasterizer.cpp           | 12 ++++++++++--
 src/video_core/renderer_opengl/gl_rasterizer.h  |  3 +++
 src/video_core/renderer_opengl/gl_state.cpp     | 17 -----------------
 src/video_core/renderer_opengl/gl_state.h       |  6 ------
 4 files changed, 13 insertions(+), 25 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 6bb6f9f47f..acdae849c9 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -977,8 +977,6 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
         viewport.depth_range_far = src.depth_range_far;
         viewport.depth_range_near = src.depth_range_near;
     }
-    state.depth_clamp.far_plane = regs.view_volume_clip_control.depth_clamp_far != 0;
-    state.depth_clamp.near_plane = regs.view_volume_clip_control.depth_clamp_near != 0;
 
     bool flip_y = false;
     if (regs.viewport_transform[0].scale_y < 0.0) {
@@ -994,6 +992,16 @@ void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
             : GL_NEGATIVE_ONE_TO_ONE;
 }
 
+void RasterizerOpenGL::SyncDepthClamp() {
+    const auto& regs = system.GPU().Maxwell3D().regs;
+    const auto& state = regs.view_volume_clip_control;
+
+    UNIMPLEMENTED_IF_MSG(state.depth_clamp_far != state.depth_clamp_near,
+                         "Unimplemented Depth clamp separation!");
+
+    oglEnable(GL_DEPTH_CLAMP, state.depth_clamp_far || state.depth_clamp_near);
+}
+
 void RasterizerOpenGL::SyncClipEnabled(
     const std::array<bool, Maxwell::Regs::NumClipDistances>& clip_mask) {
 
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 68abe9a216..8afc3c2053 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -132,6 +132,9 @@ private:
     /// Syncs the viewport and depth range to match the guest state
     void SyncViewport(OpenGLState& current_state);
 
+    /// Syncs the depth clamp state
+    void SyncDepthClamp();
+
     /// Syncs the clip enabled status to match the guest state
     void SyncClipEnabled(
         const std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances>& clip_mask);
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 45fa3042de..6b5eea342a 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -87,9 +87,6 @@ OpenGLState::OpenGLState() = default;
 
 void OpenGLState::SetDefaultViewports() {
     viewports.fill(Viewport{});
-
-    depth_clamp.far_plane = false;
-    depth_clamp.near_plane = false;
 }
 
 void OpenGLState::ApplyFramebufferState() {
@@ -140,19 +137,6 @@ void OpenGLState::ApplyMultisample() {
            multisample_control.alpha_to_one);
 }
 
-void OpenGLState::ApplyDepthClamp() {
-    if (depth_clamp.far_plane == cur_state.depth_clamp.far_plane &&
-        depth_clamp.near_plane == cur_state.depth_clamp.near_plane) {
-        return;
-    }
-    cur_state.depth_clamp = depth_clamp;
-
-    UNIMPLEMENTED_IF_MSG(depth_clamp.far_plane != depth_clamp.near_plane,
-                         "Unimplemented Depth Clamp Separation!");
-
-    Enable(GL_DEPTH_CLAMP, depth_clamp.far_plane || depth_clamp.near_plane);
-}
-
 void OpenGLState::ApplySRgb() {
     if (cur_state.framebuffer_srgb.enabled == framebuffer_srgb.enabled)
         return;
@@ -362,7 +346,6 @@ void OpenGLState::Apply() {
     ApplyMultisample();
     ApplyRasterizerDiscard();
     ApplyColorMask();
-    ApplyDepthClamp();
     ApplyViewport();
     ApplyStencilTest();
     ApplySRgb();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 036eeae97d..366753714c 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -26,11 +26,6 @@ public:
         bool enabled = false; // GL_CLAMP_FRAGMENT_COLOR_ARB
     } fragment_color_clamp;
 
-    struct {
-        bool far_plane = false;
-        bool near_plane = false;
-    } depth_clamp; // GL_DEPTH_CLAMP
-
     bool rasterizer_discard = false; // GL_RASTERIZER_DISCARD
 
     struct ColorMask {
@@ -139,7 +134,6 @@ public:
     void ApplyTextures();
     void ApplySamplers();
     void ApplyImages();
-    void ApplyDepthClamp();
     void ApplyClipControl();
     void ApplyRenderBuffer();
 

From d5ab0358b64266be928a15265c4071744eed061e Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 00:16:52 -0300
Subject: [PATCH 15/76] gl_state: Remove VAO cache and tracking

---
 .../renderer_opengl/gl_rasterizer.cpp         | 98 +++++++------------
 .../renderer_opengl/gl_rasterizer.h           | 12 +--
 .../renderer_opengl/gl_resource_manager.cpp   | 18 ----
 .../renderer_opengl/gl_resource_manager.h     | 25 -----
 src/video_core/renderer_opengl/gl_state.cpp   | 14 ---
 src/video_core/renderer_opengl/gl_state.h     |  3 -
 .../renderer_opengl/renderer_opengl.cpp       | 22 ++---
 .../renderer_opengl/renderer_opengl.h         |  1 -
 src/video_core/renderer_opengl/utils.cpp      |  8 +-
 src/video_core/renderer_opengl/utils.h        |  3 +-
 10 files changed, 52 insertions(+), 152 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index acdae849c9..9658d379c0 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -107,7 +107,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWind
     state.draw.shader_program = 0;
     state.Apply();
 
-    LOG_DEBUG(Render_OpenGL, "Sync fixed function OpenGL state here");
     CheckExtensions();
 }
 
@@ -121,66 +120,41 @@ void RasterizerOpenGL::CheckExtensions() {
     }
 }
 
-GLuint RasterizerOpenGL::SetupVertexFormat() {
+void RasterizerOpenGL::SetupVertexFormat() {
     auto& gpu = system.GPU().Maxwell3D();
     const auto& regs = gpu.regs;
 
     MICROPROFILE_SCOPE(OpenGL_VAO);
 
-    auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format);
-    auto& vao_entry = iter->second;
+    // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL. Enables
+    // the first 16 vertex attributes always, as we don't know which ones are actually used until
+    // shader time. Note, Tegra technically supports 32, but we're capping this to 16 for now to
+    // avoid OpenGL errors.
+    // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
+    // assume every shader uses them all.
+    for (u32 index = 0; index < 16; ++index) {
+        const auto& attrib = regs.vertex_attrib_format[index];
 
-    if (is_cache_miss) {
-        vao_entry.Create();
-        const GLuint vao = vao_entry.handle;
-
-        // Eventhough we are using DSA to create this vertex array, there is a bug on Intel's blob
-        // that fails to properly create the vertex array if it's not bound even after creating it
-        // with glCreateVertexArrays
-        state.draw.vertex_array = vao;
-        state.ApplyVertexArrayState();
-
-        // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL.
-        // Enables the first 16 vertex attributes always, as we don't know which ones are actually
-        // used until shader time. Note, Tegra technically supports 32, but we're capping this to 16
-        // for now to avoid OpenGL errors.
-        // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
-        // assume every shader uses them all.
-        for (u32 index = 0; index < 16; ++index) {
-            const auto& attrib = regs.vertex_attrib_format[index];
-
-            // Ignore invalid attributes.
-            if (!attrib.IsValid())
-                continue;
-
-            const auto& buffer = regs.vertex_array[attrib.buffer];
-            LOG_TRACE(Render_OpenGL,
-                      "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}",
-                      index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(),
-                      attrib.offset.Value(), attrib.IsNormalized());
-
-            ASSERT(buffer.IsEnabled());
-
-            glEnableVertexArrayAttrib(vao, index);
-            if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
-                attrib.type ==
-                    Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
-                glVertexArrayAttribIFormat(vao, index, attrib.ComponentCount(),
-                                           MaxwellToGL::VertexType(attrib), attrib.offset);
-            } else {
-                glVertexArrayAttribFormat(
-                    vao, index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
-                    attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
-            }
-            glVertexArrayAttribBinding(vao, index, attrib.buffer);
+        // Ignore invalid attributes.
+        if (!attrib.IsValid()) {
+            glDisableVertexAttribArray(index);
+            continue;
         }
-    }
+        glEnableVertexAttribArray(index);
 
-    state.draw.vertex_array = vao_entry.handle;
-    return vao_entry.handle;
+        if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt ||
+            attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) {
+            glVertexAttribIFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
+                                  attrib.offset);
+        } else {
+            glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
+                                 attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
+        }
+        glVertexAttribBinding(index, attrib.buffer);
+    }
 }
 
-void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
+void RasterizerOpenGL::SetupVertexBuffer() {
     auto& gpu = system.GPU().Maxwell3D();
     const auto& regs = gpu.regs;
 
@@ -189,8 +163,9 @@ void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
     // Upload all guest vertex arrays sequentially to our buffer
     for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
         const auto& vertex_array = regs.vertex_array[index];
-        if (!vertex_array.IsEnabled())
+        if (!vertex_array.IsEnabled()) {
             continue;
+        }
 
         const GPUVAddr start = vertex_array.StartAddress();
         const GPUVAddr end = regs.vertex_array_limit[index].LimitAddress();
@@ -205,15 +180,15 @@ void RasterizerOpenGL::SetupVertexBuffer(GLuint vao) {
 
         if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
             // Enable vertex buffer instancing with the specified divisor.
-            glVertexArrayBindingDivisor(vao, index, vertex_array.divisor);
+            glVertexBindingDivisor(index, vertex_array.divisor);
         } else {
             // Disable the vertex buffer instancing.
-            glVertexArrayBindingDivisor(vao, index, 0);
+            glVertexBindingDivisor(index, 0);
         }
     }
 }
 
-void RasterizerOpenGL::SetupVertexInstances(GLuint vao) {
+void RasterizerOpenGL::SetupVertexInstances() {
     auto& gpu = system.GPU().Maxwell3D();
     const auto& regs = gpu.regs;
 
@@ -222,10 +197,10 @@ void RasterizerOpenGL::SetupVertexInstances(GLuint vao) {
         if (regs.instanced_arrays.IsInstancingEnabled(index) &&
             regs.vertex_array[index].divisor != 0) {
             // Enable vertex buffer instancing with the specified divisor.
-            glVertexArrayBindingDivisor(vao, index, regs.vertex_array[index].divisor);
+            glVertexBindingDivisor(index, regs.vertex_array[index].divisor);
         } else {
             // Disable the vertex buffer instancing.
-            glVertexArrayBindingDivisor(vao, index, 0);
+            glVertexBindingDivisor(index, 0);
         }
     }
 }
@@ -559,13 +534,12 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
     buffer_cache.Map(buffer_size);
 
     // Prepare vertex array format.
-    const GLuint vao = SetupVertexFormat();
-    vertex_array_pushbuffer.Setup(vao);
+    SetupVertexFormat();
+    vertex_array_pushbuffer.Setup();
 
     // Upload vertex and index data.
-    SetupVertexBuffer(vao);
-    SetupVertexInstances(vao);
-
+    SetupVertexBuffer();
+    SetupVertexInstances();
     GLintptr index_buffer_offset;
     if (is_indexed) {
         index_buffer_offset = SetupIndexBuffer();
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 8afc3c2053..b97f9f5186 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -194,11 +194,11 @@ private:
 
     std::size_t CalculateIndexBufferSize() const;
 
-    /// Updates and returns a vertex array object representing current vertex format
-    GLuint SetupVertexFormat();
+    /// Updates the current vertex format
+    void SetupVertexFormat();
 
-    void SetupVertexBuffer(GLuint vao);
-    void SetupVertexInstances(GLuint vao);
+    void SetupVertexBuffer();
+    void SetupVertexInstances();
 
     GLintptr SetupIndexBuffer();
 
@@ -217,10 +217,6 @@ private:
     ScreenInfo& screen_info;
 
     std::unique_ptr<GLShader::ProgramManager> shader_program_manager;
-    std::map<std::array<Tegra::Engines::Maxwell3D::Regs::VertexAttribute,
-                        Tegra::Engines::Maxwell3D::Regs::NumVertexAttributes>,
-             OGLVertexArray>
-        vertex_array_cache;
 
     static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
     OGLBufferCache buffer_cache;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index c0aee770fe..00355c1daa 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -189,24 +189,6 @@ void OGLSync::Release() {
     handle = 0;
 }
 
-void OGLVertexArray::Create() {
-    if (handle != 0)
-        return;
-
-    MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
-    glCreateVertexArrays(1, &handle);
-}
-
-void OGLVertexArray::Release() {
-    if (handle == 0)
-        return;
-
-    MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
-    glDeleteVertexArrays(1, &handle);
-    OpenGLState::GetCurState().ResetVertexArray(handle).Apply();
-    handle = 0;
-}
-
 void OGLFramebuffer::Create() {
     if (handle != 0)
         return;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index 995a4e45e9..de93f42122 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -241,31 +241,6 @@ public:
     GLsync handle = 0;
 };
 
-class OGLVertexArray : private NonCopyable {
-public:
-    OGLVertexArray() = default;
-
-    OGLVertexArray(OGLVertexArray&& o) noexcept : handle(std::exchange(o.handle, 0)) {}
-
-    ~OGLVertexArray() {
-        Release();
-    }
-
-    OGLVertexArray& operator=(OGLVertexArray&& o) noexcept {
-        Release();
-        handle = std::exchange(o.handle, 0);
-        return *this;
-    }
-
-    /// Creates a new internal OpenGL resource and stores the handle
-    void Create();
-
-    /// Deletes the internal OpenGL resource
-    void Release();
-
-    GLuint handle = 0;
-};
-
 class OGLFramebuffer : private NonCopyable {
 public:
     OGLFramebuffer() = default;
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 6b5eea342a..1c39e7fba7 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -98,12 +98,6 @@ void OpenGLState::ApplyFramebufferState() {
     }
 }
 
-void OpenGLState::ApplyVertexArrayState() {
-    if (UpdateValue(cur_state.draw.vertex_array, draw.vertex_array)) {
-        glBindVertexArray(draw.vertex_array);
-    }
-}
-
 void OpenGLState::ApplyShaderProgram() {
     if (UpdateValue(cur_state.draw.shader_program, draw.shader_program)) {
         glUseProgram(draw.shader_program);
@@ -338,7 +332,6 @@ void OpenGLState::ApplyImages() {
 void OpenGLState::Apply() {
     MICROPROFILE_SCOPE(OpenGL_State);
     ApplyFramebufferState();
-    ApplyVertexArrayState();
     ApplyShaderProgram();
     ApplyProgramPipeline();
     ApplyClipDistances();
@@ -411,13 +404,6 @@ OpenGLState& OpenGLState::ResetPipeline(GLuint handle) {
     return *this;
 }
 
-OpenGLState& OpenGLState::ResetVertexArray(GLuint handle) {
-    if (draw.vertex_array == handle) {
-        draw.vertex_array = 0;
-    }
-    return *this;
-}
-
 OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) {
     if (draw.read_framebuffer == handle) {
         draw.read_framebuffer = 0;
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 366753714c..f7c722b363 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -74,7 +74,6 @@ public:
     struct {
         GLuint read_framebuffer = 0; // GL_READ_FRAMEBUFFER_BINDING
         GLuint draw_framebuffer = 0; // GL_DRAW_FRAMEBUFFER_BINDING
-        GLuint vertex_array = 0;     // GL_VERTEX_ARRAY_BINDING
         GLuint shader_program = 0;   // GL_CURRENT_PROGRAM
         GLuint program_pipeline = 0; // GL_PROGRAM_PIPELINE_BINDING
     } draw;
@@ -117,7 +116,6 @@ public:
     void Apply();
 
     void ApplyFramebufferState();
-    void ApplyVertexArrayState();
     void ApplyShaderProgram();
     void ApplyProgramPipeline();
     void ApplyClipDistances();
@@ -142,7 +140,6 @@ public:
     OpenGLState& ResetSampler(GLuint handle);
     OpenGLState& ResetProgram(GLuint handle);
     OpenGLState& ResetPipeline(GLuint handle);
-    OpenGLState& ResetVertexArray(GLuint handle);
     OpenGLState& ResetFramebuffer(GLuint handle);
     OpenGLState& ResetRenderbuffer(GLuint handle);
 
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 9cd67e05e7..2fb5938e2b 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -441,22 +441,8 @@ void RendererOpenGL::InitOpenGLObjects() {
     // Generate VBO handle for drawing
     vertex_buffer.Create();
 
-    // Generate VAO
-    vertex_array.Create();
-    state.draw.vertex_array = vertex_array.handle;
-
     // Attach vertex data to VAO
     glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW);
-    glVertexArrayAttribFormat(vertex_array.handle, PositionLocation, 2, GL_FLOAT, GL_FALSE,
-                              offsetof(ScreenRectVertex, position));
-    glVertexArrayAttribFormat(vertex_array.handle, TexCoordLocation, 2, GL_FLOAT, GL_FALSE,
-                              offsetof(ScreenRectVertex, tex_coord));
-    glVertexArrayAttribBinding(vertex_array.handle, PositionLocation, 0);
-    glVertexArrayAttribBinding(vertex_array.handle, TexCoordLocation, 0);
-    glEnableVertexArrayAttrib(vertex_array.handle, PositionLocation);
-    glEnableVertexArrayAttrib(vertex_array.handle, TexCoordLocation);
-    glVertexArrayVertexBuffer(vertex_array.handle, 0, vertex_buffer.handle, 0,
-                              sizeof(ScreenRectVertex));
 
     // Allocate textures for the screen
     screen_info.texture.resource.Create(GL_TEXTURE_2D);
@@ -581,6 +567,14 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
     glCullFace(GL_BACK);
     glFrontFace(GL_CW);
 
+    glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
+                         offsetof(ScreenRectVertex, position));
+    glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE,
+                         offsetof(ScreenRectVertex, tex_coord));
+    glVertexAttribBinding(PositionLocation, 0);
+    glVertexAttribBinding(TexCoordLocation, 0);
+    glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
+
     glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
     // Restore default state
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index d45e69cbca..978a4d0eb6 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -96,7 +96,6 @@ private:
     OpenGLState state;
 
     // OpenGL object IDs
-    OGLVertexArray vertex_array;
     OGLBuffer vertex_buffer;
     OGLProgram shader;
     OGLFramebuffer screenshot_framebuffer;
diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp
index ac99e63853..f2aaf06dbb 100644
--- a/src/video_core/renderer_opengl/utils.cpp
+++ b/src/video_core/renderer_opengl/utils.cpp
@@ -24,8 +24,7 @@ VertexArrayPushBuffer::VertexArrayPushBuffer() = default;
 
 VertexArrayPushBuffer::~VertexArrayPushBuffer() = default;
 
-void VertexArrayPushBuffer::Setup(GLuint vao_) {
-    vao = vao_;
+void VertexArrayPushBuffer::Setup() {
     index_buffer = nullptr;
     vertex_buffers.clear();
 }
@@ -41,13 +40,12 @@ void VertexArrayPushBuffer::SetVertexBuffer(GLuint binding_index, const GLuint*
 
 void VertexArrayPushBuffer::Bind() {
     if (index_buffer) {
-        glVertexArrayElementBuffer(vao, *index_buffer);
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *index_buffer);
     }
 
     // TODO(Rodrigo): Find a way to ARB_multi_bind this
     for (const auto& entry : vertex_buffers) {
-        glVertexArrayVertexBuffer(vao, entry.binding_index, *entry.buffer, entry.offset,
-                                  entry.stride);
+        glBindVertexBuffer(entry.binding_index, *entry.buffer, entry.offset, entry.stride);
     }
 }
 
diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h
index 3ad7c02d4b..e8612a9ecc 100644
--- a/src/video_core/renderer_opengl/utils.h
+++ b/src/video_core/renderer_opengl/utils.h
@@ -16,7 +16,7 @@ public:
     explicit VertexArrayPushBuffer();
     ~VertexArrayPushBuffer();
 
-    void Setup(GLuint vao_);
+    void Setup();
 
     void SetIndexBuffer(const GLuint* buffer);
 
@@ -28,7 +28,6 @@ public:
 private:
     struct Entry;
 
-    GLuint vao{};
     const GLuint* index_buffer{};
     std::vector<Entry> vertex_buffers;
 };

From 04d11341911a0c9ed3558fc6084d305ae262399a Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 01:01:41 -0300
Subject: [PATCH 16/76] gl_state: Remove framebuffer sRGB tracking

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp    | 10 +++++++++-
 src/video_core/renderer_opengl/gl_rasterizer.h      |  3 +++
 src/video_core/renderer_opengl/gl_state.cpp         | 12 ------------
 src/video_core/renderer_opengl/gl_state.h           |  5 -----
 src/video_core/renderer_opengl/gl_texture_cache.cpp |  8 +++++++-
 src/video_core/renderer_opengl/renderer_opengl.cpp  |  8 ++++++--
 6 files changed, 25 insertions(+), 21 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 9658d379c0..f5aa84a169 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -332,7 +332,6 @@ void RasterizerOpenGL::ConfigureFramebuffers() {
     View depth_surface = texture_cache.GetDepthBufferSurface(true);
 
     const auto& regs = gpu.regs;
-    state.framebuffer_srgb.enabled = regs.framebuffer_srgb != 0;
     UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0);
 
     // Bind the framebuffer surfaces
@@ -455,6 +454,9 @@ void RasterizerOpenGL::Clear() {
         }
     }
 
+    // TODO: Signal state tracker about these changes
+    SyncFramebufferSRGB();
+
     if (!use_color && !use_depth && !use_stencil) {
         // No color surface nor depth/stencil surface are enabled
         return;
@@ -511,6 +513,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
     SyncPointState();
     SyncPolygonOffset();
     SyncAlphaTest();
+    SyncFramebufferSRGB();
 
     buffer_cache.Acquire();
 
@@ -1198,4 +1201,9 @@ void RasterizerOpenGL::SyncAlphaTest() {
     }
 }
 
+void RasterizerOpenGL::SyncFramebufferSRGB() {
+    const auto& regs = system.GPU().Maxwell3D().regs;
+    oglEnable(GL_FRAMEBUFFER_SRGB, regs.framebuffer_srgb);
+}
+
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index b97f9f5186..91179323dc 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -187,6 +187,9 @@ private:
     /// Syncs the alpha test state to match the guest state
     void SyncAlphaTest();
 
+    /// Syncs the framebuffer sRGB state to match the guest state
+    void SyncFramebufferSRGB();
+
     /// Check for extension that are not strictly required but are needed for correct emulation
     void CheckExtensions();
 
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 1c39e7fba7..e67db758d5 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -131,17 +131,6 @@ void OpenGLState::ApplyMultisample() {
            multisample_control.alpha_to_one);
 }
 
-void OpenGLState::ApplySRgb() {
-    if (cur_state.framebuffer_srgb.enabled == framebuffer_srgb.enabled)
-        return;
-    cur_state.framebuffer_srgb.enabled = framebuffer_srgb.enabled;
-    if (framebuffer_srgb.enabled) {
-        glEnable(GL_FRAMEBUFFER_SRGB);
-    } else {
-        glDisable(GL_FRAMEBUFFER_SRGB);
-    }
-}
-
 void OpenGLState::ApplyRasterizerDiscard() {
     Enable(GL_RASTERIZER_DISCARD, cur_state.rasterizer_discard, rasterizer_discard);
 }
@@ -341,7 +330,6 @@ void OpenGLState::Apply() {
     ApplyColorMask();
     ApplyViewport();
     ApplyStencilTest();
-    ApplySRgb();
     ApplyBlending();
     ApplyTextures();
     ApplySamplers();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index f7c722b363..6c5126687e 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -13,10 +13,6 @@ namespace OpenGL {
 
 class OpenGLState {
 public:
-    struct {
-        bool enabled = false; // GL_FRAMEBUFFER_SRGB
-    } framebuffer_srgb;
-
     struct {
         bool alpha_to_coverage = false; // GL_ALPHA_TO_COVERAGE
         bool alpha_to_one = false;      // GL_ALPHA_TO_ONE
@@ -121,7 +117,6 @@ public:
     void ApplyClipDistances();
     void ApplyFragmentColorClamp();
     void ApplyMultisample();
-    void ApplySRgb();
     void ApplyRasterizerDiscard();
     void ApplyColorMask();
     void ApplyStencilTest();
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 942cc6c0a9..0bdbb70a4d 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -528,9 +528,15 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     OpenGLState state;
     state.draw.read_framebuffer = src_framebuffer.handle;
     state.draw.draw_framebuffer = dst_framebuffer.handle;
-    state.framebuffer_srgb.enabled = dst_params.srgb_conversion;
     state.Apply();
 
+    // TODO: Signal state tracker about these changes
+    if (dst_params.srgb_conversion) {
+        glEnable(GL_FRAMEBUFFER_SRGB);
+    } else {
+        glDisable(GL_FRAMEBUFFER_SRGB);
+    }
+
     u32 buffers{};
 
     UNIMPLEMENTED_IF(src_params.target == SurfaceTarget::Texture3D);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 2fb5938e2b..4d41ac7b4d 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -555,11 +555,15 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
     };
 
     state.textures[0] = screen_info.display_texture;
-    state.framebuffer_srgb.enabled = screen_info.display_srgb;
     state.Apply();
 
     // TODO: Signal state tracker about these changes
     glEnable(GL_CULL_FACE);
+    if (screen_info.display_srgb) {
+        glEnable(GL_FRAMEBUFFER_SRGB);
+    } else {
+        glDisable(GL_FRAMEBUFFER_SRGB);
+    }
     glDisable(GL_COLOR_LOGIC_OP);
     glDisable(GL_ALPHA_TEST);
     glDisable(GL_DEPTH_TEST);
@@ -577,8 +581,8 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
 
     glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
     // Restore default state
-    state.framebuffer_srgb.enabled = false;
     state.textures[0] = 0;
     state.Apply();
 }

From f92236976b870cfb8be5a9efa03e4fb171d0ce00 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 01:04:36 -0300
Subject: [PATCH 17/76] gl_state: Remove multisample tracking

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 4 ++--
 src/video_core/renderer_opengl/gl_state.cpp      | 8 --------
 src/video_core/renderer_opengl/gl_state.h        | 6 ------
 3 files changed, 2 insertions(+), 16 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index f5aa84a169..dc2d601561 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1083,8 +1083,8 @@ void RasterizerOpenGL::SyncColorMask() {
 
 void RasterizerOpenGL::SyncMultiSampleState() {
     const auto& regs = system.GPU().Maxwell3D().regs;
-    state.multisample_control.alpha_to_coverage = regs.multisample_control.alpha_to_coverage != 0;
-    state.multisample_control.alpha_to_one = regs.multisample_control.alpha_to_one != 0;
+    oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage);
+    oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one);
 }
 
 void RasterizerOpenGL::SyncFragmentColorClampState() {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index e67db758d5..b726adfc79 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -124,13 +124,6 @@ void OpenGLState::ApplyFragmentColorClamp() {
     }
 }
 
-void OpenGLState::ApplyMultisample() {
-    Enable(GL_SAMPLE_ALPHA_TO_COVERAGE, cur_state.multisample_control.alpha_to_coverage,
-           multisample_control.alpha_to_coverage);
-    Enable(GL_SAMPLE_ALPHA_TO_ONE, cur_state.multisample_control.alpha_to_one,
-           multisample_control.alpha_to_one);
-}
-
 void OpenGLState::ApplyRasterizerDiscard() {
     Enable(GL_RASTERIZER_DISCARD, cur_state.rasterizer_discard, rasterizer_discard);
 }
@@ -325,7 +318,6 @@ void OpenGLState::Apply() {
     ApplyProgramPipeline();
     ApplyClipDistances();
     ApplyFragmentColorClamp();
-    ApplyMultisample();
     ApplyRasterizerDiscard();
     ApplyColorMask();
     ApplyViewport();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 6c5126687e..5a8a728f2e 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -13,11 +13,6 @@ namespace OpenGL {
 
 class OpenGLState {
 public:
-    struct {
-        bool alpha_to_coverage = false; // GL_ALPHA_TO_COVERAGE
-        bool alpha_to_one = false;      // GL_ALPHA_TO_ONE
-    } multisample_control;
-
     struct {
         bool enabled = false; // GL_CLAMP_FRAGMENT_COLOR_ARB
     } fragment_color_clamp;
@@ -116,7 +111,6 @@ public:
     void ApplyProgramPipeline();
     void ApplyClipDistances();
     void ApplyFragmentColorClamp();
-    void ApplyMultisample();
     void ApplyRasterizerDiscard();
     void ApplyColorMask();
     void ApplyStencilTest();

From 2392b548bee87553b39f50c1159640b0dabc4b13 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 01:07:34 -0300
Subject: [PATCH 18/76] gl_state: Remove clamp framebuffer color tracking

This commit doesn't reset it for screen draws because clamping doesn't
change anything there.
---
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 10 ++++++----
 src/video_core/renderer_opengl/gl_state.cpp      |  8 --------
 src/video_core/renderer_opengl/gl_state.h        |  5 -----
 3 files changed, 6 insertions(+), 17 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index dc2d601561..d7971f86fd 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -415,6 +415,11 @@ void RasterizerOpenGL::Clear() {
         clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
         clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
         clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
+
+        // TODO: Signal state tracker about these changes
+        SyncFramebufferSRGB();
+        // TODO(Rodrigo): Determine if clamping is used on clears
+        SyncFragmentColorClampState();
     }
     if (regs.clear_buffers.Z) {
         ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
@@ -454,9 +459,6 @@ void RasterizerOpenGL::Clear() {
         }
     }
 
-    // TODO: Signal state tracker about these changes
-    SyncFramebufferSRGB();
-
     if (!use_color && !use_depth && !use_stencil) {
         // No color surface nor depth/stencil surface are enabled
         return;
@@ -1089,7 +1091,7 @@ void RasterizerOpenGL::SyncMultiSampleState() {
 
 void RasterizerOpenGL::SyncFragmentColorClampState() {
     const auto& regs = system.GPU().Maxwell3D().regs;
-    state.fragment_color_clamp.enabled = regs.frag_color_clamp != 0;
+    glClampColor(GL_CLAMP_FRAGMENT_COLOR, regs.frag_color_clamp ? GL_TRUE : GL_FALSE);
 }
 
 void RasterizerOpenGL::SyncBlendState() {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index b726adfc79..f8325d6e7d 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -117,13 +117,6 @@ void OpenGLState::ApplyClipDistances() {
     }
 }
 
-void OpenGLState::ApplyFragmentColorClamp() {
-    if (UpdateValue(cur_state.fragment_color_clamp.enabled, fragment_color_clamp.enabled)) {
-        glClampColor(GL_CLAMP_FRAGMENT_COLOR_ARB,
-                     fragment_color_clamp.enabled ? GL_TRUE : GL_FALSE);
-    }
-}
-
 void OpenGLState::ApplyRasterizerDiscard() {
     Enable(GL_RASTERIZER_DISCARD, cur_state.rasterizer_discard, rasterizer_discard);
 }
@@ -317,7 +310,6 @@ void OpenGLState::Apply() {
     ApplyShaderProgram();
     ApplyProgramPipeline();
     ApplyClipDistances();
-    ApplyFragmentColorClamp();
     ApplyRasterizerDiscard();
     ApplyColorMask();
     ApplyViewport();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 5a8a728f2e..89eb30dfcc 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -13,10 +13,6 @@ namespace OpenGL {
 
 class OpenGLState {
 public:
-    struct {
-        bool enabled = false; // GL_CLAMP_FRAGMENT_COLOR_ARB
-    } fragment_color_clamp;
-
     bool rasterizer_discard = false; // GL_RASTERIZER_DISCARD
 
     struct ColorMask {
@@ -110,7 +106,6 @@ public:
     void ApplyShaderProgram();
     void ApplyProgramPipeline();
     void ApplyClipDistances();
-    void ApplyFragmentColorClamp();
     void ApplyRasterizerDiscard();
     void ApplyColorMask();
     void ApplyStencilTest();

From 0914c70b7f9d68e71779fa32c474ed896b225704 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 01:19:15 -0300
Subject: [PATCH 19/76] gl_state: Remove color mask tracking

---
 .../renderer_opengl/gl_rasterizer.cpp         | 25 ++++++++-----------
 src/video_core/renderer_opengl/gl_state.cpp   | 16 ------------
 src/video_core/renderer_opengl/gl_state.h     | 10 --------
 .../renderer_opengl/renderer_opengl.cpp       |  1 +
 4 files changed, 12 insertions(+), 40 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index d7971f86fd..d0c8119297 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -411,12 +411,10 @@ void RasterizerOpenGL::Clear() {
         use_color = true;
     }
     if (use_color) {
-        clear_state.color_mask[0].red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE;
-        clear_state.color_mask[0].green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE;
-        clear_state.color_mask[0].blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE;
-        clear_state.color_mask[0].alpha_enabled = regs.clear_buffers.A ? GL_TRUE : GL_FALSE;
-
         // TODO: Signal state tracker about these changes
+        glColorMaski(0, regs.clear_buffers.R, regs.clear_buffers.G, regs.clear_buffers.B,
+                     regs.clear_buffers.A);
+
         SyncFramebufferSRGB();
         // TODO(Rodrigo): Determine if clamping is used on clears
         SyncFragmentColorClampState();
@@ -1071,15 +1069,14 @@ void RasterizerOpenGL::SyncColorMask() {
     auto& maxwell3d = system.GPU().Maxwell3D();
     const auto& regs = maxwell3d.regs;
 
-    const std::size_t count =
-        regs.independent_blend_enable ? Tegra::Engines::Maxwell3D::Regs::NumRenderTargets : 1;
-    for (std::size_t i = 0; i < count; i++) {
-        const auto& source = regs.color_mask[regs.color_mask_common ? 0 : i];
-        auto& dest = state.color_mask[i];
-        dest.red_enabled = (source.R == 0) ? GL_FALSE : GL_TRUE;
-        dest.green_enabled = (source.G == 0) ? GL_FALSE : GL_TRUE;
-        dest.blue_enabled = (source.B == 0) ? GL_FALSE : GL_TRUE;
-        dest.alpha_enabled = (source.A == 0) ? GL_FALSE : GL_TRUE;
+    if (regs.color_mask_common) {
+        auto& mask = regs.color_mask[0];
+        glColorMask(mask.R, mask.B, mask.G, mask.A);
+    } else {
+        for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
+            const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : i];
+            glColorMaski(static_cast<GLuint>(i), mask.R, mask.G, mask.B, mask.A);
+        }
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index f8325d6e7d..bcacc55908 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -121,21 +121,6 @@ void OpenGLState::ApplyRasterizerDiscard() {
     Enable(GL_RASTERIZER_DISCARD, cur_state.rasterizer_discard, rasterizer_discard);
 }
 
-void OpenGLState::ApplyColorMask() {
-    for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
-        const auto& updated = color_mask[i];
-        auto& current = cur_state.color_mask[i];
-        if (updated.red_enabled != current.red_enabled ||
-            updated.green_enabled != current.green_enabled ||
-            updated.blue_enabled != current.blue_enabled ||
-            updated.alpha_enabled != current.alpha_enabled) {
-            current = updated;
-            glColorMaski(static_cast<GLuint>(i), updated.red_enabled, updated.green_enabled,
-                         updated.blue_enabled, updated.alpha_enabled);
-        }
-    }
-}
-
 void OpenGLState::ApplyStencilTest() {
     Enable(GL_STENCIL_TEST, cur_state.stencil.test_enabled, stencil.test_enabled);
 
@@ -311,7 +296,6 @@ void OpenGLState::Apply() {
     ApplyProgramPipeline();
     ApplyClipDistances();
     ApplyRasterizerDiscard();
-    ApplyColorMask();
     ApplyViewport();
     ApplyStencilTest();
     ApplyBlending();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 89eb30dfcc..ccc302014c 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -15,15 +15,6 @@ class OpenGLState {
 public:
     bool rasterizer_discard = false; // GL_RASTERIZER_DISCARD
 
-    struct ColorMask {
-        GLboolean red_enabled = GL_TRUE;
-        GLboolean green_enabled = GL_TRUE;
-        GLboolean blue_enabled = GL_TRUE;
-        GLboolean alpha_enabled = GL_TRUE;
-    };
-    std::array<ColorMask, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets>
-        color_mask; // GL_COLOR_WRITEMASK
-
     struct {
         bool test_enabled = false; // GL_STENCIL_TEST
         struct {
@@ -107,7 +98,6 @@ public:
     void ApplyProgramPipeline();
     void ApplyClipDistances();
     void ApplyRasterizerDiscard();
-    void ApplyColorMask();
     void ApplyStencilTest();
     void ApplyViewport();
     void ApplyTargetBlending(std::size_t target, bool force);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 4d41ac7b4d..6dbf727ee1 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -570,6 +570,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
     glDisable(GL_POLYGON_OFFSET_FILL);
     glCullFace(GL_BACK);
     glFrontFace(GL_CW);
+    glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
 
     glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
                          offsetof(ScreenRectVertex, position));

From 7c16b3551b5294d86a7dc8e0721c081bd88547ed Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 01:28:17 -0300
Subject: [PATCH 20/76] gl_state: Remove scissor test tracking

---
 .../renderer_opengl/gl_rasterizer.cpp         | 32 +++++------------
 .../renderer_opengl/gl_rasterizer.h           |  2 +-
 src/video_core/renderer_opengl/gl_state.cpp   | 35 -------------------
 src/video_core/renderer_opengl/gl_state.h     | 10 ------
 .../renderer_opengl/gl_texture_cache.cpp      |  1 +
 .../renderer_opengl/renderer_opengl.cpp       |  1 +
 6 files changed, 12 insertions(+), 69 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index d0c8119297..3ccedcf55d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -467,12 +467,10 @@ void RasterizerOpenGL::Clear() {
     SyncViewport(clear_state);
     SyncRasterizeEnable(clear_state);
     if (regs.clear_flags.scissor) {
-        SyncScissorTest(clear_state);
+        SyncScissorTest();
     }
 
-    if (regs.clear_flags.viewport) {
-        clear_state.EmulateViewportWithScissor();
-    }
+    UNIMPLEMENTED_IF(regs.clear_flags.viewport);
 
     clear_state.Apply();
 
@@ -508,7 +506,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
     SyncLogicOpState();
     SyncCullMode();
     SyncPrimitiveRestart();
-    SyncScissorTest(state);
+    SyncScissorTest();
     SyncTransformFeedback();
     SyncPointState();
     SyncPolygonOffset();
@@ -1140,25 +1138,13 @@ void RasterizerOpenGL::SyncLogicOpState() {
     }
 }
 
-void RasterizerOpenGL::SyncScissorTest(OpenGLState& current_state) {
+void RasterizerOpenGL::SyncScissorTest() {
     const auto& regs = system.GPU().Maxwell3D().regs;
-    const bool geometry_shaders_enabled =
-        regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry));
-    const std::size_t viewport_count =
-        geometry_shaders_enabled ? Tegra::Engines::Maxwell3D::Regs::NumViewports : 1;
-    for (std::size_t i = 0; i < viewport_count; i++) {
-        const auto& src = regs.scissor_test[i];
-        auto& dst = current_state.viewports[i].scissor;
-        dst.enabled = (src.enable != 0);
-        if (dst.enabled == 0) {
-            return;
-        }
-        const u32 width = src.max_x - src.min_x;
-        const u32 height = src.max_y - src.min_y;
-        dst.x = src.min_x;
-        dst.y = src.min_y;
-        dst.width = width;
-        dst.height = height;
+    for (std::size_t index = 0; index < Maxwell::NumViewports; ++index) {
+        const auto& src = regs.scissor_test[index];
+        oglEnablei(GL_SCISSOR_TEST, src.enable, static_cast<GLuint>(index));
+        glScissorIndexed(static_cast<GLuint>(index), src.min_x, src.min_y, src.max_x - src.min_x,
+                         src.max_y - src.min_y);
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 91179323dc..0450657a79 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -167,7 +167,7 @@ private:
     void SyncMultiSampleState();
 
     /// Syncs the scissor test state to match the guest state
-    void SyncScissorTest(OpenGLState& current_state);
+    void SyncScissorTest();
 
     /// Syncs the transform feedback state to match the guest state
     void SyncTransformFeedback();
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index bcacc55908..dcea16fd3d 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -171,19 +171,6 @@ void OpenGLState::ApplyViewport() {
             current.depth_range_far = updated.depth_range_far;
             glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
         }
-
-        Enable(GL_SCISSOR_TEST, i, current.scissor.enabled, updated.scissor.enabled);
-
-        if (current.scissor.x != updated.scissor.x || current.scissor.y != updated.scissor.y ||
-            current.scissor.width != updated.scissor.width ||
-            current.scissor.height != updated.scissor.height) {
-            current.scissor.x = updated.scissor.x;
-            current.scissor.y = updated.scissor.y;
-            current.scissor.width = updated.scissor.width;
-            current.scissor.height = updated.scissor.height;
-            glScissorIndexed(i, updated.scissor.x, updated.scissor.y, updated.scissor.width,
-                             updated.scissor.height);
-        }
     }
 }
 
@@ -306,28 +293,6 @@ void OpenGLState::Apply() {
     ApplyRenderBuffer();
 }
 
-void OpenGLState::EmulateViewportWithScissor() {
-    auto& current = viewports[0];
-    if (current.scissor.enabled) {
-        const GLint left = std::max(current.x, current.scissor.x);
-        const GLint right =
-            std::max(current.x + current.width, current.scissor.x + current.scissor.width);
-        const GLint bottom = std::max(current.y, current.scissor.y);
-        const GLint top =
-            std::max(current.y + current.height, current.scissor.y + current.scissor.height);
-        current.scissor.x = std::max(left, 0);
-        current.scissor.y = std::max(bottom, 0);
-        current.scissor.width = std::max(right - left, 0);
-        current.scissor.height = std::max(top - bottom, 0);
-    } else {
-        current.scissor.enabled = true;
-        current.scissor.x = current.x;
-        current.scissor.y = current.y;
-        current.scissor.width = current.width;
-        current.scissor.height = current.height;
-    }
-}
-
 OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
     for (auto& texture : textures) {
         if (texture == handle) {
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index ccc302014c..44eb35dd52 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -63,13 +63,6 @@ public:
         GLint height = 0;
         GLfloat depth_range_near = 0.0f; // GL_DEPTH_RANGE
         GLfloat depth_range_far = 1.0f;  // GL_DEPTH_RANGE
-        struct {
-            bool enabled = false; // GL_SCISSOR_TEST
-            GLint x = 0;
-            GLint y = 0;
-            GLsizei width = 0;
-            GLsizei height = 0;
-        } scissor;
     };
     std::array<Viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
 
@@ -117,9 +110,6 @@ public:
     OpenGLState& ResetFramebuffer(GLuint handle);
     OpenGLState& ResetRenderbuffer(GLuint handle);
 
-    /// Viewport does not affects glClearBuffer so emulate viewport using scissor test
-    void EmulateViewportWithScissor();
-
 private:
     static OpenGLState cur_state;
 };
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 0bdbb70a4d..f6cb02c53c 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -536,6 +536,7 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     } else {
         glDisable(GL_FRAMEBUFFER_SRGB);
     }
+    glDisablei(GL_SCISSOR_TEST, 0);
 
     u32 buffers{};
 
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 6dbf727ee1..0d5ef9ef66 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -568,6 +568,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
     glDisable(GL_ALPHA_TEST);
     glDisable(GL_DEPTH_TEST);
     glDisable(GL_POLYGON_OFFSET_FILL);
+    glDisablei(GL_SCISSOR_TEST, 0);
     glCullFace(GL_BACK);
     glFrontFace(GL_CW);
     glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

From d3e433a38048c5d32c0929446008586e975ccd0e Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 01:50:38 -0300
Subject: [PATCH 21/76] gl_state: Remove viewport and depth range tracking

---
 src/video_core/engines/maxwell_3d.h           | 18 +++----
 .../renderer_opengl/gl_rasterizer.cpp         | 24 +++------
 .../renderer_opengl/gl_rasterizer.h           |  2 +-
 src/video_core/renderer_opengl/gl_state.cpp   | 29 ----------
 src/video_core/renderer_opengl/gl_state.h     | 12 -----
 .../renderer_opengl/renderer_opengl.cpp       | 53 ++++++++-----------
 .../renderer_opengl/renderer_opengl.h         |  2 -
 7 files changed, 39 insertions(+), 101 deletions(-)

diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 3a641c1820..2134d6e4f7 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -574,7 +574,7 @@ public:
             f32 translate_z;
             INSERT_UNION_PADDING_WORDS(2);
 
-            Common::Rectangle<s32> GetRect() const {
+            Common::Rectangle<f32> GetRect() const {
                 return {
                     GetX(),               // left
                     GetY() + GetHeight(), // top
@@ -583,20 +583,20 @@ public:
                 };
             };
 
-            s32 GetX() const {
-                return static_cast<s32>(std::max(0.0f, translate_x - std::fabs(scale_x)));
+            f32 GetX() const {
+                return std::max(0.0f, translate_x - std::fabs(scale_x));
             }
 
-            s32 GetY() const {
-                return static_cast<s32>(std::max(0.0f, translate_y - std::fabs(scale_y)));
+            f32 GetY() const {
+                return std::max(0.0f, translate_y - std::fabs(scale_y));
             }
 
-            s32 GetWidth() const {
-                return static_cast<s32>(translate_x + std::fabs(scale_x)) - GetX();
+            f32 GetWidth() const {
+                return translate_x + std::fabs(scale_x) - GetX();
             }
 
-            s32 GetHeight() const {
-                return static_cast<s32>(translate_y + std::fabs(scale_y)) - GetY();
+            f32 GetHeight() const {
+                return translate_y + std::fabs(scale_y) - GetY();
             }
         };
 
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 3ccedcf55d..63295761ad 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -360,7 +360,6 @@ void RasterizerOpenGL::ConfigureFramebuffers() {
     texture_cache.GuardRenderTargets(false);
 
     state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(key);
-    SyncViewport(state);
 }
 
 void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb,
@@ -405,7 +404,6 @@ void RasterizerOpenGL::Clear() {
     SCOPE_EXIT({ prev_state.Apply(); });
 
     OpenGLState clear_state{OpenGLState::GetCurState()};
-    clear_state.SetDefaultViewports();
     if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
         regs.clear_buffers.A) {
         use_color = true;
@@ -464,7 +462,6 @@ void RasterizerOpenGL::Clear() {
 
     ConfigureClearFramebuffer(clear_state, use_color, use_depth, use_stencil);
 
-    SyncViewport(clear_state);
     SyncRasterizeEnable(clear_state);
     if (regs.clear_flags.scissor) {
         SyncScissorTest();
@@ -496,6 +493,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
 
     query_cache.UpdateCounters();
 
+    SyncViewport();
     SyncRasterizeEnable(state);
     SyncColorMask();
     SyncFragmentColorClampState();
@@ -935,22 +933,14 @@ void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& t
     state.images[binding] = view->GetTexture();
 }
 
-void RasterizerOpenGL::SyncViewport(OpenGLState& current_state) {
+void RasterizerOpenGL::SyncViewport() {
     const auto& regs = system.GPU().Maxwell3D().regs;
-    const bool geometry_shaders_enabled =
-        regs.IsShaderConfigEnabled(static_cast<size_t>(Maxwell::ShaderProgram::Geometry));
-    const std::size_t viewport_count =
-        geometry_shaders_enabled ? Tegra::Engines::Maxwell3D::Regs::NumViewports : 1;
-    for (std::size_t i = 0; i < viewport_count; i++) {
-        auto& viewport = current_state.viewports[i];
+    for (std::size_t i = 0; i < Maxwell::NumViewports; ++i) {
         const auto& src = regs.viewports[i];
-        const Common::Rectangle<s32> viewport_rect{regs.viewport_transform[i].GetRect()};
-        viewport.x = viewport_rect.left;
-        viewport.y = viewport_rect.bottom;
-        viewport.width = viewport_rect.GetWidth();
-        viewport.height = viewport_rect.GetHeight();
-        viewport.depth_range_far = src.depth_range_far;
-        viewport.depth_range_near = src.depth_range_near;
+        const Common::Rectangle<f32> rect{regs.viewport_transform[i].GetRect()};
+        glViewportIndexedf(static_cast<GLuint>(i), rect.left, rect.bottom, rect.GetWidth(),
+                           rect.GetHeight());
+        glDepthRangef(src.depth_range_near, src.depth_range_far);
     }
 
     bool flip_y = false;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 0450657a79..d1d0aec326 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -130,7 +130,7 @@ private:
                     const GLShader::ImageEntry& entry);
 
     /// Syncs the viewport and depth range to match the guest state
-    void SyncViewport(OpenGLState& current_state);
+    void SyncViewport();
 
     /// Syncs the depth clamp state
     void SyncDepthClamp();
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index dcea16fd3d..7c08cc3c21 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -85,10 +85,6 @@ void Enable(GLenum cap, GLuint index, bool& current_value, bool new_value) {
 
 OpenGLState::OpenGLState() = default;
 
-void OpenGLState::SetDefaultViewports() {
-    viewports.fill(Viewport{});
-}
-
 void OpenGLState::ApplyFramebufferState() {
     if (UpdateValue(cur_state.draw.read_framebuffer, draw.read_framebuffer)) {
         glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
@@ -150,30 +146,6 @@ void OpenGLState::ApplyStencilTest() {
     ConfigStencil(GL_BACK, stencil.back, cur_state.stencil.back);
 }
 
-void OpenGLState::ApplyViewport() {
-    for (GLuint i = 0; i < static_cast<GLuint>(Maxwell::NumViewports); ++i) {
-        const auto& updated = viewports[i];
-        auto& current = cur_state.viewports[i];
-
-        if (current.x != updated.x || current.y != updated.y || current.width != updated.width ||
-            current.height != updated.height) {
-            current.x = updated.x;
-            current.y = updated.y;
-            current.width = updated.width;
-            current.height = updated.height;
-            glViewportIndexedf(i, static_cast<GLfloat>(updated.x), static_cast<GLfloat>(updated.y),
-                               static_cast<GLfloat>(updated.width),
-                               static_cast<GLfloat>(updated.height));
-        }
-        if (current.depth_range_near != updated.depth_range_near ||
-            current.depth_range_far != updated.depth_range_far) {
-            current.depth_range_near = updated.depth_range_near;
-            current.depth_range_far = updated.depth_range_far;
-            glDepthRangeIndexed(i, updated.depth_range_near, updated.depth_range_far);
-        }
-    }
-}
-
 void OpenGLState::ApplyGlobalBlending() {
     const Blend& updated = blend[0];
     Blend& current = cur_state.blend[0];
@@ -283,7 +255,6 @@ void OpenGLState::Apply() {
     ApplyProgramPipeline();
     ApplyClipDistances();
     ApplyRasterizerDiscard();
-    ApplyViewport();
     ApplyStencilTest();
     ApplyBlending();
     ApplyTextures();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 44eb35dd52..b4c957c0df 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -56,16 +56,6 @@ public:
         GLuint program_pipeline = 0; // GL_PROGRAM_PIPELINE_BINDING
     } draw;
 
-    struct Viewport {
-        GLint x = 0;
-        GLint y = 0;
-        GLint width = 0;
-        GLint height = 0;
-        GLfloat depth_range_near = 0.0f; // GL_DEPTH_RANGE
-        GLfloat depth_range_far = 1.0f;  // GL_DEPTH_RANGE
-    };
-    std::array<Viewport, Tegra::Engines::Maxwell3D::Regs::NumViewports> viewports;
-
     std::array<bool, 8> clip_distance = {}; // GL_CLIP_DISTANCE
 
     struct {
@@ -82,7 +72,6 @@ public:
         return cur_state;
     }
 
-    void SetDefaultViewports();
     /// Apply this state as the current OpenGL state
     void Apply();
 
@@ -92,7 +81,6 @@ public:
     void ApplyClipDistances();
     void ApplyRasterizerDiscard();
     void ApplyStencilTest();
-    void ApplyViewport();
     void ApplyTargetBlending(std::size_t target, bool force);
     void ApplyGlobalBlending();
     void ApplyBlending();
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 0d5ef9ef66..12e820979d 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -205,8 +205,8 @@ constexpr GLint TexCoordLocation = 1;
 constexpr GLint ModelViewMatrixLocation = 0;
 
 struct ScreenRectVertex {
-    constexpr ScreenRectVertex(GLfloat x, GLfloat y, GLfloat u, GLfloat v)
-        : position{{x, y}}, tex_coord{{u, v}} {}
+    constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v)
+        : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {}
 
     std::array<GLfloat, 2> position;
     std::array<GLfloat, 2> tex_coord;
@@ -514,8 +514,18 @@ void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
     glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height);
 }
 
-void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w,
-                                         float h) {
+void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
+    if (renderer_settings.set_background_color) {
+        // Update background color before drawing
+        glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
+                     0.0f);
+    }
+
+    // Set projection matrix
+    const std::array ortho_matrix =
+        MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
+    glUniformMatrix3x2fv(ModelViewMatrixLocation, 1, GL_FALSE, ortho_matrix.data());
+
     const auto& texcoords = screen_info.display_texcoords;
     auto left = texcoords.left;
     auto right = texcoords.right;
@@ -547,12 +557,14 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
                   static_cast<f32>(screen_info.texture.height);
     }
 
+    const auto& screen = layout.screen;
     const std::array vertices = {
-        ScreenRectVertex(x, y, texcoords.top * scale_u, left * scale_v),
-        ScreenRectVertex(x + w, y, texcoords.bottom * scale_u, left * scale_v),
-        ScreenRectVertex(x, y + h, texcoords.top * scale_u, right * scale_v),
-        ScreenRectVertex(x + w, y + h, texcoords.bottom * scale_u, right * scale_v),
+        ScreenRectVertex(screen.left, screen.top, texcoords.top * scale_u, left * scale_v),
+        ScreenRectVertex(screen.right, screen.top, texcoords.bottom * scale_u, left * scale_v),
+        ScreenRectVertex(screen.left, screen.bottom, texcoords.top * scale_u, right * scale_v),
+        ScreenRectVertex(screen.right, screen.bottom, texcoords.bottom * scale_u, right * scale_v),
     };
+    glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
 
     state.textures[0] = screen_info.display_texture;
     state.Apply();
@@ -572,6 +584,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
     glCullFace(GL_BACK);
     glFrontFace(GL_CW);
     glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+    glViewport(0, 0, layout.width, layout.height);
 
     glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
                          offsetof(ScreenRectVertex, position));
@@ -581,7 +594,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
     glVertexAttribBinding(TexCoordLocation, 0);
     glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
 
-    glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
+    glClear(GL_COLOR_BUFFER_BIT);
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
     // Restore default state
@@ -589,28 +602,6 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
     state.Apply();
 }
 
-void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
-    if (renderer_settings.set_background_color) {
-        // Update background color before drawing
-        glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
-                     0.0f);
-    }
-
-    const auto& screen = layout.screen;
-
-    glViewport(0, 0, layout.width, layout.height);
-    glClear(GL_COLOR_BUFFER_BIT);
-
-    // Set projection matrix
-    const std::array ortho_matrix =
-        MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
-    glUniformMatrix3x2fv(ModelViewMatrixLocation, 1, GL_FALSE, ortho_matrix.data());
-
-    DrawScreenTriangles(screen_info, static_cast<float>(screen.left),
-                        static_cast<float>(screen.top), static_cast<float>(screen.GetWidth()),
-                        static_cast<float>(screen.GetHeight()));
-}
-
 void RendererOpenGL::TryPresent(int timeout_ms) {
     const auto& layout = render_window.GetFramebufferLayout();
     auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 978a4d0eb6..42a2141d85 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -76,8 +76,6 @@ private:
     /// Draws the emulated screens to the emulator window.
     void DrawScreen(const Layout::FramebufferLayout& layout);
 
-    void DrawScreenTriangles(const ScreenInfo& screen_info, float x, float y, float w, float h);
-
     void RenderScreenshot();
 
     /// Loads framebuffer from emulated memory into the active OpenGL texture.

From e8125af8dd8efac3f7171e234b5aee6edfadc626 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 02:11:01 -0300
Subject: [PATCH 22/76] gl_state: Remove rasterizer disable tracking

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp    | 8 ++++----
 src/video_core/renderer_opengl/gl_rasterizer.h      | 2 +-
 src/video_core/renderer_opengl/gl_state.cpp         | 5 -----
 src/video_core/renderer_opengl/gl_state.h           | 3 ---
 src/video_core/renderer_opengl/gl_texture_cache.cpp | 2 ++
 src/video_core/renderer_opengl/renderer_opengl.cpp  | 1 +
 6 files changed, 8 insertions(+), 13 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 63295761ad..cb3c813988 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -462,7 +462,7 @@ void RasterizerOpenGL::Clear() {
 
     ConfigureClearFramebuffer(clear_state, use_color, use_depth, use_stencil);
 
-    SyncRasterizeEnable(clear_state);
+    SyncRasterizeEnable();
     if (regs.clear_flags.scissor) {
         SyncScissorTest();
     }
@@ -494,7 +494,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
     query_cache.UpdateCounters();
 
     SyncViewport();
-    SyncRasterizeEnable(state);
+    SyncRasterizeEnable();
     SyncColorMask();
     SyncFragmentColorClampState();
     SyncMultiSampleState();
@@ -1048,9 +1048,9 @@ void RasterizerOpenGL::SyncStencilTestState() {
     }
 }
 
-void RasterizerOpenGL::SyncRasterizeEnable(OpenGLState& current_state) {
+void RasterizerOpenGL::SyncRasterizeEnable() {
     const auto& regs = system.GPU().Maxwell3D().regs;
-    current_state.rasterizer_discard = regs.rasterize_enable == 0;
+    oglEnable(GL_RASTERIZER_DISCARD, regs.rasterize_enable == 0);
 }
 
 void RasterizerOpenGL::SyncColorMask() {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index d1d0aec326..de21a35114 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -176,7 +176,7 @@ private:
     void SyncPointState();
 
     /// Syncs the rasterizer enable state to match the guest state
-    void SyncRasterizeEnable(OpenGLState& current_state);
+    void SyncRasterizeEnable();
 
     /// Syncs Color Mask
     void SyncColorMask();
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 7c08cc3c21..96c3f40f46 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -113,10 +113,6 @@ void OpenGLState::ApplyClipDistances() {
     }
 }
 
-void OpenGLState::ApplyRasterizerDiscard() {
-    Enable(GL_RASTERIZER_DISCARD, cur_state.rasterizer_discard, rasterizer_discard);
-}
-
 void OpenGLState::ApplyStencilTest() {
     Enable(GL_STENCIL_TEST, cur_state.stencil.test_enabled, stencil.test_enabled);
 
@@ -254,7 +250,6 @@ void OpenGLState::Apply() {
     ApplyShaderProgram();
     ApplyProgramPipeline();
     ApplyClipDistances();
-    ApplyRasterizerDiscard();
     ApplyStencilTest();
     ApplyBlending();
     ApplyTextures();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index b4c957c0df..6520c88d29 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -13,8 +13,6 @@ namespace OpenGL {
 
 class OpenGLState {
 public:
-    bool rasterizer_discard = false; // GL_RASTERIZER_DISCARD
-
     struct {
         bool test_enabled = false; // GL_STENCIL_TEST
         struct {
@@ -79,7 +77,6 @@ public:
     void ApplyShaderProgram();
     void ApplyProgramPipeline();
     void ApplyClipDistances();
-    void ApplyRasterizerDiscard();
     void ApplyStencilTest();
     void ApplyTargetBlending(std::size_t target, bool force);
     void ApplyGlobalBlending();
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index f6cb02c53c..ed2daf74c1 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -536,6 +536,8 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     } else {
         glDisable(GL_FRAMEBUFFER_SRGB);
     }
+    // TODO(Rodrigo): Find out if rasterizer discard affects blits
+    glDisable(GL_RASTERIZER_DISCARD);
     glDisablei(GL_SCISSOR_TEST, 0);
 
     u32 buffers{};
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 12e820979d..d18adaddce 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -580,6 +580,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glDisable(GL_ALPHA_TEST);
     glDisable(GL_DEPTH_TEST);
     glDisable(GL_POLYGON_OFFSET_FILL);
+    glDisable(GL_RASTERIZER_DISCARD);
     glDisablei(GL_SCISSOR_TEST, 0);
     glCullFace(GL_BACK);
     glFrontFace(GL_CW);

From 1eee891f6e73423a9aa6147f980be5aea799e7ce Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 02:20:08 -0300
Subject: [PATCH 23/76] gl_state: Remove clip distances tracking

---
 src/video_core/engines/maxwell_3d.h              | 11 +----------
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 10 ++--------
 src/video_core/renderer_opengl/gl_state.cpp      |  8 --------
 src/video_core/renderer_opengl/gl_state.h        |  3 ---
 4 files changed, 3 insertions(+), 29 deletions(-)

diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 2134d6e4f7..b0fb0fb7db 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -872,16 +872,7 @@ public:
 
                 INSERT_UNION_PADDING_WORDS(0x35);
 
-                union {
-                    BitField<0, 1, u32> c0;
-                    BitField<1, 1, u32> c1;
-                    BitField<2, 1, u32> c2;
-                    BitField<3, 1, u32> c3;
-                    BitField<4, 1, u32> c4;
-                    BitField<5, 1, u32> c5;
-                    BitField<6, 1, u32> c6;
-                    BitField<7, 1, u32> c7;
-                } clip_distance_enabled;
+                u32 clip_distance_enabled;
 
                 u32 samplecnt_enable;
 
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index cb3c813988..f4efddcc05 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -969,16 +969,10 @@ void RasterizerOpenGL::SyncDepthClamp() {
 
 void RasterizerOpenGL::SyncClipEnabled(
     const std::array<bool, Maxwell::Regs::NumClipDistances>& clip_mask) {
-
     const auto& regs = system.GPU().Maxwell3D().regs;
-    const std::array<bool, Maxwell::Regs::NumClipDistances> reg_state{
-        regs.clip_distance_enabled.c0 != 0, regs.clip_distance_enabled.c1 != 0,
-        regs.clip_distance_enabled.c2 != 0, regs.clip_distance_enabled.c3 != 0,
-        regs.clip_distance_enabled.c4 != 0, regs.clip_distance_enabled.c5 != 0,
-        regs.clip_distance_enabled.c6 != 0, regs.clip_distance_enabled.c7 != 0};
-
     for (std::size_t i = 0; i < Maxwell::Regs::NumClipDistances; ++i) {
-        state.clip_distance[i] = reg_state[i] && clip_mask[i];
+        oglEnable(static_cast<GLenum>(GL_CLIP_DISTANCE0 + i),
+                  clip_mask[i] && ((regs.clip_distance_enabled >> i) & 1));
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 96c3f40f46..5505fee734 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -106,13 +106,6 @@ void OpenGLState::ApplyProgramPipeline() {
     }
 }
 
-void OpenGLState::ApplyClipDistances() {
-    for (std::size_t i = 0; i < clip_distance.size(); ++i) {
-        Enable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i), cur_state.clip_distance[i],
-               clip_distance[i]);
-    }
-}
-
 void OpenGLState::ApplyStencilTest() {
     Enable(GL_STENCIL_TEST, cur_state.stencil.test_enabled, stencil.test_enabled);
 
@@ -249,7 +242,6 @@ void OpenGLState::Apply() {
     ApplyFramebufferState();
     ApplyShaderProgram();
     ApplyProgramPipeline();
-    ApplyClipDistances();
     ApplyStencilTest();
     ApplyBlending();
     ApplyTextures();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 6520c88d29..e0bfd16adf 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -54,8 +54,6 @@ public:
         GLuint program_pipeline = 0; // GL_PROGRAM_PIPELINE_BINDING
     } draw;
 
-    std::array<bool, 8> clip_distance = {}; // GL_CLIP_DISTANCE
-
     struct {
         GLenum origin = GL_LOWER_LEFT;
         GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE;
@@ -76,7 +74,6 @@ public:
     void ApplyFramebufferState();
     void ApplyShaderProgram();
     void ApplyProgramPipeline();
-    void ApplyClipDistances();
     void ApplyStencilTest();
     void ApplyTargetBlending(std::size_t target, bool force);
     void ApplyGlobalBlending();

From 07a954e67f786fad4b6324837489af705788a6b9 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 02:27:26 -0300
Subject: [PATCH 24/76] gl_state: Remove clip control tracking

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp    | 11 ++++++-----
 src/video_core/renderer_opengl/gl_state.cpp         |  8 --------
 src/video_core/renderer_opengl/gl_state.h           |  6 ------
 src/video_core/renderer_opengl/gl_texture_cache.cpp |  1 +
 src/video_core/renderer_opengl/renderer_opengl.cpp  |  1 +
 5 files changed, 8 insertions(+), 19 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index f4efddcc05..8f9bb4c938 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -467,6 +467,9 @@ void RasterizerOpenGL::Clear() {
         SyncScissorTest();
     }
 
+    // TODO: Signal state tracker about these changes
+    glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
+
     UNIMPLEMENTED_IF(regs.clear_flags.viewport);
 
     clear_state.Apply();
@@ -950,11 +953,9 @@ void RasterizerOpenGL::SyncViewport() {
     if (regs.screen_y_control.y_negate != 0) {
         flip_y = !flip_y;
     }
-    state.clip_control.origin = flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT;
-    state.clip_control.depth_mode =
-        regs.depth_mode == Tegra::Engines::Maxwell3D::Regs::DepthMode::ZeroToOne
-            ? GL_ZERO_TO_ONE
-            : GL_NEGATIVE_ONE_TO_ONE;
+    glClipControl(flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT,
+                  regs.depth_mode == Maxwell::DepthMode::ZeroToOne ? GL_ZERO_TO_ONE
+                                                                   : GL_NEGATIVE_ONE_TO_ONE);
 }
 
 void RasterizerOpenGL::SyncDepthClamp() {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 5505fee734..69a8a4eb14 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -195,13 +195,6 @@ void OpenGLState::ApplyBlending() {
     cur_state.independant_blend.enabled = independant_blend.enabled;
 }
 
-void OpenGLState::ApplyClipControl() {
-    if (UpdateTie(std::tie(cur_state.clip_control.origin, cur_state.clip_control.depth_mode),
-                  std::tie(clip_control.origin, clip_control.depth_mode))) {
-        glClipControl(clip_control.origin, clip_control.depth_mode);
-    }
-}
-
 void OpenGLState::ApplyRenderBuffer() {
     if (cur_state.renderbuffer != renderbuffer) {
         cur_state.renderbuffer = renderbuffer;
@@ -247,7 +240,6 @@ void OpenGLState::Apply() {
     ApplyTextures();
     ApplySamplers();
     ApplyImages();
-    ApplyClipControl();
     ApplyRenderBuffer();
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index e0bfd16adf..6ea625c56c 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -54,11 +54,6 @@ public:
         GLuint program_pipeline = 0; // GL_PROGRAM_PIPELINE_BINDING
     } draw;
 
-    struct {
-        GLenum origin = GL_LOWER_LEFT;
-        GLenum depth_mode = GL_NEGATIVE_ONE_TO_ONE;
-    } clip_control;
-
     GLuint renderbuffer{}; // GL_RENDERBUFFER_BINDING
 
     OpenGLState();
@@ -81,7 +76,6 @@ public:
     void ApplyTextures();
     void ApplySamplers();
     void ApplyImages();
-    void ApplyClipControl();
     void ApplyRenderBuffer();
 
     /// Resets any references to the given resource
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index ed2daf74c1..85d41f8267 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -539,6 +539,7 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     // TODO(Rodrigo): Find out if rasterizer discard affects blits
     glDisable(GL_RASTERIZER_DISCARD);
     glDisablei(GL_SCISSOR_TEST, 0);
+    glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
 
     u32 buffers{};
 
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index d18adaddce..a4cf6a4890 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -585,6 +585,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glCullFace(GL_BACK);
     glFrontFace(GL_CW);
     glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+    glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
     glViewport(0, 0, layout.width, layout.height);
 
     glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,

From 7d9a5e9e30b9e197e5fe3bfd10116e7ea078494a Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 03:34:29 -0300
Subject: [PATCH 25/76] gl_state: Remove stencil test tracking

---
 .../renderer_opengl/gl_rasterizer.cpp         | 65 +++++--------------
 src/video_core/renderer_opengl/gl_state.cpp   | 30 ---------
 src/video_core/renderer_opengl/gl_state.h     | 14 ----
 .../renderer_opengl/renderer_opengl.cpp       |  1 +
 4 files changed, 18 insertions(+), 92 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 8f9bb4c938..573f14cab5 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -427,32 +427,6 @@ void RasterizerOpenGL::Clear() {
     if (regs.clear_buffers.S) {
         ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!");
         use_stencil = true;
-        clear_state.stencil.test_enabled = true;
-
-        if (regs.clear_flags.stencil) {
-            // Stencil affects the clear so fill it with the used masks
-            clear_state.stencil.front.test_func = GL_ALWAYS;
-            clear_state.stencil.front.test_mask = regs.stencil_front_func_mask;
-            clear_state.stencil.front.action_stencil_fail = GL_KEEP;
-            clear_state.stencil.front.action_depth_fail = GL_KEEP;
-            clear_state.stencil.front.action_depth_pass = GL_KEEP;
-            clear_state.stencil.front.write_mask = regs.stencil_front_mask;
-            if (regs.stencil_two_side_enable) {
-                clear_state.stencil.back.test_func = GL_ALWAYS;
-                clear_state.stencil.back.test_mask = regs.stencil_back_func_mask;
-                clear_state.stencil.back.action_stencil_fail = GL_KEEP;
-                clear_state.stencil.back.action_depth_fail = GL_KEEP;
-                clear_state.stencil.back.action_depth_pass = GL_KEEP;
-                clear_state.stencil.back.write_mask = regs.stencil_back_mask;
-            } else {
-                clear_state.stencil.back.test_func = GL_ALWAYS;
-                clear_state.stencil.back.test_mask = 0xFFFFFFFF;
-                clear_state.stencil.back.write_mask = 0xFFFFFFFF;
-                clear_state.stencil.back.action_stencil_fail = GL_KEEP;
-                clear_state.stencil.back.action_depth_fail = GL_KEEP;
-                clear_state.stencil.back.action_depth_pass = GL_KEEP;
-            }
-        }
     }
 
     if (!use_color && !use_depth && !use_stencil) {
@@ -1011,35 +985,30 @@ void RasterizerOpenGL::SyncDepthTestState() {
 void RasterizerOpenGL::SyncStencilTestState() {
     auto& maxwell3d = system.GPU().Maxwell3D();
     const auto& regs = maxwell3d.regs;
-    state.stencil.test_enabled = regs.stencil_enable != 0;
 
+    oglEnable(GL_STENCIL_TEST, regs.stencil_enable);
     if (!regs.stencil_enable) {
         return;
     }
 
-    state.stencil.front.test_func = MaxwellToGL::ComparisonOp(regs.stencil_front_func_func);
-    state.stencil.front.test_ref = regs.stencil_front_func_ref;
-    state.stencil.front.test_mask = regs.stencil_front_func_mask;
-    state.stencil.front.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_fail);
-    state.stencil.front.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_front_op_zfail);
-    state.stencil.front.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_front_op_zpass);
-    state.stencil.front.write_mask = regs.stencil_front_mask;
+    glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func),
+                          regs.stencil_front_func_ref, regs.stencil_front_func_mask);
+    glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail),
+                        MaxwellToGL::StencilOp(regs.stencil_front_op_zfail),
+                        MaxwellToGL::StencilOp(regs.stencil_front_op_zpass));
+    glStencilMaskSeparate(GL_FRONT, regs.stencil_front_mask);
+
     if (regs.stencil_two_side_enable) {
-        state.stencil.back.test_func = MaxwellToGL::ComparisonOp(regs.stencil_back_func_func);
-        state.stencil.back.test_ref = regs.stencil_back_func_ref;
-        state.stencil.back.test_mask = regs.stencil_back_func_mask;
-        state.stencil.back.action_stencil_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_fail);
-        state.stencil.back.action_depth_fail = MaxwellToGL::StencilOp(regs.stencil_back_op_zfail);
-        state.stencil.back.action_depth_pass = MaxwellToGL::StencilOp(regs.stencil_back_op_zpass);
-        state.stencil.back.write_mask = regs.stencil_back_mask;
+        glStencilFuncSeparate(GL_BACK, MaxwellToGL::ComparisonOp(regs.stencil_back_func_func),
+                              regs.stencil_back_func_ref, regs.stencil_back_func_mask);
+        glStencilOpSeparate(GL_BACK, MaxwellToGL::StencilOp(regs.stencil_back_op_fail),
+                            MaxwellToGL::StencilOp(regs.stencil_back_op_zfail),
+                            MaxwellToGL::StencilOp(regs.stencil_back_op_zpass));
+        glStencilMaskSeparate(GL_BACK, regs.stencil_back_mask);
     } else {
-        state.stencil.back.test_func = GL_ALWAYS;
-        state.stencil.back.test_ref = 0;
-        state.stencil.back.test_mask = 0xFFFFFFFF;
-        state.stencil.back.write_mask = 0xFFFFFFFF;
-        state.stencil.back.action_stencil_fail = GL_KEEP;
-        state.stencil.back.action_depth_fail = GL_KEEP;
-        state.stencil.back.action_depth_pass = GL_KEEP;
+        glStencilFuncSeparate(GL_BACK, GL_ALWAYS, 0, 0xFFFFFFFF);
+        glStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_KEEP);
+        glStencilMaskSeparate(GL_BACK, 0xFFFFFFFF);
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 69a8a4eb14..d62a55b2f7 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -106,35 +106,6 @@ void OpenGLState::ApplyProgramPipeline() {
     }
 }
 
-void OpenGLState::ApplyStencilTest() {
-    Enable(GL_STENCIL_TEST, cur_state.stencil.test_enabled, stencil.test_enabled);
-
-    const auto ConfigStencil = [](GLenum face, const auto& config, auto& current) {
-        if (current.test_func != config.test_func || current.test_ref != config.test_ref ||
-            current.test_mask != config.test_mask) {
-            current.test_func = config.test_func;
-            current.test_ref = config.test_ref;
-            current.test_mask = config.test_mask;
-            glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask);
-        }
-        if (current.action_depth_fail != config.action_depth_fail ||
-            current.action_depth_pass != config.action_depth_pass ||
-            current.action_stencil_fail != config.action_stencil_fail) {
-            current.action_depth_fail = config.action_depth_fail;
-            current.action_depth_pass = config.action_depth_pass;
-            current.action_stencil_fail = config.action_stencil_fail;
-            glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail,
-                                config.action_depth_pass);
-        }
-        if (current.write_mask != config.write_mask) {
-            current.write_mask = config.write_mask;
-            glStencilMaskSeparate(face, config.write_mask);
-        }
-    };
-    ConfigStencil(GL_FRONT, stencil.front, cur_state.stencil.front);
-    ConfigStencil(GL_BACK, stencil.back, cur_state.stencil.back);
-}
-
 void OpenGLState::ApplyGlobalBlending() {
     const Blend& updated = blend[0];
     Blend& current = cur_state.blend[0];
@@ -235,7 +206,6 @@ void OpenGLState::Apply() {
     ApplyFramebufferState();
     ApplyShaderProgram();
     ApplyProgramPipeline();
-    ApplyStencilTest();
     ApplyBlending();
     ApplyTextures();
     ApplySamplers();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 6ea625c56c..18f36da086 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -13,19 +13,6 @@ namespace OpenGL {
 
 class OpenGLState {
 public:
-    struct {
-        bool test_enabled = false; // GL_STENCIL_TEST
-        struct {
-            GLenum test_func = GL_ALWAYS;         // GL_STENCIL_FUNC
-            GLint test_ref = 0;                   // GL_STENCIL_REF
-            GLuint test_mask = 0xFFFFFFFF;        // GL_STENCIL_VALUE_MASK
-            GLuint write_mask = 0xFFFFFFFF;       // GL_STENCIL_WRITEMASK
-            GLenum action_stencil_fail = GL_KEEP; // GL_STENCIL_FAIL
-            GLenum action_depth_fail = GL_KEEP;   // GL_STENCIL_PASS_DEPTH_FAIL
-            GLenum action_depth_pass = GL_KEEP;   // GL_STENCIL_PASS_DEPTH_PASS
-        } front, back;
-    } stencil;
-
     struct Blend {
         bool enabled = false;              // GL_BLEND
         GLenum rgb_equation = GL_FUNC_ADD; // GL_BLEND_EQUATION_RGB
@@ -69,7 +56,6 @@ public:
     void ApplyFramebufferState();
     void ApplyShaderProgram();
     void ApplyProgramPipeline();
-    void ApplyStencilTest();
     void ApplyTargetBlending(std::size_t target, bool force);
     void ApplyGlobalBlending();
     void ApplyBlending();
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index a4cf6a4890..fcadc09d93 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -579,6 +579,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glDisable(GL_COLOR_LOGIC_OP);
     glDisable(GL_ALPHA_TEST);
     glDisable(GL_DEPTH_TEST);
+    glDisable(GL_STENCIL_TEST);
     glDisable(GL_POLYGON_OFFSET_FILL);
     glDisable(GL_RASTERIZER_DISCARD);
     glDisablei(GL_SCISSOR_TEST, 0);

From 1bc0da3dea5a8502e63f5c123151328ccb2ba8d6 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 03:51:50 -0300
Subject: [PATCH 26/76] gl_state: Remove blend state tracking

---
 .../renderer_opengl/gl_rasterizer.cpp         | 49 ++++++++-------
 src/video_core/renderer_opengl/gl_state.cpp   | 61 -------------------
 src/video_core/renderer_opengl/gl_state.h     | 18 ------
 .../renderer_opengl/gl_texture_cache.cpp      |  3 +
 .../renderer_opengl/renderer_opengl.cpp       |  1 +
 5 files changed, 28 insertions(+), 104 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 573f14cab5..f916f348ff 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -442,7 +442,9 @@ void RasterizerOpenGL::Clear() {
     }
 
     // TODO: Signal state tracker about these changes
+    // TODO(Rodrigo): Find out if these changes affect clearing
     glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
+    glDisablei(GL_BLEND, 0);
 
     UNIMPLEMENTED_IF(regs.clear_flags.viewport);
 
@@ -1049,37 +1051,34 @@ void RasterizerOpenGL::SyncBlendState() {
 
     glBlendColor(regs.blend_color.r, regs.blend_color.g, regs.blend_color.b, regs.blend_color.a);
 
-    state.independant_blend.enabled = regs.independent_blend_enable;
-    if (!state.independant_blend.enabled) {
-        auto& blend = state.blend[0];
+    if (!regs.independent_blend_enable) {
         const auto& src = regs.blend;
-        blend.enabled = src.enable[0] != 0;
-        if (blend.enabled) {
-            blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
-            blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
-            blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
-            blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
-            blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
-            blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
-        }
-        for (std::size_t i = 1; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
-            state.blend[i].enabled = false;
+        oglEnable(GL_BLEND, src.enable[0]);
+        if (!src.enable[0]) {
+            return;
         }
+        glBlendFuncSeparate(MaxwellToGL::BlendFunc(src.factor_source_rgb),
+                            MaxwellToGL::BlendFunc(src.factor_dest_rgb),
+                            MaxwellToGL::BlendFunc(src.factor_source_a),
+                            MaxwellToGL::BlendFunc(src.factor_dest_a));
+        glBlendEquationSeparate(MaxwellToGL::BlendEquation(src.equation_rgb),
+                                MaxwellToGL::BlendEquation(src.equation_a));
         return;
     }
 
-    for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
-        auto& blend = state.blend[i];
-        const auto& src = regs.independent_blend[i];
-        blend.enabled = regs.blend.enable[i] != 0;
-        if (!blend.enabled)
+    for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
+        oglEnablei(GL_BLEND, regs.blend.enable[i], static_cast<GLuint>(i));
+        if (!regs.blend.enable[i]) {
             continue;
-        blend.rgb_equation = MaxwellToGL::BlendEquation(src.equation_rgb);
-        blend.src_rgb_func = MaxwellToGL::BlendFunc(src.factor_source_rgb);
-        blend.dst_rgb_func = MaxwellToGL::BlendFunc(src.factor_dest_rgb);
-        blend.a_equation = MaxwellToGL::BlendEquation(src.equation_a);
-        blend.src_a_func = MaxwellToGL::BlendFunc(src.factor_source_a);
-        blend.dst_a_func = MaxwellToGL::BlendFunc(src.factor_dest_a);
+        }
+        const auto& src = regs.independent_blend[i];
+        glBlendFuncSeparatei(static_cast<GLuint>(i), MaxwellToGL::BlendFunc(src.factor_source_rgb),
+                             MaxwellToGL::BlendFunc(src.factor_dest_rgb),
+                             MaxwellToGL::BlendFunc(src.factor_source_a),
+                             MaxwellToGL::BlendFunc(src.factor_dest_a));
+        glBlendEquationSeparatei(static_cast<GLuint>(i),
+                                 MaxwellToGL::BlendEquation(src.equation_rgb),
+                                 MaxwellToGL::BlendEquation(src.equation_a));
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index d62a55b2f7..3cdb9b4a05 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -106,66 +106,6 @@ void OpenGLState::ApplyProgramPipeline() {
     }
 }
 
-void OpenGLState::ApplyGlobalBlending() {
-    const Blend& updated = blend[0];
-    Blend& current = cur_state.blend[0];
-
-    Enable(GL_BLEND, current.enabled, updated.enabled);
-
-    if (current.src_rgb_func != updated.src_rgb_func ||
-        current.dst_rgb_func != updated.dst_rgb_func || current.src_a_func != updated.src_a_func ||
-        current.dst_a_func != updated.dst_a_func) {
-        current.src_rgb_func = updated.src_rgb_func;
-        current.dst_rgb_func = updated.dst_rgb_func;
-        current.src_a_func = updated.src_a_func;
-        current.dst_a_func = updated.dst_a_func;
-        glBlendFuncSeparate(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
-                            updated.dst_a_func);
-    }
-
-    if (current.rgb_equation != updated.rgb_equation || current.a_equation != updated.a_equation) {
-        current.rgb_equation = updated.rgb_equation;
-        current.a_equation = updated.a_equation;
-        glBlendEquationSeparate(updated.rgb_equation, updated.a_equation);
-    }
-}
-
-void OpenGLState::ApplyTargetBlending(std::size_t target, bool force) {
-    const Blend& updated = blend[target];
-    Blend& current = cur_state.blend[target];
-
-    if (current.enabled != updated.enabled || force) {
-        current.enabled = updated.enabled;
-        Enable(GL_BLEND, static_cast<GLuint>(target), updated.enabled);
-    }
-
-    if (UpdateTie(std::tie(current.src_rgb_func, current.dst_rgb_func, current.src_a_func,
-                           current.dst_a_func),
-                  std::tie(updated.src_rgb_func, updated.dst_rgb_func, updated.src_a_func,
-                           updated.dst_a_func))) {
-        glBlendFuncSeparatei(static_cast<GLuint>(target), updated.src_rgb_func,
-                             updated.dst_rgb_func, updated.src_a_func, updated.dst_a_func);
-    }
-
-    if (UpdateTie(std::tie(current.rgb_equation, current.a_equation),
-                  std::tie(updated.rgb_equation, updated.a_equation))) {
-        glBlendEquationSeparatei(static_cast<GLuint>(target), updated.rgb_equation,
-                                 updated.a_equation);
-    }
-}
-
-void OpenGLState::ApplyBlending() {
-    if (independant_blend.enabled) {
-        const bool force = independant_blend.enabled != cur_state.independant_blend.enabled;
-        for (std::size_t target = 0; target < Maxwell::NumRenderTargets; ++target) {
-            ApplyTargetBlending(target, force);
-        }
-    } else {
-        ApplyGlobalBlending();
-    }
-    cur_state.independant_blend.enabled = independant_blend.enabled;
-}
-
 void OpenGLState::ApplyRenderBuffer() {
     if (cur_state.renderbuffer != renderbuffer) {
         cur_state.renderbuffer = renderbuffer;
@@ -206,7 +146,6 @@ void OpenGLState::Apply() {
     ApplyFramebufferState();
     ApplyShaderProgram();
     ApplyProgramPipeline();
-    ApplyBlending();
     ApplyTextures();
     ApplySamplers();
     ApplyImages();
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 18f36da086..29126b80a5 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -13,21 +13,6 @@ namespace OpenGL {
 
 class OpenGLState {
 public:
-    struct Blend {
-        bool enabled = false;              // GL_BLEND
-        GLenum rgb_equation = GL_FUNC_ADD; // GL_BLEND_EQUATION_RGB
-        GLenum a_equation = GL_FUNC_ADD;   // GL_BLEND_EQUATION_ALPHA
-        GLenum src_rgb_func = GL_ONE;      // GL_BLEND_SRC_RGB
-        GLenum dst_rgb_func = GL_ZERO;     // GL_BLEND_DST_RGB
-        GLenum src_a_func = GL_ONE;        // GL_BLEND_SRC_ALPHA
-        GLenum dst_a_func = GL_ZERO;       // GL_BLEND_DST_ALPHA
-    };
-    std::array<Blend, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> blend;
-
-    struct {
-        bool enabled = false;
-    } independant_blend;
-
     static constexpr std::size_t NumSamplers = 32 * 5;
     static constexpr std::size_t NumImages = 8 * 5;
     std::array<GLuint, NumSamplers> textures = {};
@@ -56,9 +41,6 @@ public:
     void ApplyFramebufferState();
     void ApplyShaderProgram();
     void ApplyProgramPipeline();
-    void ApplyTargetBlending(std::size_t target, bool force);
-    void ApplyGlobalBlending();
-    void ApplyBlending();
     void ApplyTextures();
     void ApplySamplers();
     void ApplyImages();
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 85d41f8267..f7a1ca3a9d 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -537,8 +537,11 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
         glDisable(GL_FRAMEBUFFER_SRGB);
     }
     // TODO(Rodrigo): Find out if rasterizer discard affects blits
+    // TODO(Rodrigo): Find out if blending affects blits
+    // TODO(Rodrigo): Find out if clip control affects blits
     glDisable(GL_RASTERIZER_DISCARD);
     glDisablei(GL_SCISSOR_TEST, 0);
+    glDisablei(GL_BLEND, 0);
     glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
 
     u32 buffers{};
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index fcadc09d93..053d8602b4 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -582,6 +582,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glDisable(GL_STENCIL_TEST);
     glDisable(GL_POLYGON_OFFSET_FILL);
     glDisable(GL_RASTERIZER_DISCARD);
+    glDisablei(GL_BLEND, 0);
     glDisablei(GL_SCISSOR_TEST, 0);
     glCullFace(GL_BACK);
     glFrontFace(GL_CW);

From 9677db03da37a61248c2ced49a9a5e53c872cc63 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 04:01:11 -0300
Subject: [PATCH 27/76] gl_state: Remove texture and sampler tracking

---
 .../renderer_opengl/gl_rasterizer.cpp         | 11 +++--
 .../renderer_opengl/gl_resource_manager.cpp   |  3 --
 src/video_core/renderer_opengl/gl_state.cpp   | 42 -------------------
 src/video_core/renderer_opengl/gl_state.h     |  7 ----
 .../renderer_opengl/renderer_opengl.cpp       |  5 ++-
 5 files changed, 8 insertions(+), 60 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index f916f348ff..669a7c3352 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -633,7 +633,6 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
     bind_ubo_pushbuffer.Bind();
     bind_ssbo_pushbuffer.Bind();
 
-    state.ApplyTextures();
     state.ApplyImages();
     state.ApplyShaderProgram();
     state.ApplyProgramPipeline();
@@ -861,20 +860,20 @@ void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextu
     const auto view = texture_cache.GetTextureSurface(texture.tic, entry);
     if (!view) {
         // Can occur when texture addr is null or its memory is unmapped/invalid
-        state.samplers[binding] = 0;
-        state.textures[binding] = 0;
+        glBindSampler(binding, 0);
+        glBindTextureUnit(binding, 0);
         return;
     }
-    state.textures[binding] = view->GetTexture();
+    glBindTextureUnit(binding, view->GetTexture());
 
     if (view->GetSurfaceParams().IsBuffer()) {
         return;
     }
-    state.samplers[binding] = sampler_cache.GetSampler(texture.tsc);
-
     // Apply swizzle to textures that are not buffers.
     view->ApplySwizzle(texture.tic.x_source, texture.tic.y_source, texture.tic.z_source,
                        texture.tic.w_source);
+
+    glBindSampler(binding, sampler_cache.GetSampler(texture.tsc));
 }
 
 void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, const Shader& shader) {
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index 00355c1daa..80666a9ed5 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -47,7 +47,6 @@ void OGLTexture::Release() {
 
     MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
     glDeleteTextures(1, &handle);
-    OpenGLState::GetCurState().UnbindTexture(handle).Apply();
     handle = 0;
 }
 
@@ -65,7 +64,6 @@ void OGLTextureView::Release() {
 
     MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
     glDeleteTextures(1, &handle);
-    OpenGLState::GetCurState().UnbindTexture(handle).Apply();
     handle = 0;
 }
 
@@ -83,7 +81,6 @@ void OGLSampler::Release() {
 
     MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
     glDeleteSamplers(1, &handle);
-    OpenGLState::GetCurState().ResetSampler(handle).Apply();
     handle = 0;
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 3cdb9b4a05..98de72b5f1 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -113,28 +113,6 @@ void OpenGLState::ApplyRenderBuffer() {
     }
 }
 
-void OpenGLState::ApplyTextures() {
-    const std::size_t size = std::size(textures);
-    for (std::size_t i = 0; i < size; ++i) {
-        if (UpdateValue(cur_state.textures[i], textures[i])) {
-            // BindTextureUnit doesn't support binding null textures, skip those binds.
-            // TODO(Rodrigo): Stop using null textures
-            if (textures[i] != 0) {
-                glBindTextureUnit(static_cast<GLuint>(i), textures[i]);
-            }
-        }
-    }
-}
-
-void OpenGLState::ApplySamplers() {
-    const std::size_t size = std::size(samplers);
-    for (std::size_t i = 0; i < size; ++i) {
-        if (UpdateValue(cur_state.samplers[i], samplers[i])) {
-            glBindSampler(static_cast<GLuint>(i), samplers[i]);
-        }
-    }
-}
-
 void OpenGLState::ApplyImages() {
     if (const auto update = UpdateArray(cur_state.images, images)) {
         glBindImageTextures(update->first, update->second, images.data() + update->first);
@@ -146,30 +124,10 @@ void OpenGLState::Apply() {
     ApplyFramebufferState();
     ApplyShaderProgram();
     ApplyProgramPipeline();
-    ApplyTextures();
-    ApplySamplers();
     ApplyImages();
     ApplyRenderBuffer();
 }
 
-OpenGLState& OpenGLState::UnbindTexture(GLuint handle) {
-    for (auto& texture : textures) {
-        if (texture == handle) {
-            texture = 0;
-        }
-    }
-    return *this;
-}
-
-OpenGLState& OpenGLState::ResetSampler(GLuint handle) {
-    for (auto& sampler : samplers) {
-        if (sampler == handle) {
-            sampler = 0;
-        }
-    }
-    return *this;
-}
-
 OpenGLState& OpenGLState::ResetProgram(GLuint handle) {
     if (draw.shader_program == handle) {
         draw.shader_program = 0;
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 29126b80a5..25dd56452a 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -13,10 +13,7 @@ namespace OpenGL {
 
 class OpenGLState {
 public:
-    static constexpr std::size_t NumSamplers = 32 * 5;
     static constexpr std::size_t NumImages = 8 * 5;
-    std::array<GLuint, NumSamplers> textures = {};
-    std::array<GLuint, NumSamplers> samplers = {};
     std::array<GLuint, NumImages> images = {};
 
     struct {
@@ -41,14 +38,10 @@ public:
     void ApplyFramebufferState();
     void ApplyShaderProgram();
     void ApplyProgramPipeline();
-    void ApplyTextures();
-    void ApplySamplers();
     void ApplyImages();
     void ApplyRenderBuffer();
 
     /// Resets any references to the given resource
-    OpenGLState& UnbindTexture(GLuint handle);
-    OpenGLState& ResetSampler(GLuint handle);
     OpenGLState& ResetProgram(GLuint handle);
     OpenGLState& ResetPipeline(GLuint handle);
     OpenGLState& ResetFramebuffer(GLuint handle);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 053d8602b4..1295121f5b 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -566,7 +566,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     };
     glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
 
-    state.textures[0] = screen_info.display_texture;
     state.Apply();
 
     // TODO: Signal state tracker about these changes
@@ -598,11 +597,13 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glVertexAttribBinding(TexCoordLocation, 0);
     glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex));
 
+    glBindTextureUnit(0, screen_info.display_texture);
+    glBindSampler(0, 0);
+
     glClear(GL_COLOR_BUFFER_BIT);
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
     // Restore default state
-    state.textures[0] = 0;
     state.Apply();
 }
 

From 17a7fa751b34ae67c600bd741969dc1bde40fbf3 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 04:17:02 -0300
Subject: [PATCH 28/76] gl_state: Remove image tracking

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp   |  6 +++---
 src/video_core/renderer_opengl/gl_state.cpp        |  7 -------
 src/video_core/renderer_opengl/gl_state.h          |  4 ----
 .../renderer_opengl/gl_texture_cache.cpp           | 14 ++++----------
 src/video_core/renderer_opengl/gl_texture_cache.h  |  5 +++++
 5 files changed, 12 insertions(+), 24 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 669a7c3352..1f94f759d7 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -633,7 +633,6 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
     bind_ubo_pushbuffer.Bind();
     bind_ssbo_pushbuffer.Bind();
 
-    state.ApplyImages();
     state.ApplyShaderProgram();
     state.ApplyProgramPipeline();
 
@@ -899,7 +898,7 @@ void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& t
                                   const GLShader::ImageEntry& entry) {
     const auto view = texture_cache.GetImageSurface(tic, entry);
     if (!view) {
-        state.images[binding] = 0;
+        glBindImageTexture(binding, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R8);
         return;
     }
     if (!tic.IsBuffer()) {
@@ -908,7 +907,8 @@ void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& t
     if (entry.IsWritten()) {
         view->MarkAsModified(texture_cache.Tick());
     }
-    state.images[binding] = view->GetTexture();
+    glBindImageTexture(binding, view->GetTexture(), 0, GL_TRUE, 0, GL_READ_WRITE,
+                       view->GetFormat());
 }
 
 void RasterizerOpenGL::SyncViewport() {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 98de72b5f1..7bb87f7ba8 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -113,18 +113,11 @@ void OpenGLState::ApplyRenderBuffer() {
     }
 }
 
-void OpenGLState::ApplyImages() {
-    if (const auto update = UpdateArray(cur_state.images, images)) {
-        glBindImageTextures(update->first, update->second, images.data() + update->first);
-    }
-}
-
 void OpenGLState::Apply() {
     MICROPROFILE_SCOPE(OpenGL_State);
     ApplyFramebufferState();
     ApplyShaderProgram();
     ApplyProgramPipeline();
-    ApplyImages();
     ApplyRenderBuffer();
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 25dd56452a..4e34fcfc24 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -13,9 +13,6 @@ namespace OpenGL {
 
 class OpenGLState {
 public:
-    static constexpr std::size_t NumImages = 8 * 5;
-    std::array<GLuint, NumImages> images = {};
-
     struct {
         GLuint read_framebuffer = 0; // GL_READ_FRAMEBUFFER_BINDING
         GLuint draw_framebuffer = 0; // GL_DRAW_FRAMEBUFFER_BINDING
@@ -38,7 +35,6 @@ public:
     void ApplyFramebufferState();
     void ApplyShaderProgram();
     void ApplyProgramPipeline();
-    void ApplyImages();
     void ApplyRenderBuffer();
 
     /// Resets any references to the given resource
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index f7a1ca3a9d..ca407c1b30 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -397,6 +397,7 @@ CachedSurfaceView::CachedSurfaceView(CachedSurface& surface, const ViewParams& p
                                      const bool is_proxy)
     : VideoCommon::ViewBase(params), surface{surface}, is_proxy{is_proxy} {
     target = GetTextureTarget(params.target);
+    format = GetFormatTuple(surface.GetSurfaceParams().pixel_format).internal_format;
     if (!is_proxy) {
         texture_view = CreateTextureView();
     }
@@ -467,17 +468,12 @@ void CachedSurfaceView::ApplySwizzle(SwizzleSource x_source, SwizzleSource y_sou
 }
 
 OGLTextureView CachedSurfaceView::CreateTextureView() const {
-    const auto& owner_params = surface.GetSurfaceParams();
     OGLTextureView texture_view;
     texture_view.Create();
 
-    const GLuint handle{texture_view.handle};
-    const FormatTuple& tuple{GetFormatTuple(owner_params.pixel_format)};
-
-    glTextureView(handle, target, surface.texture.handle, tuple.internal_format, params.base_level,
+    glTextureView(texture_view.handle, target, surface.texture.handle, format, params.base_level,
                   params.num_levels, params.base_layer, params.num_layers);
-
-    ApplyTextureDefaults(owner_params, handle);
+    ApplyTextureDefaults(surface.GetSurfaceParams(), texture_view.handle);
 
     return texture_view;
 }
@@ -521,9 +517,7 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     const auto& dst_params{dst_view->GetSurfaceParams()};
 
     OpenGLState prev_state{OpenGLState::GetCurState()};
-    SCOPE_EXIT({
-        prev_state.Apply();
-    });
+    SCOPE_EXIT({ prev_state.Apply(); });
 
     OpenGLState state;
     state.draw.read_framebuffer = src_framebuffer.handle;
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 8e13ab38bb..303534ca6d 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -96,6 +96,10 @@ public:
         return texture_view.handle;
     }
 
+    GLenum GetFormat() const {
+        return format;
+    }
+
     const SurfaceParams& GetSurfaceParams() const {
         return surface.GetSurfaceParams();
     }
@@ -113,6 +117,7 @@ private:
 
     CachedSurface& surface;
     GLenum target{};
+    GLenum format{};
 
     OGLTextureView texture_view;
     u32 swizzle{};

From 5ccb07933ac0f33d0ecb909d29bf65ee4ab70d8e Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 05:01:43 -0300
Subject: [PATCH 29/76] gl_state: Remove framebuffer tracking

---
 .../renderer_opengl/gl_framebuffer_cache.cpp  |  3 +-
 .../renderer_opengl/gl_rasterizer.cpp         |  5 ++-
 .../renderer_opengl/gl_resource_manager.cpp   |  4 +--
 src/video_core/renderer_opengl/gl_state.cpp   | 35 -------------------
 src/video_core/renderer_opengl/gl_state.h     |  8 -----
 .../renderer_opengl/gl_texture_cache.cpp      | 17 +++------
 .../renderer_opengl/renderer_opengl.cpp       | 33 ++++++++---------
 7 files changed, 23 insertions(+), 82 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp b/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
index 874ed3c6e7..d624a62723 100644
--- a/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
@@ -36,8 +36,7 @@ OGLFramebuffer FramebufferCacheOpenGL::CreateFramebuffer(const FramebufferCacheK
     framebuffer.Create();
 
     // TODO(Rodrigo): Use DSA here after Nvidia fixes their framebuffer DSA bugs.
-    local_state.draw.draw_framebuffer = framebuffer.handle;
-    local_state.ApplyFramebufferState();
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle);
 
     if (key.zeta) {
         const bool stencil = key.zeta->GetSurfaceParams().type == SurfaceType::DepthStencil;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 1f94f759d7..b826146fbb 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -359,7 +359,7 @@ void RasterizerOpenGL::ConfigureFramebuffers() {
 
     texture_cache.GuardRenderTargets(false);
 
-    state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(key);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer_cache.GetFramebuffer(key));
 }
 
 void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb,
@@ -384,8 +384,7 @@ void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, boo
     key.colors[0] = color_surface;
     key.zeta = depth_surface;
 
-    current_state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(key);
-    current_state.ApplyFramebufferState();
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer_cache.GetFramebuffer(key));
 }
 
 void RasterizerOpenGL::Clear() {
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index 80666a9ed5..21ea6fde46 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -20,7 +20,7 @@ void OGLRenderbuffer::Create() {
         return;
 
     MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
-    glGenRenderbuffers(1, &handle);
+    glCreateRenderbuffers(1, &handle);
 }
 
 void OGLRenderbuffer::Release() {
@@ -29,7 +29,6 @@ void OGLRenderbuffer::Release() {
 
     MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
     glDeleteRenderbuffers(1, &handle);
-    OpenGLState::GetCurState().ResetRenderbuffer(handle).Apply();
     handle = 0;
 }
 
@@ -200,7 +199,6 @@ void OGLFramebuffer::Release() {
 
     MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
     glDeleteFramebuffers(1, &handle);
-    OpenGLState::GetCurState().ResetFramebuffer(handle).Apply();
     handle = 0;
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index 7bb87f7ba8..be4ca6bf01 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -85,15 +85,6 @@ void Enable(GLenum cap, GLuint index, bool& current_value, bool new_value) {
 
 OpenGLState::OpenGLState() = default;
 
-void OpenGLState::ApplyFramebufferState() {
-    if (UpdateValue(cur_state.draw.read_framebuffer, draw.read_framebuffer)) {
-        glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer);
-    }
-    if (UpdateValue(cur_state.draw.draw_framebuffer, draw.draw_framebuffer)) {
-        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, draw.draw_framebuffer);
-    }
-}
-
 void OpenGLState::ApplyShaderProgram() {
     if (UpdateValue(cur_state.draw.shader_program, draw.shader_program)) {
         glUseProgram(draw.shader_program);
@@ -106,19 +97,10 @@ void OpenGLState::ApplyProgramPipeline() {
     }
 }
 
-void OpenGLState::ApplyRenderBuffer() {
-    if (cur_state.renderbuffer != renderbuffer) {
-        cur_state.renderbuffer = renderbuffer;
-        glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
-    }
-}
-
 void OpenGLState::Apply() {
     MICROPROFILE_SCOPE(OpenGL_State);
-    ApplyFramebufferState();
     ApplyShaderProgram();
     ApplyProgramPipeline();
-    ApplyRenderBuffer();
 }
 
 OpenGLState& OpenGLState::ResetProgram(GLuint handle) {
@@ -135,21 +117,4 @@ OpenGLState& OpenGLState::ResetPipeline(GLuint handle) {
     return *this;
 }
 
-OpenGLState& OpenGLState::ResetFramebuffer(GLuint handle) {
-    if (draw.read_framebuffer == handle) {
-        draw.read_framebuffer = 0;
-    }
-    if (draw.draw_framebuffer == handle) {
-        draw.draw_framebuffer = 0;
-    }
-    return *this;
-}
-
-OpenGLState& OpenGLState::ResetRenderbuffer(GLuint handle) {
-    if (renderbuffer == handle) {
-        renderbuffer = 0;
-    }
-    return *this;
-}
-
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index 4e34fcfc24..c9a8397362 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -14,14 +14,10 @@ namespace OpenGL {
 class OpenGLState {
 public:
     struct {
-        GLuint read_framebuffer = 0; // GL_READ_FRAMEBUFFER_BINDING
-        GLuint draw_framebuffer = 0; // GL_DRAW_FRAMEBUFFER_BINDING
         GLuint shader_program = 0;   // GL_CURRENT_PROGRAM
         GLuint program_pipeline = 0; // GL_PROGRAM_PIPELINE_BINDING
     } draw;
 
-    GLuint renderbuffer{}; // GL_RENDERBUFFER_BINDING
-
     OpenGLState();
 
     /// Get the currently active OpenGL state
@@ -32,16 +28,12 @@ public:
     /// Apply this state as the current OpenGL state
     void Apply();
 
-    void ApplyFramebufferState();
     void ApplyShaderProgram();
     void ApplyProgramPipeline();
-    void ApplyRenderBuffer();
 
     /// Resets any references to the given resource
     OpenGLState& ResetProgram(GLuint handle);
     OpenGLState& ResetPipeline(GLuint handle);
-    OpenGLState& ResetFramebuffer(GLuint handle);
-    OpenGLState& ResetRenderbuffer(GLuint handle);
 
 private:
     static OpenGLState cur_state;
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index ca407c1b30..5fe6aa5d69 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -515,14 +515,8 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
                                    const Tegra::Engines::Fermi2D::Config& copy_config) {
     const auto& src_params{src_view->GetSurfaceParams()};
     const auto& dst_params{dst_view->GetSurfaceParams()};
-
-    OpenGLState prev_state{OpenGLState::GetCurState()};
-    SCOPE_EXIT({ prev_state.Apply(); });
-
-    OpenGLState state;
-    state.draw.read_framebuffer = src_framebuffer.handle;
-    state.draw.draw_framebuffer = dst_framebuffer.handle;
-    state.Apply();
+    UNIMPLEMENTED_IF(src_params.target == SurfaceTarget::Texture3D);
+    UNIMPLEMENTED_IF(dst_params.target == SurfaceTarget::Texture3D);
 
     // TODO: Signal state tracker about these changes
     if (dst_params.srgb_conversion) {
@@ -538,11 +532,10 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     glDisablei(GL_BLEND, 0);
     glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
 
-    u32 buffers{};
-
-    UNIMPLEMENTED_IF(src_params.target == SurfaceTarget::Texture3D);
-    UNIMPLEMENTED_IF(dst_params.target == SurfaceTarget::Texture3D);
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, src_framebuffer.handle);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_framebuffer.handle);
 
+    GLenum buffers = 0;
     if (src_params.type == SurfaceType::ColorTexture) {
         src_view->Attach(GL_COLOR_ATTACHMENT0, GL_READ_FRAMEBUFFER);
         glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 1295121f5b..da4b4bf342 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -86,28 +86,22 @@ public:
     }
 
     void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
-        OpenGLState prev_state = OpenGLState::GetCurState();
-        OpenGLState state = OpenGLState::GetCurState();
-
         // Recreate the color texture attachment
         frame->color.Release();
         frame->color.Create();
-        state.renderbuffer = frame->color.handle;
-        state.Apply();
-        glRenderbufferStorage(GL_RENDERBUFFER, frame->is_srgb ? GL_SRGB8 : GL_RGB8, width, height);
+        const GLenum internal_format = frame->is_srgb ? GL_SRGB8 : GL_RGB8;
+        glNamedRenderbufferStorage(frame->color.handle, internal_format, width, height);
 
         // Recreate the FBO for the render target
         frame->render.Release();
         frame->render.Create();
-        state.draw.read_framebuffer = frame->render.handle;
-        state.draw.draw_framebuffer = frame->render.handle;
-        state.Apply();
+        glBindFramebuffer(GL_FRAMEBUFFER, frame->render.handle);
         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
                                   frame->color.handle);
         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
             LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
         }
-        prev_state.Apply();
+
         frame->width = width;
         frame->height = height;
         frame->color_reloaded = true;
@@ -353,8 +347,7 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
             frame->is_srgb = screen_info.display_srgb;
             frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height);
         }
-        state.draw.draw_framebuffer = frame->render.handle;
-        state.Apply();
+        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame->render.handle);
         DrawScreen(layout);
         // Create a fence for the frontend to wait on and swap this frame to OffTex
         frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
@@ -647,12 +640,14 @@ void RendererOpenGL::RenderScreenshot() {
         return;
     }
 
+    GLint old_read_fb;
+    GLint old_draw_fb;
+    glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb);
+    glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb);
+
     // Draw the current frame to the screenshot framebuffer
     screenshot_framebuffer.Create();
-    GLuint old_read_fb = state.draw.read_framebuffer;
-    GLuint old_draw_fb = state.draw.draw_framebuffer;
-    state.draw.read_framebuffer = state.draw.draw_framebuffer = screenshot_framebuffer.handle;
-    state.Apply();
+    glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);
 
     Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
 
@@ -669,11 +664,11 @@ void RendererOpenGL::RenderScreenshot() {
                  renderer_settings.screenshot_bits);
 
     screenshot_framebuffer.Release();
-    state.draw.read_framebuffer = old_read_fb;
-    state.draw.draw_framebuffer = old_draw_fb;
-    state.Apply();
     glDeleteRenderbuffers(1, &renderbuffer);
 
+    glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb);
+
     renderer_settings.screenshot_complete_callback();
     renderer_settings.screenshot_requested = false;
 }

From 1c4bf9cbfa4e4303890f73be11eb1dee88d070fe Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 16:04:41 -0300
Subject: [PATCH 30/76] gl_state: Remove program tracking

---
 .../renderer_opengl/gl_rasterizer.cpp         | 28 ++++++--------
 .../renderer_opengl/gl_rasterizer.h           |  5 +--
 .../renderer_opengl/gl_resource_manager.cpp   |  2 -
 .../renderer_opengl/gl_shader_manager.cpp     | 18 +++------
 .../renderer_opengl/gl_shader_manager.h       | 17 ++++-----
 src/video_core/renderer_opengl/gl_state.cpp   | 32 +---------------
 src/video_core/renderer_opengl/gl_state.h     | 12 ------
 .../renderer_opengl/renderer_opengl.cpp       | 37 +++++++++++++++----
 .../renderer_opengl/renderer_opengl.h         |  7 +++-
 9 files changed, 63 insertions(+), 95 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index b826146fbb..84c4d110c1 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -99,14 +99,11 @@ void oglEnablei(GLenum cap, bool state, GLuint index) {
 } // Anonymous namespace
 
 RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
-                                   ScreenInfo& info)
+                                   ScreenInfo& info, GLShader::ProgramManager& program_manager)
     : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device},
       shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, system{system},
-      screen_info{info}, buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
-    shader_program_manager = std::make_unique<GLShader::ProgramManager>();
-    state.draw.shader_program = 0;
-    state.Apply();
-
+      screen_info{info}, program_manager{program_manager}, buffer_cache{*this, system, device,
+                                                                        STREAM_BUFFER_SIZE} {
     CheckExtensions();
 }
 
@@ -228,10 +225,10 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
         if (!gpu.regs.IsShaderConfigEnabled(index)) {
             switch (program) {
             case Maxwell::ShaderProgram::Geometry:
-                shader_program_manager->UseTrivialGeometryShader();
+                program_manager.UseGeometryShader(0);
                 break;
             case Maxwell::ShaderProgram::Fragment:
-                shader_program_manager->UseTrivialFragmentShader();
+                program_manager.UseFragmentShader(0);
                 break;
             default:
                 break;
@@ -262,13 +259,13 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
         switch (program) {
         case Maxwell::ShaderProgram::VertexA:
         case Maxwell::ShaderProgram::VertexB:
-            shader_program_manager->UseProgrammableVertexShader(program_handle);
+            program_manager.UseVertexShader(program_handle);
             break;
         case Maxwell::ShaderProgram::Geometry:
-            shader_program_manager->UseProgrammableGeometryShader(program_handle);
+            program_manager.UseGeometryShader(program_handle);
             break;
         case Maxwell::ShaderProgram::Fragment:
-            shader_program_manager->UseProgrammableFragmentShader(program_handle);
+            program_manager.UseFragmentShader(program_handle);
             break;
         default:
             UNIMPLEMENTED_MSG("Unimplemented shader index={}, enable={}, offset=0x{:08X}", index,
@@ -550,7 +547,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
     bind_ubo_pushbuffer.Bind();
     bind_ssbo_pushbuffer.Bind();
 
-    shader_program_manager->ApplyTo(state);
+    program_manager.Update();
     state.Apply();
 
     if (texture_cache.TextureBarrier()) {
@@ -613,8 +610,8 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
     const ProgramVariant variant(launch_desc.block_dim_x, launch_desc.block_dim_y,
                                  launch_desc.block_dim_z, launch_desc.shared_alloc,
                                  launch_desc.local_pos_alloc);
-    state.draw.shader_program = kernel->GetHandle(variant);
-    state.draw.program_pipeline = 0;
+    glUseProgramStages(program_manager.GetHandle(), GL_COMPUTE_SHADER_BIT,
+                       kernel->GetHandle(variant));
 
     const std::size_t buffer_size =
         Tegra::Engines::KeplerCompute::NumConstBuffers *
@@ -632,9 +629,6 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
     bind_ubo_pushbuffer.Bind();
     bind_ssbo_pushbuffer.Bind();
 
-    state.ApplyShaderProgram();
-    state.ApplyProgramPipeline();
-
     glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z);
     ++num_queued_commands;
 }
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index de21a35114..b8158c152f 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -55,7 +55,7 @@ struct DrawParameters;
 class RasterizerOpenGL : public VideoCore::RasterizerAccelerated {
 public:
     explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
-                              ScreenInfo& info);
+                              ScreenInfo& info, GLShader::ProgramManager& program_manager);
     ~RasterizerOpenGL() override;
 
     void Draw(bool is_indexed, bool is_instanced) override;
@@ -218,8 +218,7 @@ private:
 
     Core::System& system;
     ScreenInfo& screen_info;
-
-    std::unique_ptr<GLShader::ProgramManager> shader_program_manager;
+    GLShader::ProgramManager& program_manager;
 
     static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
     OGLBufferCache buffer_cache;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index 21ea6fde46..404fd65567 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -123,7 +123,6 @@ void OGLProgram::Release() {
 
     MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
     glDeleteProgram(handle);
-    OpenGLState::GetCurState().ResetProgram(handle).Apply();
     handle = 0;
 }
 
@@ -141,7 +140,6 @@ void OGLPipeline::Release() {
 
     MICROPROFILE_SCOPE(OpenGL_ResourceDeletion);
     glDeleteProgramPipelines(1, &handle);
-    OpenGLState::GetCurState().ResetPipeline(handle).Apply();
     handle = 0;
 }
 
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 75d3fac042..15f3cd0669 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -10,27 +10,21 @@ namespace OpenGL::GLShader {
 
 using Tegra::Engines::Maxwell3D;
 
-ProgramManager::ProgramManager() {
+ProgramManager::~ProgramManager() = default;
+
+void ProgramManager::Create() {
     pipeline.Create();
 }
 
-ProgramManager::~ProgramManager() = default;
-
-void ProgramManager::ApplyTo(OpenGLState& state) {
-    UpdatePipeline();
-    state.draw.shader_program = 0;
-    state.draw.program_pipeline = pipeline.handle;
-}
-
-void ProgramManager::UpdatePipeline() {
+void ProgramManager::Update() {
     // Avoid updating the pipeline when values have no changed
     if (old_state == current_state) {
         return;
     }
 
     // Workaround for AMD bug
-    constexpr GLenum all_used_stages{GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT |
-                                     GL_FRAGMENT_SHADER_BIT};
+    static constexpr GLenum all_used_stages{GL_VERTEX_SHADER_BIT | GL_GEOMETRY_SHADER_BIT |
+                                            GL_FRAGMENT_SHADER_BIT};
     glUseProgramStages(pipeline.handle, all_used_stages, 0);
 
     glUseProgramStages(pipeline.handle, GL_VERTEX_SHADER_BIT, current_state.vertex_shader);
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 478c165ce9..db23a27e85 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -29,25 +29,26 @@ static_assert(sizeof(MaxwellUniformData) < 16384,
 
 class ProgramManager {
 public:
-    explicit ProgramManager();
     ~ProgramManager();
 
-    void ApplyTo(OpenGLState& state);
+    void Create();
 
-    void UseProgrammableVertexShader(GLuint program) {
+    void Update();
+
+    void UseVertexShader(GLuint program) {
         current_state.vertex_shader = program;
     }
 
-    void UseProgrammableGeometryShader(GLuint program) {
+    void UseGeometryShader(GLuint program) {
         current_state.geometry_shader = program;
     }
 
-    void UseProgrammableFragmentShader(GLuint program) {
+    void UseFragmentShader(GLuint program) {
         current_state.fragment_shader = program;
     }
 
-    void UseTrivialGeometryShader() {
-        current_state.geometry_shader = 0;
+    GLuint GetHandle() const {
+        return pipeline.handle;
     }
 
     void UseTrivialFragmentShader() {
@@ -70,8 +71,6 @@ private:
         GLuint geometry_shader{};
     };
 
-    void UpdatePipeline();
-
     OGLPipeline pipeline;
     PipelineState current_state;
     PipelineState old_state;
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
index be4ca6bf01..e8a23d41d0 100644
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ b/src/video_core/renderer_opengl/gl_state.cpp
@@ -85,36 +85,6 @@ void Enable(GLenum cap, GLuint index, bool& current_value, bool new_value) {
 
 OpenGLState::OpenGLState() = default;
 
-void OpenGLState::ApplyShaderProgram() {
-    if (UpdateValue(cur_state.draw.shader_program, draw.shader_program)) {
-        glUseProgram(draw.shader_program);
-    }
-}
-
-void OpenGLState::ApplyProgramPipeline() {
-    if (UpdateValue(cur_state.draw.program_pipeline, draw.program_pipeline)) {
-        glBindProgramPipeline(draw.program_pipeline);
-    }
-}
-
-void OpenGLState::Apply() {
-    MICROPROFILE_SCOPE(OpenGL_State);
-    ApplyShaderProgram();
-    ApplyProgramPipeline();
-}
-
-OpenGLState& OpenGLState::ResetProgram(GLuint handle) {
-    if (draw.shader_program == handle) {
-        draw.shader_program = 0;
-    }
-    return *this;
-}
-
-OpenGLState& OpenGLState::ResetPipeline(GLuint handle) {
-    if (draw.program_pipeline == handle) {
-        draw.program_pipeline = 0;
-    }
-    return *this;
-}
+void OpenGLState::Apply() {}
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
index c9a8397362..f62e2e2e19 100644
--- a/src/video_core/renderer_opengl/gl_state.h
+++ b/src/video_core/renderer_opengl/gl_state.h
@@ -13,11 +13,6 @@ namespace OpenGL {
 
 class OpenGLState {
 public:
-    struct {
-        GLuint shader_program = 0;   // GL_CURRENT_PROGRAM
-        GLuint program_pipeline = 0; // GL_PROGRAM_PIPELINE_BINDING
-    } draw;
-
     OpenGLState();
 
     /// Get the currently active OpenGL state
@@ -28,13 +23,6 @@ public:
     /// Apply this state as the current OpenGL state
     void Apply();
 
-    void ApplyShaderProgram();
-    void ApplyProgramPipeline();
-
-    /// Resets any references to the given resource
-    OpenGLState& ResetProgram(GLuint handle);
-    OpenGLState& ResetPipeline(GLuint handle);
-
 private:
     static OpenGLState cur_state;
 };
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index da4b4bf342..ace3aec882 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -20,6 +20,7 @@
 #include "core/telemetry_session.h"
 #include "video_core/morton.h"
 #include "video_core/renderer_opengl/gl_rasterizer.h"
+#include "video_core/renderer_opengl/gl_shader_manager.h"
 #include "video_core/renderer_opengl/renderer_opengl.h"
 
 namespace OpenGL {
@@ -158,9 +159,13 @@ public:
 
 namespace {
 
-constexpr char vertex_shader[] = R"(
+constexpr char VERTEX_SHADER[] = R"(
 #version 430 core
 
+out gl_PerVertex {
+    vec4 gl_Position;
+};
+
 layout (location = 0) in vec2 vert_position;
 layout (location = 1) in vec2 vert_tex_coord;
 layout (location = 0) out vec2 frag_tex_coord;
@@ -181,7 +186,7 @@ void main() {
 }
 )";
 
-constexpr char fragment_shader[] = R"(
+constexpr char FRAGMENT_SHADER[] = R"(
 #version 430 core
 
 layout (location = 0) in vec2 frag_tex_coord;
@@ -426,10 +431,19 @@ void RendererOpenGL::InitOpenGLObjects() {
     glClearColor(Settings::values.bg_red, Settings::values.bg_green, Settings::values.bg_blue,
                  0.0f);
 
-    // Link shaders and get variable locations
-    shader.CreateFromSource(vertex_shader, nullptr, fragment_shader);
-    state.draw.shader_program = shader.handle;
-    state.Apply();
+    // Create shader programs
+    OGLShader vertex_shader;
+    vertex_shader.Create(VERTEX_SHADER, GL_VERTEX_SHADER);
+
+    OGLShader fragment_shader;
+    fragment_shader.Create(FRAGMENT_SHADER, GL_FRAGMENT_SHADER);
+
+    vertex_program.Create(true, false, vertex_shader.handle);
+    fragment_program.Create(true, false, fragment_shader.handle);
+
+    // Create program pipeline
+    program_manager.Create();
+    glBindProgramPipeline(program_manager.GetHandle());
 
     // Generate VBO handle for drawing
     vertex_buffer.Create();
@@ -468,7 +482,8 @@ void RendererOpenGL::CreateRasterizer() {
     if (rasterizer) {
         return;
     }
-    rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info);
+    rasterizer =
+        std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info, program_manager);
 }
 
 void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
@@ -517,7 +532,8 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     // Set projection matrix
     const std::array ortho_matrix =
         MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height));
-    glUniformMatrix3x2fv(ModelViewMatrixLocation, 1, GL_FALSE, ortho_matrix.data());
+    glProgramUniformMatrix3x2fv(vertex_program.handle, ModelViewMatrixLocation, 1, GL_FALSE,
+                                std::data(ortho_matrix));
 
     const auto& texcoords = screen_info.display_texcoords;
     auto left = texcoords.left;
@@ -562,6 +578,11 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     state.Apply();
 
     // TODO: Signal state tracker about these changes
+    program_manager.UseVertexShader(vertex_program.handle);
+    program_manager.UseGeometryShader(0);
+    program_manager.UseFragmentShader(fragment_program.handle);
+    program_manager.Update();
+
     glEnable(GL_CULL_FACE);
     if (screen_info.display_srgb) {
         glEnable(GL_FRAMEBUFFER_SRGB);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 42a2141d85..f1225269f2 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -10,6 +10,7 @@
 #include "common/math_util.h"
 #include "video_core/renderer_base.h"
 #include "video_core/renderer_opengl/gl_resource_manager.h"
+#include "video_core/renderer_opengl/gl_shader_manager.h"
 #include "video_core/renderer_opengl/gl_state.h"
 
 namespace Core {
@@ -95,12 +96,16 @@ private:
 
     // OpenGL object IDs
     OGLBuffer vertex_buffer;
-    OGLProgram shader;
+    OGLProgram vertex_program;
+    OGLProgram fragment_program;
     OGLFramebuffer screenshot_framebuffer;
 
     /// Display information for Switch screen
     ScreenInfo screen_info;
 
+    /// Global dummy shader pipeline
+    GLShader::ProgramManager program_manager;
+
     /// OpenGL framebuffer data
     std::vector<u8> gl_framebuffer_data;
 

From b92dfcd7f2a03b04a1d6090d7dd2684986f7adee Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 16:15:24 -0300
Subject: [PATCH 31/76] gl_state: Remove completely

---
 src/video_core/CMakeLists.txt                 |  2 -
 .../renderer_opengl/gl_framebuffer_cache.cpp  |  1 -
 .../renderer_opengl/gl_framebuffer_cache.h    |  2 -
 .../renderer_opengl/gl_rasterizer.cpp         | 13 +--
 .../renderer_opengl/gl_rasterizer.h           |  5 +-
 .../renderer_opengl/gl_resource_manager.cpp   |  1 -
 .../renderer_opengl/gl_shader_manager.h       |  1 -
 src/video_core/renderer_opengl/gl_state.cpp   | 90 -------------------
 src/video_core/renderer_opengl/gl_state.h     | 31 -------
 .../renderer_opengl/gl_stream_buffer.cpp      |  1 -
 .../renderer_opengl/gl_texture_cache.cpp      |  1 -
 .../renderer_opengl/renderer_opengl.cpp       |  5 --
 .../renderer_opengl/renderer_opengl.h         |  3 -
 13 files changed, 4 insertions(+), 152 deletions(-)
 delete mode 100644 src/video_core/renderer_opengl/gl_state.cpp
 delete mode 100644 src/video_core/renderer_opengl/gl_state.h

diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index db65e7bf3f..b897528824 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -71,8 +71,6 @@ add_library(video_core STATIC
     renderer_opengl/gl_shader_util.h
     renderer_opengl/gl_state_tracker.cpp
     renderer_opengl/gl_state_tracker.h
-    renderer_opengl/gl_state.cpp
-    renderer_opengl/gl_state.h
     renderer_opengl/gl_stream_buffer.cpp
     renderer_opengl/gl_stream_buffer.h
     renderer_opengl/gl_texture_cache.cpp
diff --git a/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp b/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
index d624a62723..b8a512cb64 100644
--- a/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
@@ -11,7 +11,6 @@
 #include "common/common_types.h"
 #include "video_core/engines/maxwell_3d.h"
 #include "video_core/renderer_opengl/gl_framebuffer_cache.h"
-#include "video_core/renderer_opengl/gl_state.h"
 
 namespace OpenGL {
 
diff --git a/src/video_core/renderer_opengl/gl_framebuffer_cache.h b/src/video_core/renderer_opengl/gl_framebuffer_cache.h
index 02ec80ae9a..8f698fee0a 100644
--- a/src/video_core/renderer_opengl/gl_framebuffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_framebuffer_cache.h
@@ -13,7 +13,6 @@
 #include "common/common_types.h"
 #include "video_core/engines/maxwell_3d.h"
 #include "video_core/renderer_opengl/gl_resource_manager.h"
-#include "video_core/renderer_opengl/gl_state.h"
 #include "video_core/renderer_opengl/gl_texture_cache.h"
 
 namespace OpenGL {
@@ -63,7 +62,6 @@ public:
 private:
     OGLFramebuffer CreateFramebuffer(const FramebufferCacheKey& key);
 
-    OpenGLState local_state;
     std::unordered_map<FramebufferCacheKey, OGLFramebuffer> cache;
 };
 
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 84c4d110c1..3ce2a7124b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -359,8 +359,8 @@ void RasterizerOpenGL::ConfigureFramebuffers() {
     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer_cache.GetFramebuffer(key));
 }
 
-void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb,
-                                                 bool using_depth_fb, bool using_stencil_fb) {
+void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color_fb, bool using_depth_fb,
+                                                 bool using_stencil_fb) {
     using VideoCore::Surface::SurfaceType;
 
     auto& gpu = system.GPU().Maxwell3D();
@@ -396,10 +396,6 @@ void RasterizerOpenGL::Clear() {
     bool use_depth{};
     bool use_stencil{};
 
-    OpenGLState prev_state{OpenGLState::GetCurState()};
-    SCOPE_EXIT({ prev_state.Apply(); });
-
-    OpenGLState clear_state{OpenGLState::GetCurState()};
     if (regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
         regs.clear_buffers.A) {
         use_color = true;
@@ -430,7 +426,7 @@ void RasterizerOpenGL::Clear() {
         return;
     }
 
-    ConfigureClearFramebuffer(clear_state, use_color, use_depth, use_stencil);
+    ConfigureClearFramebuffer(use_color, use_depth, use_stencil);
 
     SyncRasterizeEnable();
     if (regs.clear_flags.scissor) {
@@ -444,8 +440,6 @@ void RasterizerOpenGL::Clear() {
 
     UNIMPLEMENTED_IF(regs.clear_flags.viewport);
 
-    clear_state.Apply();
-
     if (use_color) {
         glClearBufferfv(GL_COLOR, 0, regs.clear_color);
     }
@@ -548,7 +542,6 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
     bind_ssbo_pushbuffer.Bind();
 
     program_manager.Update();
-    state.Apply();
 
     if (texture_cache.TextureBarrier()) {
         glTextureBarrier();
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index b8158c152f..48443bdff6 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -30,7 +30,6 @@
 #include "video_core/renderer_opengl/gl_shader_cache.h"
 #include "video_core/renderer_opengl/gl_shader_decompiler.h"
 #include "video_core/renderer_opengl/gl_shader_manager.h"
-#include "video_core/renderer_opengl/gl_state.h"
 #include "video_core/renderer_opengl/gl_texture_cache.h"
 #include "video_core/renderer_opengl/utils.h"
 #include "video_core/textures/texture.h"
@@ -86,8 +85,7 @@ private:
     /// Configures the color and depth framebuffer states.
     void ConfigureFramebuffers();
 
-    void ConfigureClearFramebuffer(OpenGLState& current_state, bool using_color_fb,
-                                   bool using_depth_fb, bool using_stencil_fb);
+    void ConfigureClearFramebuffer(bool using_color_fb, bool using_depth_fb, bool using_stencil_fb);
 
     /// Configures the current constbuffers to use for the draw command.
     void SetupDrawConstBuffers(std::size_t stage_index, const Shader& shader);
@@ -208,7 +206,6 @@ private:
     void SetupShaders(GLenum primitive_mode);
 
     const Device device;
-    OpenGLState state;
 
     TextureCacheOpenGL texture_cache;
     ShaderCacheOpenGL shader_cache;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index 404fd65567..97803d4800 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -8,7 +8,6 @@
 #include "common/microprofile.h"
 #include "video_core/renderer_opengl/gl_resource_manager.h"
 #include "video_core/renderer_opengl/gl_shader_util.h"
-#include "video_core/renderer_opengl/gl_state.h"
 
 MICROPROFILE_DEFINE(OpenGL_ResourceCreation, "OpenGL", "Resource Creation", MP_RGB(128, 128, 192));
 MICROPROFILE_DEFINE(OpenGL_ResourceDeletion, "OpenGL", "Resource Deletion", MP_RGB(128, 128, 192));
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index db23a27e85..e94cd75aa0 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -9,7 +9,6 @@
 #include <glad/glad.h>
 
 #include "video_core/renderer_opengl/gl_resource_manager.h"
-#include "video_core/renderer_opengl/gl_state.h"
 #include "video_core/renderer_opengl/maxwell_to_gl.h"
 
 namespace OpenGL::GLShader {
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp
deleted file mode 100644
index e8a23d41d0..0000000000
--- a/src/video_core/renderer_opengl/gl_state.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <iterator>
-#include <glad/glad.h>
-#include "common/assert.h"
-#include "common/logging/log.h"
-#include "common/microprofile.h"
-#include "video_core/renderer_opengl/gl_state.h"
-
-MICROPROFILE_DEFINE(OpenGL_State, "OpenGL", "State Change", MP_RGB(192, 128, 128));
-
-namespace OpenGL {
-
-using Maxwell = Tegra::Engines::Maxwell3D::Regs;
-
-OpenGLState OpenGLState::cur_state;
-
-namespace {
-
-template <typename T>
-bool UpdateValue(T& current_value, const T new_value) {
-    const bool changed = current_value != new_value;
-    current_value = new_value;
-    return changed;
-}
-
-template <typename T1, typename T2>
-bool UpdateTie(T1 current_value, const T2 new_value) {
-    const bool changed = current_value != new_value;
-    current_value = new_value;
-    return changed;
-}
-
-template <typename T>
-std::optional<std::pair<GLuint, GLsizei>> UpdateArray(T& current_values, const T& new_values) {
-    std::optional<std::size_t> first;
-    std::size_t last;
-    for (std::size_t i = 0; i < std::size(current_values); ++i) {
-        if (!UpdateValue(current_values[i], new_values[i])) {
-            continue;
-        }
-        if (!first) {
-            first = i;
-        }
-        last = i;
-    }
-    if (!first) {
-        return std::nullopt;
-    }
-    return std::make_pair(static_cast<GLuint>(*first), static_cast<GLsizei>(last - *first + 1));
-}
-
-void Enable(GLenum cap, bool enable) {
-    if (enable) {
-        glEnable(cap);
-    } else {
-        glDisable(cap);
-    }
-}
-
-void Enable(GLenum cap, GLuint index, bool enable) {
-    if (enable) {
-        glEnablei(cap, index);
-    } else {
-        glDisablei(cap, index);
-    }
-}
-
-void Enable(GLenum cap, bool& current_value, bool new_value) {
-    if (UpdateValue(current_value, new_value)) {
-        Enable(cap, new_value);
-    }
-}
-
-void Enable(GLenum cap, GLuint index, bool& current_value, bool new_value) {
-    if (UpdateValue(current_value, new_value)) {
-        Enable(cap, index, new_value);
-    }
-}
-
-} // Anonymous namespace
-
-OpenGLState::OpenGLState() = default;
-
-void OpenGLState::Apply() {}
-
-} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h
deleted file mode 100644
index f62e2e2e19..0000000000
--- a/src/video_core/renderer_opengl/gl_state.h
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2015 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <array>
-#include <type_traits>
-#include <glad/glad.h>
-#include "video_core/engines/maxwell_3d.h"
-
-namespace OpenGL {
-
-class OpenGLState {
-public:
-    OpenGLState();
-
-    /// Get the currently active OpenGL state
-    static OpenGLState GetCurState() {
-        return cur_state;
-    }
-
-    /// Apply this state as the current OpenGL state
-    void Apply();
-
-private:
-    static OpenGLState cur_state;
-};
-static_assert(std::is_trivially_copyable_v<OpenGLState>);
-
-} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
index 35ba334e41..6ec328c538 100644
--- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp
+++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp
@@ -7,7 +7,6 @@
 #include "common/alignment.h"
 #include "common/assert.h"
 #include "common/microprofile.h"
-#include "video_core/renderer_opengl/gl_state.h"
 #include "video_core/renderer_opengl/gl_stream_buffer.h"
 
 MICROPROFILE_DEFINE(OpenGL_StreamBuffer, "OpenGL", "Stream Buffer Orphaning",
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 5fe6aa5d69..e2a58f0adc 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -10,7 +10,6 @@
 #include "core/core.h"
 #include "video_core/morton.h"
 #include "video_core/renderer_opengl/gl_resource_manager.h"
-#include "video_core/renderer_opengl/gl_state.h"
 #include "video_core/renderer_opengl/gl_texture_cache.h"
 #include "video_core/renderer_opengl/utils.h"
 #include "video_core/texture_cache/surface_base.h"
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index ace3aec882..5e16bb99bd 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -575,8 +575,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     };
     glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
 
-    state.Apply();
-
     // TODO: Signal state tracker about these changes
     program_manager.UseVertexShader(vertex_program.handle);
     program_manager.UseGeometryShader(0);
@@ -616,9 +614,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
 
     glClear(GL_COLOR_BUFFER_BIT);
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-
-    // Restore default state
-    state.Apply();
 }
 
 void RendererOpenGL::TryPresent(int timeout_ms) {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index f1225269f2..ca670e7c81 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -11,7 +11,6 @@
 #include "video_core/renderer_base.h"
 #include "video_core/renderer_opengl/gl_resource_manager.h"
 #include "video_core/renderer_opengl/gl_shader_manager.h"
-#include "video_core/renderer_opengl/gl_state.h"
 
 namespace Core {
 class System;
@@ -92,8 +91,6 @@ private:
     Core::Frontend::EmuWindow& emu_window;
     Core::System& system;
 
-    OpenGLState state;
-
     // OpenGL object IDs
     OGLBuffer vertex_buffer;
     OGLProgram vertex_program;

From eed789d0d134fbeef1c16f9829b5c1b4b7dabb17 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 26 Dec 2019 22:14:10 -0300
Subject: [PATCH 32/76] video_core: Reintroduce dirty flags infrastructure

---
 src/core/core.cpp                         |  1 +
 src/video_core/CMakeLists.txt             |  1 +
 src/video_core/dirty_flags.h              | 28 +++++++++++++++++++++++
 src/video_core/dma_pusher.cpp             |  3 +++
 src/video_core/engines/kepler_compute.cpp |  3 +++
 src/video_core/engines/kepler_memory.cpp  |  3 +++
 src/video_core/engines/maxwell_3d.cpp     | 14 +++++++++++-
 src/video_core/engines/maxwell_3d.h       | 14 ++++++++++++
 src/video_core/engines/maxwell_dma.cpp    |  3 +++
 src/video_core/rasterizer_interface.h     |  3 +++
 10 files changed, 72 insertions(+), 1 deletion(-)
 create mode 100644 src/video_core/dirty_flags.h

diff --git a/src/core/core.cpp b/src/core/core.cpp
index a82faf1270..2185081262 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -174,6 +174,7 @@ struct System::Impl {
         }
         interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
         gpu_core = VideoCore::CreateGPU(system);
+        renderer->Rasterizer().SetupDirtyFlags();
 
         is_powered_on = true;
         exit_lock = false;
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index b897528824..18b774ac7e 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -2,6 +2,7 @@ add_library(video_core STATIC
     buffer_cache/buffer_block.h
     buffer_cache/buffer_cache.h
     buffer_cache/map_interval.h
+    dirty_flags.h
     dma_pusher.cpp
     dma_pusher.h
     engines/const_buffer_engine_interface.h
diff --git a/src/video_core/dirty_flags.h b/src/video_core/dirty_flags.h
new file mode 100644
index 0000000000..d9058bcab2
--- /dev/null
+++ b/src/video_core/dirty_flags.h
@@ -0,0 +1,28 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+namespace VideoCommon::Dirty {
+
+enum : u8 {
+    NullEntry = 0,
+
+    RenderTargets,
+    ColorBuffer0,
+    ColorBuffer1,
+    ColorBuffer2,
+    ColorBuffer3,
+    ColorBuffer4,
+    ColorBuffer5,
+    ColorBuffer6,
+    ColorBuffer7,
+    ZetaBuffer,
+
+    LastCommonEntry,
+};
+
+} // namespace VideoCommon::Dirty
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index a42d37c810..713c14182a 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -21,6 +21,9 @@ MICROPROFILE_DEFINE(DispatchCalls, "GPU", "Execute command buffer", MP_RGB(128,
 void DmaPusher::DispatchCalls() {
     MICROPROFILE_SCOPE(DispatchCalls);
 
+    // On entering GPU code, assume all memory may be touched by the ARM core.
+    gpu.Maxwell3D().OnMemoryWrite();
+
     dma_pushbuffer_subindex = 0;
 
     while (Core::System::GetInstance().IsPoweredOn()) {
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 254ad68100..ae52afa799 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -38,6 +38,9 @@ void KeplerCompute::CallMethod(const GPU::MethodCall& method_call) {
     case KEPLER_COMPUTE_REG_INDEX(data_upload): {
         const bool is_last_call = method_call.IsLastCall();
         upload_state.ProcessData(method_call.argument, is_last_call);
+        if (is_last_call) {
+            system.GPU().Maxwell3D().OnMemoryWrite();
+        }
         break;
     }
     case KEPLER_COMPUTE_REG_INDEX(launch):
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp
index b504b450ed..597872e439 100644
--- a/src/video_core/engines/kepler_memory.cpp
+++ b/src/video_core/engines/kepler_memory.cpp
@@ -33,6 +33,9 @@ void KeplerMemory::CallMethod(const GPU::MethodCall& method_call) {
     case KEPLERMEMORY_REG_INDEX(data): {
         const bool is_last_call = method_call.IsLastCall();
         upload_state.ProcessData(method_call.argument, is_last_call);
+        if (is_last_call) {
+            system.GPU().Maxwell3D().OnMemoryWrite();
+        }
         break;
     }
     }
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 7a6bf764c3..db710bf357 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -26,6 +26,8 @@ Maxwell3D::Maxwell3D(Core::System& system, VideoCore::RasterizerInterface& raste
                      MemoryManager& memory_manager)
     : system{system}, rasterizer{rasterizer}, memory_manager{memory_manager},
       macro_interpreter{*this}, upload_state{memory_manager, regs.upload} {
+    dirty.flags.flip();
+
     InitializeRegisterDefaults();
 }
 
@@ -158,7 +160,13 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
     ASSERT_MSG(method < Regs::NUM_REGS,
                "Invalid Maxwell3D register, increase the size of the Regs structure");
 
-    regs.reg_array[method] = method_call.argument;
+    if (regs.reg_array[method] != method_call.argument) {
+        regs.reg_array[method] = method_call.argument;
+
+        for (const auto& table : dirty.tables) {
+            dirty.flags[table[method]] = true;
+        }
+    }
 
     switch (method) {
     case MAXWELL3D_REG_INDEX(macros.data): {
@@ -243,6 +251,9 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
     case MAXWELL3D_REG_INDEX(data_upload): {
         const bool is_last_call = method_call.IsLastCall();
         upload_state.ProcessData(method_call.argument, is_last_call);
+        if (is_last_call) {
+            OnMemoryWrite();
+        }
         break;
     }
     default:
@@ -549,6 +560,7 @@ void Maxwell3D::FinishCBData() {
 
     const u32 id = cb_data_state.id;
     memory_manager.WriteBlock(address, cb_data_state.buffer[id].data(), size);
+    OnMemoryWrite();
 
     cb_data_state.id = null_cb_data;
     cb_data_state.current = null_cb_data;
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index b0fb0fb7db..72848b1e84 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -6,6 +6,7 @@
 
 #include <array>
 #include <bitset>
+#include <limits>
 #include <optional>
 #include <type_traits>
 #include <unordered_map>
@@ -1274,6 +1275,13 @@ public:
         return execute_on;
     }
 
+    /// Notify a memory write has happened.
+    void OnMemoryWrite() {
+        for (const u8 store : dirty.on_write_stores) {
+            dirty.flags[store] = true;
+        }
+    }
+
     enum class MMEDrawMode : u32 {
         Undefined,
         Array,
@@ -1289,6 +1297,12 @@ public:
         u32 gl_end_count{};
     } mme_draw;
 
+    struct {
+        std::bitset<std::numeric_limits<u8>::max()> flags;
+        std::array<std::array<u8, Regs::NUM_REGS>, 3> tables{};
+        std::array<u8, 32> on_write_stores{};
+    } dirty;
+
 private:
     void InitializeRegisterDefaults();
 
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index ae51765a69..c2610f992a 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -56,6 +56,9 @@ void MaxwellDMA::HandleCopy() {
         return;
     }
 
+    // All copies here update the main memory, so mark all rasterizer states as invalid.
+    system.GPU().Maxwell3D().OnMemoryWrite();
+
     if (regs.exec.is_dst_linear && regs.exec.is_src_linear) {
         // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D
         // buffer of length `x_count`, otherwise we copy a 2D image of dimensions (x_count,
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index f18eaf4bc2..3e4514b943 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -89,6 +89,9 @@ public:
     virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false,
                                    const DiskResourceLoadCallback& callback = {}) {}
 
+    /// Initializes renderer dirty flags
+    virtual void SetupDirtyFlags() {}
+
     /// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
     GuestDriverProfile& AccessGuestDriverProfile() {
         return guest_driver_profile;

From 9e74e6988b881c6889074bd2335239eb2e491e91 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sat, 28 Dec 2019 21:41:41 -0300
Subject: [PATCH 33/76] maxwell_3d: Flatten cull and front face registers

---
 src/video_core/engines/maxwell_3d.cpp         |  6 ++--
 src/video_core/engines/maxwell_3d.h           | 30 +++++++++----------
 .../renderer_opengl/gl_rasterizer.cpp         |  6 ++--
 .../renderer_opengl/maxwell_to_gl.h           | 14 ++++-----
 .../renderer_vulkan/fixed_pipeline_state.cpp  | 15 +++++-----
 .../renderer_vulkan/fixed_pipeline_state.h    |  8 ++---
 .../renderer_vulkan/maxwell_to_vk.cpp         | 14 ++++-----
 .../renderer_vulkan/maxwell_to_vk.h           |  4 +--
 8 files changed, 47 insertions(+), 50 deletions(-)

diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index db710bf357..89050361e7 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -76,8 +76,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
     regs.stencil_back_mask = 0xFFFFFFFF;
 
     regs.depth_test_func = Regs::ComparisonOp::Always;
-    regs.cull.front_face = Regs::Cull::FrontFace::CounterClockWise;
-    regs.cull.cull_face = Regs::Cull::CullFace::Back;
+    regs.front_face = Regs::FrontFace::CounterClockWise;
+    regs.cull_face = Regs::CullFace::Back;
 
     // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a
     // register carrying a default value. Assume it's OpenGL's default (1).
@@ -96,7 +96,7 @@ void Maxwell3D::InitializeRegisterDefaults() {
     regs.rasterize_enable = 1;
     regs.rt_separate_frag_data = 1;
     regs.framebuffer_srgb = 1;
-    regs.cull.front_face = Maxwell3D::Regs::Cull::FrontFace::ClockWise;
+    regs.front_face = Maxwell3D::Regs::FrontFace::ClockWise;
 
     mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true;
     mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true;
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 72848b1e84..8edfa6a342 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -432,21 +432,15 @@ public:
             GeneratedPrimitives = 0x1F,
         };
 
-        struct Cull {
-            enum class FrontFace : u32 {
-                ClockWise = 0x0900,
-                CounterClockWise = 0x0901,
-            };
+        enum class FrontFace : u32 {
+            ClockWise = 0x0900,
+            CounterClockWise = 0x0901,
+        };
 
-            enum class CullFace : u32 {
-                Front = 0x0404,
-                Back = 0x0405,
-                FrontAndBack = 0x0408,
-            };
-
-            u32 enabled;
-            FrontFace front_face;
-            CullFace cull_face;
+        enum class CullFace : u32 {
+            Front = 0x0404,
+            Back = 0x0405,
+            FrontAndBack = 0x0408,
         };
 
         struct Blend {
@@ -1052,7 +1046,9 @@ public:
 
                 INSERT_UNION_PADDING_WORDS(1);
 
-                Cull cull;
+                u32 cull_test_enabled;
+                FrontFace front_face;
+                CullFace cull_face;
 
                 u32 pixel_center_integer;
 
@@ -1491,7 +1487,9 @@ ASSERT_REG_POSITION(index_array, 0x5F2);
 ASSERT_REG_POSITION(polygon_offset_clamp, 0x61F);
 ASSERT_REG_POSITION(instanced_arrays, 0x620);
 ASSERT_REG_POSITION(vp_point_size, 0x644);
-ASSERT_REG_POSITION(cull, 0x646);
+ASSERT_REG_POSITION(cull_test_enabled, 0x646);
+ASSERT_REG_POSITION(front_face, 0x647);
+ASSERT_REG_POSITION(cull_face, 0x648);
 ASSERT_REG_POSITION(pixel_center_integer, 0x649);
 ASSERT_REG_POSITION(viewport_transform_enabled, 0x64B);
 ASSERT_REG_POSITION(view_volume_clip_control, 0x64F);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 3ce2a7124b..2fb8ec33b2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -945,10 +945,10 @@ void RasterizerOpenGL::SyncClipCoef() {
 void RasterizerOpenGL::SyncCullMode() {
     const auto& regs = system.GPU().Maxwell3D().regs;
 
-    oglEnable(GL_CULL_FACE, regs.cull.enabled);
-    glCullFace(MaxwellToGL::CullFace(regs.cull.cull_face));
+    oglEnable(GL_CULL_FACE, regs.cull_test_enabled);
+    glCullFace(MaxwellToGL::CullFace(regs.cull_face));
 
-    glFrontFace(MaxwellToGL::FrontFace(regs.cull.front_face));
+    glFrontFace(MaxwellToGL::FrontFace(regs.front_face));
 }
 
 void RasterizerOpenGL::SyncPrimitiveRestart() {
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index 7ed5056282..4c8db7cc8a 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -401,24 +401,24 @@ inline GLenum StencilOp(Maxwell::StencilOp stencil) {
     return GL_KEEP;
 }
 
-inline GLenum FrontFace(Maxwell::Cull::FrontFace front_face) {
+inline GLenum FrontFace(Maxwell::FrontFace front_face) {
     switch (front_face) {
-    case Maxwell::Cull::FrontFace::ClockWise:
+    case Maxwell::FrontFace::ClockWise:
         return GL_CW;
-    case Maxwell::Cull::FrontFace::CounterClockWise:
+    case Maxwell::FrontFace::CounterClockWise:
         return GL_CCW;
     }
     LOG_ERROR(Render_OpenGL, "Unimplemented front face cull={}", static_cast<u32>(front_face));
     return GL_CCW;
 }
 
-inline GLenum CullFace(Maxwell::Cull::CullFace cull_face) {
+inline GLenum CullFace(Maxwell::CullFace cull_face) {
     switch (cull_face) {
-    case Maxwell::Cull::CullFace::Front:
+    case Maxwell::CullFace::Front:
         return GL_FRONT;
-    case Maxwell::Cull::CullFace::Back:
+    case Maxwell::CullFace::Back:
         return GL_BACK;
-    case Maxwell::Cull::CullFace::FrontAndBack:
+    case Maxwell::CullFace::FrontAndBack:
         return GL_FRONT_AND_BACK;
     }
     LOG_ERROR(Render_OpenGL, "Unimplemented cull face={}", static_cast<u32>(cull_face));
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 4e3ff231ea..2bb3765550 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -112,19 +112,18 @@ constexpr FixedPipelineState::Rasterizer GetRasterizerState(const Maxwell& regs)
     const auto& clip = regs.view_volume_clip_control;
     const bool depth_clamp_enabled = clip.depth_clamp_near == 1 || clip.depth_clamp_far == 1;
 
-    Maxwell::Cull::FrontFace front_face = regs.cull.front_face;
+    Maxwell::FrontFace front_face = regs.front_face;
     if (regs.screen_y_control.triangle_rast_flip != 0 &&
         regs.viewport_transform[0].scale_y > 0.0f) {
-        if (front_face == Maxwell::Cull::FrontFace::CounterClockWise)
-            front_face = Maxwell::Cull::FrontFace::ClockWise;
-        else if (front_face == Maxwell::Cull::FrontFace::ClockWise)
-            front_face = Maxwell::Cull::FrontFace::CounterClockWise;
+        if (front_face == Maxwell::FrontFace::CounterClockWise)
+            front_face = Maxwell::FrontFace::ClockWise;
+        else if (front_face == Maxwell::FrontFace::ClockWise)
+            front_face = Maxwell::FrontFace::CounterClockWise;
     }
 
     const bool gl_ndc = regs.depth_mode == Maxwell::DepthMode::MinusOneToOne;
-    return FixedPipelineState::Rasterizer(regs.cull.enabled, depth_bias_enabled,
-                                          depth_clamp_enabled, gl_ndc, regs.cull.cull_face,
-                                          front_face);
+    return FixedPipelineState::Rasterizer(regs.cull_test_enabled, depth_bias_enabled,
+                                          depth_clamp_enabled, gl_ndc, regs.cull_face, front_face);
 }
 
 } // Anonymous namespace
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index 87056ef371..4c8ba7f90b 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -171,8 +171,8 @@ struct FixedPipelineState {
 
     struct Rasterizer {
         constexpr Rasterizer(bool cull_enable, bool depth_bias_enable, bool depth_clamp_enable,
-                             bool ndc_minus_one_to_one, Maxwell::Cull::CullFace cull_face,
-                             Maxwell::Cull::FrontFace front_face)
+                             bool ndc_minus_one_to_one, Maxwell::CullFace cull_face,
+                             Maxwell::FrontFace front_face)
             : cull_enable{cull_enable}, depth_bias_enable{depth_bias_enable},
               depth_clamp_enable{depth_clamp_enable}, ndc_minus_one_to_one{ndc_minus_one_to_one},
               cull_face{cull_face}, front_face{front_face} {}
@@ -182,8 +182,8 @@ struct FixedPipelineState {
         bool depth_bias_enable;
         bool depth_clamp_enable;
         bool ndc_minus_one_to_one;
-        Maxwell::Cull::CullFace cull_face;
-        Maxwell::Cull::FrontFace front_face;
+        Maxwell::CullFace cull_face;
+        Maxwell::FrontFace front_face;
 
         std::size_t Hash() const noexcept;
 
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index ef66dd141f..088b072ef2 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -572,24 +572,24 @@ vk::BlendFactor BlendFactor(Maxwell::Blend::Factor factor) {
     return {};
 }
 
-vk::FrontFace FrontFace(Maxwell::Cull::FrontFace front_face) {
+vk::FrontFace FrontFace(Maxwell::FrontFace front_face) {
     switch (front_face) {
-    case Maxwell::Cull::FrontFace::ClockWise:
+    case Maxwell::FrontFace::ClockWise:
         return vk::FrontFace::eClockwise;
-    case Maxwell::Cull::FrontFace::CounterClockWise:
+    case Maxwell::FrontFace::CounterClockWise:
         return vk::FrontFace::eCounterClockwise;
     }
     UNIMPLEMENTED_MSG("Unimplemented front face={}", static_cast<u32>(front_face));
     return {};
 }
 
-vk::CullModeFlags CullFace(Maxwell::Cull::CullFace cull_face) {
+vk::CullModeFlags CullFace(Maxwell::CullFace cull_face) {
     switch (cull_face) {
-    case Maxwell::Cull::CullFace::Front:
+    case Maxwell::CullFace::Front:
         return vk::CullModeFlagBits::eFront;
-    case Maxwell::Cull::CullFace::Back:
+    case Maxwell::CullFace::Back:
         return vk::CullModeFlagBits::eBack;
-    case Maxwell::Cull::CullFace::FrontAndBack:
+    case Maxwell::CullFace::FrontAndBack:
         return vk::CullModeFlagBits::eFrontAndBack;
     }
     UNIMPLEMENTED_MSG("Unimplemented cull face={}", static_cast<u32>(cull_face));
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.h b/src/video_core/renderer_vulkan/maxwell_to_vk.h
index 7e9678b7be..24f6ab5443 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.h
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.h
@@ -54,9 +54,9 @@ vk::BlendOp BlendEquation(Maxwell::Blend::Equation equation);
 
 vk::BlendFactor BlendFactor(Maxwell::Blend::Factor factor);
 
-vk::FrontFace FrontFace(Maxwell::Cull::FrontFace front_face);
+vk::FrontFace FrontFace(Maxwell::FrontFace front_face);
 
-vk::CullModeFlags CullFace(Maxwell::Cull::CullFace cull_face);
+vk::CullModeFlags CullFace(Maxwell::CullFace cull_face);
 
 vk::ComponentSwizzle SwizzleSource(Tegra::Texture::SwizzleSource swizzle);
 

From dacf83ac0257727a48c971ca1cfcd220976c461f Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sat, 28 Dec 2019 21:45:56 -0300
Subject: [PATCH 34/76] renderer_opengl: Reintroduce dirty flags for render
 targets

---
 .../renderer_opengl/gl_rasterizer.cpp         | 20 +++--
 .../renderer_opengl/gl_rasterizer.h           |  6 +-
 .../renderer_opengl/gl_state_tracker.cpp      | 85 +++++++++++++++++++
 .../renderer_opengl/gl_state_tracker.h        | 56 ++++++++++++
 .../renderer_opengl/gl_texture_cache.cpp      |  7 +-
 .../renderer_opengl/gl_texture_cache.h        |  5 +-
 .../renderer_opengl/renderer_opengl.cpp       |  6 +-
 .../renderer_opengl/renderer_opengl.h         |  3 +
 src/video_core/texture_cache/texture_cache.h  | 20 ++++-
 9 files changed, 195 insertions(+), 13 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 2fb8ec33b2..a1675355e1 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -99,11 +99,12 @@ void oglEnablei(GLenum cap, bool state, GLuint index) {
 } // Anonymous namespace
 
 RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
-                                   ScreenInfo& info, GLShader::ProgramManager& program_manager)
-    : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device},
+                                   ScreenInfo& info, GLShader::ProgramManager& program_manager,
+                                   StateTracker& state_tracker)
+    : RasterizerAccelerated{system.Memory()}, texture_cache{system, *this, device, state_tracker},
       shader_cache{*this, system, emu_window, device}, query_cache{system, *this}, system{system},
-      screen_info{info}, program_manager{program_manager}, buffer_cache{*this, system, device,
-                                                                        STREAM_BUFFER_SIZE} {
+      screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker},
+      buffer_cache{*this, system, device, STREAM_BUFFER_SIZE} {
     CheckExtensions();
 }
 
@@ -320,9 +321,17 @@ void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
     shader_cache.LoadDiskCache(stop_loading, callback);
 }
 
+void RasterizerOpenGL::SetupDirtyFlags() {
+    state_tracker.Initialize();
+}
+
 void RasterizerOpenGL::ConfigureFramebuffers() {
     MICROPROFILE_SCOPE(OpenGL_Framebuffer);
     auto& gpu = system.GPU().Maxwell3D();
+    if (!gpu.dirty.flags[VideoCommon::Dirty::RenderTargets]) {
+        return;
+    }
+    gpu.dirty.flags[VideoCommon::Dirty::RenderTargets] = false;
 
     texture_cache.GuardRenderTargets(true);
 
@@ -361,8 +370,6 @@ void RasterizerOpenGL::ConfigureFramebuffers() {
 
 void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color_fb, bool using_depth_fb,
                                                  bool using_stencil_fb) {
-    using VideoCore::Surface::SurfaceType;
-
     auto& gpu = system.GPU().Maxwell3D();
     const auto& regs = gpu.regs;
 
@@ -381,6 +388,7 @@ void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color_fb, bool using
     key.colors[0] = color_surface;
     key.zeta = depth_surface;
 
+    state_tracker.NotifyFramebuffer();
     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer_cache.GetFramebuffer(key));
 }
 
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 48443bdff6..22a3a33525 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -30,6 +30,7 @@
 #include "video_core/renderer_opengl/gl_shader_cache.h"
 #include "video_core/renderer_opengl/gl_shader_decompiler.h"
 #include "video_core/renderer_opengl/gl_shader_manager.h"
+#include "video_core/renderer_opengl/gl_state_tracker.h"
 #include "video_core/renderer_opengl/gl_texture_cache.h"
 #include "video_core/renderer_opengl/utils.h"
 #include "video_core/textures/texture.h"
@@ -54,7 +55,8 @@ struct DrawParameters;
 class RasterizerOpenGL : public VideoCore::RasterizerAccelerated {
 public:
     explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
-                              ScreenInfo& info, GLShader::ProgramManager& program_manager);
+                              ScreenInfo& info, GLShader::ProgramManager& program_manager,
+                              StateTracker& state_tracker);
     ~RasterizerOpenGL() override;
 
     void Draw(bool is_indexed, bool is_instanced) override;
@@ -75,6 +77,7 @@ public:
                            u32 pixel_stride) override;
     void LoadDiskResources(const std::atomic_bool& stop_loading,
                            const VideoCore::DiskResourceLoadCallback& callback) override;
+    void SetupDirtyFlags() override;
 
     /// Returns true when there are commands queued to the OpenGL server.
     bool AnyCommandQueued() const {
@@ -216,6 +219,7 @@ private:
     Core::System& system;
     ScreenInfo& screen_info;
     GLShader::ProgramManager& program_manager;
+    StateTracker& state_tracker;
 
     static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
     OGLBufferCache buffer_cache;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index e69de29bb2..268b9351e8 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -0,0 +1,85 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <array>
+#include <cstddef>
+#include <type_traits>
+
+#include "common/common_types.h"
+#include "core/core.h"
+#include "video_core/engines/maxwell_3d.h"
+#include "video_core/gpu.h"
+#include "video_core/renderer_opengl/gl_state_tracker.h"
+
+#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
+#define NUM(field_name) (sizeof(Maxwell3D::Regs::field_name) / sizeof(u32))
+
+namespace OpenGL {
+
+namespace {
+
+using namespace Dirty;
+using namespace VideoCommon::Dirty;
+using Tegra::Engines::Maxwell3D;
+using Regs = Maxwell3D::Regs;
+using Dirty = std::remove_reference_t<decltype(Maxwell3D::dirty)>;
+using Tables = std::remove_reference_t<decltype(Maxwell3D::dirty.tables)>;
+using Table = std::remove_reference_t<decltype(Maxwell3D::dirty.tables[0])>;
+
+template <typename Integer>
+void FillBlock(Table& table, std::size_t begin, std::size_t num, Integer dirty_index) {
+    const auto it = std::begin(table) + begin;
+    std::fill(it, it + num, static_cast<u8>(dirty_index));
+}
+
+template <typename Integer1, typename Integer2>
+void FillBlock(Tables& tables, std::size_t begin, std::size_t num, Integer1 index_a,
+               Integer2 index_b) {
+    FillBlock(tables[0], begin, num, index_a);
+    FillBlock(tables[1], begin, num, index_b);
+}
+
+void SetupDirtyRenderTargets(Tables& tables) {
+    static constexpr std::size_t num_per_rt = NUM(rt[0]);
+    static constexpr std::size_t begin = OFF(rt);
+    static constexpr std::size_t num = num_per_rt * Regs::NumRenderTargets;
+    for (std::size_t rt = 0; rt < Regs::NumRenderTargets; ++rt) {
+        FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt);
+    }
+    FillBlock(tables[1], begin, num, RenderTargets);
+
+    static constexpr std::array zeta_flags{ZetaBuffer, RenderTargets};
+    for (std::size_t i = 0; i < std::size(zeta_flags); ++i) {
+        const u8 flag = zeta_flags[i];
+        auto& table = tables[i];
+        table[OFF(zeta_enable)] = flag;
+        table[OFF(zeta_width)] = flag;
+        table[OFF(zeta_height)] = flag;
+        FillBlock(table, OFF(zeta), NUM(zeta), flag);
+    }
+}
+
+} // Anonymous namespace
+
+StateTracker::StateTracker(Core::System& system) : system{system} {}
+
+void StateTracker::Initialize() {
+    auto& dirty = system.GPU().Maxwell3D().dirty;
+    std::size_t entry_index = 0;
+    const auto AddEntry = [&dirty, &entry_index](std::size_t dirty_register) {
+        dirty.on_write_stores[entry_index++] = static_cast<u8>(dirty_register);
+    };
+
+    AddEntry(RenderTargets);
+    for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) {
+        AddEntry(ColorBuffer0 + i);
+    }
+    AddEntry(ZetaBuffer);
+
+    auto& tables = dirty.tables;
+    SetupDirtyRenderTargets(tables);
+}
+
+} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index e69de29bb2..91d4bb8d3a 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -0,0 +1,56 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "video_core/dirty_flags.h"
+#include "video_core/engines/maxwell_3d.h"
+
+namespace Core {
+class System;
+}
+
+namespace OpenGL {
+
+namespace Dirty {
+enum : u8 {
+    First = VideoCommon::Dirty::LastCommonEntry,
+
+    VertexFormats,
+    VertexBuffers,
+    VertexInstances,
+    Shaders,
+    Viewports,
+    CullTestEnable,
+    FrontFace,
+    CullFace,
+    PrimitiveRestart,
+    DepthTest,
+    StencilTest,
+    ColorMask,
+    BlendState,
+    PolygonOffset,
+
+    VertexBuffer0 = PolygonOffset + 8,
+    VertexInstance0 = VertexBuffer0 + 32,
+};
+}
+
+class StateTracker {
+public:
+    explicit StateTracker(Core::System& system);
+
+    void Initialize();
+
+    void NotifyFramebuffer() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[VideoCommon::Dirty::RenderTargets] = true;
+    }
+
+private:
+    Core::System& system;
+};
+
+} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index e2a58f0adc..1cadcf2871 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -10,6 +10,7 @@
 #include "core/core.h"
 #include "video_core/morton.h"
 #include "video_core/renderer_opengl/gl_resource_manager.h"
+#include "video_core/renderer_opengl/gl_state_tracker.h"
 #include "video_core/renderer_opengl/gl_texture_cache.h"
 #include "video_core/renderer_opengl/utils.h"
 #include "video_core/texture_cache/surface_base.h"
@@ -479,8 +480,8 @@ OGLTextureView CachedSurfaceView::CreateTextureView() const {
 
 TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,
                                        VideoCore::RasterizerInterface& rasterizer,
-                                       const Device& device)
-    : TextureCacheBase{system, rasterizer} {
+                                       const Device& device, StateTracker& state_tracker)
+    : TextureCacheBase{system, rasterizer}, state_tracker{state_tracker} {
     src_framebuffer.Create();
     dst_framebuffer.Create();
 }
@@ -518,6 +519,8 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     UNIMPLEMENTED_IF(dst_params.target == SurfaceTarget::Texture3D);
 
     // TODO: Signal state tracker about these changes
+    state_tracker.NotifyFramebuffer();
+
     if (dst_params.srgb_conversion) {
         glEnable(GL_FRAMEBUFFER_SRGB);
     } else {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 303534ca6d..6658c6ffd8 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -27,6 +27,7 @@ using VideoCommon::ViewParams;
 class CachedSurfaceView;
 class CachedSurface;
 class TextureCacheOpenGL;
+class StateTracker;
 
 using Surface = std::shared_ptr<CachedSurface>;
 using View = std::shared_ptr<CachedSurfaceView>;
@@ -127,7 +128,7 @@ private:
 class TextureCacheOpenGL final : public TextureCacheBase {
 public:
     explicit TextureCacheOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
-                                const Device& device);
+                                const Device& device, StateTracker& state_tracker);
     ~TextureCacheOpenGL();
 
 protected:
@@ -144,6 +145,8 @@ protected:
 private:
     GLuint FetchPBO(std::size_t buffer_size);
 
+    StateTracker& state_tracker;
+
     OGLFramebuffer src_framebuffer;
     OGLFramebuffer dst_framebuffer;
     std::unordered_map<u32, OGLBuffer> copy_pbo_cache;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 5e16bb99bd..36c634e0d3 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -482,8 +482,8 @@ void RendererOpenGL::CreateRasterizer() {
     if (rasterizer) {
         return;
     }
-    rasterizer =
-        std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info, program_manager);
+    rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info,
+                                                    program_manager, state_tracker);
 }
 
 void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
@@ -576,6 +576,8 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
 
     // TODO: Signal state tracker about these changes
+    state_tracker.NotifyFramebuffer();
+
     program_manager.UseVertexShader(vertex_program.handle);
     program_manager.UseGeometryShader(0);
     program_manager.UseFragmentShader(fragment_program.handle);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index ca670e7c81..33073ce5b0 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -11,6 +11,7 @@
 #include "video_core/renderer_base.h"
 #include "video_core/renderer_opengl/gl_resource_manager.h"
 #include "video_core/renderer_opengl/gl_shader_manager.h"
+#include "video_core/renderer_opengl/gl_state_tracker.h"
 
 namespace Core {
 class System;
@@ -91,6 +92,8 @@ private:
     Core::Frontend::EmuWindow& emu_window;
     Core::System& system;
 
+    StateTracker state_tracker{system};
+
     // OpenGL object IDs
     OGLBuffer vertex_buffer;
     OGLProgram vertex_program;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index ec6dfa49eb..51373b6871 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -22,6 +22,7 @@
 #include "core/core.h"
 #include "core/memory.h"
 #include "core/settings.h"
+#include "video_core/dirty_flags.h"
 #include "video_core/engines/fermi_2d.h"
 #include "video_core/engines/maxwell_3d.h"
 #include "video_core/gpu.h"
@@ -142,6 +143,10 @@ public:
     TView GetDepthBufferSurface(bool preserve_contents) {
         std::lock_guard lock{mutex};
         auto& maxwell3d = system.GPU().Maxwell3D();
+        if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ZetaBuffer]) {
+            return depth_buffer.view;
+        }
+        maxwell3d.dirty.flags[VideoCommon::Dirty::ZetaBuffer] = false;
 
         const auto& regs{maxwell3d.regs};
         const auto gpu_addr{regs.zeta.Address()};
@@ -170,6 +175,10 @@ public:
         std::lock_guard lock{mutex};
         ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
         auto& maxwell3d = system.GPU().Maxwell3D();
+        if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ColorBuffer0 + index]) {
+            return render_targets[index].view;
+        }
+        maxwell3d.dirty.flags[VideoCommon::Dirty::ColorBuffer0 + index] = false;
 
         const auto& regs{maxwell3d.regs};
         if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
@@ -310,7 +319,16 @@ protected:
     // and reading it from a separate buffer.
     virtual void BufferCopy(TSurface& src_surface, TSurface& dst_surface) = 0;
 
-    void ManageRenderTargetUnregister([[maybe_unused]] TSurface& surface) {}
+    void ManageRenderTargetUnregister(TSurface& surface) {
+        auto& dirty = system.GPU().Maxwell3D().dirty;
+        const u32 index = surface->GetRenderTarget();
+        if (index == DEPTH_RT) {
+            dirty.flags[VideoCommon::Dirty::ZetaBuffer] = true;
+        } else {
+            dirty.flags[VideoCommon::Dirty::ColorBuffer0 + index] = true;
+        }
+        dirty.flags[VideoCommon::Dirty::RenderTargets] = true;
+    }
 
     void Register(TSurface surface) {
         const GPUVAddr gpu_addr = surface->GetGpuAddr();

From 7f52efdf61d16d6eaa7eea2500ceb28d9f1041e1 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sat, 28 Dec 2019 22:12:12 -0300
Subject: [PATCH 35/76] gl_state_tracker: Implement dirty flags for viewports

---
 .../renderer_opengl/gl_rasterizer.cpp         | 31 ++++++++++++++-----
 .../renderer_opengl/gl_state_tracker.cpp      | 17 ++++++++++
 .../renderer_opengl/gl_state_tracker.h        | 10 +++++-
 .../renderer_opengl/renderer_opengl.cpp       |  5 ++-
 4 files changed, 54 insertions(+), 9 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a1675355e1..2427b8c073 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -906,13 +906,30 @@ void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& t
 }
 
 void RasterizerOpenGL::SyncViewport() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
-    for (std::size_t i = 0; i < Maxwell::NumViewports; ++i) {
-        const auto& src = regs.viewports[i];
-        const Common::Rectangle<f32> rect{regs.viewport_transform[i].GetRect()};
-        glViewportIndexedf(static_cast<GLuint>(i), rect.left, rect.bottom, rect.GetWidth(),
-                           rect.GetHeight());
-        glDepthRangef(src.depth_range_near, src.depth_range_far);
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    const auto& regs = gpu.regs;
+
+    if (flags[Dirty::Viewports]) {
+        flags[Dirty::Viewports] = false;
+
+        const bool force = flags[Dirty::ViewportTransform];
+        flags[Dirty::ViewportTransform] = false;
+
+        for (std::size_t i = 0; i < Maxwell::NumViewports; ++i) {
+            if (!force && !flags[Dirty::Viewport0 + i]) {
+                continue;
+            }
+            flags[Dirty::Viewport0 + i] = false;
+
+            const Common::Rectangle<f32> rect{regs.viewport_transform[i].GetRect()};
+            glViewportIndexedf(static_cast<GLuint>(i), rect.left, rect.bottom, rect.GetWidth(),
+                               rect.GetHeight());
+
+            const auto& src = regs.viewports[i];
+            glDepthRangeIndexed(static_cast<GLuint>(i), static_cast<GLdouble>(src.depth_range_near),
+                                static_cast<GLdouble>(src.depth_range_far));
+        }
     }
 
     bool flip_y = false;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 268b9351e8..6293f61021 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -61,6 +61,22 @@ void SetupDirtyRenderTargets(Tables& tables) {
     }
 }
 
+void SetupDirtyViewports(Tables& tables) {
+    for (std::size_t i = 0; i < Regs::NumViewports; ++i) {
+        const std::size_t transf_offset = OFF(viewport_transform) + i * NUM(viewport_transform[0]);
+        const std::size_t viewport_offset = OFF(viewports) + i * NUM(viewports[0]);
+
+        FillBlock(tables[0], transf_offset, NUM(viewport_transform[0]), Viewport0 + i);
+        FillBlock(tables[0], viewport_offset, NUM(viewports[0]), Viewport0 + i);
+    }
+
+    FillBlock(tables[1], OFF(viewport_transform), NUM(viewport_transform), Viewports);
+    FillBlock(tables[1], OFF(viewports), NUM(viewports), Viewports);
+
+    tables[0][OFF(viewport_transform_enabled)] = ViewportTransform;
+    tables[1][OFF(viewport_transform_enabled)] = Viewports;
+}
+
 } // Anonymous namespace
 
 StateTracker::StateTracker(Core::System& system) : system{system} {}
@@ -80,6 +96,7 @@ void StateTracker::Initialize() {
 
     auto& tables = dirty.tables;
     SetupDirtyRenderTargets(tables);
+    SetupDirtyViewports(tables);
 }
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 91d4bb8d3a..93c64a44a9 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -23,6 +23,7 @@ enum : u8 {
     VertexInstances,
     Shaders,
     Viewports,
+    ViewportTransform,
     CullTestEnable,
     FrontFace,
     CullFace,
@@ -33,7 +34,8 @@ enum : u8 {
     BlendState,
     PolygonOffset,
 
-    VertexBuffer0 = PolygonOffset + 8,
+    Viewport0,
+    VertexBuffer0 = Viewport0 + 16,
     VertexInstance0 = VertexBuffer0 + 32,
 };
 }
@@ -44,6 +46,12 @@ public:
 
     void Initialize();
 
+    void NotifyViewport0() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::Viewports] = true;
+        flags[OpenGL::Dirty::Viewport0] = true;
+    }
+
     void NotifyFramebuffer() {
         auto& flags = system.GPU().Maxwell3D().dirty.flags;
         flags[VideoCommon::Dirty::RenderTargets] = true;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 36c634e0d3..73d2d90273 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -576,6 +576,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
 
     // TODO: Signal state tracker about these changes
+    state_tracker.NotifyViewport0();
     state_tracker.NotifyFramebuffer();
 
     program_manager.UseVertexShader(vertex_program.handle);
@@ -601,7 +602,9 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glFrontFace(GL_CW);
     glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
     glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
-    glViewport(0, 0, layout.width, layout.height);
+    glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width),
+                       static_cast<GLfloat>(layout.height));
+    glDepthRangeIndexed(0, 0.0, 0.0);
 
     glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
                          offsetof(ScreenRectVertex, position));

From ba6f390448987c01ae0e4b7ffe98b77bbd47c6d9 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sat, 28 Dec 2019 22:31:00 -0300
Subject: [PATCH 36/76] gl_state_tracker: Implement dirty flags for scissors

---
 .../renderer_opengl/gl_rasterizer.cpp         | 24 +++++++++++---
 .../renderer_opengl/gl_state_tracker.cpp      |  9 +++++
 .../renderer_opengl/gl_state_tracker.h        | 33 +++++++++++++++----
 .../renderer_opengl/gl_texture_cache.cpp      |  1 +
 .../renderer_opengl/renderer_opengl.cpp       |  1 +
 5 files changed, 58 insertions(+), 10 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 2427b8c073..2ec4c9f551 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1102,12 +1102,28 @@ void RasterizerOpenGL::SyncLogicOpState() {
 }
 
 void RasterizerOpenGL::SyncScissorTest() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::Scissors]) {
+        return;
+    }
+    flags[Dirty::Scissors] = false;
+
+    const auto& regs = gpu.regs;
     for (std::size_t index = 0; index < Maxwell::NumViewports; ++index) {
+        if (!flags[Dirty::Scissor0 + index]) {
+            continue;
+        }
+        flags[Dirty::Scissor0 + index] = false;
+
         const auto& src = regs.scissor_test[index];
-        oglEnablei(GL_SCISSOR_TEST, src.enable, static_cast<GLuint>(index));
-        glScissorIndexed(static_cast<GLuint>(index), src.min_x, src.min_y, src.max_x - src.min_x,
-                         src.max_y - src.min_y);
+        if (src.enable) {
+            glEnablei(GL_SCISSOR_TEST, static_cast<GLuint>(index));
+            glScissorIndexed(static_cast<GLuint>(index), src.min_x, src.min_y,
+                             src.max_x - src.min_x, src.max_y - src.min_y);
+        } else {
+            glDisablei(GL_SCISSOR_TEST, static_cast<GLuint>(index));
+        }
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 6293f61021..9e1db59ae2 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -77,6 +77,14 @@ void SetupDirtyViewports(Tables& tables) {
     tables[1][OFF(viewport_transform_enabled)] = Viewports;
 }
 
+void SetupDirtyScissors(Tables& tables) {
+    for (std::size_t i = 0; i < Regs::NumViewports; ++i) {
+        const std::size_t offset = OFF(scissor_test) + i * NUM(scissor_test[0]);
+        FillBlock(tables[0], offset, NUM(scissor_test[0]), Scissor0 + i);
+    }
+    FillBlock(tables[1], OFF(scissor_test), NUM(scissor_test), Scissors);
+}
+
 } // Anonymous namespace
 
 StateTracker::StateTracker(Core::System& system) : system{system} {}
@@ -97,6 +105,7 @@ void StateTracker::Initialize() {
     auto& tables = dirty.tables;
     SetupDirtyRenderTargets(tables);
     SetupDirtyViewports(tables);
+    SetupDirtyScissors(tables);
 }
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 93c64a44a9..5153dc5d14 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -15,15 +15,30 @@ class System;
 namespace OpenGL {
 
 namespace Dirty {
+
 enum : u8 {
     First = VideoCommon::Dirty::LastCommonEntry,
 
     VertexFormats,
+
     VertexBuffers,
+    VertexBuffer0,
+    VertexBuffer31 = VertexBuffer0 + 31,
+
     VertexInstances,
-    Shaders,
-    Viewports,
+    VertexInstance0,
+    VertexInstance31 = VertexInstance0 + 31,
+
     ViewportTransform,
+    Viewports,
+    Viewport0,
+    Viewport15 = Viewport0 + 15,
+
+    Scissors,
+    Scissor0,
+    Scissor15 = Scissor0 + 15,
+
+    Shaders,
     CullTestEnable,
     FrontFace,
     CullFace,
@@ -34,11 +49,11 @@ enum : u8 {
     BlendState,
     PolygonOffset,
 
-    Viewport0,
-    VertexBuffer0 = Viewport0 + 16,
-    VertexInstance0 = VertexBuffer0 + 32,
+    Last
 };
-}
+static_assert(Last <= 0xff);
+
+} // namespace Dirty
 
 class StateTracker {
 public:
@@ -52,6 +67,12 @@ public:
         flags[OpenGL::Dirty::Viewport0] = true;
     }
 
+    void NotifyScissor0() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::Scissors] = true;
+        flags[OpenGL::Dirty::Scissor0] = true;
+    }
+
     void NotifyFramebuffer() {
         auto& flags = system.GPU().Maxwell3D().dirty.flags;
         flags[VideoCommon::Dirty::RenderTargets] = true;
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 1cadcf2871..a02326b9f4 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -519,6 +519,7 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     UNIMPLEMENTED_IF(dst_params.target == SurfaceTarget::Texture3D);
 
     // TODO: Signal state tracker about these changes
+    state_tracker.NotifyScissor0();
     state_tracker.NotifyFramebuffer();
 
     if (dst_params.srgb_conversion) {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 73d2d90273..f2b07ac81c 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -577,6 +577,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
 
     // TODO: Signal state tracker about these changes
     state_tracker.NotifyViewport0();
+    state_tracker.NotifyScissor0();
     state_tracker.NotifyFramebuffer();
 
     program_manager.UseVertexShader(vertex_program.handle);

From 6530144ccb0adccf101fcc443f9b21a027faa7a7 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sat, 28 Dec 2019 22:51:04 -0300
Subject: [PATCH 37/76] gl_state_tracker: Implement dirty flags for color masks

---
 .../renderer_opengl/gl_rasterizer.cpp         | 41 ++++++++++++++-----
 .../renderer_opengl/gl_state_tracker.cpp      | 11 +++++
 .../renderer_opengl/gl_state_tracker.h        | 11 +++++
 .../renderer_opengl/renderer_opengl.cpp       |  1 +
 4 files changed, 54 insertions(+), 10 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 2ec4c9f551..b4cec274da 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -410,8 +410,9 @@ void RasterizerOpenGL::Clear() {
     }
     if (use_color) {
         // TODO: Signal state tracker about these changes
-        glColorMaski(0, regs.clear_buffers.R, regs.clear_buffers.G, regs.clear_buffers.B,
-                     regs.clear_buffers.A);
+        state_tracker.NotifyColorMask0();
+        glColorMaski(0, regs.clear_buffers.R != 0, regs.clear_buffers.G != 0,
+                     regs.clear_buffers.B != 0, regs.clear_buffers.A != 0);
 
         SyncFramebufferSRGB();
         // TODO(Rodrigo): Determine if clamping is used on clears
@@ -1030,17 +1031,37 @@ void RasterizerOpenGL::SyncRasterizeEnable() {
 }
 
 void RasterizerOpenGL::SyncColorMask() {
-    auto& maxwell3d = system.GPU().Maxwell3D();
-    const auto& regs = maxwell3d.regs;
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::ColorMasks]) {
+        return;
+    }
+    flags[Dirty::ColorMasks] = false;
 
+    const bool force = flags[Dirty::ColorMaskCommon];
+    flags[Dirty::ColorMaskCommon] = false;
+
+    const auto& regs = gpu.regs;
     if (regs.color_mask_common) {
-        auto& mask = regs.color_mask[0];
-        glColorMask(mask.R, mask.B, mask.G, mask.A);
-    } else {
-        for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
-            const auto& mask = regs.color_mask[regs.color_mask_common ? 0 : i];
-            glColorMaski(static_cast<GLuint>(i), mask.R, mask.G, mask.B, mask.A);
+        if (!force && !flags[Dirty::ColorMask0]) {
+            return;
         }
+        flags[Dirty::ColorMask0] = false;
+
+        auto& mask = regs.color_mask[0];
+        glColorMask(mask.R != 0, mask.B != 0, mask.G != 0, mask.A != 0);
+        return;
+    }
+
+    // Path without color_mask_common set
+    for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
+        if (!force && !flags[Dirty::ColorMask0 + i]) {
+            continue;
+        }
+        flags[Dirty::ColorMask0 + i] = false;
+
+        const auto& mask = regs.color_mask[i];
+        glColorMaski(static_cast<GLuint>(i), mask.R != 0, mask.G != 0, mask.B != 0, mask.A != 0);
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 9e1db59ae2..3c6231d588 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -61,6 +61,16 @@ void SetupDirtyRenderTargets(Tables& tables) {
     }
 }
 
+void SetupDirtyColorMasks(Tables& tables) {
+    tables[0][OFF(color_mask_common)] = ColorMaskCommon;
+    for (std::size_t rt = 0; rt < Regs::NumRenderTargets; ++rt) {
+        const std::size_t offset = OFF(color_mask) + rt * NUM(color_mask[0]);
+        FillBlock(tables[0], offset, NUM(color_mask[0]), ColorMask0 + rt);
+    }
+
+    FillBlock(tables[1], OFF(color_mask), NUM(color_mask), ColorMasks);
+}
+
 void SetupDirtyViewports(Tables& tables) {
     for (std::size_t i = 0; i < Regs::NumViewports; ++i) {
         const std::size_t transf_offset = OFF(viewport_transform) + i * NUM(viewport_transform[0]);
@@ -104,6 +114,7 @@ void StateTracker::Initialize() {
 
     auto& tables = dirty.tables;
     SetupDirtyRenderTargets(tables);
+    SetupDirtyColorMasks(tables);
     SetupDirtyViewports(tables);
     SetupDirtyScissors(tables);
 }
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 5153dc5d14..0ad7c349a4 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -38,6 +38,11 @@ enum : u8 {
     Scissor0,
     Scissor15 = Scissor0 + 15,
 
+    ColorMaskCommon,
+    ColorMasks,
+    ColorMask0,
+    ColorMask7 = ColorMask0 + 7,
+
     Shaders,
     CullTestEnable,
     FrontFace,
@@ -73,6 +78,12 @@ public:
         flags[OpenGL::Dirty::Scissor0] = true;
     }
 
+    void NotifyColorMask0() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::ColorMasks] = true;
+        flags[OpenGL::Dirty::ColorMask0] = true;
+    }
+
     void NotifyFramebuffer() {
         auto& flags = system.GPU().Maxwell3D().dirty.flags;
         flags[VideoCommon::Dirty::RenderTargets] = true;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index f2b07ac81c..3ff7c8fb14 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -578,6 +578,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     // TODO: Signal state tracker about these changes
     state_tracker.NotifyViewport0();
     state_tracker.NotifyScissor0();
+    state_tracker.NotifyColorMask0();
     state_tracker.NotifyFramebuffer();
 
     program_manager.UseVertexShader(vertex_program.handle);

From 69ad6279e45db408ba3add0b540660ae34ba8e3f Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sat, 28 Dec 2019 23:08:40 -0300
Subject: [PATCH 38/76] gl_state_tracker: Implement dirty flags for vertex
 formats

---
 .../renderer_opengl/gl_rasterizer.cpp         | 28 +++++++++++++------
 .../renderer_opengl/gl_state_tracker.cpp      | 10 +++++++
 .../renderer_opengl/gl_state_tracker.h        | 12 ++++++++
 .../renderer_opengl/renderer_opengl.cpp       |  3 ++
 4 files changed, 44 insertions(+), 9 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index b4cec274da..211b11489b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -120,7 +120,11 @@ void RasterizerOpenGL::CheckExtensions() {
 
 void RasterizerOpenGL::SetupVertexFormat() {
     auto& gpu = system.GPU().Maxwell3D();
-    const auto& regs = gpu.regs;
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::VertexFormats]) {
+        return;
+    }
+    flags[Dirty::VertexFormats] = false;
 
     MICROPROFILE_SCOPE(OpenGL_VAO);
 
@@ -130,25 +134,31 @@ void RasterizerOpenGL::SetupVertexFormat() {
     // avoid OpenGL errors.
     // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
     // assume every shader uses them all.
-    for (u32 index = 0; index < 16; ++index) {
-        const auto& attrib = regs.vertex_attrib_format[index];
+    for (std::size_t index = 0; index < 16; ++index) {
+        if (!flags[Dirty::VertexFormat0 + index]) {
+            continue;
+        }
+        flags[Dirty::VertexFormat0 + index] = false;
+
+        const auto attrib = gpu.regs.vertex_attrib_format[index];
+        const auto gl_index = static_cast<GLuint>(index);
 
         // Ignore invalid attributes.
         if (!attrib.IsValid()) {
-            glDisableVertexAttribArray(index);
+            glDisableVertexAttribArray(gl_index);
             continue;
         }
-        glEnableVertexAttribArray(index);
+        glEnableVertexAttribArray(gl_index);
 
         if (attrib.type == Maxwell::VertexAttribute::Type::SignedInt ||
             attrib.type == Maxwell::VertexAttribute::Type::UnsignedInt) {
-            glVertexAttribIFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
-                                  attrib.offset);
+            glVertexAttribIFormat(gl_index, attrib.ComponentCount(),
+                                  MaxwellToGL::VertexType(attrib), attrib.offset);
         } else {
-            glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
+            glVertexAttribFormat(gl_index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
                                  attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
         }
-        glVertexAttribBinding(index, attrib.buffer);
+        glVertexAttribBinding(gl_index, attrib.buffer);
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 3c6231d588..572a438566 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -71,6 +71,15 @@ void SetupDirtyColorMasks(Tables& tables) {
     FillBlock(tables[1], OFF(color_mask), NUM(color_mask), ColorMasks);
 }
 
+void SetupDirtyVertexFormat(Tables& tables) {
+    for (std::size_t i = 0; i < Regs::NumVertexAttributes; ++i) {
+        const std::size_t offset = OFF(vertex_attrib_format) + i * NUM(vertex_attrib_format[0]);
+        FillBlock(tables[0], offset, NUM(vertex_attrib_format[0]), VertexFormat0 + i);
+    }
+
+    FillBlock(tables[1], OFF(vertex_attrib_format), Regs::NumVertexAttributes, VertexFormats);
+}
+
 void SetupDirtyViewports(Tables& tables) {
     for (std::size_t i = 0; i < Regs::NumViewports; ++i) {
         const std::size_t transf_offset = OFF(viewport_transform) + i * NUM(viewport_transform[0]);
@@ -117,6 +126,7 @@ void StateTracker::Initialize() {
     SetupDirtyColorMasks(tables);
     SetupDirtyViewports(tables);
     SetupDirtyScissors(tables);
+    SetupDirtyVertexFormat(tables);
 }
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 0ad7c349a4..7add22d888 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -20,6 +20,8 @@ enum : u8 {
     First = VideoCommon::Dirty::LastCommonEntry,
 
     VertexFormats,
+    VertexFormat0,
+    VertexFormat31 = VertexFormat0 + 31,
 
     VertexBuffers,
     VertexBuffer0,
@@ -66,6 +68,16 @@ public:
 
     void Initialize();
 
+    void NotifyScreenDrawVertexArray() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::VertexFormats] = true;
+        flags[OpenGL::Dirty::VertexFormat0 + 0] = true;
+        flags[OpenGL::Dirty::VertexFormat0 + 1] = true;
+
+        flags[OpenGL::Dirty::VertexBuffers] = true;
+        flags[OpenGL::Dirty::VertexBuffer0] = true;
+    }
+
     void NotifyViewport0() {
         auto& flags = system.GPU().Maxwell3D().dirty.flags;
         flags[OpenGL::Dirty::Viewports] = true;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 3ff7c8fb14..caa193c50b 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -576,6 +576,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices));
 
     // TODO: Signal state tracker about these changes
+    state_tracker.NotifyScreenDrawVertexArray();
     state_tracker.NotifyViewport0();
     state_tracker.NotifyScissor0();
     state_tracker.NotifyColorMask0();
@@ -608,6 +609,8 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
                        static_cast<GLfloat>(layout.height));
     glDepthRangeIndexed(0, 0.0, 0.0);
 
+    glEnableVertexAttribArray(PositionLocation);
+    glEnableVertexAttribArray(TexCoordLocation);
     glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
                          offsetof(ScreenRectVertex, position));
     glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE,

From 9b08698a0cd1c958a4479ca544dc35333aa0e370 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sun, 29 Dec 2019 01:05:02 -0300
Subject: [PATCH 39/76] maxwell_3d: Change write dirty flags to a bitset

---
 src/video_core/engines/maxwell_3d.h           |  6 ++---
 .../renderer_opengl/gl_state_tracker.cpp      | 22 +++++++++----------
 .../renderer_opengl/gl_state_tracker.h        |  4 +++-
 3 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 8edfa6a342..beaf3ffb62 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1273,9 +1273,7 @@ public:
 
     /// Notify a memory write has happened.
     void OnMemoryWrite() {
-        for (const u8 store : dirty.on_write_stores) {
-            dirty.flags[store] = true;
-        }
+        dirty.flags |= dirty.on_write_stores;
     }
 
     enum class MMEDrawMode : u32 {
@@ -1295,8 +1293,8 @@ public:
 
     struct {
         std::bitset<std::numeric_limits<u8>::max()> flags;
+        std::bitset<std::numeric_limits<u8>::max()> on_write_stores;
         std::array<std::array<u8, Regs::NUM_REGS>, 3> tables{};
-        std::array<u8, 32> on_write_stores{};
     } dirty;
 
 private:
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 572a438566..319fd825bb 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -110,23 +110,23 @@ StateTracker::StateTracker(Core::System& system) : system{system} {}
 
 void StateTracker::Initialize() {
     auto& dirty = system.GPU().Maxwell3D().dirty;
-    std::size_t entry_index = 0;
-    const auto AddEntry = [&dirty, &entry_index](std::size_t dirty_register) {
-        dirty.on_write_stores[entry_index++] = static_cast<u8>(dirty_register);
-    };
-
-    AddEntry(RenderTargets);
-    for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) {
-        AddEntry(ColorBuffer0 + i);
-    }
-    AddEntry(ZetaBuffer);
-
     auto& tables = dirty.tables;
     SetupDirtyRenderTargets(tables);
     SetupDirtyColorMasks(tables);
     SetupDirtyViewports(tables);
     SetupDirtyScissors(tables);
     SetupDirtyVertexFormat(tables);
+
+    auto& store = dirty.on_write_stores;
+    store[RenderTargets] = true;
+    store[ZetaBuffer] = true;
+    for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) {
+        store[ColorBuffer0 + i] = true;
+    }
+    store[VertexBuffers] = true;
+    for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) {
+        store[VertexBuffer0 + i] = true;
+    }
 }
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 7add22d888..a368aefd75 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <limits>
+
 #include "common/common_types.h"
 #include "video_core/dirty_flags.h"
 #include "video_core/engines/maxwell_3d.h"
@@ -58,7 +60,7 @@ enum : u8 {
 
     Last
 };
-static_assert(Last <= 0xff);
+static_assert(Last <= std::numeric_limits<u8>::max());
 
 } // namespace Dirty
 

From 758ad3f75d49be811237c297265038f80c16ee8c Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sun, 29 Dec 2019 01:28:53 -0300
Subject: [PATCH 40/76] gl_state_tracker: Add dirty flags for buffers and
 divisors

---
 .../renderer_opengl/gl_rasterizer.cpp         | 51 +++++++++++--------
 .../renderer_opengl/gl_state_tracker.cpp      | 21 ++++++++
 .../renderer_opengl/gl_state_tracker.h        |  4 ++
 .../renderer_opengl/renderer_opengl.cpp       |  2 +
 4 files changed, 56 insertions(+), 22 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 211b11489b..bb89985ccb 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -164,12 +164,22 @@ void RasterizerOpenGL::SetupVertexFormat() {
 
 void RasterizerOpenGL::SetupVertexBuffer() {
     auto& gpu = system.GPU().Maxwell3D();
-    const auto& regs = gpu.regs;
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::VertexBuffers]) {
+        return;
+    }
+    flags[Dirty::VertexBuffers] = false;
 
     MICROPROFILE_SCOPE(OpenGL_VB);
 
     // Upload all guest vertex arrays sequentially to our buffer
-    for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
+    const auto& regs = gpu.regs;
+    for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
+        if (!flags[Dirty::VertexBuffer0 + index]) {
+            continue;
+        }
+        flags[Dirty::VertexBuffer0 + index] = false;
+
         const auto& vertex_array = regs.vertex_array[index];
         if (!vertex_array.IsEnabled()) {
             continue;
@@ -183,33 +193,30 @@ void RasterizerOpenGL::SetupVertexBuffer() {
         const auto [vertex_buffer, vertex_buffer_offset] = buffer_cache.UploadMemory(start, size);
 
         // Bind the vertex array to the buffer at the current offset.
-        vertex_array_pushbuffer.SetVertexBuffer(index, vertex_buffer, vertex_buffer_offset,
-                                                vertex_array.stride);
-
-        if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
-            // Enable vertex buffer instancing with the specified divisor.
-            glVertexBindingDivisor(index, vertex_array.divisor);
-        } else {
-            // Disable the vertex buffer instancing.
-            glVertexBindingDivisor(index, 0);
-        }
+        vertex_array_pushbuffer.SetVertexBuffer(static_cast<GLuint>(index), vertex_buffer,
+                                                vertex_buffer_offset, vertex_array.stride);
     }
 }
 
 void RasterizerOpenGL::SetupVertexInstances() {
     auto& gpu = system.GPU().Maxwell3D();
-    const auto& regs = gpu.regs;
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::VertexInstances]) {
+        return;
+    }
+    flags[Dirty::VertexInstances] = false;
 
-    // Upload all guest vertex arrays sequentially to our buffer
-    for (u32 index = 0; index < 16; ++index) {
-        if (regs.instanced_arrays.IsInstancingEnabled(index) &&
-            regs.vertex_array[index].divisor != 0) {
-            // Enable vertex buffer instancing with the specified divisor.
-            glVertexBindingDivisor(index, regs.vertex_array[index].divisor);
-        } else {
-            // Disable the vertex buffer instancing.
-            glVertexBindingDivisor(index, 0);
+    const auto& regs = gpu.regs;
+    for (std::size_t index = 0; index < 16; ++index) {
+        if (!flags[Dirty::VertexInstance0 + index]) {
+            continue;
         }
+        flags[Dirty::VertexInstance0 + index] = false;
+
+        const auto gl_index = static_cast<GLuint>(index);
+        const bool instancing_enabled = regs.instanced_arrays.IsInstancingEnabled(gl_index);
+        const GLuint divisor = instancing_enabled ? regs.vertex_array[index].divisor : 0;
+        glVertexBindingDivisor(gl_index, divisor);
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 319fd825bb..7150b9247f 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -71,6 +71,26 @@ void SetupDirtyColorMasks(Tables& tables) {
     FillBlock(tables[1], OFF(color_mask), NUM(color_mask), ColorMasks);
 }
 
+void SetupDirtyVertexArrays(Tables& tables) {
+    static constexpr std::size_t num_array = 3;
+    static constexpr std::size_t instance_base_offset = 3;
+    for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) {
+        const std::size_t array_offset = OFF(vertex_array) + i * NUM(vertex_array[0]);
+        const std::size_t limit_offset = OFF(vertex_array_limit) + i * NUM(vertex_array_limit[0]);
+
+        FillBlock(tables, array_offset, num_array, VertexBuffer0 + i, VertexBuffers);
+        FillBlock(tables, limit_offset, NUM(vertex_array_limit), VertexBuffer0 + i, VertexBuffers);
+
+        const std::size_t instance_array_offset = array_offset + instance_base_offset;
+        tables[0][instance_array_offset] = static_cast<u8>(VertexInstance0 + i);
+        tables[1][instance_array_offset] = VertexInstances;
+
+        const std::size_t instance_offset = OFF(instanced_arrays) + i;
+        tables[0][instance_offset] = static_cast<u8>(VertexInstance0 + i);
+        tables[1][instance_offset] = VertexInstances;
+    }
+}
+
 void SetupDirtyVertexFormat(Tables& tables) {
     for (std::size_t i = 0; i < Regs::NumVertexAttributes; ++i) {
         const std::size_t offset = OFF(vertex_attrib_format) + i * NUM(vertex_attrib_format[0]);
@@ -115,6 +135,7 @@ void StateTracker::Initialize() {
     SetupDirtyColorMasks(tables);
     SetupDirtyViewports(tables);
     SetupDirtyScissors(tables);
+    SetupDirtyVertexArrays(tables);
     SetupDirtyVertexFormat(tables);
 
     auto& store = dirty.on_write_stores;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index a368aefd75..85667cee1e 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -78,6 +78,10 @@ public:
 
         flags[OpenGL::Dirty::VertexBuffers] = true;
         flags[OpenGL::Dirty::VertexBuffer0] = true;
+
+        flags[OpenGL::Dirty::VertexInstances] = true;
+        flags[OpenGL::Dirty::VertexInstance0 + 0] = true;
+        flags[OpenGL::Dirty::VertexInstance0 + 1] = true;
     }
 
     void NotifyViewport0() {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index caa193c50b..cbe9164884 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -611,6 +611,8 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
 
     glEnableVertexAttribArray(PositionLocation);
     glEnableVertexAttribArray(TexCoordLocation);
+    glVertexAttribDivisor(PositionLocation, 0);
+    glVertexAttribDivisor(TexCoordLocation, 0);
     glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE,
                          offsetof(ScreenRectVertex, position));
     glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE,

From f7ec078592468fa22ff377b996a720c8be82c2dc Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sun, 29 Dec 2019 02:03:05 -0300
Subject: [PATCH 41/76] gl_state_tracker: Implement dirty flags for clip
 distances and shaders

---
 .../renderer_opengl/gl_rasterizer.cpp         | 28 ++++++++++++-------
 .../renderer_opengl/gl_rasterizer.h           |  5 ++--
 .../renderer_opengl/gl_shader_cache.cpp       |  5 ++++
 .../renderer_opengl/gl_shader_decompiler.cpp  |  5 +++-
 .../renderer_opengl/gl_shader_decompiler.h    |  2 +-
 .../renderer_opengl/gl_state_tracker.cpp      | 11 ++++++++
 .../renderer_opengl/gl_state_tracker.h        |  1 +
 7 files changed, 43 insertions(+), 14 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index bb89985ccb..717f127e9d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -232,8 +232,7 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() {
 void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
     MICROPROFILE_SCOPE(OpenGL_Shader);
     auto& gpu = system.GPU().Maxwell3D();
-
-    std::array<bool, Maxwell::NumClipDistances> clip_distances{};
+    u32 clip_distances = 0;
 
     for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
         const auto& shader_config = gpu.regs.shader_config[index];
@@ -294,9 +293,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
         // When a clip distance is enabled but not set in the shader it crops parts of the screen
         // (sometimes it's half the screen, sometimes three quarters). To avoid this, enable the
         // clip distances only when it's written by a shader stage.
-        for (std::size_t i = 0; i < Maxwell::NumClipDistances; ++i) {
-            clip_distances[i] = clip_distances[i] || shader->GetShaderEntries().clip_distances[i];
-        }
+        clip_distances |= shader->GetShaderEntries().clip_distances;
 
         // When VertexA is enabled, we have dual vertex shaders
         if (program == Maxwell::ShaderProgram::VertexA) {
@@ -306,6 +303,7 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
     }
 
     SyncClipEnabled(clip_distances);
+    gpu.dirty.flags[Dirty::Shaders] = false;
 }
 
 std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -972,12 +970,22 @@ void RasterizerOpenGL::SyncDepthClamp() {
     oglEnable(GL_DEPTH_CLAMP, state.depth_clamp_far || state.depth_clamp_near);
 }
 
-void RasterizerOpenGL::SyncClipEnabled(
-    const std::array<bool, Maxwell::Regs::NumClipDistances>& clip_mask) {
-    const auto& regs = system.GPU().Maxwell3D().regs;
+void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) {
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::ClipDistances] && !flags[Dirty::Shaders]) {
+        return;
+    }
+    flags[Dirty::ClipDistances] = false;
+
+    clip_mask &= gpu.regs.clip_distance_enabled;
+    if (clip_mask == last_clip_distance_mask) {
+        return;
+    }
+    last_clip_distance_mask = clip_mask;
+
     for (std::size_t i = 0; i < Maxwell::Regs::NumClipDistances; ++i) {
-        oglEnable(static_cast<GLenum>(GL_CLIP_DISTANCE0 + i),
-                  clip_mask[i] && ((regs.clip_distance_enabled >> i) & 1));
+        oglEnable(static_cast<GLenum>(GL_CLIP_DISTANCE0 + i), (clip_mask >> i) & 1);
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 22a3a33525..11206f5579 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -137,8 +137,7 @@ private:
     void SyncDepthClamp();
 
     /// Syncs the clip enabled status to match the guest state
-    void SyncClipEnabled(
-        const std::array<bool, Tegra::Engines::Maxwell3D::Regs::NumClipDistances>& clip_mask);
+    void SyncClipEnabled(u32 clip_mask);
 
     /// Syncs the clip coefficients to match the guest state
     void SyncClipCoef();
@@ -230,6 +229,8 @@ private:
 
     /// Number of commands queued to the OpenGL driver. Reseted on flush.
     std::size_t num_queued_commands = 0;
+
+    u32 last_clip_distance_mask = 0;
 };
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index bef141f63b..4cb89db8ca 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -22,6 +22,7 @@
 #include "video_core/renderer_opengl/gl_shader_cache.h"
 #include "video_core/renderer_opengl/gl_shader_decompiler.h"
 #include "video_core/renderer_opengl/gl_shader_disk_cache.h"
+#include "video_core/renderer_opengl/gl_state_tracker.h"
 #include "video_core/renderer_opengl/utils.h"
 #include "video_core/shader/shader_ir.h"
 
@@ -623,6 +624,10 @@ bool ShaderCacheOpenGL::GenerateUnspecializedShaders(
 }
 
 Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
+    if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) {
+        return last_shaders[static_cast<std::size_t>(program)];
+    }
+
     auto& memory_manager{system.GPU().MemoryManager()};
     const GPUVAddr address{GetShaderAddress(system, program)};
 
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 4735000b5d..3a41ed30cb 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -2547,7 +2547,10 @@ ShaderEntries GetEntries(const VideoCommon::Shader::ShaderIR& ir) {
     for (const auto& image : ir.GetImages()) {
         entries.images.emplace_back(image);
     }
-    entries.clip_distances = ir.GetClipDistances();
+    const auto clip_distances = ir.GetClipDistances();
+    for (std::size_t i = 0; i < std::size(clip_distances); ++i) {
+        entries.clip_distances = (clip_distances[i] ? 1U : 0U) << i;
+    }
     entries.shader_length = ir.GetLength();
     return entries;
 }
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index 7876f48d60..0f692c1db6 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -74,7 +74,7 @@ struct ShaderEntries {
     std::vector<GlobalMemoryEntry> global_memory_entries;
     std::vector<SamplerEntry> samplers;
     std::vector<ImageEntry> images;
-    std::array<bool, Maxwell::NumClipDistances> clip_distances{};
+    u32 clip_distances{};
     std::size_t shader_length{};
 };
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 7150b9247f..bc5942a7f2 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -124,6 +124,15 @@ void SetupDirtyScissors(Tables& tables) {
     FillBlock(tables[1], OFF(scissor_test), NUM(scissor_test), Scissors);
 }
 
+void SetupDirtyShaders(Tables& tables) {
+    FillBlock(tables[0], OFF(shader_config[0]), NUM(shader_config[0]) * Regs::MaxShaderProgram,
+              Shaders);
+}
+
+void SetupDirtyMisc(Tables& tables) {
+    tables[0][OFF(clip_distance_enabled)] = ClipDistances;
+}
+
 } // Anonymous namespace
 
 StateTracker::StateTracker(Core::System& system) : system{system} {}
@@ -137,6 +146,8 @@ void StateTracker::Initialize() {
     SetupDirtyScissors(tables);
     SetupDirtyVertexArrays(tables);
     SetupDirtyVertexFormat(tables);
+    SetupDirtyShaders(tables);
+    SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
     store[RenderTargets] = true;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 85667cee1e..11fdc6de43 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -48,6 +48,7 @@ enum : u8 {
     ColorMask7 = ColorMask0 + 7,
 
     Shaders,
+    ClipDistances,
     CullTestEnable,
     FrontFace,
     CullFace,

From b01dd7d1c86265dd19508ea15e4ff4db31681470 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sun, 29 Dec 2019 18:14:40 -0300
Subject: [PATCH 42/76] gl_state_tracker: Implement dirty flags for blending

---
 .../renderer_opengl/gl_rasterizer.cpp         | 51 ++++++++++++++-----
 .../renderer_opengl/gl_state_tracker.cpp      | 16 ++++++
 .../renderer_opengl/gl_state_tracker.h        | 14 ++++-
 .../renderer_opengl/gl_texture_cache.cpp      |  1 +
 .../renderer_opengl/renderer_opengl.cpp       |  1 +
 5 files changed, 68 insertions(+), 15 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 717f127e9d..cedfe5db18 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -458,6 +458,7 @@ void RasterizerOpenGL::Clear() {
     }
 
     // TODO: Signal state tracker about these changes
+    state_tracker.NotifyBlend0();
     // TODO(Rodrigo): Find out if these changes affect clearing
     glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
     glDisablei(GL_BLEND, 0);
@@ -1102,31 +1103,53 @@ void RasterizerOpenGL::SyncFragmentColorClampState() {
 }
 
 void RasterizerOpenGL::SyncBlendState() {
-    auto& maxwell3d = system.GPU().Maxwell3D();
-    const auto& regs = maxwell3d.regs;
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    const auto& regs = gpu.regs;
 
-    glBlendColor(regs.blend_color.r, regs.blend_color.g, regs.blend_color.b, regs.blend_color.a);
+    if (flags[Dirty::BlendColor]) {
+        flags[Dirty::BlendColor] = false;
+        glBlendColor(regs.blend_color.r, regs.blend_color.g, regs.blend_color.b,
+                     regs.blend_color.a);
+    }
+
+    // TODO(Rodrigo): Revisit blending, there are several registers we are not reading
+
+    if (!flags[Dirty::BlendStates]) {
+        return;
+    }
+    flags[Dirty::BlendStates] = false;
 
     if (!regs.independent_blend_enable) {
-        const auto& src = regs.blend;
-        oglEnable(GL_BLEND, src.enable[0]);
-        if (!src.enable[0]) {
+        if (!regs.blend.enable[0]) {
+            glDisable(GL_BLEND);
             return;
         }
-        glBlendFuncSeparate(MaxwellToGL::BlendFunc(src.factor_source_rgb),
-                            MaxwellToGL::BlendFunc(src.factor_dest_rgb),
-                            MaxwellToGL::BlendFunc(src.factor_source_a),
-                            MaxwellToGL::BlendFunc(src.factor_dest_a));
-        glBlendEquationSeparate(MaxwellToGL::BlendEquation(src.equation_rgb),
-                                MaxwellToGL::BlendEquation(src.equation_a));
+        glEnable(GL_BLEND);
+        glBlendFuncSeparate(MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb),
+                            MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb),
+                            MaxwellToGL::BlendFunc(regs.blend.factor_source_a),
+                            MaxwellToGL::BlendFunc(regs.blend.factor_dest_a));
+        glBlendEquationSeparate(MaxwellToGL::BlendEquation(regs.blend.equation_rgb),
+                                MaxwellToGL::BlendEquation(regs.blend.equation_a));
         return;
     }
 
+    const bool force = flags[Dirty::BlendIndependentEnabled];
+    flags[Dirty::BlendIndependentEnabled] = false;
+
     for (std::size_t i = 0; i < Maxwell::NumRenderTargets; ++i) {
-        oglEnablei(GL_BLEND, regs.blend.enable[i], static_cast<GLuint>(i));
-        if (!regs.blend.enable[i]) {
+        if (!force && !flags[Dirty::BlendState0 + i]) {
             continue;
         }
+        flags[Dirty::BlendState0 + i] = false;
+
+        if (!regs.blend.enable[i]) {
+            glDisablei(GL_BLEND, static_cast<GLuint>(i));
+            continue;
+        }
+        glEnablei(GL_BLEND, static_cast<GLuint>(i));
+
         const auto& src = regs.independent_blend[i];
         glBlendFuncSeparatei(static_cast<GLuint>(i), MaxwellToGL::BlendFunc(src.factor_source_rgb),
                              MaxwellToGL::BlendFunc(src.factor_dest_rgb),
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index bc5942a7f2..2da1b65fce 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -129,6 +129,21 @@ void SetupDirtyShaders(Tables& tables) {
               Shaders);
 }
 
+void SetupDirtyBlend(Tables& tables) {
+    FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendColor);
+
+    tables[0][OFF(independent_blend_enable)] = BlendIndependentEnabled;
+
+    for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) {
+        const std::size_t offset = OFF(independent_blend) + i * NUM(independent_blend[0]);
+        FillBlock(tables[0], offset, NUM(independent_blend[0]), BlendState0 + i);
+
+        tables[0][OFF(blend.enable) + i] = static_cast<u8>(BlendState0 + i);
+    }
+    FillBlock(tables[1], OFF(independent_blend), NUM(independent_blend), BlendStates);
+    FillBlock(tables[1], OFF(blend), NUM(blend), BlendStates);
+}
+
 void SetupDirtyMisc(Tables& tables) {
     tables[0][OFF(clip_distance_enabled)] = ClipDistances;
 }
@@ -147,6 +162,7 @@ void StateTracker::Initialize() {
     SetupDirtyVertexArrays(tables);
     SetupDirtyVertexFormat(tables);
     SetupDirtyShaders(tables);
+    SetupDirtyBlend(tables);
     SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 11fdc6de43..a9b470eee4 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -47,8 +47,15 @@ enum : u8 {
     ColorMask0,
     ColorMask7 = ColorMask0 + 7,
 
+    BlendColor,
+    BlendIndependentEnabled,
+    BlendStates,
+    BlendState0,
+    BlendState7 = BlendState0 + 7,
+
     Shaders,
     ClipDistances,
+
     CullTestEnable,
     FrontFace,
     CullFace,
@@ -56,7 +63,6 @@ enum : u8 {
     DepthTest,
     StencilTest,
     ColorMask,
-    BlendState,
     PolygonOffset,
 
     Last
@@ -103,6 +109,12 @@ public:
         flags[OpenGL::Dirty::ColorMask0] = true;
     }
 
+    void NotifyBlend0() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::BlendStates] = true;
+        flags[OpenGL::Dirty::BlendState0] = true;
+    }
+
     void NotifyFramebuffer() {
         auto& flags = system.GPU().Maxwell3D().dirty.flags;
         flags[VideoCommon::Dirty::RenderTargets] = true;
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index a02326b9f4..46572eb431 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -520,6 +520,7 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
 
     // TODO: Signal state tracker about these changes
     state_tracker.NotifyScissor0();
+    state_tracker.NotifyBlend0();
     state_tracker.NotifyFramebuffer();
 
     if (dst_params.srgb_conversion) {
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index cbe9164884..d81c680778 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -580,6 +580,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     state_tracker.NotifyViewport0();
     state_tracker.NotifyScissor0();
     state_tracker.NotifyColorMask0();
+    state_tracker.NotifyBlend0();
     state_tracker.NotifyFramebuffer();
 
     program_manager.UseVertexShader(vertex_program.handle);

From b910a83a47a2b566a760dbd20ff5902f303044d4 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sun, 29 Dec 2019 19:23:40 -0300
Subject: [PATCH 43/76] gl_state_tracker: Implement dirty flags for front face
 and culling

---
 .../renderer_opengl/gl_rasterizer.cpp         | 21 +++++++++++++++----
 .../renderer_opengl/gl_state_tracker.cpp      |  9 +++++++-
 .../renderer_opengl/gl_state_tracker.h        | 13 ++++++++++--
 .../renderer_opengl/renderer_opengl.cpp       |  2 ++
 4 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index cedfe5db18..6d87b4e298 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -995,12 +995,25 @@ void RasterizerOpenGL::SyncClipCoef() {
 }
 
 void RasterizerOpenGL::SyncCullMode() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    const auto& regs = gpu.regs;
 
-    oglEnable(GL_CULL_FACE, regs.cull_test_enabled);
-    glCullFace(MaxwellToGL::CullFace(regs.cull_face));
+    if (flags[Dirty::CullTest]) {
+        flags[Dirty::CullTest] = false;
 
-    glFrontFace(MaxwellToGL::FrontFace(regs.front_face));
+        if (regs.cull_test_enabled) {
+            glEnable(GL_CULL_FACE);
+            glCullFace(MaxwellToGL::CullFace(regs.cull_face));
+        } else {
+            glDisable(GL_CULL_FACE);
+        }
+    }
+
+    if (flags[Dirty::FrontFace]) {
+        flags[Dirty::FrontFace] = false;
+        glFrontFace(MaxwellToGL::FrontFace(regs.front_face));
+    }
 }
 
 void RasterizerOpenGL::SyncPrimitiveRestart() {
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 2da1b65fce..05bba5d1d3 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -145,7 +145,14 @@ void SetupDirtyBlend(Tables& tables) {
 }
 
 void SetupDirtyMisc(Tables& tables) {
-    tables[0][OFF(clip_distance_enabled)] = ClipDistances;
+    auto& table = tables[0];
+
+    table[OFF(clip_distance_enabled)] = ClipDistances;
+
+    table[OFF(front_face)] = FrontFace;
+
+    table[OFF(cull_test_enabled)] = CullTest;
+    table[OFF(cull_face)] = CullTest;
 }
 
 } // Anonymous namespace
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index a9b470eee4..1d854f38e2 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -56,9 +56,8 @@ enum : u8 {
     Shaders,
     ClipDistances,
 
-    CullTestEnable,
     FrontFace,
-    CullFace,
+    CullTest,
     PrimitiveRestart,
     DepthTest,
     StencilTest,
@@ -120,6 +119,16 @@ public:
         flags[VideoCommon::Dirty::RenderTargets] = true;
     }
 
+    void NotifyFrontFace() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::FrontFace] = true;
+    }
+
+    void NotifyCullTest() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::CullTest] = true;
+    }
+
 private:
     Core::System& system;
 };
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index d81c680778..e21d7e7a3a 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -582,6 +582,8 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     state_tracker.NotifyColorMask0();
     state_tracker.NotifyBlend0();
     state_tracker.NotifyFramebuffer();
+    state_tracker.NotifyFrontFace();
+    state_tracker.NotifyCullTest();
 
     program_manager.UseVertexShader(vertex_program.handle);
     program_manager.UseGeometryShader(0);

From 40a2c57df5459ff965f1ea507bbca0e36ab17c94 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sun, 29 Dec 2019 22:56:21 -0300
Subject: [PATCH 44/76] gl_state_tracker: Implement depth dirty flags

---
 .../renderer_opengl/gl_rasterizer.cpp         | 20 ++++++++++++++-----
 .../renderer_opengl/gl_state_tracker.cpp      |  8 ++++++++
 .../renderer_opengl/gl_state_tracker.h        |  8 +++++++-
 .../renderer_opengl/renderer_opengl.cpp       |  1 +
 4 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 6d87b4e298..d747e29add 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1024,13 +1024,23 @@ void RasterizerOpenGL::SyncPrimitiveRestart() {
 }
 
 void RasterizerOpenGL::SyncDepthTestState() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
 
-    glDepthMask(regs.depth_write_enabled ? GL_TRUE : GL_FALSE);
+    const auto& regs = gpu.regs;
+    if (flags[Dirty::DepthMask]) {
+        flags[Dirty::DepthMask] = false;
+        glDepthMask(regs.depth_write_enabled ? GL_TRUE : GL_FALSE);
+    }
 
-    oglEnable(GL_DEPTH_TEST, regs.depth_test_enable);
-    if (regs.depth_test_enable) {
-        glDepthFunc(MaxwellToGL::ComparisonOp(regs.depth_test_func));
+    if (flags[Dirty::DepthTest]) {
+        flags[Dirty::DepthTest] = false;
+        if (regs.depth_test_enable) {
+            glEnable(GL_DEPTH_TEST);
+            glDepthFunc(MaxwellToGL::ComparisonOp(regs.depth_test_func));
+        } else {
+            glDisable(GL_DEPTH_TEST);
+        }
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 05bba5d1d3..9994406564 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -129,6 +129,13 @@ void SetupDirtyShaders(Tables& tables) {
               Shaders);
 }
 
+void SetupDirtyDepthTest(Tables& tables) {
+    auto& table = tables[0];
+    table[OFF(depth_test_enable)] = DepthTest;
+    table[OFF(depth_write_enabled)] = DepthMask;
+    table[OFF(depth_test_func)] = DepthTest;
+}
+
 void SetupDirtyBlend(Tables& tables) {
     FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendColor);
 
@@ -169,6 +176,7 @@ void StateTracker::Initialize() {
     SetupDirtyVertexArrays(tables);
     SetupDirtyVertexFormat(tables);
     SetupDirtyShaders(tables);
+    SetupDirtyDepthTest(tables);
     SetupDirtyBlend(tables);
     SetupDirtyMisc(tables);
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 1d854f38e2..dd6cfe02a8 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -58,8 +58,9 @@ enum : u8 {
 
     FrontFace,
     CullTest,
-    PrimitiveRestart,
+    DepthMask,
     DepthTest,
+    PrimitiveRestart,
     StencilTest,
     ColorMask,
     PolygonOffset,
@@ -129,6 +130,11 @@ public:
         flags[OpenGL::Dirty::CullTest] = true;
     }
 
+    void NotifyDepthTest() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::DepthTest] = true;
+    }
+
 private:
     Core::System& system;
 };
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index e21d7e7a3a..8fa4ecb289 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -584,6 +584,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     state_tracker.NotifyFramebuffer();
     state_tracker.NotifyFrontFace();
     state_tracker.NotifyCullTest();
+    state_tracker.NotifyDepthTest();
 
     program_manager.UseVertexShader(vertex_program.handle);
     program_manager.UseGeometryShader(0);

From 37536d7a4996d0008d8988f50dcbc7c126a99c14 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sun, 29 Dec 2019 23:08:32 -0300
Subject: [PATCH 45/76] gl_state_tracker: Implement dirty flags for stencil
 testing

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp   | 12 +++++++++---
 .../renderer_opengl/gl_state_tracker.cpp           | 14 ++++++++++++++
 src/video_core/renderer_opengl/gl_state_tracker.h  |  5 +++++
 src/video_core/renderer_opengl/renderer_opengl.cpp |  1 +
 4 files changed, 29 insertions(+), 3 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index d747e29add..bc4542b694 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1045,14 +1045,20 @@ void RasterizerOpenGL::SyncDepthTestState() {
 }
 
 void RasterizerOpenGL::SyncStencilTestState() {
-    auto& maxwell3d = system.GPU().Maxwell3D();
-    const auto& regs = maxwell3d.regs;
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::StencilTest]) {
+        return;
+    }
+    flags[Dirty::StencilTest] = false;
 
-    oglEnable(GL_STENCIL_TEST, regs.stencil_enable);
+    const auto& regs = gpu.regs;
     if (!regs.stencil_enable) {
+        glDisable(GL_STENCIL_TEST);
         return;
     }
 
+    glEnable(GL_STENCIL_TEST);
     glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func),
                           regs.stencil_front_func_ref, regs.stencil_front_func_mask);
     glStencilOpSeparate(GL_FRONT, MaxwellToGL::StencilOp(regs.stencil_front_op_fail),
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 9994406564..eae47827b2 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -136,6 +136,19 @@ void SetupDirtyDepthTest(Tables& tables) {
     table[OFF(depth_test_func)] = DepthTest;
 }
 
+void SetupDirtyStencilTest(Tables& tables) {
+    static constexpr std::array offsets = {
+        OFF(stencil_enable),          OFF(stencil_front_func_func), OFF(stencil_front_func_ref),
+        OFF(stencil_front_func_mask), OFF(stencil_front_op_fail),   OFF(stencil_front_op_zfail),
+        OFF(stencil_front_op_zpass),  OFF(stencil_front_mask),      OFF(stencil_two_side_enable),
+        OFF(stencil_back_func_func),  OFF(stencil_back_func_ref),   OFF(stencil_back_func_mask),
+        OFF(stencil_back_op_fail),    OFF(stencil_back_op_zfail),   OFF(stencil_back_op_zpass),
+        OFF(stencil_back_mask)};
+    for (const auto offset : offsets) {
+        tables[0][offset] = StencilTest;
+    }
+}
+
 void SetupDirtyBlend(Tables& tables) {
     FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendColor);
 
@@ -177,6 +190,7 @@ void StateTracker::Initialize() {
     SetupDirtyVertexFormat(tables);
     SetupDirtyShaders(tables);
     SetupDirtyDepthTest(tables);
+    SetupDirtyStencilTest(tables);
     SetupDirtyBlend(tables);
     SetupDirtyMisc(tables);
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index dd6cfe02a8..2eaec2a0d9 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -135,6 +135,11 @@ public:
         flags[OpenGL::Dirty::DepthTest] = true;
     }
 
+    void NotifyStencilTest() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::StencilTest] = true;
+    }
+
 private:
     Core::System& system;
 };
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 8fa4ecb289..2449e28ac1 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -585,6 +585,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     state_tracker.NotifyFrontFace();
     state_tracker.NotifyCullTest();
     state_tracker.NotifyDepthTest();
+    state_tracker.NotifyStencilTest();
 
     program_manager.UseVertexShader(vertex_program.handle);
     program_manager.UseGeometryShader(0);

From 46a1888e02f04c1478fbf61ae003f14d09c70a42 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sun, 29 Dec 2019 23:25:53 -0300
Subject: [PATCH 46/76] gl_state_tracker: Implement dirty flags for primitive
 restart

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp  | 15 ++++++++++++---
 .../renderer_opengl/gl_state_tracker.cpp          |  5 +++++
 src/video_core/renderer_opengl/gl_state_tracker.h |  4 ++--
 3 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index bc4542b694..ebb072d918 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1017,10 +1017,19 @@ void RasterizerOpenGL::SyncCullMode() {
 }
 
 void RasterizerOpenGL::SyncPrimitiveRestart() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::PrimitiveRestart]) {
+        return;
+    }
+    flags[Dirty::PrimitiveRestart] = false;
 
-    oglEnable(GL_PRIMITIVE_RESTART, regs.primitive_restart.enabled);
-    glPrimitiveRestartIndex(regs.primitive_restart.index);
+    if (gpu.regs.primitive_restart.enabled) {
+        glEnable(GL_PRIMITIVE_RESTART);
+        glPrimitiveRestartIndex(gpu.regs.primitive_restart.index);
+    } else {
+        glDisable(GL_PRIMITIVE_RESTART);
+    }
 }
 
 void RasterizerOpenGL::SyncDepthTestState() {
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index eae47827b2..c07b7f1361 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -164,6 +164,10 @@ void SetupDirtyBlend(Tables& tables) {
     FillBlock(tables[1], OFF(blend), NUM(blend), BlendStates);
 }
 
+void SetupDirtyPrimitiveRestart(Tables& tables) {
+    FillBlock(tables[0], OFF(primitive_restart), NUM(primitive_restart), PrimitiveRestart);
+}
+
 void SetupDirtyMisc(Tables& tables) {
     auto& table = tables[0];
 
@@ -192,6 +196,7 @@ void StateTracker::Initialize() {
     SetupDirtyDepthTest(tables);
     SetupDirtyStencilTest(tables);
     SetupDirtyBlend(tables);
+    SetupDirtyPrimitiveRestart(tables);
     SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 2eaec2a0d9..af50b1a29b 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -56,13 +56,13 @@ enum : u8 {
     Shaders,
     ClipDistances,
 
+    ColorMask,
     FrontFace,
     CullTest,
     DepthMask,
     DepthTest,
-    PrimitiveRestart,
     StencilTest,
-    ColorMask,
+    PrimitiveRestart,
     PolygonOffset,
 
     Last

From 9e46953580eab7b5b1e7b813db0284a9d2f78f27 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 30 Dec 2019 00:22:43 -0300
Subject: [PATCH 47/76] gl_state_tracker: Implement dirty flags for polygon
 offsets

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp    |  9 +++++++--
 src/video_core/renderer_opengl/gl_state_tracker.cpp | 11 +++++++++++
 src/video_core/renderer_opengl/gl_state_tracker.h   |  5 +++++
 src/video_core/renderer_opengl/renderer_opengl.cpp  |  1 +
 4 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index ebb072d918..78838b8d18 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1249,9 +1249,14 @@ void RasterizerOpenGL::SyncPointState() {
 }
 
 void RasterizerOpenGL::SyncPolygonOffset() {
-    auto& maxwell3d = system.GPU().Maxwell3D();
-    const auto& regs = maxwell3d.regs;
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::PolygonOffset]) {
+        return;
+    }
+    flags[Dirty::PolygonOffset] = false;
 
+    const auto& regs = gpu.regs;
     oglEnable(GL_POLYGON_OFFSET_FILL, regs.polygon_offset_fill_enable);
     oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable);
     oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable);
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index c07b7f1361..7cb874ac97 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -168,6 +168,16 @@ void SetupDirtyPrimitiveRestart(Tables& tables) {
     FillBlock(tables[0], OFF(primitive_restart), NUM(primitive_restart), PrimitiveRestart);
 }
 
+void SetupDirtyPolygonOffset(Tables& tables) {
+    auto& table = tables[0];
+    table[OFF(polygon_offset_fill_enable)] = PolygonOffset;
+    table[OFF(polygon_offset_line_enable)] = PolygonOffset;
+    table[OFF(polygon_offset_point_enable)] = PolygonOffset;
+    table[OFF(polygon_offset_factor)] = PolygonOffset;
+    table[OFF(polygon_offset_units)] = PolygonOffset;
+    table[OFF(polygon_offset_clamp)] = PolygonOffset;
+}
+
 void SetupDirtyMisc(Tables& tables) {
     auto& table = tables[0];
 
@@ -197,6 +207,7 @@ void StateTracker::Initialize() {
     SetupDirtyStencilTest(tables);
     SetupDirtyBlend(tables);
     SetupDirtyPrimitiveRestart(tables);
+    SetupDirtyPolygonOffset(tables);
     SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index af50b1a29b..20c63595c4 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -140,6 +140,11 @@ public:
         flags[OpenGL::Dirty::StencilTest] = true;
     }
 
+    void NotifyPolygonOffset() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::PolygonOffset] = true;
+    }
+
 private:
     Core::System& system;
 };
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 2449e28ac1..6304060440 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -586,6 +586,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     state_tracker.NotifyCullTest();
     state_tracker.NotifyDepthTest();
     state_tracker.NotifyStencilTest();
+    state_tracker.NotifyPolygonOffset();
 
     program_manager.UseVertexShader(vertex_program.handle);
     program_manager.UseGeometryShader(0);

From 3c22bd92d878fcfe7b710e84b478b962fd325417 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 30 Dec 2019 00:37:35 -0300
Subject: [PATCH 48/76] gl_state_tracker: Implement dirty flags for alpha
 testing

---
 .../renderer_opengl/gl_rasterizer.cpp          | 18 ++++++++++++++----
 .../renderer_opengl/gl_state_tracker.cpp       |  8 ++++++++
 .../renderer_opengl/gl_state_tracker.h         |  1 +
 .../renderer_opengl/renderer_opengl.cpp        |  3 +--
 4 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 78838b8d18..5949f53abb 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1267,13 +1267,23 @@ void RasterizerOpenGL::SyncPolygonOffset() {
 }
 
 void RasterizerOpenGL::SyncAlphaTest() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
-    UNIMPLEMENTED_IF_MSG(regs.alpha_test_enabled != 0 && regs.rt_control.count > 1,
-                         "Alpha Testing is enabled with more than one rendertarget");
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::AlphaTest]) {
+        return;
+    }
+    flags[Dirty::AlphaTest] = false;
+
+    const auto& regs = gpu.regs;
+    if (regs.alpha_test_enabled && regs.rt_control.count > 1) {
+        LOG_WARNING(Render_OpenGL, "Alpha testing with more than one render target is not tested");
+    }
 
-    oglEnable(GL_ALPHA_TEST, regs.alpha_test_enabled);
     if (regs.alpha_test_enabled) {
+        glEnable(GL_ALPHA_TEST);
         glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref);
+    } else {
+        glDisable(GL_ALPHA_TEST);
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 7cb874ac97..314d6f14d5 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -149,6 +149,13 @@ void SetupDirtyStencilTest(Tables& tables) {
     }
 }
 
+void SetupDirtyAlphaTest(Tables& tables) {
+    auto& table = tables[0];
+    table[OFF(alpha_test_ref)] = AlphaTest;
+    table[OFF(alpha_test_func)] = AlphaTest;
+    table[OFF(alpha_test_enabled)] = AlphaTest;
+}
+
 void SetupDirtyBlend(Tables& tables) {
     FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendColor);
 
@@ -205,6 +212,7 @@ void StateTracker::Initialize() {
     SetupDirtyShaders(tables);
     SetupDirtyDepthTest(tables);
     SetupDirtyStencilTest(tables);
+    SetupDirtyAlphaTest(tables);
     SetupDirtyBlend(tables);
     SetupDirtyPrimitiveRestart(tables);
     SetupDirtyPolygonOffset(tables);
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 20c63595c4..bef4e6ce67 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -62,6 +62,7 @@ enum : u8 {
     DepthMask,
     DepthTest,
     StencilTest,
+    AlphaTest,
     PrimitiveRestart,
     PolygonOffset,
 
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 6304060440..1be7e491ab 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -195,7 +195,7 @@ layout (location = 0) out vec4 color;
 layout (binding = 0) uniform sampler2D color_texture;
 
 void main() {
-    color = texture(color_texture, frag_tex_coord);
+    color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
 }
 )";
 
@@ -600,7 +600,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
         glDisable(GL_FRAMEBUFFER_SRGB);
     }
     glDisable(GL_COLOR_LOGIC_OP);
-    glDisable(GL_ALPHA_TEST);
     glDisable(GL_DEPTH_TEST);
     glDisable(GL_STENCIL_TEST);
     glDisable(GL_POLYGON_OFFSET_FILL);

From b727d99441e20893faf24410947e158c8faf2d09 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 30 Dec 2019 00:43:15 -0300
Subject: [PATCH 49/76] gl_state_tracker: Implement dirty flags for multisample

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp    | 7 +++++++
 src/video_core/renderer_opengl/gl_state_tracker.cpp | 5 +++++
 src/video_core/renderer_opengl/gl_state_tracker.h   | 1 +
 3 files changed, 13 insertions(+)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 5949f53abb..e4875608ab 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1130,6 +1130,13 @@ void RasterizerOpenGL::SyncColorMask() {
 }
 
 void RasterizerOpenGL::SyncMultiSampleState() {
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::MultisampleControl]) {
+        return;
+    }
+    flags[Dirty::MultisampleControl] = false;
+
     const auto& regs = system.GPU().Maxwell3D().regs;
     oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage);
     oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one);
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 314d6f14d5..c979046a34 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -185,6 +185,10 @@ void SetupDirtyPolygonOffset(Tables& tables) {
     table[OFF(polygon_offset_clamp)] = PolygonOffset;
 }
 
+void SetupDirtyMultisampleControl(Tables& tables) {
+    FillBlock(tables[0], OFF(multisample_control), NUM(multisample_control), MultisampleControl);
+}
+
 void SetupDirtyMisc(Tables& tables) {
     auto& table = tables[0];
 
@@ -216,6 +220,7 @@ void StateTracker::Initialize() {
     SetupDirtyBlend(tables);
     SetupDirtyPrimitiveRestart(tables);
     SetupDirtyPolygonOffset(tables);
+    SetupDirtyMultisampleControl(tables);
     SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index bef4e6ce67..afa8efc282 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -65,6 +65,7 @@ enum : u8 {
     AlphaTest,
     PrimitiveRestart,
     PolygonOffset,
+    MultisampleControl,
 
     Last
 };

From d8f5c450516c3956b422f0487293ea02620a3518 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 30 Dec 2019 00:49:19 -0300
Subject: [PATCH 50/76] gl_state_tracker: Implement dirty flags for rasterize
 enable

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp    | 10 ++++++++--
 src/video_core/renderer_opengl/gl_state_tracker.cpp |  5 +++++
 src/video_core/renderer_opengl/gl_state_tracker.h   |  6 ++++++
 src/video_core/renderer_opengl/gl_texture_cache.cpp |  1 +
 src/video_core/renderer_opengl/renderer_opengl.cpp  |  1 +
 5 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index e4875608ab..a650113ca0 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1090,8 +1090,14 @@ void RasterizerOpenGL::SyncStencilTestState() {
 }
 
 void RasterizerOpenGL::SyncRasterizeEnable() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
-    oglEnable(GL_RASTERIZER_DISCARD, regs.rasterize_enable == 0);
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::RasterizeEnable]) {
+        return;
+    }
+    flags[Dirty::RasterizeEnable] = false;
+
+    oglEnable(GL_RASTERIZER_DISCARD, gpu.regs.rasterize_enable == 0);
 }
 
 void RasterizerOpenGL::SyncColorMask() {
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index c979046a34..ebfb1945f8 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -189,6 +189,10 @@ void SetupDirtyMultisampleControl(Tables& tables) {
     FillBlock(tables[0], OFF(multisample_control), NUM(multisample_control), MultisampleControl);
 }
 
+void SetupDirtyRasterizeEnable(Tables& tables) {
+    tables[0][OFF(rasterize_enable)] = RasterizeEnable;
+}
+
 void SetupDirtyMisc(Tables& tables) {
     auto& table = tables[0];
 
@@ -221,6 +225,7 @@ void StateTracker::Initialize() {
     SetupDirtyPrimitiveRestart(tables);
     SetupDirtyPolygonOffset(tables);
     SetupDirtyMultisampleControl(tables);
+    SetupDirtyRasterizeEnable(tables);
     SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index afa8efc282..73a63d31dd 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -66,6 +66,7 @@ enum : u8 {
     PrimitiveRestart,
     PolygonOffset,
     MultisampleControl,
+    RasterizeEnable,
 
     Last
 };
@@ -147,6 +148,11 @@ public:
         flags[OpenGL::Dirty::PolygonOffset] = true;
     }
 
+    void NotifyRasterizeEnable() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::RasterizeEnable] = true;
+    }
+
 private:
     Core::System& system;
 };
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 46572eb431..81ab956044 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -522,6 +522,7 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     state_tracker.NotifyScissor0();
     state_tracker.NotifyBlend0();
     state_tracker.NotifyFramebuffer();
+    state_tracker.NotifyRasterizeEnable();
 
     if (dst_params.srgb_conversion) {
         glEnable(GL_FRAMEBUFFER_SRGB);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 1be7e491ab..41378978df 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -587,6 +587,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     state_tracker.NotifyDepthTest();
     state_tracker.NotifyStencilTest();
     state_tracker.NotifyPolygonOffset();
+    state_tracker.NotifyRasterizeEnable();
 
     program_manager.UseVertexShader(vertex_program.handle);
     program_manager.UseGeometryShader(0);

From 13afd0e5b0a5e84f2a979515185ece5b54d3fed4 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 30 Dec 2019 00:53:53 -0300
Subject: [PATCH 51/76] gl_state_tracker: Implement dirty flags for sRGB

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp    | 10 ++++++++--
 src/video_core/renderer_opengl/gl_state_tracker.cpp |  5 +++++
 src/video_core/renderer_opengl/gl_state_tracker.h   |  6 ++++++
 src/video_core/renderer_opengl/gl_texture_cache.cpp |  1 +
 src/video_core/renderer_opengl/renderer_opengl.cpp  |  1 +
 5 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a650113ca0..47ad834aaf 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1301,8 +1301,14 @@ void RasterizerOpenGL::SyncAlphaTest() {
 }
 
 void RasterizerOpenGL::SyncFramebufferSRGB() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
-    oglEnable(GL_FRAMEBUFFER_SRGB, regs.framebuffer_srgb);
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::FramebufferSRGB]) {
+        return;
+    }
+    flags[Dirty::FramebufferSRGB] = false;
+
+    oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb);
 }
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index ebfb1945f8..a99a94affb 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -193,6 +193,10 @@ void SetupDirtyRasterizeEnable(Tables& tables) {
     tables[0][OFF(rasterize_enable)] = RasterizeEnable;
 }
 
+void SetupDirtyFramebufferSRGB(Tables& tables) {
+    tables[0][OFF(framebuffer_srgb)] = FramebufferSRGB;
+}
+
 void SetupDirtyMisc(Tables& tables) {
     auto& table = tables[0];
 
@@ -226,6 +230,7 @@ void StateTracker::Initialize() {
     SetupDirtyPolygonOffset(tables);
     SetupDirtyMultisampleControl(tables);
     SetupDirtyRasterizeEnable(tables);
+    SetupDirtyFramebufferSRGB(tables);
     SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 73a63d31dd..7cba66359c 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -67,6 +67,7 @@ enum : u8 {
     PolygonOffset,
     MultisampleControl,
     RasterizeEnable,
+    FramebufferSRGB,
 
     Last
 };
@@ -153,6 +154,11 @@ public:
         flags[OpenGL::Dirty::RasterizeEnable] = true;
     }
 
+    void NotifyFramebufferSRGB() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::FramebufferSRGB] = true;
+    }
+
 private:
     Core::System& system;
 };
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 81ab956044..d8a2bf9eb7 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -523,6 +523,7 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     state_tracker.NotifyBlend0();
     state_tracker.NotifyFramebuffer();
     state_tracker.NotifyRasterizeEnable();
+    state_tracker.NotifyFramebufferSRGB();
 
     if (dst_params.srgb_conversion) {
         glEnable(GL_FRAMEBUFFER_SRGB);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 41378978df..f8b6f98f72 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -588,6 +588,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     state_tracker.NotifyStencilTest();
     state_tracker.NotifyPolygonOffset();
     state_tracker.NotifyRasterizeEnable();
+    state_tracker.NotifyFramebufferSRGB();
 
     program_manager.UseVertexShader(vertex_program.handle);
     program_manager.UseGeometryShader(0);

From bf1a1d989f03b6597a34de8d97b29c189e293134 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 30 Dec 2019 00:57:50 -0300
Subject: [PATCH 52/76] gl_state_tracker: Implement dirty flags for logic op

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp    | 12 ++++++++++--
 src/video_core/renderer_opengl/gl_state_tracker.cpp |  5 +++++
 src/video_core/renderer_opengl/gl_state_tracker.h   |  6 ++++++
 src/video_core/renderer_opengl/renderer_opengl.cpp  |  1 +
 4 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 47ad834aaf..4cb050da66 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1213,11 +1213,19 @@ void RasterizerOpenGL::SyncBlendState() {
 }
 
 void RasterizerOpenGL::SyncLogicOpState() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::LogicOp]) {
+        return;
+    }
+    flags[Dirty::LogicOp] = false;
 
-    oglEnable(GL_COLOR_LOGIC_OP, regs.logic_op.enable);
+    const auto& regs = gpu.regs;
     if (regs.logic_op.enable) {
+        glEnable(GL_COLOR_LOGIC_OP);
         glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation));
+    } else {
+        glDisable(GL_COLOR_LOGIC_OP);
     }
 }
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index a99a94affb..d6ad25ee9b 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -197,6 +197,10 @@ void SetupDirtyFramebufferSRGB(Tables& tables) {
     tables[0][OFF(framebuffer_srgb)] = FramebufferSRGB;
 }
 
+void SetupDirtyLogicOp(Tables& tables) {
+    FillBlock(tables[0], OFF(logic_op), NUM(logic_op), LogicOp);
+}
+
 void SetupDirtyMisc(Tables& tables) {
     auto& table = tables[0];
 
@@ -231,6 +235,7 @@ void StateTracker::Initialize() {
     SetupDirtyMultisampleControl(tables);
     SetupDirtyRasterizeEnable(tables);
     SetupDirtyFramebufferSRGB(tables);
+    SetupDirtyLogicOp(tables);
     SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 7cba66359c..9901d2b0dd 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -68,6 +68,7 @@ enum : u8 {
     MultisampleControl,
     RasterizeEnable,
     FramebufferSRGB,
+    LogicOp,
 
     Last
 };
@@ -159,6 +160,11 @@ public:
         flags[OpenGL::Dirty::FramebufferSRGB] = true;
     }
 
+    void NotifyLogicOp() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::LogicOp] = true;
+    }
+
 private:
     Core::System& system;
 };
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index f8b6f98f72..7391412c2b 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -589,6 +589,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     state_tracker.NotifyPolygonOffset();
     state_tracker.NotifyRasterizeEnable();
     state_tracker.NotifyFramebufferSRGB();
+    state_tracker.NotifyLogicOp();
 
     program_manager.UseVertexShader(vertex_program.handle);
     program_manager.UseGeometryShader(0);

From 231601763c4717f8d7978341994a479e4283fa3d Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 30 Dec 2019 01:20:08 -0300
Subject: [PATCH 53/76] gl_state_tracker: Implement dirty flags for fragment
 color clamp

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp    | 10 ++++++++--
 src/video_core/renderer_opengl/gl_state_tracker.cpp |  5 +++++
 src/video_core/renderer_opengl/gl_state_tracker.h   |  1 +
 3 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4cb050da66..7ffb8fa09b 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1149,8 +1149,14 @@ void RasterizerOpenGL::SyncMultiSampleState() {
 }
 
 void RasterizerOpenGL::SyncFragmentColorClampState() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
-    glClampColor(GL_CLAMP_FRAGMENT_COLOR, regs.frag_color_clamp ? GL_TRUE : GL_FALSE);
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::FragmentClampColor]) {
+        return;
+    }
+    flags[Dirty::FragmentClampColor] = false;
+
+    glClampColor(GL_CLAMP_FRAGMENT_COLOR, gpu.regs.frag_color_clamp ? GL_TRUE : GL_FALSE);
 }
 
 void RasterizerOpenGL::SyncBlendState() {
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index d6ad25ee9b..538ab97e09 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -201,6 +201,10 @@ void SetupDirtyLogicOp(Tables& tables) {
     FillBlock(tables[0], OFF(logic_op), NUM(logic_op), LogicOp);
 }
 
+void SetupDirtyFragmentClampColor(Tables& tables) {
+    tables[0][OFF(frag_color_clamp)] = FragmentClampColor;
+}
+
 void SetupDirtyMisc(Tables& tables) {
     auto& table = tables[0];
 
@@ -236,6 +240,7 @@ void StateTracker::Initialize() {
     SetupDirtyRasterizeEnable(tables);
     SetupDirtyFramebufferSRGB(tables);
     SetupDirtyLogicOp(tables);
+    SetupDirtyFragmentClampColor(tables);
     SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 9901d2b0dd..db92a2e5c9 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -69,6 +69,7 @@ enum : u8 {
     RasterizeEnable,
     FramebufferSRGB,
     LogicOp,
+    FragmentClampColor,
 
     Last
 };

From 4f8d152b1810bbfb2900de6520dbf9df93f9a67d Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 30 Dec 2019 01:27:42 -0300
Subject: [PATCH 54/76] gl_state_tracker: Implement dirty flags for point sizes

---
 .../renderer_opengl/gl_rasterizer.cpp         | 21 +++++++++++++++----
 .../renderer_opengl/gl_state_tracker.cpp      |  7 +++++++
 .../renderer_opengl/gl_state_tracker.h        |  1 +
 3 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 7ffb8fa09b..ec19369279 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1267,12 +1267,25 @@ void RasterizerOpenGL::SyncTransformFeedback() {
 }
 
 void RasterizerOpenGL::SyncPointState() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::PointSize]) {
+        return;
+    }
+    flags[Dirty::PointSize] = false;
+
+    oglEnable(GL_POINT_SPRITE, gpu.regs.point_sprite_enable);
+
+    if (gpu.regs.vp_point_size.enable) {
+        // By definition of GL_POINT_SIZE, it only matters if GL_PROGRAM_POINT_SIZE is disabled.
+        glEnable(GL_PROGRAM_POINT_SIZE);
+        return;
+    }
+
     // Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
     // in OpenGL).
-    oglEnable(GL_PROGRAM_POINT_SIZE, regs.vp_point_size.enable);
-    oglEnable(GL_POINT_SPRITE, regs.point_sprite_enable);
-    glPointSize(std::max(1.0f, regs.point_size));
+    glPointSize(std::max(1.0f, gpu.regs.point_size));
+    glDisable(GL_PROGRAM_POINT_SIZE);
 }
 
 void RasterizerOpenGL::SyncPolygonOffset() {
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 538ab97e09..8bb827ac57 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -205,6 +205,12 @@ void SetupDirtyFragmentClampColor(Tables& tables) {
     tables[0][OFF(frag_color_clamp)] = FragmentClampColor;
 }
 
+void SetupDirtyPointSize(Tables& tables) {
+    tables[0][OFF(vp_point_size)] = PointSize;
+    tables[0][OFF(point_size)] = PointSize;
+    tables[0][OFF(point_sprite_enable)] = PointSize;
+}
+
 void SetupDirtyMisc(Tables& tables) {
     auto& table = tables[0];
 
@@ -241,6 +247,7 @@ void StateTracker::Initialize() {
     SetupDirtyFramebufferSRGB(tables);
     SetupDirtyLogicOp(tables);
     SetupDirtyFragmentClampColor(tables);
+    SetupDirtyPointSize(tables);
     SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index db92a2e5c9..90b17a7d6b 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -70,6 +70,7 @@ enum : u8 {
     FramebufferSRGB,
     LogicOp,
     FragmentClampColor,
+    PointSize,
 
     Last
 };

From a42a6e1a2c41b59a779d92a94123f38d88c3fec3 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 30 Dec 2019 01:40:27 -0300
Subject: [PATCH 55/76] gl_state_tracker: Implement dirty flags for clip
 control

---
 .../renderer_opengl/gl_rasterizer.cpp         | 30 +++++++++++--------
 .../renderer_opengl/gl_state_tracker.cpp      |  7 +++++
 .../renderer_opengl/gl_state_tracker.h        |  6 ++++
 .../renderer_opengl/gl_texture_cache.cpp      |  2 --
 .../renderer_opengl/renderer_opengl.cpp       |  1 +
 5 files changed, 31 insertions(+), 15 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index ec19369279..133ac6c0f2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -460,7 +460,6 @@ void RasterizerOpenGL::Clear() {
     // TODO: Signal state tracker about these changes
     state_tracker.NotifyBlend0();
     // TODO(Rodrigo): Find out if these changes affect clearing
-    glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
     glDisablei(GL_BLEND, 0);
 
     UNIMPLEMENTED_IF(regs.clear_flags.viewport);
@@ -927,7 +926,23 @@ void RasterizerOpenGL::SyncViewport() {
     auto& flags = gpu.dirty.flags;
     const auto& regs = gpu.regs;
 
-    if (flags[Dirty::Viewports]) {
+    const bool dirty_viewport = flags[Dirty::Viewports];
+    if (dirty_viewport || flags[Dirty::ClipControl]) {
+        flags[Dirty::ClipControl] = false;
+
+        bool flip_y = false;
+        if (regs.viewport_transform[0].scale_y < 0.0) {
+            flip_y = !flip_y;
+        }
+        if (regs.screen_y_control.y_negate != 0) {
+            flip_y = !flip_y;
+        }
+        glClipControl(flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT,
+                      regs.depth_mode == Maxwell::DepthMode::ZeroToOne ? GL_ZERO_TO_ONE
+                                                                       : GL_NEGATIVE_ONE_TO_ONE);
+    }
+
+    if (dirty_viewport) {
         flags[Dirty::Viewports] = false;
 
         const bool force = flags[Dirty::ViewportTransform];
@@ -948,17 +963,6 @@ void RasterizerOpenGL::SyncViewport() {
                                 static_cast<GLdouble>(src.depth_range_far));
         }
     }
-
-    bool flip_y = false;
-    if (regs.viewport_transform[0].scale_y < 0.0) {
-        flip_y = !flip_y;
-    }
-    if (regs.screen_y_control.y_negate != 0) {
-        flip_y = !flip_y;
-    }
-    glClipControl(flip_y ? GL_UPPER_LEFT : GL_LOWER_LEFT,
-                  regs.depth_mode == Maxwell::DepthMode::ZeroToOne ? GL_ZERO_TO_ONE
-                                                                   : GL_NEGATIVE_ONE_TO_ONE);
 }
 
 void RasterizerOpenGL::SyncDepthClamp() {
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 8bb827ac57..3d64b1ea4a 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -211,6 +211,12 @@ void SetupDirtyPointSize(Tables& tables) {
     tables[0][OFF(point_sprite_enable)] = PointSize;
 }
 
+void SetupDirtyClipControl(Tables& tables) {
+    auto& table = tables[0];
+    table[OFF(screen_y_control)] = ClipControl;
+    table[OFF(depth_mode)] = ClipControl;
+}
+
 void SetupDirtyMisc(Tables& tables) {
     auto& table = tables[0];
 
@@ -248,6 +254,7 @@ void StateTracker::Initialize() {
     SetupDirtyLogicOp(tables);
     SetupDirtyFragmentClampColor(tables);
     SetupDirtyPointSize(tables);
+    SetupDirtyClipControl(tables);
     SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 90b17a7d6b..5269dadff9 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -71,6 +71,7 @@ enum : u8 {
     LogicOp,
     FragmentClampColor,
     PointSize,
+    ClipControl,
 
     Last
 };
@@ -167,6 +168,11 @@ public:
         flags[OpenGL::Dirty::LogicOp] = true;
     }
 
+    void NotifyClipControl() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::ClipControl] = true;
+    }
+
 private:
     Core::System& system;
 };
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index d8a2bf9eb7..4b86018d5f 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -532,11 +532,9 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     }
     // TODO(Rodrigo): Find out if rasterizer discard affects blits
     // TODO(Rodrigo): Find out if blending affects blits
-    // TODO(Rodrigo): Find out if clip control affects blits
     glDisable(GL_RASTERIZER_DISCARD);
     glDisablei(GL_SCISSOR_TEST, 0);
     glDisablei(GL_BLEND, 0);
-    glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
 
     glBindFramebuffer(GL_READ_FRAMEBUFFER, src_framebuffer.handle);
     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_framebuffer.handle);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 7391412c2b..7b4c2b80c7 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -590,6 +590,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     state_tracker.NotifyRasterizeEnable();
     state_tracker.NotifyFramebufferSRGB();
     state_tracker.NotifyLogicOp();
+    state_tracker.NotifyClipControl();
 
     program_manager.UseVertexShader(vertex_program.handle);
     program_manager.UseGeometryShader(0);

From a5bfc0d045cada34248d3de493889371d22ade7c Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Tue, 31 Dec 2019 18:16:58 -0300
Subject: [PATCH 56/76] gl_state_tracker: Track state of index buffers

---
 src/video_core/renderer_opengl/gl_rasterizer.h    |  2 +-
 src/video_core/renderer_opengl/gl_state_tracker.h | 13 +++++++++++++
 src/video_core/renderer_opengl/utils.cpp          |  7 ++++---
 src/video_core/renderer_opengl/utils.h            |  6 +++++-
 4 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 11206f5579..b24c6661b4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -223,7 +223,7 @@ private:
     static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
     OGLBufferCache buffer_cache;
 
-    VertexArrayPushBuffer vertex_array_pushbuffer;
+    VertexArrayPushBuffer vertex_array_pushbuffer{state_tracker};
     BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
     BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
 
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 5269dadff9..992b915fb2 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -6,7 +6,10 @@
 
 #include <limits>
 
+#include <glad/glad.h>
+
 #include "common/common_types.h"
+#include "core/core.h"
 #include "video_core/dirty_flags.h"
 #include "video_core/engines/maxwell_3d.h"
 
@@ -85,6 +88,14 @@ public:
 
     void Initialize();
 
+    void BindIndexBuffer(GLuint new_index_buffer) {
+        if (index_buffer == new_index_buffer) {
+            return;
+        }
+        index_buffer = new_index_buffer;
+        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, new_index_buffer);
+    }
+
     void NotifyScreenDrawVertexArray() {
         auto& flags = system.GPU().Maxwell3D().dirty.flags;
         flags[OpenGL::Dirty::VertexFormats] = true;
@@ -175,6 +186,8 @@ public:
 
 private:
     Core::System& system;
+
+    GLuint index_buffer = 0;
 };
 
 } // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/utils.cpp b/src/video_core/renderer_opengl/utils.cpp
index f2aaf06dbb..b751086fab 100644
--- a/src/video_core/renderer_opengl/utils.cpp
+++ b/src/video_core/renderer_opengl/utils.cpp
@@ -9,6 +9,7 @@
 #include <glad/glad.h>
 
 #include "common/common_types.h"
+#include "video_core/renderer_opengl/gl_state_tracker.h"
 #include "video_core/renderer_opengl/utils.h"
 
 namespace OpenGL {
@@ -20,7 +21,8 @@ struct VertexArrayPushBuffer::Entry {
     GLsizei stride{};
 };
 
-VertexArrayPushBuffer::VertexArrayPushBuffer() = default;
+VertexArrayPushBuffer::VertexArrayPushBuffer(StateTracker& state_tracker)
+    : state_tracker{state_tracker} {}
 
 VertexArrayPushBuffer::~VertexArrayPushBuffer() = default;
 
@@ -40,10 +42,9 @@ void VertexArrayPushBuffer::SetVertexBuffer(GLuint binding_index, const GLuint*
 
 void VertexArrayPushBuffer::Bind() {
     if (index_buffer) {
-        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, *index_buffer);
+        state_tracker.BindIndexBuffer(*index_buffer);
     }
 
-    // TODO(Rodrigo): Find a way to ARB_multi_bind this
     for (const auto& entry : vertex_buffers) {
         glBindVertexBuffer(entry.binding_index, *entry.buffer, entry.offset, entry.stride);
     }
diff --git a/src/video_core/renderer_opengl/utils.h b/src/video_core/renderer_opengl/utils.h
index e8612a9ecc..47ee3177ba 100644
--- a/src/video_core/renderer_opengl/utils.h
+++ b/src/video_core/renderer_opengl/utils.h
@@ -11,9 +11,11 @@
 
 namespace OpenGL {
 
+class StateTracker;
+
 class VertexArrayPushBuffer final {
 public:
-    explicit VertexArrayPushBuffer();
+    explicit VertexArrayPushBuffer(StateTracker& state_tracker);
     ~VertexArrayPushBuffer();
 
     void Setup();
@@ -28,6 +30,8 @@ public:
 private:
     struct Entry;
 
+    StateTracker& state_tracker;
+
     const GLuint* index_buffer{};
     std::vector<Entry> vertex_buffers;
 };

From 15cadc394848619be5e0ccaa43dc00488476218d Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 2 Jan 2020 22:27:52 -0300
Subject: [PATCH 57/76] maxwell_3d: Use two tables instead of three for dirty
 flags

---
 src/video_core/engines/maxwell_3d.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index beaf3ffb62..3ff6dec754 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1294,7 +1294,7 @@ public:
     struct {
         std::bitset<std::numeric_limits<u8>::max()> flags;
         std::bitset<std::numeric_limits<u8>::max()> on_write_stores;
-        std::array<std::array<u8, Regs::NUM_REGS>, 3> tables{};
+        std::array<std::array<u8, Regs::NUM_REGS>, 2> tables{};
     } dirty;
 
 private:

From 98c8948b238793271154c1718eaabeaeb2f0bb1b Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 2 Jan 2020 22:30:41 -0300
Subject: [PATCH 58/76] gl_rasterizer: Minor sort changes to clearing

---
 .../renderer_opengl/gl_rasterizer.cpp         | 20 +++++++++----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 133ac6c0f2..dabb22ae8c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -408,13 +408,12 @@ void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color_fb, bool using
 }
 
 void RasterizerOpenGL::Clear() {
-    const auto& maxwell3d = system.GPU().Maxwell3D();
-
-    if (!maxwell3d.ShouldExecute()) {
+    const auto& gpu = system.GPU().Maxwell3D();
+    if (!gpu.ShouldExecute()) {
         return;
     }
 
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = gpu.regs;
     bool use_color{};
     bool use_depth{};
     bool use_stencil{};
@@ -424,14 +423,13 @@ void RasterizerOpenGL::Clear() {
         use_color = true;
     }
     if (use_color) {
-        // TODO: Signal state tracker about these changes
         state_tracker.NotifyColorMask0();
         glColorMaski(0, regs.clear_buffers.R != 0, regs.clear_buffers.G != 0,
                      regs.clear_buffers.B != 0, regs.clear_buffers.A != 0);
 
-        SyncFramebufferSRGB();
         // TODO(Rodrigo): Determine if clamping is used on clears
         SyncFragmentColorClampState();
+        SyncFramebufferSRGB();
     }
     if (regs.clear_buffers.Z) {
         ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
@@ -441,7 +439,7 @@ void RasterizerOpenGL::Clear() {
         glDepthMask(GL_TRUE);
     }
     if (regs.clear_buffers.S) {
-        ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!");
+        ASSERT_MSG(regs.zeta_enable, "Tried to clear stencil but buffer is not enabled!");
         use_stencil = true;
     }
 
@@ -450,20 +448,20 @@ void RasterizerOpenGL::Clear() {
         return;
     }
 
-    ConfigureClearFramebuffer(use_color, use_depth, use_stencil);
-
     SyncRasterizeEnable();
+
     if (regs.clear_flags.scissor) {
         SyncScissorTest();
     }
 
-    // TODO: Signal state tracker about these changes
+    // TODO(Rodrigo): Find out if blending affects clearing
     state_tracker.NotifyBlend0();
-    // TODO(Rodrigo): Find out if these changes affect clearing
     glDisablei(GL_BLEND, 0);
 
     UNIMPLEMENTED_IF(regs.clear_flags.viewport);
 
+    ConfigureClearFramebuffer(use_color, use_depth, use_stencil);
+
     if (use_color) {
         glClearBufferfv(GL_COLOR, 0, regs.clear_color);
     }

From 35bb9239cae38a173204cfc36bdfa22aec88fb00 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 2 Jan 2020 22:31:04 -0300
Subject: [PATCH 59/76] gl_rasterizer: Notify depth mask changes on clear

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp  | 2 +-
 src/video_core/renderer_opengl/gl_state_tracker.h | 5 +++++
 2 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index dabb22ae8c..c8aa342efc 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -435,7 +435,7 @@ void RasterizerOpenGL::Clear() {
         ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear Z but buffer is not enabled!");
         use_depth = true;
 
-        // TODO: Signal state tracker about these changes
+        state_tracker.NotifyDepthMask();
         glDepthMask(GL_TRUE);
     }
     if (regs.clear_buffers.S) {
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 992b915fb2..a390e0b990 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -149,6 +149,11 @@ public:
         flags[OpenGL::Dirty::CullTest] = true;
     }
 
+    void NotifyDepthMask() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::DepthMask] = true;
+    }
+
     void NotifyDepthTest() {
         auto& flags = system.GPU().Maxwell3D().dirty.flags;
         flags[OpenGL::Dirty::DepthTest] = true;

From 3ce66776eccc23a3c1af69ce6ce079e28361fd58 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 2 Jan 2020 22:31:33 -0300
Subject: [PATCH 60/76] gl_rasterizer: Disable scissor 0 when scissor is not
 used on clear

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index c8aa342efc..e035be867c 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -452,6 +452,9 @@ void RasterizerOpenGL::Clear() {
 
     if (regs.clear_flags.scissor) {
         SyncScissorTest();
+    } else {
+        state_tracker.NotifyScissor0();
+        glDisablei(GL_SCISSOR_TEST, 0);
     }
 
     // TODO(Rodrigo): Find out if blending affects clearing

From 2eeea907138926e7d712bf20f92c1f3d1cc2d594 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 2 Jan 2020 22:41:20 -0300
Subject: [PATCH 61/76] gl_state_tracker: Implement dirty flags for depth clamp
 enabling

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp    | 12 +++++++++---
 src/video_core/renderer_opengl/gl_state_tracker.cpp |  5 +++++
 src/video_core/renderer_opengl/gl_state_tracker.h   |  1 +
 3 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index e035be867c..3ffa9988e6 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -493,6 +493,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
     SyncFragmentColorClampState();
     SyncMultiSampleState();
     SyncDepthTestState();
+    SyncDepthClamp();
     SyncStencilTestState();
     SyncBlendState();
     SyncLogicOpState();
@@ -967,11 +968,16 @@ void RasterizerOpenGL::SyncViewport() {
 }
 
 void RasterizerOpenGL::SyncDepthClamp() {
-    const auto& regs = system.GPU().Maxwell3D().regs;
-    const auto& state = regs.view_volume_clip_control;
+    auto& gpu = system.GPU().Maxwell3D();
+    auto& flags = gpu.dirty.flags;
+    if (!flags[Dirty::DepthClampEnabled]) {
+        return;
+    }
+    flags[Dirty::DepthClampEnabled] = false;
 
+    const auto& state = gpu.regs.view_volume_clip_control;
     UNIMPLEMENTED_IF_MSG(state.depth_clamp_far != state.depth_clamp_near,
-                         "Unimplemented Depth clamp separation!");
+                         "Unimplemented depth clamp separation!");
 
     oglEnable(GL_DEPTH_CLAMP, state.depth_clamp_far || state.depth_clamp_near);
 }
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 3d64b1ea4a..fa87330288 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -217,6 +217,10 @@ void SetupDirtyClipControl(Tables& tables) {
     table[OFF(depth_mode)] = ClipControl;
 }
 
+void SetupDirtyDepthClampEnabled(Tables& tables) {
+    tables[0][OFF(view_volume_clip_control)] = DepthClampEnabled;
+}
+
 void SetupDirtyMisc(Tables& tables) {
     auto& table = tables[0];
 
@@ -255,6 +259,7 @@ void StateTracker::Initialize() {
     SetupDirtyFragmentClampColor(tables);
     SetupDirtyPointSize(tables);
     SetupDirtyClipControl(tables);
+    SetupDirtyDepthClampEnabled(tables);
     SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index a390e0b990..38b38c4a7e 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -75,6 +75,7 @@ enum : u8 {
     FragmentClampColor,
     PointSize,
     ClipControl,
+    DepthClampEnabled,
 
     Last
 };

From 62437943a72d5ec2d5e0734767855ea54ea257a0 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 2 Jan 2020 22:42:56 -0300
Subject: [PATCH 62/76] gl_rasterizer: Only apply polygon offset clamp if
 enabled

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 3ffa9988e6..8a762fd0db 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -1312,9 +1312,12 @@ void RasterizerOpenGL::SyncPolygonOffset() {
     oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable);
     oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable);
 
-    // Hardware divides polygon offset units by two
-    glPolygonOffsetClamp(regs.polygon_offset_factor, regs.polygon_offset_units / 2.0f,
-                         regs.polygon_offset_clamp);
+    if (regs.polygon_offset_fill_enable || regs.polygon_offset_line_enable ||
+        regs.polygon_offset_point_enable) {
+        // Hardware divides polygon offset units by two
+        glPolygonOffsetClamp(regs.polygon_offset_factor, regs.polygon_offset_units / 2.0f,
+                             regs.polygon_offset_clamp);
+    }
 }
 
 void RasterizerOpenGL::SyncAlphaTest() {

From b1498d2c54b11b9a9c0ab307f03377f6661ab873 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 13 Jan 2020 17:20:02 -0300
Subject: [PATCH 63/76] gl_rasterizer: Remove num vertex buffers magic number

---
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 8a762fd0db..84c285db8d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -54,6 +54,8 @@ MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255
 
 namespace {
 
+constexpr std::size_t NumSupportedVertexAttributes = 16;
+
 template <typename Engine, typename Entry>
 Tegra::Texture::FullTextureInfo GetTextureInfo(const Engine& engine, const Entry& entry,
                                                Tegra::Engines::ShaderType shader_type,
@@ -134,7 +136,7 @@ void RasterizerOpenGL::SetupVertexFormat() {
     // avoid OpenGL errors.
     // TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
     // assume every shader uses them all.
-    for (std::size_t index = 0; index < 16; ++index) {
+    for (std::size_t index = 0; index < NumSupportedVertexAttributes; ++index) {
         if (!flags[Dirty::VertexFormat0 + index]) {
             continue;
         }
@@ -207,7 +209,7 @@ void RasterizerOpenGL::SetupVertexInstances() {
     flags[Dirty::VertexInstances] = false;
 
     const auto& regs = gpu.regs;
-    for (std::size_t index = 0; index < 16; ++index) {
+    for (std::size_t index = 0; index < NumSupportedVertexAttributes; ++index) {
         if (!flags[Dirty::VertexInstance0 + index]) {
             continue;
         }

From 1bd95a314f07732319a71fca2aba81d400ca4c83 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 20 Feb 2020 20:35:53 -0300
Subject: [PATCH 64/76] vk_state_tracker: Initial implementation

Add support for render targets and viewports.
---
 src/video_core/CMakeLists.txt                 |  2 +
 .../renderer_vulkan/renderer_vulkan.cpp       |  8 +-
 .../renderer_vulkan/renderer_vulkan.h         |  4 +
 .../renderer_vulkan/vk_rasterizer.cpp         | 17 +++-
 .../renderer_vulkan/vk_rasterizer.h           |  5 +-
 .../renderer_vulkan/vk_scheduler.cpp          | 21 ++--
 src/video_core/renderer_vulkan/vk_scheduler.h | 42 +-------
 .../renderer_vulkan/vk_state_tracker.cpp      | 97 +++++++++++++++++++
 .../renderer_vulkan/vk_state_tracker.h        | 53 ++++++++++
 .../renderer_vulkan/vk_texture_cache.cpp      |  1 +
 10 files changed, 198 insertions(+), 52 deletions(-)
 create mode 100644 src/video_core/renderer_vulkan/vk_state_tracker.cpp
 create mode 100644 src/video_core/renderer_vulkan/vk_state_tracker.h

diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 18b774ac7e..06c174a5bc 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -199,6 +199,8 @@ if (ENABLE_VULKAN)
         renderer_vulkan/vk_shader_util.h
         renderer_vulkan/vk_staging_buffer_pool.cpp
         renderer_vulkan/vk_staging_buffer_pool.h
+        renderer_vulkan/vk_state_tracker.cpp
+        renderer_vulkan/vk_state_tracker.h
         renderer_vulkan/vk_stream_buffer.cpp
         renderer_vulkan/vk_stream_buffer.h
         renderer_vulkan/vk_swapchain.cpp
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index ddc62bc975..42bb014187 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -27,6 +27,7 @@
 #include "video_core/renderer_vulkan/vk_rasterizer.h"
 #include "video_core/renderer_vulkan/vk_resource_manager.h"
 #include "video_core/renderer_vulkan/vk_scheduler.h"
+#include "video_core/renderer_vulkan/vk_state_tracker.h"
 #include "video_core/renderer_vulkan/vk_swapchain.h"
 
 namespace Vulkan {
@@ -177,10 +178,13 @@ bool RendererVulkan::Init() {
     swapchain = std::make_unique<VKSwapchain>(surface, *device);
     swapchain->Create(framebuffer.width, framebuffer.height, false);
 
-    scheduler = std::make_unique<VKScheduler>(*device, *resource_manager);
+    state_tracker = std::make_unique<StateTracker>(system);
+
+    scheduler = std::make_unique<VKScheduler>(*device, *resource_manager, *state_tracker);
 
     rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device,
-                                                    *resource_manager, *memory_manager, *scheduler);
+                                                    *resource_manager, *memory_manager,
+                                                    *state_tracker, *scheduler);
 
     blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device,
                                                  *resource_manager, *memory_manager, *swapchain,
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index f513397f0e..3da08d2e43 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -4,8 +4,10 @@
 
 #pragma once
 
+#include <memory>
 #include <optional>
 #include <vector>
+
 #include "video_core/renderer_base.h"
 #include "video_core/renderer_vulkan/declarations.h"
 
@@ -15,6 +17,7 @@ class System;
 
 namespace Vulkan {
 
+class StateTracker;
 class VKBlitScreen;
 class VKDevice;
 class VKFence;
@@ -61,6 +64,7 @@ private:
     std::unique_ptr<VKSwapchain> swapchain;
     std::unique_ptr<VKMemoryManager> memory_manager;
     std::unique_ptr<VKResourceManager> resource_manager;
+    std::unique_ptr<StateTracker> state_tracker;
     std::unique_ptr<VKScheduler> scheduler;
     std::unique_ptr<VKBlitScreen> blit_screen;
 };
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index b1be41a218..41cbf4134e 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -36,6 +36,7 @@
 #include "video_core/renderer_vulkan/vk_sampler_cache.h"
 #include "video_core/renderer_vulkan/vk_scheduler.h"
 #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
+#include "video_core/renderer_vulkan/vk_state_tracker.h"
 #include "video_core/renderer_vulkan/vk_texture_cache.h"
 #include "video_core/renderer_vulkan/vk_update_descriptor.h"
 
@@ -277,10 +278,11 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf,
 RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& renderer,
                                    VKScreenInfo& screen_info, const VKDevice& device,
                                    VKResourceManager& resource_manager,
-                                   VKMemoryManager& memory_manager, VKScheduler& scheduler)
+                                   VKMemoryManager& memory_manager, StateTracker& state_tracker,
+                                   VKScheduler& scheduler)
     : RasterizerAccelerated{system.Memory()}, system{system}, render_window{renderer},
       screen_info{screen_info}, device{device}, resource_manager{resource_manager},
-      memory_manager{memory_manager}, scheduler{scheduler},
+      memory_manager{memory_manager}, state_tracker{state_tracker}, scheduler{scheduler},
       staging_pool(device, memory_manager, scheduler), descriptor_pool(device),
       update_descriptor_queue(device, scheduler),
       quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
@@ -545,6 +547,10 @@ bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config,
     return true;
 }
 
+void RasterizerVulkan::SetupDirtyFlags() {
+    state_tracker.Initialize();
+}
+
 void RasterizerVulkan::FlushWork() {
     static constexpr u32 DRAWS_TO_DISPATCH = 4096;
 
@@ -568,7 +574,9 @@ void RasterizerVulkan::FlushWork() {
 
 RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments() {
     MICROPROFILE_SCOPE(Vulkan_RenderTargets);
-    constexpr bool update_rendertargets = true;
+    auto& dirty = system.GPU().Maxwell3D().dirty.flags;
+    const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets];
+    dirty[VideoCommon::Dirty::RenderTargets] = false;
 
     texture_cache.GuardRenderTargets(true);
 
@@ -971,6 +979,9 @@ void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const Ima
 }
 
 void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu) {
+    if (!state_tracker.TouchViewports()) {
+        return;
+    }
     const auto& regs = gpu.regs;
     const std::array viewports{
         GetViewportState(device, regs, 0),  GetViewportState(device, regs, 1),
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index 4dc8af6e82..a79440eba6 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -96,6 +96,7 @@ struct hash<Vulkan::FramebufferCacheKey> {
 
 namespace Vulkan {
 
+class StateTracker;
 class BufferBindings;
 
 struct ImageView {
@@ -108,7 +109,7 @@ public:
     explicit RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& render_window,
                               VKScreenInfo& screen_info, const VKDevice& device,
                               VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
-                              VKScheduler& scheduler);
+                              StateTracker& state_tracker, VKScheduler& scheduler);
     ~RasterizerVulkan() override;
 
     void Draw(bool is_indexed, bool is_instanced) override;
@@ -127,6 +128,7 @@ public:
                                const Tegra::Engines::Fermi2D::Config& copy_config) override;
     bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
                            u32 pixel_stride) override;
+    void SetupDirtyFlags() override;
 
     /// Maximum supported size that a constbuffer can have in bytes.
     static constexpr std::size_t MaxConstbufferSize = 0x10000;
@@ -241,6 +243,7 @@ private:
     const VKDevice& device;
     VKResourceManager& resource_manager;
     VKMemoryManager& memory_manager;
+    StateTracker& state_tracker;
     VKScheduler& scheduler;
 
     VKStagingBufferPool staging_pool;
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 92bd6c3444..b61d4fe63d 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -2,6 +2,12 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <thread>
+#include <utility>
+
 #include "common/assert.h"
 #include "common/microprofile.h"
 #include "video_core/renderer_vulkan/declarations.h"
@@ -9,6 +15,7 @@
 #include "video_core/renderer_vulkan/vk_query_cache.h"
 #include "video_core/renderer_vulkan/vk_resource_manager.h"
 #include "video_core/renderer_vulkan/vk_scheduler.h"
+#include "video_core/renderer_vulkan/vk_state_tracker.h"
 
 namespace Vulkan {
 
@@ -29,9 +36,10 @@ void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf,
     last = nullptr;
 }
 
-VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager)
-    : device{device}, resource_manager{resource_manager}, next_fence{
-                                                              &resource_manager.CommitFence()} {
+VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
+                         StateTracker& state_tracker)
+    : device{device}, resource_manager{resource_manager}, state_tracker{state_tracker},
+      next_fence{&resource_manager.CommitFence()} {
     AcquireNewChunk();
     AllocateNewContext();
     worker_thread = std::thread(&VKScheduler::WorkerThread, this);
@@ -157,12 +165,7 @@ void VKScheduler::AllocateNewContext() {
 
 void VKScheduler::InvalidateState() {
     state.graphics_pipeline = nullptr;
-    state.viewports = false;
-    state.scissors = false;
-    state.depth_bias = false;
-    state.blend_constants = false;
-    state.depth_bounds = false;
-    state.stencil_values = false;
+    state_tracker.InvalidateCommandBufferState();
 }
 
 void VKScheduler::EndPendingOperations() {
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 62fd7858b1..c7cc291c31 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -17,6 +17,7 @@
 
 namespace Vulkan {
 
+class StateTracker;
 class VKDevice;
 class VKFence;
 class VKQueryCache;
@@ -43,7 +44,8 @@ private:
 /// OpenGL-like operations on Vulkan command buffers.
 class VKScheduler {
 public:
-    explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager);
+    explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
+                         StateTracker& state_tracker);
     ~VKScheduler();
 
     /// Sends the current execution context to the GPU.
@@ -74,36 +76,6 @@ public:
         query_cache = &query_cache_;
     }
 
-    /// Returns true when viewports have been set in the current command buffer.
-    bool TouchViewports() {
-        return std::exchange(state.viewports, true);
-    }
-
-    /// Returns true when scissors have been set in the current command buffer.
-    bool TouchScissors() {
-        return std::exchange(state.scissors, true);
-    }
-
-    /// Returns true when depth bias have been set in the current command buffer.
-    bool TouchDepthBias() {
-        return std::exchange(state.depth_bias, true);
-    }
-
-    /// Returns true when blend constants have been set in the current command buffer.
-    bool TouchBlendConstants() {
-        return std::exchange(state.blend_constants, true);
-    }
-
-    /// Returns true when depth bounds have been set in the current command buffer.
-    bool TouchDepthBounds() {
-        return std::exchange(state.depth_bounds, true);
-    }
-
-    /// Returns true when stencil values have been set in the current command buffer.
-    bool TouchStencilValues() {
-        return std::exchange(state.stencil_values, true);
-    }
-
     /// Send work to a separate thread.
     template <typename T>
     void Record(T&& command) {
@@ -217,6 +189,8 @@ private:
 
     const VKDevice& device;
     VKResourceManager& resource_manager;
+    StateTracker& state_tracker;
+
     VKQueryCache* query_cache = nullptr;
 
     vk::CommandBuffer current_cmdbuf;
@@ -226,12 +200,6 @@ private:
     struct State {
         std::optional<vk::RenderPassBeginInfo> renderpass;
         vk::Pipeline graphics_pipeline;
-        bool viewports = false;
-        bool scissors = false;
-        bool depth_bias = false;
-        bool blend_constants = false;
-        bool depth_bounds = false;
-        bool stencil_values = false;
     } state;
 
     std::unique_ptr<CommandChunk> chunk;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
new file mode 100644
index 0000000000..d44992dc9f
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -0,0 +1,97 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <type_traits>
+
+#include "common/common_types.h"
+#include "core/core.h"
+#include "video_core/engines/maxwell_3d.h"
+#include "video_core/gpu.h"
+#include "video_core/renderer_vulkan/vk_state_tracker.h"
+
+#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
+#define NUM(field_name) (sizeof(Maxwell3D::Regs::field_name) / sizeof(u32))
+
+namespace Vulkan {
+
+namespace {
+
+using namespace Dirty;
+using namespace VideoCommon::Dirty;
+using Tegra::Engines::Maxwell3D;
+using Regs = Maxwell3D::Regs;
+using Dirty = std::remove_reference_t<decltype(Maxwell3D::dirty)>;
+using Tables = std::remove_reference_t<decltype(Maxwell3D::dirty.tables)>;
+using Table = std::remove_reference_t<decltype(Maxwell3D::dirty.tables[0])>;
+using Flags = std::remove_reference_t<decltype(Maxwell3D::dirty.flags)>;
+
+Flags MakeInvalidationFlags() {
+    Flags flags{};
+    flags[Viewports] = true;
+    return flags;
+}
+
+template <typename Integer>
+void FillBlock(Table& table, std::size_t begin, std::size_t num, Integer dirty_index) {
+    const auto it = std::begin(table) + begin;
+    std::fill(it, it + num, static_cast<u8>(dirty_index));
+}
+
+template <typename Integer1, typename Integer2>
+void FillBlock(Tables& tables, std::size_t begin, std::size_t num, Integer1 index_a,
+               Integer2 index_b) {
+    FillBlock(tables[0], begin, num, index_a);
+    FillBlock(tables[1], begin, num, index_b);
+}
+
+void SetupDirtyRenderTargets(Tables& tables) {
+    static constexpr std::size_t num_per_rt = NUM(rt[0]);
+    static constexpr std::size_t begin = OFF(rt);
+    static constexpr std::size_t num = num_per_rt * Regs::NumRenderTargets;
+    for (std::size_t rt = 0; rt < Regs::NumRenderTargets; ++rt) {
+        FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt);
+    }
+    FillBlock(tables[1], begin, num, RenderTargets);
+
+    static constexpr std::array zeta_flags{ZetaBuffer, RenderTargets};
+    for (std::size_t i = 0; i < std::size(zeta_flags); ++i) {
+        const u8 flag = zeta_flags[i];
+        auto& table = tables[i];
+        table[OFF(zeta_enable)] = flag;
+        table[OFF(zeta_width)] = flag;
+        table[OFF(zeta_height)] = flag;
+        FillBlock(table, OFF(zeta), NUM(zeta), flag);
+    }
+}
+
+void SetupDirtyViewports(Tables& tables) {
+    FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
+    FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
+    tables[0][OFF(viewport_transform_enabled)] = Viewports;
+}
+
+} // Anonymous namespace
+
+StateTracker::StateTracker(Core::System& system)
+    : system{system}, invalidation_flags{MakeInvalidationFlags()} {}
+
+void StateTracker::Initialize() {
+    auto& dirty = system.GPU().Maxwell3D().dirty;
+    auto& tables = dirty.tables;
+    SetupDirtyRenderTargets(tables);
+    SetupDirtyViewports(tables);
+
+    auto& store = dirty.on_write_stores;
+    store[RenderTargets] = true;
+    store[ZetaBuffer] = true;
+    for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) {
+        store[ColorBuffer0 + i] = true;
+    }
+}
+
+void StateTracker::InvalidateCommandBufferState() {
+    system.GPU().Maxwell3D().dirty.flags |= invalidation_flags;
+}
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
new file mode 100644
index 0000000000..9ec7b5136e
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -0,0 +1,53 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <type_traits> // REMOVE ME
+#include <utility>
+
+#include "common/common_types.h"
+#include "core/core.h"
+#include "video_core/dirty_flags.h"
+#include "video_core/engines/maxwell_3d.h"
+
+namespace Vulkan {
+
+namespace Dirty {
+
+enum : u8 {
+    First = VideoCommon::Dirty::LastCommonEntry,
+
+    Viewports,
+};
+
+} // namespace Dirty
+
+class StateTracker {
+public:
+    explicit StateTracker(Core::System& system);
+
+    void Initialize();
+
+    void InvalidateCommandBufferState();
+
+    bool TouchViewports() {
+        return Exchange(Dirty::Viewports, false);
+    }
+
+private:
+    using Flags = std::remove_reference_t<decltype(Tegra::Engines::Maxwell3D::dirty.flags)>;
+
+    bool Exchange(std::size_t id, bool new_value) const noexcept {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        const bool is_dirty = flags[id];
+        flags[id] = new_value;
+        return is_dirty;
+    }
+
+    Core::System& system;
+    Flags invalidation_flags;
+};
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 51b0d38a65..73d92a5aef 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -22,6 +22,7 @@
 #include "video_core/renderer_vulkan/vk_device.h"
 #include "video_core/renderer_vulkan/vk_memory_manager.h"
 #include "video_core/renderer_vulkan/vk_rasterizer.h"
+#include "video_core/renderer_vulkan/vk_scheduler.h"
 #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
 #include "video_core/renderer_vulkan/vk_texture_cache.h"
 #include "video_core/surface.h"

From 42f18749658bb4f3ce2e6be7677596d41ac8cd6c Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Thu, 20 Feb 2020 22:19:57 -0300
Subject: [PATCH 65/76] vk_state_tracker: Implement dirty flags for scissors

---
 src/video_core/renderer_vulkan/vk_rasterizer.cpp    | 3 +++
 src/video_core/renderer_vulkan/vk_state_tracker.cpp | 6 ++++++
 src/video_core/renderer_vulkan/vk_state_tracker.h   | 5 +++++
 3 files changed, 14 insertions(+)

diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 41cbf4134e..207ef0f0df 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -998,6 +998,9 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu) {
 }
 
 void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu) {
+    if (!state_tracker.TouchScissors()) {
+        return;
+    }
     const auto& regs = gpu.regs;
     const std::array scissors = {
         GetScissorState(regs, 0),  GetScissorState(regs, 1),  GetScissorState(regs, 2),
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index d44992dc9f..0e00a90798 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -29,6 +29,7 @@ using Flags = std::remove_reference_t<decltype(Maxwell3D::dirty.flags)>;
 Flags MakeInvalidationFlags() {
     Flags flags{};
     flags[Viewports] = true;
+    flags[Scissors] = true;
     return flags;
 }
 
@@ -71,6 +72,10 @@ void SetupDirtyViewports(Tables& tables) {
     tables[0][OFF(viewport_transform_enabled)] = Viewports;
 }
 
+void SetupDirtyScissors(Tables& tables) {
+    FillBlock(tables[0], OFF(scissor_test), NUM(scissor_test), Scissors);
+}
+
 } // Anonymous namespace
 
 StateTracker::StateTracker(Core::System& system)
@@ -81,6 +86,7 @@ void StateTracker::Initialize() {
     auto& tables = dirty.tables;
     SetupDirtyRenderTargets(tables);
     SetupDirtyViewports(tables);
+    SetupDirtyScissors(tables);
 
     auto& store = dirty.on_write_stores;
     store[RenderTargets] = true;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 9ec7b5136e..ba8f3a854e 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -20,6 +20,7 @@ enum : u8 {
     First = VideoCommon::Dirty::LastCommonEntry,
 
     Viewports,
+    Scissors,
 };
 
 } // namespace Dirty
@@ -36,6 +37,10 @@ public:
         return Exchange(Dirty::Viewports, false);
     }
 
+    bool TouchScissors() {
+        return Exchange(Dirty::Scissors, false);
+    }
+
 private:
     using Flags = std::remove_reference_t<decltype(Tegra::Engines::Maxwell3D::dirty.flags)>;
 

From a33870996b00f18179048debf183f1cb5c499593 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 21 Feb 2020 00:28:45 -0300
Subject: [PATCH 66/76] vk_state_tracker: Implement dirty flags for depth bias

---
 src/video_core/renderer_vulkan/vk_rasterizer.cpp    | 3 +++
 src/video_core/renderer_vulkan/vk_state_tracker.cpp | 9 +++++++++
 src/video_core/renderer_vulkan/vk_state_tracker.h   | 5 +++++
 3 files changed, 17 insertions(+)

diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 207ef0f0df..be2c4d1491 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -1015,6 +1015,9 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu) {
 }
 
 void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D& gpu) {
+    if (!state_tracker.TouchDepthBias()) {
+        return;
+    }
     const auto& regs = gpu.regs;
     scheduler.Record([constant = regs.polygon_offset_units, clamp = regs.polygon_offset_clamp,
                       factor = regs.polygon_offset_factor](auto cmdbuf, auto& dld) {
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 0e00a90798..c75caaa566 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -30,6 +30,7 @@ Flags MakeInvalidationFlags() {
     Flags flags{};
     flags[Viewports] = true;
     flags[Scissors] = true;
+    flags[DepthBias] = true;
     return flags;
 }
 
@@ -76,6 +77,13 @@ void SetupDirtyScissors(Tables& tables) {
     FillBlock(tables[0], OFF(scissor_test), NUM(scissor_test), Scissors);
 }
 
+void SetupDirtyDepthBias(Tables& tables) {
+    auto& table = tables[0];
+    table[OFF(polygon_offset_units)] = DepthBias;
+    table[OFF(polygon_offset_clamp)] = DepthBias;
+    table[OFF(polygon_offset_factor)] = DepthBias;
+}
+
 } // Anonymous namespace
 
 StateTracker::StateTracker(Core::System& system)
@@ -87,6 +95,7 @@ void StateTracker::Initialize() {
     SetupDirtyRenderTargets(tables);
     SetupDirtyViewports(tables);
     SetupDirtyScissors(tables);
+    SetupDirtyDepthBias(tables);
 
     auto& store = dirty.on_write_stores;
     store[RenderTargets] = true;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index ba8f3a854e..088b471451 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -21,6 +21,7 @@ enum : u8 {
 
     Viewports,
     Scissors,
+    DepthBias,
 };
 
 } // namespace Dirty
@@ -41,6 +42,10 @@ public:
         return Exchange(Dirty::Scissors, false);
     }
 
+    bool TouchDepthBias() {
+        return Exchange(Dirty::DepthBias, false);
+    }
+
 private:
     using Flags = std::remove_reference_t<decltype(Tegra::Engines::Maxwell3D::dirty.flags)>;
 

From cd0e28c9ecb2ecd4579ad9abb8899255f60334da Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 21 Feb 2020 00:37:29 -0300
Subject: [PATCH 67/76] vk_state_tracker: Implement dirty flags for blend
 constants

---
 src/video_core/renderer_vulkan/vk_rasterizer.cpp    | 3 +++
 src/video_core/renderer_vulkan/vk_state_tracker.cpp | 6 ++++++
 src/video_core/renderer_vulkan/vk_state_tracker.h   | 5 +++++
 3 files changed, 14 insertions(+)

diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index be2c4d1491..958f90f99c 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -1026,6 +1026,9 @@ void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D& gpu) {
 }
 
 void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D& gpu) {
+    if (!state_tracker.TouchBlendConstants()) {
+        return;
+    }
     const std::array blend_color = {gpu.regs.blend_color.r, gpu.regs.blend_color.g,
                                     gpu.regs.blend_color.b, gpu.regs.blend_color.a};
     scheduler.Record([blend_color](auto cmdbuf, auto& dld) {
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index c75caaa566..c7e54c68b2 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -31,6 +31,7 @@ Flags MakeInvalidationFlags() {
     flags[Viewports] = true;
     flags[Scissors] = true;
     flags[DepthBias] = true;
+    flags[BlendConstants] = true;
     return flags;
 }
 
@@ -84,6 +85,10 @@ void SetupDirtyDepthBias(Tables& tables) {
     table[OFF(polygon_offset_factor)] = DepthBias;
 }
 
+void SetupDirtyBlendConstants(Tables& tables) {
+    FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendConstants);
+}
+
 } // Anonymous namespace
 
 StateTracker::StateTracker(Core::System& system)
@@ -96,6 +101,7 @@ void StateTracker::Initialize() {
     SetupDirtyViewports(tables);
     SetupDirtyScissors(tables);
     SetupDirtyDepthBias(tables);
+    SetupDirtyBlendConstants(tables);
 
     auto& store = dirty.on_write_stores;
     store[RenderTargets] = true;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 088b471451..a0493813a9 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -22,6 +22,7 @@ enum : u8 {
     Viewports,
     Scissors,
     DepthBias,
+    BlendConstants,
 };
 
 } // namespace Dirty
@@ -46,6 +47,10 @@ public:
         return Exchange(Dirty::DepthBias, false);
     }
 
+    bool TouchBlendConstants() {
+        return Exchange(Dirty::BlendConstants, false);
+    }
+
 private:
     using Flags = std::remove_reference_t<decltype(Tegra::Engines::Maxwell3D::dirty.flags)>;
 

From f9df2c6bcdc1a6129289fb2878d4471c04e55fc7 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 21 Feb 2020 00:54:39 -0300
Subject: [PATCH 68/76] vk_state_tracker: Implement dirty flags for depth
 bounds

---
 src/video_core/renderer_vulkan/vk_rasterizer.cpp    | 3 +++
 src/video_core/renderer_vulkan/vk_state_tracker.cpp | 6 ++++++
 src/video_core/renderer_vulkan/vk_state_tracker.h   | 5 +++++
 3 files changed, 14 insertions(+)

diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 958f90f99c..7029b3d5eb 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -1037,6 +1037,9 @@ void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D& gpu) {
 }
 
 void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D& gpu) {
+    if (!state_tracker.TouchDepthBounds()) {
+        return;
+    }
     const auto& regs = gpu.regs;
     scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]](
                          auto cmdbuf, auto& dld) { cmdbuf.setDepthBounds(min, max, dld); });
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index c7e54c68b2..b55180dd8a 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -32,6 +32,7 @@ Flags MakeInvalidationFlags() {
     flags[Scissors] = true;
     flags[DepthBias] = true;
     flags[BlendConstants] = true;
+    flags[DepthBounds] = true;
     return flags;
 }
 
@@ -89,6 +90,10 @@ void SetupDirtyBlendConstants(Tables& tables) {
     FillBlock(tables[0], OFF(blend_color), NUM(blend_color), BlendConstants);
 }
 
+void SetupDirtyDepthBounds(Tables& tables) {
+    FillBlock(tables[0], OFF(depth_bounds), NUM(depth_bounds), DepthBounds);
+}
+
 } // Anonymous namespace
 
 StateTracker::StateTracker(Core::System& system)
@@ -102,6 +107,7 @@ void StateTracker::Initialize() {
     SetupDirtyScissors(tables);
     SetupDirtyDepthBias(tables);
     SetupDirtyBlendConstants(tables);
+    SetupDirtyDepthBounds(tables);
 
     auto& store = dirty.on_write_stores;
     store[RenderTargets] = true;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index a0493813a9..25b5c647b7 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -23,6 +23,7 @@ enum : u8 {
     Scissors,
     DepthBias,
     BlendConstants,
+    DepthBounds,
 };
 
 } // namespace Dirty
@@ -51,6 +52,10 @@ public:
         return Exchange(Dirty::BlendConstants, false);
     }
 
+    bool TouchDepthBounds() {
+        return Exchange(Dirty::DepthBounds, false);
+    }
+
 private:
     using Flags = std::remove_reference_t<decltype(Tegra::Engines::Maxwell3D::dirty.flags)>;
 

From 6ac3eb4d87af7793d805dc9f7fc43f45e59e212e Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 21 Feb 2020 01:09:02 -0300
Subject: [PATCH 69/76] vk_state_tracker: Implement dirty flags for stencil
 properties

---
 src/video_core/renderer_vulkan/vk_rasterizer.cpp    |  3 +++
 src/video_core/renderer_vulkan/vk_state_tracker.cpp | 13 +++++++++++++
 src/video_core/renderer_vulkan/vk_state_tracker.h   |  5 +++++
 3 files changed, 21 insertions(+)

diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 7029b3d5eb..eb9c49d5e9 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -1046,6 +1046,9 @@ void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D& gpu) {
 }
 
 void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D& gpu) {
+    if (!state_tracker.TouchStencilProperties()) {
+        return;
+    }
     const auto& regs = gpu.regs;
     if (regs.stencil_two_side_enable) {
         // Separate values per face
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index b55180dd8a..3fd0476b6e 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -33,6 +33,7 @@ Flags MakeInvalidationFlags() {
     flags[DepthBias] = true;
     flags[BlendConstants] = true;
     flags[DepthBounds] = true;
+    flags[StencilProperties] = true;
     return flags;
 }
 
@@ -94,6 +95,17 @@ void SetupDirtyDepthBounds(Tables& tables) {
     FillBlock(tables[0], OFF(depth_bounds), NUM(depth_bounds), DepthBounds);
 }
 
+void SetupDirtyStencilProperties(Tables& tables) {
+    auto& table = tables[0];
+    table[OFF(stencil_two_side_enable)] = StencilProperties;
+    table[OFF(stencil_front_func_ref)] = StencilProperties;
+    table[OFF(stencil_front_mask)] = StencilProperties;
+    table[OFF(stencil_front_func_mask)] = StencilProperties;
+    table[OFF(stencil_back_func_ref)] = StencilProperties;
+    table[OFF(stencil_back_mask)] = StencilProperties;
+    table[OFF(stencil_back_func_mask)] = StencilProperties;
+}
+
 } // Anonymous namespace
 
 StateTracker::StateTracker(Core::System& system)
@@ -108,6 +120,7 @@ void StateTracker::Initialize() {
     SetupDirtyDepthBias(tables);
     SetupDirtyBlendConstants(tables);
     SetupDirtyDepthBounds(tables);
+    SetupDirtyStencilProperties(tables);
 
     auto& store = dirty.on_write_stores;
     store[RenderTargets] = true;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 25b5c647b7..1d8434dd08 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -24,6 +24,7 @@ enum : u8 {
     DepthBias,
     BlendConstants,
     DepthBounds,
+    StencilProperties,
 };
 
 } // namespace Dirty
@@ -56,6 +57,10 @@ public:
         return Exchange(Dirty::DepthBounds, false);
     }
 
+    bool TouchStencilProperties() {
+        return Exchange(Dirty::StencilProperties, false);
+    }
+
 private:
     using Flags = std::remove_reference_t<decltype(Tegra::Engines::Maxwell3D::dirty.flags)>;
 

From 042256c6bbbe27a71805aa2dabe2cac436134b3d Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 21 Feb 2020 01:19:07 -0300
Subject: [PATCH 70/76] state_tracker: Remove type traits with named structures

---
 src/video_core/engines/maxwell_3d.h                 | 12 ++++++++----
 src/video_core/renderer_opengl/gl_state_tracker.cpp |  6 ++----
 src/video_core/renderer_vulkan/vk_state_tracker.cpp | 11 ++++++-----
 src/video_core/renderer_vulkan/vk_state_tracker.h   | 11 ++++++-----
 4 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index 3ff6dec754..491cff3707 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -1291,10 +1291,14 @@ public:
         u32 gl_end_count{};
     } mme_draw;
 
-    struct {
-        std::bitset<std::numeric_limits<u8>::max()> flags;
-        std::bitset<std::numeric_limits<u8>::max()> on_write_stores;
-        std::array<std::array<u8, Regs::NUM_REGS>, 2> tables{};
+    struct DirtyState {
+        using Flags = std::bitset<std::numeric_limits<u8>::max()>;
+        using Table = std::array<u8, Regs::NUM_REGS>;
+        using Tables = std::array<Table, 2>;
+
+        Flags flags;
+        Flags on_write_stores;
+        Tables tables{};
     } dirty;
 
 private:
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index fa87330288..d5088cfa52 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -5,7 +5,6 @@
 #include <algorithm>
 #include <array>
 #include <cstddef>
-#include <type_traits>
 
 #include "common/common_types.h"
 #include "core/core.h"
@@ -24,9 +23,8 @@ using namespace Dirty;
 using namespace VideoCommon::Dirty;
 using Tegra::Engines::Maxwell3D;
 using Regs = Maxwell3D::Regs;
-using Dirty = std::remove_reference_t<decltype(Maxwell3D::dirty)>;
-using Tables = std::remove_reference_t<decltype(Maxwell3D::dirty.tables)>;
-using Table = std::remove_reference_t<decltype(Maxwell3D::dirty.tables[0])>;
+using Tables = Maxwell3D::DirtyState::Tables;
+using Table = Maxwell3D::DirtyState::Table;
 
 template <typename Integer>
 void FillBlock(Table& table, std::size_t begin, std::size_t num, Integer dirty_index) {
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 3fd0476b6e..67229ffcc3 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -2,7 +2,9 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
-#include <type_traits>
+#include <algorithm>
+#include <cstddef>
+#include <iterator>
 
 #include "common/common_types.h"
 #include "core/core.h"
@@ -21,10 +23,9 @@ using namespace Dirty;
 using namespace VideoCommon::Dirty;
 using Tegra::Engines::Maxwell3D;
 using Regs = Maxwell3D::Regs;
-using Dirty = std::remove_reference_t<decltype(Maxwell3D::dirty)>;
-using Tables = std::remove_reference_t<decltype(Maxwell3D::dirty.tables)>;
-using Table = std::remove_reference_t<decltype(Maxwell3D::dirty.tables[0])>;
-using Flags = std::remove_reference_t<decltype(Maxwell3D::dirty.flags)>;
+using Tables = Maxwell3D::DirtyState::Tables;
+using Table = Maxwell3D::DirtyState::Table;
+using Flags = Maxwell3D::DirtyState::Flags;
 
 Flags MakeInvalidationFlags() {
     Flags flags{};
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 1d8434dd08..03bc415b25 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -4,8 +4,8 @@
 
 #pragma once
 
-#include <type_traits> // REMOVE ME
-#include <utility>
+#include <cstddef>
+#include <limits>
 
 #include "common/common_types.h"
 #include "core/core.h"
@@ -25,7 +25,10 @@ enum : u8 {
     BlendConstants,
     DepthBounds,
     StencilProperties,
+
+    Last
 };
+static_assert(Last <= std::numeric_limits<u8>::max());
 
 } // namespace Dirty
 
@@ -62,8 +65,6 @@ public:
     }
 
 private:
-    using Flags = std::remove_reference_t<decltype(Tegra::Engines::Maxwell3D::dirty.flags)>;
-
     bool Exchange(std::size_t id, bool new_value) const noexcept {
         auto& flags = system.GPU().Maxwell3D().dirty.flags;
         const bool is_dirty = flags[id];
@@ -72,7 +73,7 @@ private:
     }
 
     Core::System& system;
-    Flags invalidation_flags;
+    Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
 };
 
 } // namespace Vulkan

From 6669b359a3857c265bd4d1ed77c1bce6bcd6a912 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 21 Feb 2020 01:21:57 -0300
Subject: [PATCH 71/76] vk_rasterizer: Pass Maxwell registers to dynamic
 updates

---
 .../renderer_vulkan/vk_rasterizer.cpp         | 35 ++++++++-----------
 .../renderer_vulkan/vk_rasterizer.h           | 12 +++----
 2 files changed, 21 insertions(+), 26 deletions(-)

diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index eb9c49d5e9..02027b7d57 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -726,13 +726,13 @@ void RasterizerVulkan::SetupImageTransitions(
 }
 
 void RasterizerVulkan::UpdateDynamicStates() {
-    auto& gpu = system.GPU().Maxwell3D();
-    UpdateViewportsState(gpu);
-    UpdateScissorsState(gpu);
-    UpdateDepthBias(gpu);
-    UpdateBlendConstants(gpu);
-    UpdateDepthBounds(gpu);
-    UpdateStencilFaces(gpu);
+    auto& regs = system.GPU().Maxwell3D().regs;
+    UpdateViewportsState(regs);
+    UpdateScissorsState(regs);
+    UpdateDepthBias(regs);
+    UpdateBlendConstants(regs);
+    UpdateDepthBounds(regs);
+    UpdateStencilFaces(regs);
 }
 
 void RasterizerVulkan::SetupVertexArrays(FixedPipelineState::VertexInput& vertex_input,
@@ -978,11 +978,10 @@ void RasterizerVulkan::SetupImage(const Tegra::Texture::TICEntry& tic, const Ima
     image_views.push_back(ImageView{std::move(view), image_layout});
 }
 
-void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu) {
+void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs) {
     if (!state_tracker.TouchViewports()) {
         return;
     }
-    const auto& regs = gpu.regs;
     const std::array viewports{
         GetViewportState(device, regs, 0),  GetViewportState(device, regs, 1),
         GetViewportState(device, regs, 2),  GetViewportState(device, regs, 3),
@@ -997,11 +996,10 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu) {
     });
 }
 
-void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu) {
+void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs) {
     if (!state_tracker.TouchScissors()) {
         return;
     }
-    const auto& regs = gpu.regs;
     const std::array scissors = {
         GetScissorState(regs, 0),  GetScissorState(regs, 1),  GetScissorState(regs, 2),
         GetScissorState(regs, 3),  GetScissorState(regs, 4),  GetScissorState(regs, 5),
@@ -1014,42 +1012,39 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu) {
     });
 }
 
-void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D& gpu) {
+void RasterizerVulkan::UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs) {
     if (!state_tracker.TouchDepthBias()) {
         return;
     }
-    const auto& regs = gpu.regs;
     scheduler.Record([constant = regs.polygon_offset_units, clamp = regs.polygon_offset_clamp,
                       factor = regs.polygon_offset_factor](auto cmdbuf, auto& dld) {
         cmdbuf.setDepthBias(constant, clamp, factor / 2.0f, dld);
     });
 }
 
-void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D& gpu) {
+void RasterizerVulkan::UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs) {
     if (!state_tracker.TouchBlendConstants()) {
         return;
     }
-    const std::array blend_color = {gpu.regs.blend_color.r, gpu.regs.blend_color.g,
-                                    gpu.regs.blend_color.b, gpu.regs.blend_color.a};
+    const std::array blend_color = {regs.blend_color.r, regs.blend_color.g, regs.blend_color.b,
+                                    regs.blend_color.a};
     scheduler.Record([blend_color](auto cmdbuf, auto& dld) {
         cmdbuf.setBlendConstants(blend_color.data(), dld);
     });
 }
 
-void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D& gpu) {
+void RasterizerVulkan::UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs) {
     if (!state_tracker.TouchDepthBounds()) {
         return;
     }
-    const auto& regs = gpu.regs;
     scheduler.Record([min = regs.depth_bounds[0], max = regs.depth_bounds[1]](
                          auto cmdbuf, auto& dld) { cmdbuf.setDepthBounds(min, max, dld); });
 }
 
-void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D& gpu) {
+void RasterizerVulkan::UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs) {
     if (!state_tracker.TouchStencilProperties()) {
         return;
     }
-    const auto& regs = gpu.regs;
     if (regs.stencil_two_side_enable) {
         // Separate values per face
         scheduler.Record(
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index a79440eba6..96ea05f0a8 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -217,12 +217,12 @@ private:
 
     void SetupImage(const Tegra::Texture::TICEntry& tic, const ImageEntry& entry);
 
-    void UpdateViewportsState(Tegra::Engines::Maxwell3D& gpu);
-    void UpdateScissorsState(Tegra::Engines::Maxwell3D& gpu);
-    void UpdateDepthBias(Tegra::Engines::Maxwell3D& gpu);
-    void UpdateBlendConstants(Tegra::Engines::Maxwell3D& gpu);
-    void UpdateDepthBounds(Tegra::Engines::Maxwell3D& gpu);
-    void UpdateStencilFaces(Tegra::Engines::Maxwell3D& gpu);
+    void UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateDepthBias(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateBlendConstants(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateDepthBounds(Tegra::Engines::Maxwell3D::Regs& regs);
+    void UpdateStencilFaces(Tegra::Engines::Maxwell3D::Regs& regs);
 
     std::size_t CalculateGraphicsStreamBufferSize(bool is_indexed) const;
 

From ac204754d4fe2aaae214025d8f1f40bcb938d74f Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 21 Feb 2020 01:56:00 -0300
Subject: [PATCH 72/76] dirty_flags: Deduplicate code between OpenGL and Vulkan

---
 src/video_core/CMakeLists.txt                 |  1 +
 src/video_core/dirty_flags.cpp                | 46 +++++++++++++++++++
 src/video_core/dirty_flags.h                  | 23 ++++++++++
 .../renderer_opengl/gl_state_tracker.cpp      | 39 +---------------
 .../renderer_vulkan/vk_state_tracker.cpp      | 41 +----------------
 5 files changed, 73 insertions(+), 77 deletions(-)
 create mode 100644 src/video_core/dirty_flags.cpp

diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 06c174a5bc..14f3b4569e 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -2,6 +2,7 @@ add_library(video_core STATIC
     buffer_cache/buffer_block.h
     buffer_cache/buffer_cache.h
     buffer_cache/map_interval.h
+    dirty_flags.cpp
     dirty_flags.h
     dma_pusher.cpp
     dma_pusher.h
diff --git a/src/video_core/dirty_flags.cpp b/src/video_core/dirty_flags.cpp
new file mode 100644
index 0000000000..4429f34059
--- /dev/null
+++ b/src/video_core/dirty_flags.cpp
@@ -0,0 +1,46 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <cstddef>
+
+#include "common/common_types.h"
+#include "video_core/dirty_flags.h"
+
+#define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
+#define NUM(field_name) (sizeof(::Tegra::Engines::Maxwell3D::Regs::field_name) / sizeof(u32))
+
+namespace VideoCommon::Dirty {
+
+using Tegra::Engines::Maxwell3D;
+
+void SetupCommonOnWriteStores(Tegra::Engines::Maxwell3D::DirtyState::Flags& store) {
+    store[RenderTargets] = true;
+    store[ZetaBuffer] = true;
+    for (std::size_t i = 0; i < Maxwell3D::Regs::NumRenderTargets; ++i) {
+        store[ColorBuffer0 + i] = true;
+    }
+}
+
+void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables) {
+    static constexpr std::size_t num_per_rt = NUM(rt[0]);
+    static constexpr std::size_t begin = OFF(rt);
+    static constexpr std::size_t num = num_per_rt * Maxwell3D::Regs::NumRenderTargets;
+    for (std::size_t rt = 0; rt < Maxwell3D::Regs::NumRenderTargets; ++rt) {
+        FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt);
+    }
+    FillBlock(tables[1], begin, num, RenderTargets);
+
+    static constexpr std::array zeta_flags{ZetaBuffer, RenderTargets};
+    for (std::size_t i = 0; i < std::size(zeta_flags); ++i) {
+        const u8 flag = zeta_flags[i];
+        auto& table = tables[i];
+        table[OFF(zeta_enable)] = flag;
+        table[OFF(zeta_width)] = flag;
+        table[OFF(zeta_height)] = flag;
+        FillBlock(table, OFF(zeta), NUM(zeta), flag);
+    }
+}
+
+} // namespace VideoCommon::Dirty
diff --git a/src/video_core/dirty_flags.h b/src/video_core/dirty_flags.h
index d9058bcab2..ed07dfc031 100644
--- a/src/video_core/dirty_flags.h
+++ b/src/video_core/dirty_flags.h
@@ -4,7 +4,12 @@
 
 #pragma once
 
+#include <algorithm>
+#include <cstddef>
+#include <iterator>
+
 #include "common/common_types.h"
+#include "video_core/engines/maxwell_3d.h"
 
 namespace VideoCommon::Dirty {
 
@@ -25,4 +30,22 @@ enum : u8 {
     LastCommonEntry,
 };
 
+template <typename Integer>
+inline void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Table& table, std::size_t begin,
+                      std::size_t num, Integer dirty_index) {
+    const auto it = std::begin(table) + begin;
+    std::fill(it, it + num, static_cast<u8>(dirty_index));
+}
+
+template <typename Integer1, typename Integer2>
+inline void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables, std::size_t begin,
+                      std::size_t num, Integer1 index_a, Integer2 index_b) {
+    FillBlock(tables[0], begin, num, index_a);
+    FillBlock(tables[1], begin, num, index_b);
+}
+
+void SetupCommonOnWriteStores(Tegra::Engines::Maxwell3D::DirtyState::Flags& store);
+
+void SetupDirtyRenderTargets(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables);
+
 } // namespace VideoCommon::Dirty
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index d5088cfa52..1e43c9ec00 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -26,39 +26,6 @@ using Regs = Maxwell3D::Regs;
 using Tables = Maxwell3D::DirtyState::Tables;
 using Table = Maxwell3D::DirtyState::Table;
 
-template <typename Integer>
-void FillBlock(Table& table, std::size_t begin, std::size_t num, Integer dirty_index) {
-    const auto it = std::begin(table) + begin;
-    std::fill(it, it + num, static_cast<u8>(dirty_index));
-}
-
-template <typename Integer1, typename Integer2>
-void FillBlock(Tables& tables, std::size_t begin, std::size_t num, Integer1 index_a,
-               Integer2 index_b) {
-    FillBlock(tables[0], begin, num, index_a);
-    FillBlock(tables[1], begin, num, index_b);
-}
-
-void SetupDirtyRenderTargets(Tables& tables) {
-    static constexpr std::size_t num_per_rt = NUM(rt[0]);
-    static constexpr std::size_t begin = OFF(rt);
-    static constexpr std::size_t num = num_per_rt * Regs::NumRenderTargets;
-    for (std::size_t rt = 0; rt < Regs::NumRenderTargets; ++rt) {
-        FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt);
-    }
-    FillBlock(tables[1], begin, num, RenderTargets);
-
-    static constexpr std::array zeta_flags{ZetaBuffer, RenderTargets};
-    for (std::size_t i = 0; i < std::size(zeta_flags); ++i) {
-        const u8 flag = zeta_flags[i];
-        auto& table = tables[i];
-        table[OFF(zeta_enable)] = flag;
-        table[OFF(zeta_width)] = flag;
-        table[OFF(zeta_height)] = flag;
-        FillBlock(table, OFF(zeta), NUM(zeta), flag);
-    }
-}
-
 void SetupDirtyColorMasks(Tables& tables) {
     tables[0][OFF(color_mask_common)] = ColorMaskCommon;
     for (std::size_t rt = 0; rt < Regs::NumRenderTargets; ++rt) {
@@ -261,11 +228,7 @@ void StateTracker::Initialize() {
     SetupDirtyMisc(tables);
 
     auto& store = dirty.on_write_stores;
-    store[RenderTargets] = true;
-    store[ZetaBuffer] = true;
-    for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) {
-        store[ColorBuffer0 + i] = true;
-    }
+    SetupCommonOnWriteStores(store);
     store[VertexBuffers] = true;
     for (std::size_t i = 0; i < Regs::NumVertexArrays; ++i) {
         store[VertexBuffer0 + i] = true;
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 67229ffcc3..d74e68b639 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -8,6 +8,7 @@
 
 #include "common/common_types.h"
 #include "core/core.h"
+#include "video_core/dirty_flags.h"
 #include "video_core/engines/maxwell_3d.h"
 #include "video_core/gpu.h"
 #include "video_core/renderer_vulkan/vk_state_tracker.h"
@@ -38,39 +39,6 @@ Flags MakeInvalidationFlags() {
     return flags;
 }
 
-template <typename Integer>
-void FillBlock(Table& table, std::size_t begin, std::size_t num, Integer dirty_index) {
-    const auto it = std::begin(table) + begin;
-    std::fill(it, it + num, static_cast<u8>(dirty_index));
-}
-
-template <typename Integer1, typename Integer2>
-void FillBlock(Tables& tables, std::size_t begin, std::size_t num, Integer1 index_a,
-               Integer2 index_b) {
-    FillBlock(tables[0], begin, num, index_a);
-    FillBlock(tables[1], begin, num, index_b);
-}
-
-void SetupDirtyRenderTargets(Tables& tables) {
-    static constexpr std::size_t num_per_rt = NUM(rt[0]);
-    static constexpr std::size_t begin = OFF(rt);
-    static constexpr std::size_t num = num_per_rt * Regs::NumRenderTargets;
-    for (std::size_t rt = 0; rt < Regs::NumRenderTargets; ++rt) {
-        FillBlock(tables[0], begin + rt * num_per_rt, num_per_rt, ColorBuffer0 + rt);
-    }
-    FillBlock(tables[1], begin, num, RenderTargets);
-
-    static constexpr std::array zeta_flags{ZetaBuffer, RenderTargets};
-    for (std::size_t i = 0; i < std::size(zeta_flags); ++i) {
-        const u8 flag = zeta_flags[i];
-        auto& table = tables[i];
-        table[OFF(zeta_enable)] = flag;
-        table[OFF(zeta_width)] = flag;
-        table[OFF(zeta_height)] = flag;
-        FillBlock(table, OFF(zeta), NUM(zeta), flag);
-    }
-}
-
 void SetupDirtyViewports(Tables& tables) {
     FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports);
     FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports);
@@ -123,12 +91,7 @@ void StateTracker::Initialize() {
     SetupDirtyDepthBounds(tables);
     SetupDirtyStencilProperties(tables);
 
-    auto& store = dirty.on_write_stores;
-    store[RenderTargets] = true;
-    store[ZetaBuffer] = true;
-    for (std::size_t i = 0; i < Regs::NumRenderTargets; ++i) {
-        store[ColorBuffer0 + i] = true;
-    }
+    SetupCommonOnWriteStores(dirty.on_write_stores);
 }
 
 void StateTracker::InvalidateCommandBufferState() {

From 887d5288efab9b98141c9d042bc2fba3fbe230d2 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 21 Feb 2020 02:55:08 -0300
Subject: [PATCH 73/76] gl_rasterizer: Don't disable blending on clears

Blending doesn't affect clears.
---
 src/video_core/renderer_opengl/gl_rasterizer.cpp | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 84c285db8d..8f977985b7 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -459,10 +459,6 @@ void RasterizerOpenGL::Clear() {
         glDisablei(GL_SCISSOR_TEST, 0);
     }
 
-    // TODO(Rodrigo): Find out if blending affects clearing
-    state_tracker.NotifyBlend0();
-    glDisablei(GL_BLEND, 0);
-
     UNIMPLEMENTED_IF(regs.clear_flags.viewport);
 
     ConfigureClearFramebuffer(use_color, use_depth, use_stencil);

From a6a350ddc3bf708276e27045e326f108f733f0b1 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 21 Feb 2020 03:25:29 -0300
Subject: [PATCH 74/76] gl_texture_cache: Remove blending disable on blits

Blending doesn't affect blits. Rasterizer discard does, update the
commentaries.
---
 src/video_core/renderer_opengl/gl_texture_cache.cpp | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 4b86018d5f..2d3838a7a1 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -518,9 +518,7 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     UNIMPLEMENTED_IF(src_params.target == SurfaceTarget::Texture3D);
     UNIMPLEMENTED_IF(dst_params.target == SurfaceTarget::Texture3D);
 
-    // TODO: Signal state tracker about these changes
     state_tracker.NotifyScissor0();
-    state_tracker.NotifyBlend0();
     state_tracker.NotifyFramebuffer();
     state_tracker.NotifyRasterizeEnable();
     state_tracker.NotifyFramebufferSRGB();
@@ -530,11 +528,8 @@ void TextureCacheOpenGL::ImageBlit(View& src_view, View& dst_view,
     } else {
         glDisable(GL_FRAMEBUFFER_SRGB);
     }
-    // TODO(Rodrigo): Find out if rasterizer discard affects blits
-    // TODO(Rodrigo): Find out if blending affects blits
     glDisable(GL_RASTERIZER_DISCARD);
     glDisablei(GL_SCISSOR_TEST, 0);
-    glDisablei(GL_BLEND, 0);
 
     glBindFramebuffer(GL_READ_FRAMEBUFFER, src_framebuffer.handle);
     glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_framebuffer.handle);

From ef7f6eb67de9d05522b05ac520ef3184250fcbd4 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 21 Feb 2020 03:29:24 -0300
Subject: [PATCH 75/76] renderer_opengl: Fix edge-case where alpha testing
 might cull presentation

---
 src/video_core/renderer_opengl/gl_state_tracker.h  | 5 +++++
 src/video_core/renderer_opengl/renderer_opengl.cpp | 2 ++
 2 files changed, 7 insertions(+)

diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 38b38c4a7e..e084829111 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -190,6 +190,11 @@ public:
         flags[OpenGL::Dirty::ClipControl] = true;
     }
 
+    void NotifyAlphaTest() {
+        auto& flags = system.GPU().Maxwell3D().dirty.flags;
+        flags[OpenGL::Dirty::AlphaTest] = true;
+    }
+
 private:
     Core::System& system;
 
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 7b4c2b80c7..a51410660d 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -591,6 +591,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     state_tracker.NotifyFramebufferSRGB();
     state_tracker.NotifyLogicOp();
     state_tracker.NotifyClipControl();
+    state_tracker.NotifyAlphaTest();
 
     program_manager.UseVertexShader(vertex_program.handle);
     program_manager.UseGeometryShader(0);
@@ -608,6 +609,7 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
     glDisable(GL_STENCIL_TEST);
     glDisable(GL_POLYGON_OFFSET_FILL);
     glDisable(GL_RASTERIZER_DISCARD);
+    glDisable(GL_ALPHA_TEST);
     glDisablei(GL_BLEND, 0);
     glDisablei(GL_SCISSOR_TEST, 0);
     glCullFace(GL_BACK);

From 735c003a7099ba8ecb50c21dde24bd1a2c6a6878 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Fri, 21 Feb 2020 20:44:39 -0300
Subject: [PATCH 76/76] video_core/dirty_flags: Address feedback

---
 src/video_core/dirty_flags.h | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/video_core/dirty_flags.h b/src/video_core/dirty_flags.h
index ed07dfc031..0dbafd3ef3 100644
--- a/src/video_core/dirty_flags.h
+++ b/src/video_core/dirty_flags.h
@@ -31,15 +31,15 @@ enum : u8 {
 };
 
 template <typename Integer>
-inline void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Table& table, std::size_t begin,
-                      std::size_t num, Integer dirty_index) {
+void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Table& table, std::size_t begin,
+               std::size_t num, Integer dirty_index) {
     const auto it = std::begin(table) + begin;
     std::fill(it, it + num, static_cast<u8>(dirty_index));
 }
 
 template <typename Integer1, typename Integer2>
-inline void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables, std::size_t begin,
-                      std::size_t num, Integer1 index_a, Integer2 index_b) {
+void FillBlock(Tegra::Engines::Maxwell3D::DirtyState::Tables& tables, std::size_t begin,
+               std::size_t num, Integer1 index_a, Integer2 index_b) {
     FillBlock(tables[0], begin, num, index_a);
     FillBlock(tables[1], begin, num, index_b);
 }