From d7990c159e956e5431c501fa94405dd04496197c Mon Sep 17 00:00:00 2001
From: Fernando Sahmkow <fsahmkow27@gmail.com>
Date: Sun, 7 Nov 2021 17:15:28 +0100
Subject: [PATCH] OpenGl: Implement Channels.

---
 .../renderer_opengl/gl_rasterizer.cpp         | 174 +++++++++++-------
 .../renderer_opengl/gl_rasterizer.h           |  13 +-
 .../renderer_opengl/gl_state_tracker.cpp      |  17 +-
 .../renderer_opengl/gl_state_tracker.h        |  82 +++++----
 .../renderer_opengl/renderer_opengl.cpp       |   2 +-
 .../renderer_vulkan/renderer_vulkan.cpp       |   2 +-
 .../renderer_vulkan/vk_rasterizer.cpp         |   2 +-
 .../renderer_vulkan/vk_state_tracker.cpp      |   8 +-
 .../renderer_vulkan/vk_state_tracker.h        |   4 +-
 9 files changed, 186 insertions(+), 118 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index e8d61bd417..8cfa0013fb 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -16,7 +16,7 @@
 #include "common/microprofile.h"
 #include "common/scope_exit.h"
 #include "common/settings.h"
-
+#include "video_core/control/channel_state.h"
 #include "video_core/engines/kepler_compute.h"
 #include "video_core/engines/maxwell_3d.h"
 #include "video_core/memory_manager.h"
@@ -56,9 +56,8 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra
                                    Core::Memory::Memory& cpu_memory_, const Device& device_,
                                    ScreenInfo& screen_info_, ProgramManager& program_manager_,
                                    StateTracker& state_tracker_)
-    : RasterizerAccelerated(cpu_memory_), gpu(gpu_), maxwell3d(gpu.Maxwell3D()),
-      kepler_compute(gpu.KeplerCompute()), gpu_memory(gpu.MemoryManager()), device(device_),
-      screen_info(screen_info_), program_manager(program_manager_), state_tracker(state_tracker_),
+    : RasterizerAccelerated(cpu_memory_), gpu(gpu_), device(device_), screen_info(screen_info_),
+      program_manager(program_manager_), state_tracker(state_tracker_),
       texture_cache_runtime(device, program_manager, state_tracker),
       texture_cache(texture_cache_runtime, *this), buffer_cache_runtime(device),
       buffer_cache(*this, cpu_memory_, buffer_cache_runtime),
@@ -70,7 +69,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra
 RasterizerOpenGL::~RasterizerOpenGL() = default;
 
 void RasterizerOpenGL::SyncVertexFormats() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::VertexFormats]) {
         return;
     }
@@ -88,7 +87,7 @@ void RasterizerOpenGL::SyncVertexFormats() {
         }
         flags[Dirty::VertexFormat0 + index] = false;
 
-        const auto attrib = maxwell3d.regs.vertex_attrib_format[index];
+        const auto attrib = maxwell3d->regs.vertex_attrib_format[index];
         const auto gl_index = static_cast<GLuint>(index);
 
         // Disable constant attributes.
