diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 63ee833913..3baf1522d4 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -461,15 +461,15 @@ void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
 }
 
 std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
-    OpenGLState& current_state, bool using_color_fb, bool using_depth_fb, bool preserve_contents,
-    std::optional<std::size_t> single_color_target) {
+    OpenGLState& current_state, bool must_reconfigure, bool using_color_fb, bool using_depth_fb,
+    bool preserve_contents, std::optional<std::size_t> single_color_target) {
     MICROPROFILE_SCOPE(OpenGL_Framebuffer);
     auto& gpu = system.GPU().Maxwell3D();
     const auto& regs = gpu.regs;
 
     const FramebufferConfigState fb_config_state{using_color_fb, using_depth_fb, preserve_contents,
                                                  single_color_target};
-    if (fb_config_state == current_framebuffer_config_state &&
+    if (!must_reconfigure && fb_config_state == current_framebuffer_config_state &&
         gpu.dirty_flags.color_buffer.none() && !gpu.dirty_flags.zeta_buffer) {
         // Only skip if the previous ConfigureFramebuffers call was from the same kind (multiple or
         // single color targets). This is done because the guest registers may not change but the
@@ -622,8 +622,9 @@ void RasterizerOpenGL::Clear() {
         return;
     }
 
-    const auto [clear_depth, clear_stencil] = ConfigureFramebuffers(
-        clear_state, use_color, use_depth || use_stencil, false, regs.clear_buffers.RT.Value());
+    const auto [clear_depth, clear_stencil] =
+        ConfigureFramebuffers(clear_state, false, use_color, use_depth || use_stencil, false,
+                              regs.clear_buffers.RT.Value());
     if (regs.clear_flags.scissor) {
         SyncScissorTest(clear_state);
     }
@@ -705,6 +706,10 @@ void RasterizerOpenGL::DrawArrays() {
     DrawParameters params = SetupDraw();
     SetupShaders(params.primitive_mode);
 
+    if (texture_cache.ConsumeReconfigurationFlag()) {
+        ConfigureFramebuffers(state, true);
+    }
+
     buffer_cache.Unmap();
 
     shader_program_manager->ApplyTo(state);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index d872e51109..970637efaa 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -101,6 +101,8 @@ private:
 
     /**
      * Configures the color and depth framebuffer states.
+     * @param must_reconfigure If true, tells the framebuffer to skip the cache and reconfigure
+     * again. Used by the texture cache to solve texception conflicts
      * @param use_color_fb If true, configure color framebuffers.
      * @param using_depth_fb If true, configure the depth/stencil framebuffer.
      * @param preserve_contents If true, tries to preserve data from a previously used framebuffer.
@@ -109,8 +111,9 @@ private:
      * (requires using_depth_fb to be true)
      */
     std::pair<bool, bool> ConfigureFramebuffers(
-        OpenGLState& current_state, bool use_color_fb = true, bool using_depth_fb = true,
-        bool preserve_contents = true, std::optional<std::size_t> single_color_target = {});
+        OpenGLState& current_state, bool must_reconfigure = false, bool use_color_fb = true,
+        bool using_depth_fb = true, bool preserve_contents = true,
+        std::optional<std::size_t> single_color_target = {});
 
     /// Configures the current constbuffers to use for the draw command.
     void SetupDrawConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h
index 017ee999e0..179e80ddba 100644
--- a/src/video_core/texture_cache/surface_base.h
+++ b/src/video_core/texture_cache/surface_base.h
@@ -55,6 +55,11 @@ public:
         return (cache_addr < end) && (cache_addr_end > start);
     }
 
+    bool IsInside(const GPUVAddr other_start, const GPUVAddr other_end) {
+        const GPUVAddr gpu_addr_end = gpu_addr + guest_memory_size;
+        return (gpu_addr <= other_start && other_end <= gpu_addr_end);
+    }
+
     // Use only when recycling a surface
     void SetGpuAddr(const GPUVAddr new_addr) {
         gpu_addr = new_addr;
@@ -105,6 +110,12 @@ public:
         return params.target == target;
     }
 
+    bool MatchesSubTexture(const SurfaceParams& rhs, const GPUVAddr other_gpu_addr) const {
+        return std::tie(gpu_addr, params.target, params.num_levels) ==
+                   std::tie(other_gpu_addr, rhs.target, rhs.num_levels) &&
+               params.target == SurfaceTarget::Texture2D && params.num_levels == 1;
+    }
+
     bool MatchesTopology(const SurfaceParams& rhs) const {
         const u32 src_bpp{params.GetBytesPerPixel()};
         const u32 dst_bpp{rhs.GetBytesPerPixel()};
@@ -121,9 +132,9 @@ public:
         }
         // Tiled surface
         if (std::tie(params.height, params.depth, params.block_width, params.block_height,
-                     params.block_depth, params.tile_width_spacing) ==
+                     params.block_depth, params.tile_width_spacing, params.num_levels) ==
             std::tie(rhs.height, rhs.depth, rhs.block_width, rhs.block_height, rhs.block_depth,
-                     rhs.tile_width_spacing)) {
+                     rhs.tile_width_spacing, rhs.num_levels)) {
             if (params.width == rhs.width) {
                 return MatchStructureResult::FullMatch;
             }
@@ -259,7 +270,7 @@ public:
 
     std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr) {
         if (view_addr < gpu_addr || params.target == SurfaceTarget::Texture3D ||
-            view_params.target == SurfaceTarget::Texture3D) {
+            params.num_levels == 1 || view_params.target == SurfaceTarget::Texture3D) {
             return {};
         }
         const auto layer_mipmap{GetLayerMipmap(view_addr)};
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 422bf3e581..96d1081476 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -120,6 +120,10 @@ public:
             return {};
         }
 
