From 56ccda1d9952368d0c1e29d7c4b486c547de9549 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Wed, 28 Jul 2021 02:47:06 -0300
Subject: [PATCH] texture_cache: Simplify image view queries and blacklisting

---
 .../renderer_opengl/gl_compute_pipeline.cpp   | 36 ++++++-----
 .../renderer_opengl/gl_graphics_pipeline.cpp  | 42 ++++++-------
 .../renderer_opengl/gl_texture_cache.cpp      | 10 ++--
 .../renderer_opengl/gl_texture_cache.h        |  5 +-
 .../renderer_vulkan/pipeline_helper.h         | 19 +++---
 .../renderer_vulkan/vk_compute_pipeline.cpp   | 60 +++++++------------
 .../renderer_vulkan/vk_graphics_pipeline.cpp  | 54 ++++-------------
 .../renderer_vulkan/vk_texture_cache.cpp      | 22 ++++---
 .../renderer_vulkan/vk_texture_cache.h        | 41 ++++++++-----
 src/video_core/texture_cache/image_base.cpp   |  2 +
 src/video_core/texture_cache/image_base.h     |  3 +
 .../texture_cache/image_view_base.cpp         |  2 +-
 .../texture_cache/image_view_base.h           |  4 +-
 src/video_core/texture_cache/texture_cache.h  | 47 ++++++++-------
 .../texture_cache/texture_cache_base.h        | 30 ++++++----
 src/video_core/texture_cache/types.h          |  7 +++
 16 files changed, 192 insertions(+), 192 deletions(-)

diff --git a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
index 19c8ca7b2e..ab2baefbb8 100644
--- a/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_compute_pipeline.cpp
@@ -79,8 +79,7 @@ void ComputePipeline::Configure() {
     }
     texture_cache.SynchronizeComputeDescriptors();
 
-    std::array<ImageViewId, MAX_TEXTURES + MAX_IMAGES> image_view_ids;
-    boost::container::static_vector<u32, MAX_TEXTURES + MAX_IMAGES> image_view_indices;
+    boost::container::static_vector<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
     std::array<GLuint, MAX_TEXTURES> samplers;
     std::array<GLuint, MAX_TEXTURES> textures;
     std::array<GLuint, MAX_IMAGES> images;
@@ -110,33 +109,39 @@ void ComputePipeline::Configure() {
         }
         return TexturePair(gpu_memory.Read<u32>(addr), via_header_index);
     }};