@@ -112,13 +111,13 @@ void RasterizerOpenGL::SyncVertexFormats() {
 }
 
 void RasterizerOpenGL::SyncVertexInstances() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::VertexInstances]) {
         return;
     }
     flags[Dirty::VertexInstances] = false;
 
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = maxwell3d->regs;
     for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) {
         if (!flags[Dirty::VertexInstance0 + index]) {
             continue;
@@ -139,11 +138,11 @@ void RasterizerOpenGL::LoadDiskResources(u64 title_id, std::stop_token stop_load
 
 void RasterizerOpenGL::Clear() {
     MICROPROFILE_SCOPE(OpenGL_Clears);
-    if (!maxwell3d.ShouldExecute()) {
+    if (!maxwell3d->ShouldExecute()) {
         return;
     }
 
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = maxwell3d->regs;
     bool use_color{};
     bool use_depth{};
     bool use_stencil{};
@@ -221,17 +220,17 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
 
     SyncState();
 
-    const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d.regs.draw.topology);
+    const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d->regs.draw.topology);
     BeginTransformFeedback(pipeline, primitive_mode);
 
-    const GLuint base_instance = static_cast<GLuint>(maxwell3d.regs.vb_base_instance);
+    const GLuint base_instance = static_cast<GLuint>(maxwell3d->regs.vb_base_instance);
     const GLsizei num_instances =
-        static_cast<GLsizei>(is_instanced ? maxwell3d.mme_draw.instance_count : 1);
+        static_cast<GLsizei>(is_instanced ? maxwell3d->mme_draw.instance_count : 1);
     if (is_indexed) {
-        const GLint base_vertex = static_cast<GLint>(maxwell3d.regs.vb_element_base);
-        const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d.regs.index_array.count);
+        const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.vb_element_base);
+        const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.index_array.count);
         const GLvoid* const offset = buffer_cache_runtime.IndexOffset();
-        const GLenum format = MaxwellToGL::IndexFormat(maxwell3d.regs.index_array.format);
+        const GLenum format = MaxwellToGL::IndexFormat(maxwell3d->regs.index_array.format);
         if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
             glDrawElements(primitive_mode, num_vertices, format, offset);
         } else if (num_instances == 1 && base_instance == 0) {
@@ -250,8 +249,8 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
                                                           base_instance);
         }
     } else {
-        const GLint base_vertex = static_cast<GLint>(maxwell3d.regs.vertex_buffer.first);
-        const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d.regs.vertex_buffer.count);
+        const GLint base_vertex = static_cast<GLint>(maxwell3d->regs.vertex_buffer.first);
+        const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d->regs.vertex_buffer.count);
         if (num_instances == 1 && base_instance == 0) {
             glDrawArrays(primitive_mode, base_vertex, num_vertices);
         } else if (base_instance == 0) {
@@ -273,7 +272,7 @@ void RasterizerOpenGL::DispatchCompute() {
         return;
     }
     pipeline->Configure();
-    const auto& qmd{kepler_compute.launch_description};
+    const auto& qmd{kepler_compute->launch_description};
     glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z);
     ++num_queued_commands;
     has_written_global_memory |= pipeline->WritesGlobalMemory();
@@ -388,10 +387,10 @@ void RasterizerOpenGL::ModifyGPUMemory(GPUVAddr addr, u64 size) {
 
 void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) {
     if (!gpu.IsAsync()) {
-        gpu_memory.Write<u32>(addr, value);
+        gpu_memory->Write<u32>(addr, value);
         return;
     }
-    auto paddr = gpu_memory.GetPointer(addr);
+    auto paddr = gpu_memory->GetPointer(addr);
     fence_manager.SignalSemaphore(paddr, value);
 }
 
@@ -483,12 +482,12 @@ Tegra::Engines::AccelerateDMAInterface& RasterizerOpenGL::AccessAccelerateDMA()
 
 void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size,
                                                 std::span<u8> memory) {
-    auto cpu_addr = gpu_memory.GpuToCpuAddress(address);
+    auto cpu_addr = gpu_memory->GpuToCpuAddress(address);
     if (!cpu_addr) [[unlikely]] {
-        gpu_memory.WriteBlock(address, memory.data(), copy_size);
+        gpu_memory->WriteBlock(address, memory.data(), copy_size);
         return;
     }
-    gpu_memory.WriteBlockUnsafe(address, memory.data(), copy_size);
+    gpu_memory->WriteBlockUnsafe(address, memory.data(), copy_size);
     {
         std::unique_lock<std::mutex> lock{buffer_cache.mutex};
         if (!buffer_cache.InlineMemory(*cpu_addr, copy_size, memory)) {
@@ -551,8 +550,8 @@ void RasterizerOpenGL::SyncState() {
 }
 
 void RasterizerOpenGL::SyncViewport() {
-    auto& flags = maxwell3d.dirty.flags;
-    const auto& regs = maxwell3d.regs;
+    auto& flags = maxwell3d->dirty.flags;
+    const auto& regs = maxwell3d->regs;
 
     const bool rescale_viewports = flags[VideoCommon::Dirty::RescaleViewports];
     const bool dirty_viewport = flags[Dirty::Viewports] || rescale_viewports;
@@ -657,23 +656,23 @@ void RasterizerOpenGL::SyncViewport() {
 }
 
 void RasterizerOpenGL::SyncDepthClamp() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::DepthClampEnabled]) {
         return;
     }
     flags[Dirty::DepthClampEnabled] = false;
 
-    oglEnable(GL_DEPTH_CLAMP, maxwell3d.regs.view_volume_clip_control.depth_clamp_disabled == 0);
+    oglEnable(GL_DEPTH_CLAMP, maxwell3d->regs.view_volume_clip_control.depth_clamp_disabled == 0);
 }
 
 void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::ClipDistances] && !flags[VideoCommon::Dirty::Shaders]) {
         return;
     }
     flags[Dirty::ClipDistances] = false;
 