+        if (regs.color_mask[index].raw == 0) {
+            return {};
+        }
+
         auto surface_view = GetSurface(gpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
                                        preserve_contents);
         if (render_targets[index].target)
@@ -183,6 +187,12 @@ public:
         return ++ticks;
     }
 
+    bool ConsumeReconfigurationFlag() {
+        const bool result = force_reconfiguration;
+        force_reconfiguration = false;
+        return result;
+    }
+
 protected:
     TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
         : system{system}, rasterizer{rasterizer} {
@@ -219,9 +229,10 @@ protected:
         rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1);
     }
 
-    void Unregister(TSurface surface) {
-        if (surface->IsProtected())
+    void Unregister(TSurface surface, const bool force_unregister = false) {
+        if (surface->IsProtected() && !force_unregister) {
             return;
+        }
         const GPUVAddr gpu_addr = surface->GetGpuAddr();
         const CacheAddr cache_ptr = surface->GetCacheAddr();
         const std::size_t size = surface->GetSizeInBytes();
@@ -365,8 +376,10 @@ private:
                                          std::min(src_params.height, dst_height), 1);
             ImageCopy(surface, new_surface, copy_params);
         }
+        force_reconfiguration = false;
         for (auto surface : overlaps) {
-            Unregister(surface);
+            force_reconfiguration |= surface->IsProtected();
+            Unregister(surface, true);
         }
         Register(new_surface);
         return {{new_surface, new_surface->GetMainView()}};
@@ -379,6 +392,7 @@ private:
         const auto cache_addr{ToCacheAddr(host_ptr)};
         const std::size_t candidate_size = params.GetGuestSizeInBytes();
         auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)};
+
         if (overlaps.empty()) {
             return InitializeSurface(gpu_addr, params, preserve_contents);
         }
@@ -403,7 +417,7 @@ private:
                     return RebuildSurface(current_surface, params);
                 }
             }
-            if (current_surface->GetSizeInBytes() <= candidate_size) {
+            if (!current_surface->IsInside(gpu_addr, gpu_addr + candidate_size)) {
                 return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents,
                                       false);
             }
@@ -530,6 +544,10 @@ private:
 
     u64 ticks{};
 
+    // Sometimes Setup Textures can hit a surface that's on the render target, when this happens
+    // we force a reconfiguration of the frame buffer after setup.
+    bool force_reconfiguration;
+
     // The internal Cache is different for the Texture Cache. It's based on buckets
     // of 1MB. This fits better for the purpose of this cache as textures are normaly
     // large in size.