-    const auto add_image{[&](const auto& desc) {
+    const auto add_image{[&](const auto& desc, bool blacklist) {
         for (u32 index = 0; index < desc.count; ++index) {
             const auto handle{read_handle(desc, index)};
-            image_view_indices.push_back(handle.first);
+            views.push_back({
+                .index = handle.first,
+                .blacklist = blacklist,
+                .id = {},
+            });
         }
     }};
     for (const auto& desc : info.texture_buffer_descriptors) {
         for (u32 index = 0; index < desc.count; ++index) {
             const auto handle{read_handle(desc, index)};
-            image_view_indices.push_back(handle.first);
+            views.push_back({handle.first});
             samplers[sampler_binding++] = 0;
         }
     }
-    std::ranges::for_each(info.image_buffer_descriptors, add_image);
+    for (const auto& desc : info.image_buffer_descriptors) {
+        add_image(desc, false);
+    }
     for (const auto& desc : info.texture_descriptors) {
         for (u32 index = 0; index < desc.count; ++index) {
             const auto handle{read_handle(desc, index)};
-            image_view_indices.push_back(handle.first);
+            views.push_back({handle.first});
 
             Sampler* const sampler = texture_cache.GetComputeSampler(handle.second);
             samplers[sampler_binding++] = sampler->Handle();
         }
     }
-    std::ranges::for_each(info.image_descriptors, add_image);
-
-    const std::span indices_span(image_view_indices.data(), image_view_indices.size());
-    texture_cache.FillComputeImageViews(indices_span, image_view_ids);
+    for (const auto& desc : info.image_descriptors) {
+        add_image(desc, true);
+    }
+    texture_cache.FillComputeImageViews(std::span(views.data(), views.size()));
 
     if (assembly_program.handle != 0) {
         program_manager.BindComputeAssemblyProgram(assembly_program.handle);
@@ -152,7 +157,7 @@ void ComputePipeline::Configure() {
             if constexpr (is_image) {
                 is_written = desc.is_written;
             }
-            ImageView& image_view{texture_cache.GetImageView(image_view_ids[texbuf_index])};
+            ImageView& image_view{texture_cache.GetImageView(views[texbuf_index].id)};
             buffer_cache.BindComputeTextureBuffer(texbuf_index, image_view.GpuAddr(),
                                                   image_view.BufferSize(), image_view.format,
                                                   is_written, is_image);
@@ -168,19 +173,20 @@ void ComputePipeline::Configure() {
     buffer_cache.runtime.SetImagePointers(textures.data(), images.data());
     buffer_cache.BindHostComputeBuffers();
 
-    const ImageId* views_it{image_view_ids.data() + num_texture_buffers + num_image_buffers};
+    const VideoCommon::ImageViewInOut* views_it{views.data() + num_texture_buffers +
+                                                num_image_buffers};
     texture_binding += num_texture_buffers;
     image_binding += num_image_buffers;
 
     for (const auto& desc : info.texture_descriptors) {
         for (u32 index = 0; index < desc.count; ++index) {
-            ImageView& image_view{texture_cache.GetImageView(*(views_it++))};
+            ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
             textures[texture_binding++] = image_view.Handle(desc.type);
         }
     }
     for (const auto& desc : info.image_descriptors) {
         for (u32 index = 0; index < desc.count; ++index) {
-            ImageView& image_view{texture_cache.GetImageView(*(views_it++))};
+            ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
             if (desc.is_written) {
                 texture_cache.MarkModification(image_view.image_id);
             }
diff --git a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
index 43ab5c03b8..0bbda79519 100644
--- a/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
+++ b/src/video_core/renderer_opengl/gl_graphics_pipeline.cpp
@@ -15,7 +15,7 @@
 #include "video_core/renderer_opengl/gl_shader_util.h"
 #include "video_core/renderer_opengl/gl_state_tracker.h"
 #include "video_core/shader_notify.h"
-#include "video_core/texture_cache/texture_cache_base.h"
+#include "video_core/texture_cache/texture_cache.h"
 
 #if defined(_MSC_VER) && defined(NDEBUG)
 #define LAMBDA_FORCEINLINE [[msvc::forceinline]]
@@ -280,10 +280,9 @@ GraphicsPipeline::GraphicsPipeline(
 
 template <typename Spec>
 void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
-    std::array<ImageId, MAX_TEXTURES + MAX_IMAGES> image_view_ids;
-    std::array<u32, MAX_TEXTURES + MAX_IMAGES> image_view_indices;
+    std::array<VideoCommon::ImageViewInOut, MAX_TEXTURES + MAX_IMAGES> views;
     std::array<GLuint, MAX_TEXTURES> samplers;
-    size_t image_view_index{};
+    size_t views_index{};
     GLsizei sampler_binding{};
 
     texture_cache.SynchronizeGraphicsDescriptors();
@@ -328,30 +327,34 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
             }
             return TexturePair(gpu_memory.Read<u32>(addr), via_header_index);
         }};
-        const auto add_image{[&](const auto& desc) {
+        const auto add_image{[&](const auto& desc, bool blacklist) LAMBDA_FORCEINLINE {
             for (u32 index = 0; index < desc.count; ++index) {
                 const auto handle{read_handle(desc, index)};
-                image_view_indices[image_view_index++] = handle.first;
+                views[views_index++] = {
+                    .index = handle.first,
+                    .blacklist = blacklist,
+                    .id = {},
+                };
             }
         }};
         if constexpr (Spec::has_texture_buffers) {
             for (const auto& desc : info.texture_buffer_descriptors) {
                 for (u32 index = 0; index < desc.count; ++index) {
                     const auto handle{read_handle(desc, index)};
-                    image_view_indices[image_view_index++] = handle.first;
+                    views[views_index++] = {handle.first};
                     samplers[sampler_binding++] = 0;
                 }
             }
         }
         if constexpr (Spec::has_image_buffers) {
             for (const auto& desc : info.image_buffer_descriptors) {
-                add_image(desc);
+                add_image(desc, false);
             }
         }
         for (const auto& desc : info.texture_descriptors) {
             for (u32 index = 0; index < desc.count; ++index) {
                 const auto handle{read_handle(desc, index)};
-                image_view_indices[image_view_index++] = handle.first;
+                views[views_index++] = {handle.first};
 
                 Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)};
                 samplers[sampler_binding++] = sampler->Handle();
@@ -359,7 +362,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
         }
         if constexpr (Spec::has_images) {
             for (const auto& desc : info.image_descriptors) {
-                add_image(desc);
+                add_image(desc, true);
             }
         }
     }};
@@ -378,13 +381,12 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
     if constexpr (Spec::enabled_stages[4]) {
         config_stage(4);
     }
-    const std::span indices_span(image_view_indices.data(), image_view_index);
-    texture_cache.FillGraphicsImageViews(indices_span, image_view_ids);
+    texture_cache.FillGraphicsImageViews<Spec::has_images>(std::span(views.data(), views_index));
 
     texture_cache.UpdateRenderTargets(false);
     state_tracker.BindFramebuffer(texture_cache.GetFramebuffer()->Handle());
 
-    ImageId* texture_buffer_index{image_view_ids.data()};
+    VideoCommon::ImageViewInOut* texture_buffer_it{views.data()};
     const auto bind_stage_info{[&](size_t stage) LAMBDA_FORCEINLINE {
         size_t index{};
         const auto add_buffer{[&](const auto& desc) {
@@ -394,12 +396,12 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
                 if constexpr (is_image) {
                     is_written = desc.is_written;
                 }
-                ImageView& image_view{texture_cache.GetImageView(*texture_buffer_index)};
+                ImageView& image_view{texture_cache.GetImageView(texture_buffer_it->id)};
                 buffer_cache.BindGraphicsTextureBuffer(stage, index, image_view.GpuAddr(),
                                                        image_view.BufferSize(), image_view.format,
                                                        is_written, is_image);
                 ++index;
-                ++texture_buffer_index;
+                ++texture_buffer_it;
             }
         }};
         const Shader::Info& info{stage_infos[stage]};
@@ -415,9 +417,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
                 add_buffer(desc);
             }
         }
-        texture_buffer_index += Shader::NumDescriptors(info.texture_descriptors);
+        texture_buffer_it += Shader::NumDescriptors(info.texture_descriptors);
         if constexpr (Spec::has_images) {
-            texture_buffer_index += Shader::NumDescriptors(info.image_descriptors);
+            texture_buffer_it += Shader::NumDescriptors(info.image_descriptors);
         }
     }};
     if constexpr (Spec::enabled_stages[0]) {
@@ -446,7 +448,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
     } else {
         program_manager.BindSourcePrograms(source_programs);
     }
-    const ImageId* views_it{image_view_ids.data()};
+    const VideoCommon::ImageViewInOut* views_it{views.data()};
     GLsizei texture_binding = 0;
     GLsizei image_binding = 0;
     std::array<GLuint, MAX_TEXTURES> textures;
@@ -464,13 +466,13 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
         const auto& info{stage_infos[stage]};
         for (const auto& desc : info.texture_descriptors) {
             for (u32 index = 0; index < desc.count; ++index) {
-                ImageView& image_view{texture_cache.GetImageView(*(views_it++))};
+                ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
                 textures[texture_binding++] = image_view.Handle(desc.type);
             }
         }
         for (const auto& desc : info.image_descriptors) {
             for (u32 index = 0; index < desc.count; ++index) {
-                ImageView& image_view{texture_cache.GetImageView(*(views_it++))};
+                ImageView& image_view{texture_cache.GetImageView((views_it++)->id)};
                 if (desc.is_written) {
                     texture_cache.MarkModification(image_view.image_id);
                 }
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 5e26955765..5d14bfc976 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -472,11 +472,7 @@ TextureCacheRuntime::TextureCacheRuntime(const Device& device_, ProgramManager&
     set_view(Shader::TextureType::ColorArray1D, null_image_1d_array.handle);
     set_view(Shader::TextureType::ColorArray2D, null_image_view_2d_array.handle);
     set_view(Shader::TextureType::ColorArrayCube, null_image_cube_array.handle);
-}
 
-TextureCacheRuntime::~TextureCacheRuntime() = default;
-
-void TextureCacheRuntime::Init() {
     resolution = Settings::values.resolution_info;
     is_rescaling_on = resolution.up_scale != 1 || resolution.down_shift != 0;
     if (is_rescaling_on) {
@@ -485,6 +481,8 @@ void TextureCacheRuntime::Init() {
     }
 }
 
+TextureCacheRuntime::~TextureCacheRuntime() = default;
+
 void TextureCacheRuntime::Finish() {
     glFinish();
 }
@@ -685,6 +683,8 @@ Image::Image(TextureCacheRuntime& runtime_, const VideoCommon::ImageInfo& info_,
     }
 }
 
+Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {}
+
 Image::~Image() = default;
 
 void Image::UploadMemory(const ImageBufferMap& map,
@@ -1076,7 +1076,7 @@ ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
                      const VideoCommon::ImageViewInfo& view_info)
     : VideoCommon::ImageViewBase{info, view_info} {}
 
-ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageParams& params)
+ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::NullImageViewParams& params)
     : VideoCommon::ImageViewBase{params}, views{runtime.null_image_views} {}
 
 GLuint ImageView::StorageView(Shader::TextureType texture_type, Shader::ImageFormat image_format) {
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index 787b63e877..e76ec522a8 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -73,8 +73,6 @@ public:
                                  StateTracker& state_tracker);
     ~TextureCacheRuntime();
 
-    void Init();
-
     void Finish();
 
     ImageBufferMap UploadStagingBuffer(size_t size);
@@ -167,6 +165,7 @@ class Image : public VideoCommon::ImageBase {
 public:
     explicit Image(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, GPUVAddr gpu_addr,
                    VAddr cpu_addr);
+    explicit Image(const VideoCommon::NullImageParams&);
 
     ~Image();
 
@@ -223,7 +222,7 @@ public:
                        const VideoCommon::ImageViewInfo&, GPUVAddr);
     explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
                        const VideoCommon::ImageViewInfo& view_info);