-    clip_mask &= maxwell3d.regs.clip_distance_enabled;
+    clip_mask &= maxwell3d->regs.clip_distance_enabled;
     if (clip_mask == last_clip_distance_mask) {
         return;
     }
@@ -689,8 +688,8 @@ void RasterizerOpenGL::SyncClipCoef() {
 }
 
 void RasterizerOpenGL::SyncCullMode() {
-    auto& flags = maxwell3d.dirty.flags;
-    const auto& regs = maxwell3d.regs;
+    auto& flags = maxwell3d->dirty.flags;
+    const auto& regs = maxwell3d->regs;
 
     if (flags[Dirty::CullTest]) {
         flags[Dirty::CullTest] = false;
@@ -705,23 +704,23 @@ void RasterizerOpenGL::SyncCullMode() {
 }
 
 void RasterizerOpenGL::SyncPrimitiveRestart() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::PrimitiveRestart]) {
         return;
     }
     flags[Dirty::PrimitiveRestart] = false;
 
-    if (maxwell3d.regs.primitive_restart.enabled) {
+    if (maxwell3d->regs.primitive_restart.enabled) {
         glEnable(GL_PRIMITIVE_RESTART);
-        glPrimitiveRestartIndex(maxwell3d.regs.primitive_restart.index);
+        glPrimitiveRestartIndex(maxwell3d->regs.primitive_restart.index);
     } else {
         glDisable(GL_PRIMITIVE_RESTART);
     }
 }
 
 void RasterizerOpenGL::SyncDepthTestState() {
-    auto& flags = maxwell3d.dirty.flags;
-    const auto& regs = maxwell3d.regs;
+    auto& flags = maxwell3d->dirty.flags;
+    const auto& regs = maxwell3d->regs;
 
     if (flags[Dirty::DepthMask]) {
         flags[Dirty::DepthMask] = false;
@@ -740,13 +739,13 @@ void RasterizerOpenGL::SyncDepthTestState() {
 }
 
 void RasterizerOpenGL::SyncStencilTestState() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::StencilTest]) {
         return;
     }
     flags[Dirty::StencilTest] = false;
 
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = maxwell3d->regs;
     oglEnable(GL_STENCIL_TEST, regs.stencil_enable);
 
     glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func),
@@ -771,23 +770,23 @@ void RasterizerOpenGL::SyncStencilTestState() {
 }
 
 void RasterizerOpenGL::SyncRasterizeEnable() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::RasterizeEnable]) {
         return;
     }
     flags[Dirty::RasterizeEnable] = false;
 
-    oglEnable(GL_RASTERIZER_DISCARD, maxwell3d.regs.rasterize_enable == 0);
+    oglEnable(GL_RASTERIZER_DISCARD, maxwell3d->regs.rasterize_enable == 0);
 }
 
 void RasterizerOpenGL::SyncPolygonModes() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::PolygonModes]) {
         return;
     }
     flags[Dirty::PolygonModes] = false;
 
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = maxwell3d->regs;
     if (regs.fill_rectangle) {
         if (!GLAD_GL_NV_fill_rectangle) {
             LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported");
@@ -820,7 +819,7 @@ void RasterizerOpenGL::SyncPolygonModes() {
 }
 
 void RasterizerOpenGL::SyncColorMask() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::ColorMasks]) {
         return;
     }
@@ -829,7 +828,7 @@ void RasterizerOpenGL::SyncColorMask() {
     const bool force = flags[Dirty::ColorMaskCommon];
     flags[Dirty::ColorMaskCommon] = false;
 
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = maxwell3d->regs;
     if (regs.color_mask_common) {
         if (!force && !flags[Dirty::ColorMask0]) {
             return;
@@ -854,30 +853,30 @@ void RasterizerOpenGL::SyncColorMask() {
 }
 
 void RasterizerOpenGL::SyncMultiSampleState() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::MultisampleControl]) {
         return;
     }
     flags[Dirty::MultisampleControl] = false;
 
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = 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);
 }
 
 void RasterizerOpenGL::SyncFragmentColorClampState() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::FragmentClampColor]) {
         return;
     }
     flags[Dirty::FragmentClampColor] = false;
 
-    glClampColor(GL_CLAMP_FRAGMENT_COLOR, maxwell3d.regs.frag_color_clamp ? GL_TRUE : GL_FALSE);
+    glClampColor(GL_CLAMP_FRAGMENT_COLOR, maxwell3d->regs.frag_color_clamp ? GL_TRUE : GL_FALSE);
 }
 
 void RasterizerOpenGL::SyncBlendState() {
-    auto& flags = maxwell3d.dirty.flags;
-    const auto& regs = maxwell3d.regs;
+    auto& flags = maxwell3d->dirty.flags;
+    const auto& regs = maxwell3d->regs;
 
     if (flags[Dirty::BlendColor]) {
         flags[Dirty::BlendColor] = false;
@@ -934,13 +933,13 @@ void RasterizerOpenGL::SyncBlendState() {
 }
 
 void RasterizerOpenGL::SyncLogicOpState() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::LogicOp]) {
         return;
     }
     flags[Dirty::LogicOp] = false;
 
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = maxwell3d->regs;
     if (regs.logic_op.enable) {
         glEnable(GL_COLOR_LOGIC_OP);
         glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation));
@@ -950,7 +949,7 @@ void RasterizerOpenGL::SyncLogicOpState() {
 }
 
 void RasterizerOpenGL::SyncScissorTest() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::Scissors] && !flags[VideoCommon::Dirty::RescaleScissors]) {
         return;
     }
@@ -959,7 +958,7 @@ void RasterizerOpenGL::SyncScissorTest() {
     const bool force = flags[VideoCommon::Dirty::RescaleScissors];
     flags[VideoCommon::Dirty::RescaleScissors] = false;
 
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = maxwell3d->regs;
 
     const auto& resolution = Settings::values.resolution_info;
     const bool is_rescaling{texture_cache.IsRescaling()};
@@ -995,39 +994,39 @@ void RasterizerOpenGL::SyncScissorTest() {
 }
 
 void RasterizerOpenGL::SyncPointState() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::PointSize]) {
         return;
     }
     flags[Dirty::PointSize] = false;
 
-    oglEnable(GL_POINT_SPRITE, maxwell3d.regs.point_sprite_enable);
-    oglEnable(GL_PROGRAM_POINT_SIZE, maxwell3d.regs.vp_point_size.enable);
+    oglEnable(GL_POINT_SPRITE, maxwell3d->regs.point_sprite_enable);
+    oglEnable(GL_PROGRAM_POINT_SIZE, maxwell3d->regs.vp_point_size.enable);
     const bool is_rescaling{texture_cache.IsRescaling()};
     const float scale = is_rescaling ? Settings::values.resolution_info.up_factor : 1.0f;