-    explicit ImageView(TextureCacheRuntime&, const VideoCommon::NullImageParams&);
+    explicit ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams&);
 
     [[nodiscard]] GLuint StorageView(Shader::TextureType texture_type,
                                      Shader::ImageFormat image_format);
diff --git a/src/video_core/renderer_vulkan/pipeline_helper.h b/src/video_core/renderer_vulkan/pipeline_helper.h
index 7ba6078df9..bf18b34d1a 100644
--- a/src/video_core/renderer_vulkan/pipeline_helper.h
+++ b/src/video_core/renderer_vulkan/pipeline_helper.h
@@ -156,28 +156,27 @@ private:
     u32 texture_bit{1u};
 };
 
-inline void PushImageDescriptors(const Shader::Info& info, const VkSampler*& samplers,
-                                 const ImageId*& image_view_ids, TextureCache& texture_cache,
+inline void PushImageDescriptors(TextureCache& texture_cache,
                                  VKUpdateDescriptorQueue& update_descriptor_queue,
-                                 RescalingPushConstant& rescaling) {
-    static constexpr VideoCommon::ImageViewId NULL_IMAGE_VIEW_ID{0};
-    image_view_ids += Shader::NumDescriptors(info.texture_buffer_descriptors);
-    image_view_ids += Shader::NumDescriptors(info.image_buffer_descriptors);
+                                 const Shader::Info& info, RescalingPushConstant& rescaling,
+                                 const VkSampler*& samplers,
+                                 const VideoCommon::ImageViewInOut*& views) {
+    views += Shader::NumDescriptors(info.texture_buffer_descriptors);
+    views += Shader::NumDescriptors(info.image_buffer_descriptors);
     for (const auto& desc : info.texture_descriptors) {
         for (u32 index = 0; index < desc.count; ++index) {
-            const VideoCommon::ImageViewId image_view_id{*(image_view_ids++)};
+            const VideoCommon::ImageViewId image_view_id{(views++)->id};
             const VkSampler sampler{*(samplers++)};
             ImageView& image_view{texture_cache.GetImageView(image_view_id)};
             const Image& image{texture_cache.GetImage(image_view.image_id)};
             const VkImageView vk_image_view{image_view.Handle(desc.type)};
             update_descriptor_queue.AddSampledImage(vk_image_view, sampler);
-            rescaling.PushTexture(image_view_id != NULL_IMAGE_VIEW_ID &&
-                                  True(image.flags & VideoCommon::ImageFlagBits::Rescaled));
+            rescaling.PushTexture(True(image.flags & VideoCommon::ImageFlagBits::Rescaled));
         }
     }
     for (const auto& desc : info.image_descriptors) {
         for (u32 index = 0; index < desc.count; ++index) {
-            ImageView& image_view{texture_cache.GetImageView(*(image_view_ids++))};
+            ImageView& image_view{texture_cache.GetImageView((views++)->id)};
             if (desc.is_written) {
                 texture_cache.MarkModification(image_view.image_id);
             }
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index 5c591e3455..f89b84c6ed 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -108,10 +108,8 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
     texture_cache.SynchronizeComputeDescriptors();
 
     static constexpr size_t max_elements = 64;
-    std::array<ImageId, max_elements> image_view_ids;
-    boost::container::static_vector<u32, max_elements> image_view_indices;
+    boost::container::static_vector<VideoCommon::ImageViewInOut, max_elements> views;
     boost::container::static_vector<VkSampler, max_elements> samplers;
-    boost::container::static_vector<bool, max_elements> image_view_blacklist;
 
     const auto& qmd{kepler_compute.launch_description};
     const auto& cbufs{qmd.const_buffer_config};
@@ -135,54 +133,37 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
         }
         return TexturePair(gpu_memory.Read<u32>(addr), via_header_index);
     }};
-    const auto add_image{[&](const auto& desc) {
+    const auto add_image{[&](const auto& desc, bool blacklist) {
         for (u32 index = 0; index < desc.count; ++index) {
             const auto handle{read_handle(desc, index)};
-            image_view_indices.push_back(handle.first);
+            views.push_back({
+                .index = handle.first,
+                .blacklist = blacklist,
+                .id = {},
+            });
         }
     }};
-    std::ranges::for_each(info.texture_buffer_descriptors, add_image);
-    std::ranges::for_each(info.image_buffer_descriptors, add_image);
+    for (const auto& desc : info.texture_buffer_descriptors) {
+        add_image(desc, false);
+    }
+    for (const auto& desc : info.image_buffer_descriptors) {
+        add_image(desc, false);
+    }
     for (const auto& desc : info.texture_descriptors) {
         for (u32 index = 0; index < desc.count; ++index) {
             const auto handle{read_handle(desc, index)};
-            image_view_indices.push_back(handle.first);
+            views.push_back({handle.first});
 
             Sampler* const sampler = texture_cache.GetComputeSampler(handle.second);
             samplers.push_back(sampler->Handle());
         }
     }
-    const u32 black_list_base = image_view_indices.size();
-    bool atleast_one_blacklisted = false;
     for (const auto& desc : info.image_descriptors) {
-        const bool is_black_listed =
-            desc.is_written && (desc.type == Shader::TextureType::Color2D ||
-                                desc.type == Shader::TextureType::ColorArray2D);
-        for (u32 index = 0; index < desc.count; ++index) {
-            image_view_blacklist.push_back(is_black_listed);
-        }
-        atleast_one_blacklisted |= is_black_listed;
-        add_image(desc);
+        add_image(desc, true);
     }
-
-    const std::span indices_span(image_view_indices.data(), image_view_indices.size());
-    bool has_listed_stuffs;
-    do {
-        has_listed_stuffs = false;
-        texture_cache.FillComputeImageViews(indices_span, image_view_ids);
-        if (atleast_one_blacklisted) {
-            for (u32 index = 0; index < image_view_blacklist.size(); index++) {
-                if (image_view_blacklist[index]) {
-                    ImageView& image_view{
-                        texture_cache.GetImageView(image_view_ids[index + black_list_base])};
-                    has_listed_stuffs |= texture_cache.BlackListImage(image_view.image_id);
-                }
-            }
-        }
-    } while (has_listed_stuffs);
+    texture_cache.FillComputeImageViews(std::span(views.data(), views.size()));
 
     buffer_cache.UnbindComputeTextureBuffers();
-    ImageId* texture_buffer_ids{image_view_ids.data()};
     size_t index{};
     const auto add_buffer{[&](const auto& desc) {
         constexpr bool is_image = std::is_same_v<decltype(desc), const ImageBufferDescriptor&>;
@@ -191,11 +172,10 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
             if constexpr (is_image) {
                 is_written = desc.is_written;
             }
-            ImageView& image_view = texture_cache.GetImageView(*texture_buffer_ids);
+            ImageView& image_view = texture_cache.GetImageView(views[index].id);
             buffer_cache.BindComputeTextureBuffer(index, image_view.GpuAddr(),
                                                   image_view.BufferSize(), image_view.format,
                                                   is_written, is_image);
-            ++texture_buffer_ids;
             ++index;
         }
     }};
@@ -207,9 +187,9 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute,
 
     RescalingPushConstant rescaling(num_textures);
     const VkSampler* samplers_it{samplers.data()};
-    const ImageId* views_it{image_view_ids.data()};
-    PushImageDescriptors(info, samplers_it, views_it, texture_cache, update_descriptor_queue,
-                         rescaling);
+    const VideoCommon::ImageViewInOut* views_it{views.data()};
+    PushImageDescriptors(texture_cache, update_descriptor_queue, info, rescaling, samplers_it,
+                         views_it);
 
     if (!is_built.load(std::memory_order::relaxed)) {
         // Wait for the pipeline to be built
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 4d966ee4bf..4efb5d735b 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -278,12 +278,10 @@ void GraphicsPipeline::AddTransition(GraphicsPipeline* transition) {
 
 template <typename Spec>
 void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
-    std::array<ImageId, MAX_IMAGE_ELEMENTS> image_view_ids;
-    std::array<u32, MAX_IMAGE_ELEMENTS> image_view_indices;
-    std::array<bool, MAX_IMAGE_ELEMENTS> image_view_blacklist;
+    std::array<VideoCommon::ImageViewInOut, MAX_IMAGE_ELEMENTS> views;
     std::array<VkSampler, MAX_IMAGE_ELEMENTS> samplers;
     size_t sampler_index{};
-    size_t image_index{};
+    size_t view_index{};
 
     texture_cache.SynchronizeGraphicsDescriptors();
 
@@ -291,8 +289,6 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
 
     const auto& regs{maxwell3d.regs};
     const bool via_header_index{regs.sampler_index == Maxwell::SamplerIndex::ViaHeaderIndex};
-    u32 start_black_list = std::numeric_limits<u32>::max();
-    u32 end_black_list = 0;
     const auto config_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
         const Shader::Info& info{stage_infos[stage]};
         buffer_cache.UnbindGraphicsStorageBuffers(stage);
@@ -329,7 +325,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
         const auto add_image{[&](const auto& desc) {
             for (u32 index = 0; index < desc.count; ++index) {
                 const auto handle{read_handle(desc, index)};
-                image_view_indices[image_index++] = handle.first;
+                views[view_index++] = {handle.first};
             }
         }};
         if constexpr (Spec::has_texture_buffers) {
@@ -345,7 +341,7 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
         for (const auto& desc : info.texture_descriptors) {
             for (u32 index = 0; index < desc.count; ++index) {
                 const auto handle{read_handle(desc, index)};
-                image_view_indices[image_index++] = handle.first;
+                views[view_index++] = {handle.first};
 
                 Sampler* const sampler{texture_cache.GetGraphicsSampler(handle.second)};
                 samplers[sampler_index++] = sampler->Handle();
@@ -353,15 +349,6 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
         }
         if constexpr (Spec::has_images) {
             for (const auto& desc : info.image_descriptors) {
-                if (desc.is_written && (desc.type == Shader::TextureType::Color2D ||
-                                        desc.type == Shader::TextureType::ColorArray2D)) {
-                    auto index_copy = image_index;
-                    for (u32 index = 0; index < desc.count; ++index) {
-                        start_black_list = std::min<u32>(start_black_list, index_copy);
-                        image_view_blacklist[index_copy++] = true;
-                        end_black_list = std::max<u32>(end_black_list, index_copy);
-                    }
-                }
                 add_image(desc);
             }
         }
@@ -381,24 +368,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
     if constexpr (Spec::enabled_stages[4]) {
         config_stage(4);
     }
-    const std::span indices_span(image_view_indices.data(), image_index);
-    bool has_listed_stuffs;
-    do {
-        has_listed_stuffs = false;
-        texture_cache.FillGraphicsImageViews(indices_span, image_view_ids);
-        if constexpr (Spec::has_images) {
-            if (start_black_list < end_black_list) {
-                for (u32 index = start_black_list; index < end_black_list; index++) {
-                    if (image_view_blacklist[index]) {
-                        ImageView& image_view{texture_cache.GetImageView(image_view_ids[index])};
-                        has_listed_stuffs |= texture_cache.BlackListImage(image_view.image_id);
-                    }
-                }
-            }
-        }
-    } while (has_listed_stuffs);
+    texture_cache.FillGraphicsImageViews<Spec::has_images>(std::span(views.data(), view_index));
 
-    ImageId* texture_buffer_index{image_view_ids.data()};
+    VideoCommon::ImageViewInOut* texture_buffer_it{views.data()};
     const auto bind_stage_info{[&](size_t stage) LAMBDA_FORCEINLINE {
         size_t index{};
         const auto add_buffer{[&](const auto& desc) {
@@ -408,12 +380,12 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
                 if constexpr (is_image) {
                     is_written = desc.is_written;
                 }
-                ImageView& image_view{texture_cache.GetImageView(*texture_buffer_index)};
+                ImageView& image_view{texture_cache.GetImageView(texture_buffer_it->id)};
                 buffer_cache.BindGraphicsTextureBuffer(stage, index, image_view.GpuAddr(),
                                                        image_view.BufferSize(), image_view.format,
                                                        is_written, is_image);
                 ++index;
-                ++texture_buffer_index;
+                ++texture_buffer_it;
             }
         }};
         buffer_cache.UnbindGraphicsTextureBuffers(stage);
@@ -429,9 +401,9 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
                 add_buffer(desc);
             }
         }
-        texture_buffer_index += Shader::NumDescriptors(info.texture_descriptors);
+        texture_buffer_it += Shader::NumDescriptors(info.texture_descriptors);
         if constexpr (Spec::has_images) {
-            texture_buffer_index += Shader::NumDescriptors(info.image_descriptors);
+            texture_buffer_it += Shader::NumDescriptors(info.image_descriptors);
         }
     }};
     if constexpr (Spec::enabled_stages[0]) {
@@ -457,11 +429,11 @@ void GraphicsPipeline::ConfigureImpl(bool is_indexed) {
 
     RescalingPushConstant rescaling(num_textures);
     const VkSampler* samplers_it{samplers.data()};
-    const ImageId* views_it{image_view_ids.data()};
+    const VideoCommon::ImageViewInOut* views_it{views.data()};
     const auto prepare_stage{[&](size_t stage) LAMBDA_FORCEINLINE {
         buffer_cache.BindHostStageBuffers(stage);
-        PushImageDescriptors(stage_infos[stage], samplers_it, views_it, texture_cache,
-                             update_descriptor_queue, rescaling);
+        PushImageDescriptors(texture_cache, update_descriptor_queue, stage_infos[stage], rescaling,
+                             samplers_it, views_it);
     }};
     if constexpr (Spec::enabled_stages[0]) {
         prepare_stage(0);
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index b21992fce7..3400066a65 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -730,10 +730,17 @@ void BlitScale(VKScheduler& scheduler, VkImage src_image, VkImage dst_image, con
 }
 } // Anonymous namespace
 
-void TextureCacheRuntime::Init() {
-    resolution = Settings::values.resolution_info;
-    is_rescaling_on = resolution.up_scale != 1 || resolution.down_shift != 0;
-}
+TextureCacheRuntime::TextureCacheRuntime(const Device& device_, VKScheduler& scheduler_,
+                                         MemoryAllocator& memory_allocator_,
+                                         StagingBufferPool& staging_buffer_pool_,
+                                         BlitImageHelper& blit_image_helper_,
+                                         ASTCDecoderPass& astc_decoder_pass_,
+                                         RenderPassCache& render_pass_cache_)
+    : device{device_}, scheduler{scheduler_}, memory_allocator{memory_allocator_},
+      staging_buffer_pool{staging_buffer_pool_}, blit_image_helper{blit_image_helper_},
+      astc_decoder_pass{astc_decoder_pass_}, render_pass_cache{render_pass_cache_},
+      resolution{Settings::values.resolution_info},
+      is_rescaling_on(resolution.up_scale != 1 || resolution.down_shift != 0) {}
 
 void TextureCacheRuntime::Finish() {
     scheduler.Finish();
@@ -1040,6 +1047,8 @@ Image::Image(TextureCacheRuntime& runtime_, const ImageInfo& info_, GPUVAddr gpu
     }
 }
 
+Image::Image(const VideoCommon::NullImageParams& params) : VideoCommon::ImageBase{params} {}
+
 Image::~Image() = default;
 
 void Image::UploadMemory(const StagingBufferRef& map, std::span<const BufferImageCopy> copies) {
@@ -1187,8 +1196,7 @@ bool Image::ScaleDown(bool save_as_backup) {
     }*/
 
     const auto& resolution = runtime->resolution;
-    vk::Image downscaled_image =
-        MakeImage(runtime->device, info);
+    vk::Image downscaled_image = MakeImage(runtime->device, info);
     MemoryCommit new_commit(
         runtime->memory_allocator.Commit(downscaled_image, MemoryUsage::DeviceLocal));
 
@@ -1301,7 +1309,7 @@ ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo& info,
     : VideoCommon::ImageViewBase{info, view_info}, gpu_addr{gpu_addr_},
       buffer_size{VideoCommon::CalculateGuestSizeInBytes(info)} {}
 
-ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::NullImageParams& params)
+ImageView::ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams& params)
     : VideoCommon::ImageViewBase{params} {}
 
 VkImageView ImageView::DepthView() {
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 958a64651b..9c39a6d99e 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -34,21 +34,16 @@ class RenderPassCache;
 class StagingBufferPool;
 class VKScheduler;
 
-struct TextureCacheRuntime {
-    const Device& device;
-    VKScheduler& scheduler;
-    MemoryAllocator& memory_allocator;
-    StagingBufferPool& staging_buffer_pool;
-    BlitImageHelper& blit_image_helper;
-    ASTCDecoderPass& astc_decoder_pass;
-    RenderPassCache& render_pass_cache;
+class TextureCacheRuntime {
+public:
     static constexpr size_t TICKS_TO_DESTROY = 6;
-    DelayedDestructionRing<vk::Image, TICKS_TO_DESTROY> prescaled_images;
-    DelayedDestructionRing<MemoryCommit, TICKS_TO_DESTROY> prescaled_commits;
-    Settings::ResolutionScalingInfo resolution;
-    bool is_rescaling_on{};
 
-    void Init();
+    explicit TextureCacheRuntime(const Device& device_, VKScheduler& scheduler_,
+                                 MemoryAllocator& memory_allocator_,
+                                 StagingBufferPool& staging_buffer_pool_,
+                                 BlitImageHelper& blit_image_helper_,
+                                 ASTCDecoderPass& astc_decoder_pass_,
+                                 RenderPassCache& render_pass_cache_);
 
     void Finish();
 
@@ -56,6 +51,10 @@ struct TextureCacheRuntime {
 
     StagingBufferRef DownloadStagingBuffer(size_t size);
 
+    void TickFrame();
+
+    u64 GetDeviceLocalMemory() const;
+
     void BlitImage(Framebuffer* dst_framebuffer, ImageView& dst, ImageView& src,
                    const Region2D& dst_region, const Region2D& src_region,
                    Tegra::Engines::Fermi2D::Filter filter,
@@ -84,15 +83,25 @@ struct TextureCacheRuntime {
         return true;
     }
 
-    void TickFrame();
+    const Device& device;
+    VKScheduler& scheduler;
+    MemoryAllocator& memory_allocator;
+    StagingBufferPool& staging_buffer_pool;
+    BlitImageHelper& blit_image_helper;
+    ASTCDecoderPass& astc_decoder_pass;
+    RenderPassCache& render_pass_cache;
 
-    u64 GetDeviceLocalMemory() const;
+    DelayedDestructionRing<vk::Image, TICKS_TO_DESTROY> prescaled_images;
+    DelayedDestructionRing<MemoryCommit, TICKS_TO_DESTROY> prescaled_commits;
+    Settings::ResolutionScalingInfo resolution;
+    bool is_rescaling_on{};
 };
 
 class Image : public VideoCommon::ImageBase {
 public:
     explicit Image(TextureCacheRuntime&, const VideoCommon::ImageInfo& info, GPUVAddr gpu_addr,
                    VAddr cpu_addr);
+    explicit Image(const VideoCommon::NullImageParams&);
 
     ~Image();
 
@@ -151,7 +160,7 @@ public:
     explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageViewInfo&, ImageId, Image&);
     explicit ImageView(TextureCacheRuntime&, const VideoCommon::ImageInfo&,
                        const VideoCommon::ImageViewInfo&, GPUVAddr);
-    explicit ImageView(TextureCacheRuntime&, const VideoCommon::NullImageParams&);
+    explicit ImageView(TextureCacheRuntime&, const VideoCommon::NullImageViewParams&);
 
     [[nodiscard]] VkImageView DepthView();
 
diff --git a/src/video_core/texture_cache/image_base.cpp b/src/video_core/texture_cache/image_base.cpp
index e9e725edf7..25a211df8f 100644
--- a/src/video_core/texture_cache/image_base.cpp
+++ b/src/video_core/texture_cache/image_base.cpp
@@ -69,6 +69,8 @@ ImageBase::ImageBase(const ImageInfo& info_, GPUVAddr gpu_addr_, VAddr cpu_addr_
     }
 }
 
+ImageBase::ImageBase(const NullImageParams&) {}
+
 ImageMapView::ImageMapView(GPUVAddr gpu_addr_, VAddr cpu_addr_, size_t size_, ImageId image_id_)
     : gpu_addr{gpu_addr_}, cpu_addr{cpu_addr_}, size{size_}, image_id{image_id_} {}
 
diff --git a/src/video_core/texture_cache/image_base.h b/src/video_core/texture_cache/image_base.h
index 97f107b4dd..9c34687e05 100644
--- a/src/video_core/texture_cache/image_base.h
+++ b/src/video_core/texture_cache/image_base.h
@@ -48,8 +48,11 @@ struct AliasedImage {
     ImageId id;
 };
 
+struct NullImageParams {};
+
 struct ImageBase {
     explicit ImageBase(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
+    explicit ImageBase(const NullImageParams&);
 
     [[nodiscard]] std::optional<SubresourceBase> TryFindBase(GPUVAddr other_addr) const noexcept;
 
diff --git a/src/video_core/texture_cache/image_view_base.cpp b/src/video_core/texture_cache/image_view_base.cpp
index 450becbebe..e66dc93205 100644
--- a/src/video_core/texture_cache/image_view_base.cpp
+++ b/src/video_core/texture_cache/image_view_base.cpp
@@ -45,6 +45,6 @@ ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_in
     ASSERT_MSG(view_info.type == ImageViewType::Buffer, "Expected texture buffer");
 }
 
-ImageViewBase::ImageViewBase(const NullImageParams&) {}
+ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {}
 
 } // namespace VideoCommon
diff --git a/src/video_core/texture_cache/image_view_base.h b/src/video_core/texture_cache/image_view_base.h
index 903f715c51..9c24c5359a 100644
--- a/src/video_core/texture_cache/image_view_base.h
+++ b/src/video_core/texture_cache/image_view_base.h
@@ -15,7 +15,7 @@ using VideoCore::Surface::PixelFormat;
 struct ImageViewInfo;
 struct ImageInfo;
 
-struct NullImageParams {};
+struct NullImageViewParams {};
 
 enum class ImageViewFlagBits : u16 {
     PreemtiveDownload = 1 << 0,
@@ -28,7 +28,7 @@ struct ImageViewBase {
     explicit ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_info,
                            ImageId image_id);
     explicit ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info);
-    explicit ImageViewBase(const NullImageParams&);
+    explicit ImageViewBase(const NullImageViewParams&);
 
     [[nodiscard]] bool IsBuffer() const noexcept {
         return type == ImageViewType::Buffer;
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index be40f6b881..4e97a9e6ac 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -36,7 +36,6 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
                               Tegra::MemoryManager& gpu_memory_)
     : runtime{runtime_}, rasterizer{rasterizer_}, maxwell3d{maxwell3d_},
       kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_} {
-    runtime.Init();
     // Configure null sampler
     TSCEntry sampler_descriptor{};
     sampler_descriptor.min_filter.Assign(Tegra::Texture::TextureFilter::Linear);
@@ -46,7 +45,8 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
 
     // Make sure the first index is reserved for the null resources
     // This way the null resource becomes a compile time constant
-    void(slot_image_views.insert(runtime, NullImageParams{}));
+    void(slot_images.insert(NullImageParams{}));
+    void(slot_image_views.insert(runtime, NullImageViewParams{}));
     void(slot_samplers.insert(runtime, sampler_descriptor));
 
     if constexpr (HAS_DEVICE_MEMORY_INFO) {
@@ -57,7 +57,7 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
         critical_memory = std::max(possible_critical_memory, DEFAULT_CRITICAL_MEMORY);
         minimum_memory = 0;
     } else {
-        // on OGL we can be more conservatives as the driver takes care.
+        // On OpenGL we can be more conservatives as the driver takes care.
         expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
         critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
         minimum_memory = expected_memory;
@@ -135,15 +135,14 @@ void TextureCache<P>::MarkModification(ImageId id) noexcept {
 }
 
 template <class P>
-void TextureCache<P>::FillGraphicsImageViews(std::span<const u32> indices,
-                                             std::span<ImageViewId> image_view_ids) {
-    FillImageViews(graphics_image_table, graphics_image_view_ids, indices, image_view_ids);
+template <bool has_blacklists>
+void TextureCache<P>::FillGraphicsImageViews(std::span<ImageViewInOut> views) {
+    FillImageViews<has_blacklists>(graphics_image_table, graphics_image_view_ids, views);
 }
 
 template <class P>
-void TextureCache<P>::FillComputeImageViews(std::span<const u32> indices,
-                                            std::span<ImageViewId> image_view_ids) {
-    FillImageViews(compute_image_table, compute_image_view_ids, indices, image_view_ids);
+void TextureCache<P>::FillComputeImageViews(std::span<ImageViewInOut> views) {
+    FillImageViews<false>(compute_image_table, compute_image_view_ids, views);
 }
 
 template <class P>
@@ -346,17 +345,26 @@ typename P::Framebuffer* TextureCache<P>::GetFramebuffer() {
 }
 
 template <class P>
+template <bool has_blacklists>
 void TextureCache<P>::FillImageViews(DescriptorTable<TICEntry>& table,
                                      std::span<ImageViewId> cached_image_view_ids,
-                                     std::span<const u32> indices,
-                                     std::span<ImageViewId> image_view_ids) {
-    ASSERT(indices.size() <= image_view_ids.size());
+                                     std::span<ImageViewInOut> views) {
+    bool has_blacklisted;
     do {
         has_deleted_images = false;
-        std::ranges::transform(indices, image_view_ids.begin(), [&](u32 index) {
-            return VisitImageView(table, cached_image_view_ids, index);
-        });
-    } while (has_deleted_images);
+        if constexpr (has_blacklists) {
+            has_blacklisted = false;
+        }
+        for (ImageViewInOut& view : views) {
+            view.id = VisitImageView(table, cached_image_view_ids, view.index);
+            if constexpr (has_blacklists) {
+                if (view.blacklist && view.id != NULL_IMAGE_VIEW_ID) {
+                    const ImageViewBase& image_view{slot_image_views[view.id]};
+                    has_blacklisted |= BlackListImage(image_view.image_id);
+                }
+            }
+        }
+    } while (has_deleted_images || (has_blacklists && has_blacklisted));
 }
 
 template <class P>
@@ -622,7 +630,7 @@ void TextureCache<P>::PopAsyncFlushes() {
 }
 
 template <class P>
-bool TextureCache<P>::IsRescaling() {
+bool TextureCache<P>::IsRescaling() const noexcept {
     return is_rescaling;
 }
 
@@ -775,12 +783,11 @@ bool TextureCache<P>::BlackListImage(ImageId image_id) {
 }
 
 template <class P>
-bool TextureCache<P>::ImageCanRescale(Image& image) {
+bool TextureCache<P>::ImageCanRescale(ImageBase& image) {
     if (True(image.flags & ImageFlagBits::Blacklisted)) {
         return false;
     }
-    if (True(image.flags & ImageFlagBits::Rescaled) ||
-        True(image.flags & ImageFlagBits::RescaleChecked)) {
+    if (True(image.flags & (ImageFlagBits::Rescaled | ImageFlagBits::RescaleChecked))) {
         return true;
     }
     if (!image.info.rescaleable) {
diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h
index 35a29cd9b4..b6cc096820 100644
--- a/src/video_core/texture_cache/texture_cache_base.h
+++ b/src/video_core/texture_cache/texture_cache_base.h
@@ -39,6 +39,16 @@ using VideoCore::Surface::PixelFormatFromDepthFormat;
 using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
 using namespace Common::Literals;
 
+struct ImageViewInOut {
+    u32 index;
+    bool blacklist;
+    union {
+        struct Empty {
+        } empty{};
+        ImageViewId id;
+    };
+};
+
 template <class P>
 class TextureCache {
     /// Address shift for caching images into a hash table
@@ -53,11 +63,6 @@ class TextureCache {
     /// True when the API can provide info about the memory of the device.
     static constexpr bool HAS_DEVICE_MEMORY_INFO = P::HAS_DEVICE_MEMORY_INFO;
 
-    /// Image view ID for null descriptors
-    static constexpr ImageViewId NULL_IMAGE_VIEW_ID{0};
-    /// Sampler ID for bugged sampler ids
-    static constexpr SamplerId NULL_SAMPLER_ID{0};
-
     static constexpr u64 DEFAULT_EXPECTED_MEMORY = 1_GiB;
     static constexpr u64 DEFAULT_CRITICAL_MEMORY = 2_GiB;
 
@@ -105,11 +110,11 @@ public:
     void MarkModification(ImageId id) noexcept;
 
     /// Fill image_view_ids with the graphics images in indices
-    void FillGraphicsImageViews(std::span<const u32> indices,
-                                std::span<ImageViewId> image_view_ids);
+    template <bool has_blacklists>
+    void FillGraphicsImageViews(std::span<ImageViewInOut> views);
 
     /// Fill image_view_ids with the compute images in indices
-    void FillComputeImageViews(std::span<const u32> indices, std::span<ImageViewId> image_view_ids);
+    void FillComputeImageViews(std::span<ImageViewInOut> views);
 
     /// Get the sampler from the graphics descriptor table in the specified index
     Sampler* GetGraphicsSampler(u32 index);
@@ -174,7 +179,7 @@ public:
     /// Return true when a CPU region is modified from the GPU
     [[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
 
-    [[nodiscard]] bool IsRescaling();
+    [[nodiscard]] bool IsRescaling() const noexcept;
 
     [[nodiscard]] bool BlackListImage(ImageId image_id);
 
@@ -216,9 +221,10 @@ private:
     void RunGarbageCollector();
 
     /// Fills image_view_ids in the image views in indices
+    template <bool has_blacklists>
     void FillImageViews(DescriptorTable<TICEntry>& table,
-                        std::span<ImageViewId> cached_image_view_ids, std::span<const u32> indices,
-                        std::span<ImageViewId> image_view_ids);
+                        std::span<ImageViewId> cached_image_view_ids,
+                        std::span<ImageViewInOut> views);
 
     /// Find or create an image view in the guest descriptor table
     ImageViewId VisitImageView(DescriptorTable<TICEntry>& table,
@@ -336,7 +342,7 @@ private:
     /// Returns true if the current clear parameters clear the whole image of a given image view
     [[nodiscard]] bool IsFullClear(ImageViewId id);
 
-    bool ImageCanRescale(Image& image);
+    bool ImageCanRescale(ImageBase& image);
     void InvalidateScale(Image& image);
     bool ScaleUp(Image& image);
     bool ScaleDown(Image& image);
diff --git a/src/video_core/texture_cache/types.h b/src/video_core/texture_cache/types.h
index 47a11cb2f8..5c274abdf8 100644
--- a/src/video_core/texture_cache/types.h
+++ b/src/video_core/texture_cache/types.h
@@ -22,6 +22,13 @@ using ImageAllocId = SlotId;
 using SamplerId = SlotId;
 using FramebufferId = SlotId;
 
+/// Fake image ID for null image views
+constexpr ImageId NULL_IMAGE_ID{0};
+/// Image view ID for null descriptors
+constexpr ImageViewId NULL_IMAGE_VIEW_ID{0};
+/// Sampler ID for bugged sampler ids
+constexpr SamplerId NULL_SAMPLER_ID{0};
+
 enum class ImageType : u32 {
     e1D,
     e2D,