-    glPointSize(std::max(1.0f, maxwell3d.regs.point_size * scale));
+    glPointSize(std::max(1.0f, maxwell3d->regs.point_size * scale));
 }
 
 void RasterizerOpenGL::SyncLineState() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::LineWidth]) {
         return;
     }
     flags[Dirty::LineWidth] = false;
 
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = maxwell3d->regs;
     oglEnable(GL_LINE_SMOOTH, regs.line_smooth_enable);
     glLineWidth(regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased);
 }
 
 void RasterizerOpenGL::SyncPolygonOffset() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::PolygonOffset]) {
         return;
     }
     flags[Dirty::PolygonOffset] = false;
 
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = maxwell3d->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);
@@ -1041,13 +1040,13 @@ void RasterizerOpenGL::SyncPolygonOffset() {
 }
 
 void RasterizerOpenGL::SyncAlphaTest() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::AlphaTest]) {
         return;
     }
     flags[Dirty::AlphaTest] = false;
 
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = maxwell3d->regs;
     if (regs.alpha_test_enabled) {
         glEnable(GL_ALPHA_TEST);
         glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref);
@@ -1057,17 +1056,17 @@ void RasterizerOpenGL::SyncAlphaTest() {
 }
 
 void RasterizerOpenGL::SyncFramebufferSRGB() {
-    auto& flags = maxwell3d.dirty.flags;
+    auto& flags = maxwell3d->dirty.flags;
     if (!flags[Dirty::FramebufferSRGB]) {
         return;
     }
     flags[Dirty::FramebufferSRGB] = false;
 
-    oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d.regs.framebuffer_srgb);
+    oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d->regs.framebuffer_srgb);
 }
 
 void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum primitive_mode) {
-    const auto& regs = maxwell3d.regs;
+    const auto& regs = maxwell3d->regs;
     if (regs.tfb_enabled == 0) {
         return;
     }
@@ -1086,11 +1085,48 @@ void RasterizerOpenGL::BeginTransformFeedback(GraphicsPipeline* program, GLenum
 }
 
 void RasterizerOpenGL::EndTransformFeedback() {
-    if (maxwell3d.regs.tfb_enabled != 0) {
+    if (maxwell3d->regs.tfb_enabled != 0) {
         glEndTransformFeedback();
     }
 }
 
+void RasterizerOpenGL::InitializeChannel(Tegra::Control::ChannelState& channel) {
+    CreateChannel(channel);
+    {
+        std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
+        texture_cache.CreateChannel(channel);
+        buffer_cache.CreateChannel(channel);
+    }
+    shader_cache.CreateChannel(channel);
+    query_cache.CreateChannel(channel);
+    state_tracker.SetupTables(channel);
+}
+
+void RasterizerOpenGL::BindChannel(Tegra::Control::ChannelState& channel) {
+    const s32 channel_id = channel.bind_id;
+    BindToChannel(channel_id);
+    {
+        std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
+        texture_cache.BindToChannel(channel_id);
+        buffer_cache.BindToChannel(channel_id);
+    }
+    shader_cache.BindToChannel(channel_id);
+    query_cache.BindToChannel(channel_id);
+    state_tracker.ChangeChannel(channel);
+    state_tracker.InvalidateState();
+}
+
+void RasterizerOpenGL::ReleaseChannel(s32 channel_id) {
+    EraseChannel(channel_id);
+    {
+        std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
+        texture_cache.EraseChannel(channel_id);
+        buffer_cache.EraseChannel(channel_id);
+    }
+    shader_cache.EraseChannel(channel_id);
+    query_cache.EraseChannel(channel_id);
+}
+
 AccelerateDMA::AccelerateDMA(BufferCache& buffer_cache_) : buffer_cache{buffer_cache_} {}
 
 bool AccelerateDMA::BufferCopy(GPUVAddr src_address, GPUVAddr dest_address, u64 amount) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 31a16fcbaf..39b20cfb20 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -12,6 +12,7 @@
 #include <glad/glad.h>
 
 #include "common/common_types.h"
+#include "video_core/control/channel_state_cache.h"
 #include "video_core/engines/maxwell_dma.h"
 #include "video_core/rasterizer_accelerated.h"
 #include "video_core/rasterizer_interface.h"
@@ -58,7 +59,8 @@ private:
     BufferCache& buffer_cache;
 };
 
-class RasterizerOpenGL : public VideoCore::RasterizerAccelerated {
+class RasterizerOpenGL : public VideoCore::RasterizerAccelerated,
+                         protected VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo> {
 public:
     explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
                               Core::Memory::Memory& cpu_memory_, const Device& device_,
@@ -107,6 +109,12 @@ public:
         return num_queued_commands > 0;
     }
 
+    void InitializeChannel(Tegra::Control::ChannelState& channel) override;
+
+    void BindChannel(Tegra::Control::ChannelState& channel) override;
+
+    void ReleaseChannel(s32 channel_id) override;
+
 private:
     static constexpr size_t MAX_TEXTURES = 192;
     static constexpr size_t MAX_IMAGES = 48;
@@ -191,9 +199,6 @@ private:
     void EndTransformFeedback();
 
     Tegra::GPU& gpu;
-    Tegra::Engines::Maxwell3D& maxwell3d;
-    Tegra::Engines::KeplerCompute& kepler_compute;
-    Tegra::MemoryManager& gpu_memory;
 
     const Device& device;
     ScreenInfo& screen_info;
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index 912725ef72..3657f867d3 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -7,8 +7,8 @@
 
 #include "common/common_types.h"
 #include "core/core.h"
+#include "video_core/control/channel_state.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)
@@ -202,9 +202,8 @@ void SetupDirtyMisc(Tables& tables) {
 
 } // Anonymous namespace
 
-StateTracker::StateTracker(Tegra::GPU& gpu) : flags{gpu.Maxwell3D().dirty.flags} {
-    auto& dirty = gpu.Maxwell3D().dirty;
-    auto& tables = dirty.tables;
+void StateTracker::SetupTables(Tegra::Control::ChannelState& channel_state) {
+    auto& tables{channel_state.maxwell_3d->dirty.tables};
     SetupDirtyFlags(tables);
     SetupDirtyColorMasks(tables);
     SetupDirtyViewports(tables);
@@ -230,4 +229,14 @@ StateTracker::StateTracker(Tegra::GPU& gpu) : flags{gpu.Maxwell3D().dirty.flags}
     SetupDirtyMisc(tables);
 }
 
+void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
+    flags = &channel_state.maxwell_3d->dirty.flags;
+}
+
+void StateTracker::InvalidateState() {
+    flags->set();
+}
+
+StateTracker::StateTracker() : flags{} {}
+
 } // 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 04e024f085..97d32768b5 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -12,8 +12,10 @@
 #include "video_core/engines/maxwell_3d.h"
 
 namespace Tegra {
-class GPU;
+namespace Control {
+struct ChannelState;
 }
+} // namespace Tegra
 
 namespace OpenGL {
 
@@ -83,7 +85,7 @@ static_assert(Last <= std::numeric_limits<u8>::max());
 
 class StateTracker {
 public:
-    explicit StateTracker(Tegra::GPU& gpu);
+    explicit StateTracker();
 
     void BindIndexBuffer(GLuint new_index_buffer) {
         if (index_buffer == new_index_buffer) {
@@ -121,94 +123,106 @@ public:
     }
 
     void NotifyScreenDrawVertexArray() {
-        flags[OpenGL::Dirty::VertexFormats] = true;
-        flags[OpenGL::Dirty::VertexFormat0 + 0] = true;
-        flags[OpenGL::Dirty::VertexFormat0 + 1] = true;
+        (*flags)[OpenGL::Dirty::VertexFormats] = true;
+        (*flags)[OpenGL::Dirty::VertexFormat0 + 0] = true;
+        (*flags)[OpenGL::Dirty::VertexFormat0 + 1] = true;
 
-        flags[VideoCommon::Dirty::VertexBuffers] = true;
-        flags[VideoCommon::Dirty::VertexBuffer0] = true;
+        (*flags)[VideoCommon::Dirty::VertexBuffers] = true;
+        (*flags)[VideoCommon::Dirty::VertexBuffer0] = true;
 
-        flags[OpenGL::Dirty::VertexInstances] = true;
-        flags[OpenGL::Dirty::VertexInstance0 + 0] = true;
-        flags[OpenGL::Dirty::VertexInstance0 + 1] = true;
+        (*flags)[OpenGL::Dirty::VertexInstances] = true;
+        (*flags)[OpenGL::Dirty::VertexInstance0 + 0] = true;
+        (*flags)[OpenGL::Dirty::VertexInstance0 + 1] = true;
     }
 
     void NotifyPolygonModes() {
-        flags[OpenGL::Dirty::PolygonModes] = true;
-        flags[OpenGL::Dirty::PolygonModeFront] = true;
-        flags[OpenGL::Dirty::PolygonModeBack] = true;
+        (*flags)[OpenGL::Dirty::PolygonModes] = true;
+        (*flags)[OpenGL::Dirty::PolygonModeFront] = true;
+        (*flags)[OpenGL::Dirty::PolygonModeBack] = true;
     }
 
     void NotifyViewport0() {
-        flags[OpenGL::Dirty::Viewports] = true;
-        flags[OpenGL::Dirty::Viewport0] = true;
+        (*flags)[OpenGL::Dirty::Viewports] = true;
+        (*flags)[OpenGL::Dirty::Viewport0] = true;
     }
 
     void NotifyScissor0() {
-        flags[OpenGL::Dirty::Scissors] = true;
-        flags[OpenGL::Dirty::Scissor0] = true;
+        (*flags)[OpenGL::Dirty::Scissors] = true;
+        (*flags)[OpenGL::Dirty::Scissor0] = true;
     }
 
     void NotifyColorMask(size_t index) {
-        flags[OpenGL::Dirty::ColorMasks] = true;
-        flags[OpenGL::Dirty::ColorMask0 + index] = true;
+        (*flags)[OpenGL::Dirty::ColorMasks] = true;
+        (*flags)[OpenGL::Dirty::ColorMask0 + index] = true;
     }
 
     void NotifyBlend0() {
-        flags[OpenGL::Dirty::BlendStates] = true;
-        flags[OpenGL::Dirty::BlendState0] = true;
+        (*flags)[OpenGL::Dirty::BlendStates] = true;
+        (*flags)[OpenGL::Dirty::BlendState0] = true;
     }
 
     void NotifyFramebuffer() {
-        flags[VideoCommon::Dirty::RenderTargets] = true;
+        (*flags)[VideoCommon::Dirty::RenderTargets] = true;
     }
 
     void NotifyFrontFace() {
-        flags[OpenGL::Dirty::FrontFace] = true;
+        (*flags)[OpenGL::Dirty::FrontFace] = true;
     }
 
     void NotifyCullTest() {
-        flags[OpenGL::Dirty::CullTest] = true;
+        (*flags)[OpenGL::Dirty::CullTest] = true;
     }
 
     void NotifyDepthMask() {
-        flags[OpenGL::Dirty::DepthMask] = true;
+        (*flags)[OpenGL::Dirty::DepthMask] = true;
     }
 
     void NotifyDepthTest() {
-        flags[OpenGL::Dirty::DepthTest] = true;
+        (*flags)[OpenGL::Dirty::DepthTest] = true;
     }
 
     void NotifyStencilTest() {
-        flags[OpenGL::Dirty::StencilTest] = true;
+        (*flags)[OpenGL::Dirty::StencilTest] = true;
     }
 
     void NotifyPolygonOffset() {
-        flags[OpenGL::Dirty::PolygonOffset] = true;
+        (*flags)[OpenGL::Dirty::PolygonOffset] = true;
     }
 
     void NotifyRasterizeEnable() {
-        flags[OpenGL::Dirty::RasterizeEnable] = true;
+        (*flags)[OpenGL::Dirty::RasterizeEnable] = true;
     }
 
     void NotifyFramebufferSRGB() {
-        flags[OpenGL::Dirty::FramebufferSRGB] = true;
+        (*flags)[OpenGL::Dirty::FramebufferSRGB] = true;
     }
 
     void NotifyLogicOp() {
-        flags[OpenGL::Dirty::LogicOp] = true;
+        (*flags)[OpenGL::Dirty::LogicOp] = true;
     }
 
     void NotifyClipControl() {
-        flags[OpenGL::Dirty::ClipControl] = true;
+        (*flags)[OpenGL::Dirty::ClipControl] = true;
     }
 
     void NotifyAlphaTest() {
-        flags[OpenGL::Dirty::AlphaTest] = true;
+        (*flags)[OpenGL::Dirty::AlphaTest] = true;
     }
 
+    void NotifyRange(u8 start, u8 end) {
+        for (auto flag = start; flag <= end; flag++) {
+            (*flags)[flag] = true;
+        }
+    }
+
+    void SetupTables(Tegra::Control::ChannelState& channel_state);
+
+    void ChangeChannel(Tegra::Control::ChannelState& channel_state);
+
+    void InvalidateState();
+
 private:
-    Tegra::Engines::Maxwell3D::DirtyState::Flags& flags;
+    Tegra::Engines::Maxwell3D::DirtyState::Flags* flags;
 
     GLuint framebuffer = 0;
     GLuint index_buffer = 0;
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 34f3f7a679..8bd5eba7e2 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -131,7 +131,7 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
                                Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
                                std::unique_ptr<Core::Frontend::GraphicsContext> context_)
     : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_},
-      emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{gpu},
+      emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, state_tracker{},
       program_manager{device},
       rasterizer(emu_window, gpu, cpu_memory, device, screen_info, program_manager, state_tracker) {
     if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) {
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index 68c2bc34c4..d12669c9d4 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -106,7 +106,7 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
       surface(CreateSurface(instance, render_window)),
       device(CreateDevice(instance, dld, *surface)),
       memory_allocator(device, false),
-      state_tracker(gpu),
+      state_tracker(),
       scheduler(device, state_tracker),
       swapchain(*surface, device, scheduler, render_window.GetFramebufferLayout().width,
                 render_window.GetFramebufferLayout().height, false),
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 5d9ff0589d..bf750452f4 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -995,7 +995,7 @@ void RasterizerVulkan::BindChannel(Tegra::Control::ChannelState& channel) {
     pipeline_cache.BindToChannel(channel_id);
     query_cache.BindToChannel(channel_id);
     state_tracker.ChangeChannel(channel);
-    scheduler.InvalidateState();
+    state_tracker.InvalidateState();
 }
 
 void RasterizerVulkan::ReleaseChannel(s32 channel_id) {
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index a87bf8dd33..5a11d3267b 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -10,7 +10,6 @@
 #include "video_core/control/channel_state.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"
 
 #define OFF(field_name) MAXWELL3D_REG_INDEX(field_name)
@@ -203,7 +202,10 @@ void StateTracker::ChangeChannel(Tegra::Control::ChannelState& channel_state) {
     flags = &channel_state.maxwell_3d->dirty.flags;
 }
 
-StateTracker::StateTracker(Tegra::GPU& gpu)
-    : flags{}, invalidation_flags{MakeInvalidationFlags()} {}
+void StateTracker::InvalidateState() {
+    flags->set();
+}
+
+StateTracker::StateTracker() : flags{}, invalidation_flags{MakeInvalidationFlags()} {}
 
 } // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 9f8a887f9c..c107d9c246 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -59,7 +59,7 @@ class StateTracker {
     using Maxwell = Tegra::Engines::Maxwell3D::Regs;
 
 public:
-    explicit StateTracker(Tegra::GPU& gpu);
+    explicit StateTracker();
 
     void InvalidateCommandBufferState() {
         (*flags) |= invalidation_flags;
@@ -149,6 +149,8 @@ public:
 
     void ChangeChannel(Tegra::Control::ChannelState& channel_state);
 
+    void InvalidateState();
+
 private:
     static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);