ソースを参照

Refactor VideoCore to use AS sepparate from Channel.

Fernando Sahmkow 4 年 前
コミット
e462191482

+ 7 - 0
src/common/hash.h

@@ -18,4 +18,11 @@ struct PairHash {
     }
 };
 
+template <typename T>
+struct IdentityHash {
+    [[nodiscard]] size_t operator()(T value) const noexcept {
+        return static_cast<size_t>(value);
+    }
+};
+
 } // namespace Common

+ 1 - 0
src/video_core/CMakeLists.txt

@@ -203,6 +203,7 @@ add_library(video_core STATIC
     texture_cache/render_targets.h
     texture_cache/samples_helper.h
     texture_cache/slot_vector.h
+    texture_cache/texture_cache.cpp
     texture_cache/texture_cache.h
     texture_cache/texture_cache_base.h
     texture_cache/types.h

+ 7 - 1
src/video_core/control/channel_state_cache.cpp

@@ -1,5 +1,11 @@
 #include "video_core/control/channel_state_cache.inc"
 
 namespace VideoCommon {
+
+ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state)
+    : maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute},
+      gpu_memory{*channel_state.memory_manager} {}
+
 template class VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo>;
-}
+
+} // namespace VideoCommon

+ 23 - 5
src/video_core/control/channel_state_cache.h

@@ -2,6 +2,7 @@
 
 #include <deque>
 #include <limits>
+#include <mutex>
 #include <unordered_map>
 
 #include "common/common_types.h"
@@ -41,9 +42,10 @@ template <class P>
 class ChannelSetupCaches {
 public:
     /// Operations for seting the channel of execution.
+    virtual ~ChannelSetupCaches();
 
     /// Create channel state.
-    void CreateChannel(Tegra::Control::ChannelState& channel);
+    virtual void CreateChannel(Tegra::Control::ChannelState& channel);
 
     /// Bind a channel for execution.
     void BindToChannel(s32 id);
@@ -51,18 +53,34 @@ public:
     /// Erase channel's state.
     void EraseChannel(s32 id);
 
+    Tegra::MemoryManager* GetFromID(size_t id) const {
+        std::unique_lock<std::mutex> lk(config_mutex);
+        const auto ref = address_spaces.find(id);
+        return ref->second.gpu_memory;
+    }
+
 protected:
     static constexpr size_t UNSET_CHANNEL{std::numeric_limits<size_t>::max()};
 
-    std::deque<P> channel_storage;
-    std::deque<size_t> free_channel_ids;
-    std::unordered_map<s32, size_t> channel_map;
-
     P* channel_state;
     size_t current_channel_id{UNSET_CHANNEL};
+    size_t current_address_space{};
     Tegra::Engines::Maxwell3D* maxwell3d;
     Tegra::Engines::KeplerCompute* kepler_compute;
     Tegra::MemoryManager* gpu_memory;
+
+    std::deque<P> channel_storage;
+    std::deque<size_t> free_channel_ids;
+    std::unordered_map<s32, size_t> channel_map;
+    struct AddresSpaceRef {
+        size_t ref_count;
+        size_t storage_id;
+        Tegra::MemoryManager* gpu_memory;
+    };
+    std::unordered_map<size_t, AddresSpaceRef> address_spaces;
+    mutable std::mutex config_mutex;
+
+    virtual void OnGPUASRegister([[maybe_unused]] size_t map_id) {}
 };
 
 } // namespace VideoCommon

+ 19 - 4
src/video_core/control/channel_state_cache.inc

@@ -6,18 +6,18 @@
 
 namespace VideoCommon {
 
-ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state)
-    : maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute},
-      gpu_memory{*channel_state.memory_manager} {}
+template <class P>
+ChannelSetupCaches<P>::~ChannelSetupCaches() = default;
 
 template <class P>
 void ChannelSetupCaches<P>::CreateChannel(struct Tegra::Control::ChannelState& channel) {
+    std::unique_lock<std::mutex> lk(config_mutex);
     ASSERT(channel_map.find(channel.bind_id) == channel_map.end() && channel.bind_id >= 0);
     auto new_id = [this, &channel]() {
         if (!free_channel_ids.empty()) {
             auto id = free_channel_ids.front();
             free_channel_ids.pop_front();
-            new (&channel_storage[id]) ChannelInfo(channel);
+            new (&channel_storage[id]) P(channel);
             return id;
         }
         channel_storage.emplace_back(channel);
@@ -27,11 +27,24 @@ void ChannelSetupCaches<P>::CreateChannel(struct Tegra::Control::ChannelState& c
     if (current_channel_id != UNSET_CHANNEL) {
         channel_state = &channel_storage[current_channel_id];
     }
+    auto as_it = address_spaces.find(channel.memory_manager->GetID());
+    if (as_it != address_spaces.end()) {
+      as_it->second.ref_count++;
+      return;
+    }
+    AddresSpaceRef new_gpu_mem_ref{
+      .ref_count = 1,
+      .storage_id = address_spaces.size(),
+      .gpu_memory = channel.memory_manager.get(),
+    };
+    address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref);
+    OnGPUASRegister(channel.memory_manager->GetID());
 }
 
 /// Bind a channel for execution.
 template <class P>
 void ChannelSetupCaches<P>::BindToChannel(s32 id) {
+    std::unique_lock<std::mutex> lk(config_mutex);
     auto it = channel_map.find(id);
     ASSERT(it != channel_map.end() && id >= 0);
     current_channel_id = it->second;
@@ -39,11 +52,13 @@ void ChannelSetupCaches<P>::BindToChannel(s32 id) {
     maxwell3d = &channel_state->maxwell3d;
     kepler_compute = &channel_state->kepler_compute;
     gpu_memory = &channel_state->gpu_memory;
+    current_address_space = gpu_memory->GetID();
 }
 
 /// Erase channel's channel_state.
 template <class P>
 void ChannelSetupCaches<P>::EraseChannel(s32 id) {
+    std::unique_lock<std::mutex> lk(config_mutex);
     const auto it = channel_map.find(id);
     ASSERT(it != channel_map.end() && id >= 0);
     const auto this_id = it->second;

+ 4 - 1
src/video_core/memory_manager.cpp

@@ -16,9 +16,12 @@
 
 namespace Tegra {
 
+std::atomic<size_t> MemoryManager::unique_identifier_generator{};
+
 MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64 page_bits_)
     : system{system_}, address_space_bits{address_space_bits_}, page_bits{page_bits_}, entries{},
-      page_table{address_space_bits, address_space_bits + page_bits - 38, page_bits} {
+      page_table{address_space_bits, address_space_bits + page_bits - 38, page_bits},
+      unique_identifier{unique_identifier_generator.fetch_add(1, std::memory_order_acq_rel)} {
     address_space_size = 1ULL << address_space_bits;
     allocate_start = address_space_bits > 32 ? 1ULL << 32 : 0;
     page_size = 1ULL << page_bits;

+ 9 - 0
src/video_core/memory_manager.h

@@ -3,6 +3,7 @@
 
 #pragma once
 
+#include <atomic>
 #include <map>
 #include <optional>
 #include <vector>
@@ -26,6 +27,10 @@ public:
                            u64 page_bits_ = 16);
     ~MemoryManager();
 
+    size_t GetID() const {
+        return unique_identifier;
+    }
+
     /// Binds a renderer to the memory manager.
     void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
 
@@ -140,6 +145,10 @@ private:
     void SetEntry(size_t position, EntryType entry);
 
     Common::MultiLevelPageTable<u32> page_table;
+
+    const size_t unique_identifier;
+
+    static std::atomic<size_t> unique_identifier_generator;
 };
 
 } // namespace Tegra

+ 16 - 0
src/video_core/texture_cache/texture_cache.cpp

@@ -0,0 +1,16 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv3 or any later version
+// Refer to the license.txt file included.
+
+#include "video_core/control/channel_state_cache.inc"
+#include "video_core/texture_cache/texture_cache_base.h"
+
+namespace VideoCommon {
+
+TextureCacheChannelInfo::TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept
+    : ChannelInfo(state), graphics_image_table{gpu_memory}, graphics_sampler_table{gpu_memory},
+      compute_image_table{gpu_memory}, compute_sampler_table{gpu_memory} {}
+
+template class VideoCommon::ChannelSetupCaches<VideoCommon::TextureCacheChannelInfo>;
+
+} // namespace VideoCommon

+ 45 - 85
src/video_core/texture_cache/texture_cache.h

@@ -1,5 +1,7 @@
-// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2021 yuzu emulator team
+// (https://github.com/skyline-emu/)
+// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3
+// or any later version Refer to the license.txt file included.
 
 #pragma once
 
@@ -41,10 +43,6 @@ TextureCache<P>::TextureCache(Runtime& runtime_, VideoCore::RasterizerInterface&
 
     // Setup channels
     current_channel_id = UNSET_CHANNEL;
-    state = nullptr;
-    maxwell3d = nullptr;
-    kepler_compute = nullptr;
-    gpu_memory = nullptr;
 
     // Make sure the first index is reserved for the null resources
     // This way the null resource becomes a compile time constant
@@ -156,23 +154,24 @@ void TextureCache<P>::MarkModification(ImageId id) noexcept {
 template <class P>
 template <bool has_blacklists>
 void TextureCache<P>::FillGraphicsImageViews(std::span<ImageViewInOut> views) {
-    FillImageViews<has_blacklists>(state->graphics_image_table, state->graphics_image_view_ids,
-                                   views);
+    FillImageViews<has_blacklists>(channel_state->graphics_image_table,
+                                   channel_state->graphics_image_view_ids, views);
 }
 
 template <class P>
 void TextureCache<P>::FillComputeImageViews(std::span<ImageViewInOut> views) {
-    FillImageViews<true>(state->compute_image_table, state->compute_image_view_ids, views);
+    FillImageViews<true>(channel_state->compute_image_table, channel_state->compute_image_view_ids,
+                         views);
 }
 
 template <class P>
 typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
-    if (index > state->graphics_sampler_table.Limit()) {
+    if (index > channel_state->graphics_sampler_table.Limit()) {
         LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
         return &slot_samplers[NULL_SAMPLER_ID];
     }
-    const auto [descriptor, is_new] = state->graphics_sampler_table.Read(index);
-    SamplerId& id = state->graphics_sampler_ids[index];
+    const auto [descriptor, is_new] = channel_state->graphics_sampler_table.Read(index);
+    SamplerId& id = channel_state->graphics_sampler_ids[index];
     if (is_new) {
         id = FindSampler(descriptor);
     }
@@ -181,12 +180,12 @@ typename P::Sampler* TextureCache<P>::GetGraphicsSampler(u32 index) {
 
 template <class P>
 typename P::Sampler* TextureCache<P>::GetComputeSampler(u32 index) {
-    if (index > state->compute_sampler_table.Limit()) {
+    if (index > channel_state->compute_sampler_table.Limit()) {
         LOG_DEBUG(HW_GPU, "Invalid sampler index={}", index);
         return &slot_samplers[NULL_SAMPLER_ID];
     }
-    const auto [descriptor, is_new] = state->compute_sampler_table.Read(index);
-    SamplerId& id = state->compute_sampler_ids[index];
+    const auto [descriptor, is_new] = channel_state->compute_sampler_table.Read(index);
+    SamplerId& id = channel_state->compute_sampler_ids[index];
     if (is_new) {
         id = FindSampler(descriptor);
     }
@@ -199,11 +198,12 @@ void TextureCache<P>::SynchronizeGraphicsDescriptors() {
     const bool linked_tsc = maxwell3d->regs.sampler_index == SamplerIndex::ViaHeaderIndex;
     const u32 tic_limit = maxwell3d->regs.tic.limit;
     const u32 tsc_limit = linked_tsc ? tic_limit : maxwell3d->regs.tsc.limit;
-    if (state->graphics_sampler_table.Synchornize(maxwell3d->regs.tsc.Address(), tsc_limit)) {
-        state->graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID);
+    if (channel_state->graphics_sampler_table.Synchornize(maxwell3d->regs.tsc.Address(),
+                                                          tsc_limit)) {
+        channel_state->graphics_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID);
     }
-    if (state->graphics_image_table.Synchornize(maxwell3d->regs.tic.Address(), tic_limit)) {
-        state->graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID);
+    if (channel_state->graphics_image_table.Synchornize(maxwell3d->regs.tic.Address(), tic_limit)) {
+        channel_state->graphics_image_view_ids.resize(tic_limit + 1, CORRUPT_ID);
     }
 }
 
@@ -213,11 +213,12 @@ void TextureCache<P>::SynchronizeComputeDescriptors() {
     const u32 tic_limit = kepler_compute->regs.tic.limit;
     const u32 tsc_limit = linked_tsc ? tic_limit : kepler_compute->regs.tsc.limit;
     const GPUVAddr tsc_gpu_addr = kepler_compute->regs.tsc.Address();
-    if (state->compute_sampler_table.Synchornize(tsc_gpu_addr, tsc_limit)) {
-        state->compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID);
+    if (channel_state->compute_sampler_table.Synchornize(tsc_gpu_addr, tsc_limit)) {
+        channel_state->compute_sampler_ids.resize(tsc_limit + 1, CORRUPT_ID);
     }
-    if (state->compute_image_table.Synchornize(kepler_compute->regs.tic.Address(), tic_limit)) {
-        state->compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID);
+    if (channel_state->compute_image_table.Synchornize(kepler_compute->regs.tic.Address(),
+                                                       tic_limit)) {
+        channel_state->compute_image_view_ids.resize(tic_limit + 1, CORRUPT_ID);
     }
 }
 
@@ -738,7 +739,7 @@ ImageViewId TextureCache<P>::FindImageView(const TICEntry& config) {
     if (!IsValidEntry(*gpu_memory, config)) {
         return NULL_IMAGE_VIEW_ID;
     }
-    const auto [pair, is_new] = state->image_views.try_emplace(config);
+    const auto [pair, is_new] = channel_state->image_views.try_emplace(config);
     ImageViewId& image_view_id = pair->second;
     if (is_new) {
         image_view_id = CreateImageView(config);
@@ -1198,7 +1199,7 @@ SamplerId TextureCache<P>::FindSampler(const TSCEntry& config) {
     if (std::ranges::all_of(config.raw, [](u64 value) { return value == 0; })) {
         return NULL_SAMPLER_ID;
     }
-    const auto [pair, is_new] = state->samplers.try_emplace(config);
+    const auto [pair, is_new] = channel_state->samplers.try_emplace(config);
     if (is_new) {
         pair->second = slot_samplers.insert(runtime, config);
     }
@@ -1327,8 +1328,8 @@ void TextureCache<P>::ForEachImageInRegionGPU(GPUVAddr gpu_addr, size_t size, Fu
     static constexpr bool BOOL_BREAK = std::is_same_v<FuncReturn, bool>;
     boost::container::small_vector<ImageId, 8> images;
     ForEachGPUPage(gpu_addr, size, [this, &images, gpu_addr, size, func](u64 page) {
-        const auto it = state->gpu_page_table.find(page);
-        if (it == state->gpu_page_table.end()) {
+        const auto it = channel_state->gpu_page_table->find(page);
+        if (it == channel_state->gpu_page_table->end()) {
             if constexpr (BOOL_BREAK) {
                 return false;
             } else {
@@ -1454,8 +1455,9 @@ void TextureCache<P>::RegisterImage(ImageId image_id) {
     }
     image.lru_index = lru_cache.Insert(image_id, frame_tick);
 
-    ForEachGPUPage(image.gpu_addr, image.guest_size_bytes,
-                   [this, image_id](u64 page) { state->gpu_page_table[page].push_back(image_id); });
+    ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, image_id](u64 page) {
+        (*channel_state->gpu_page_table)[page].push_back(image_id);
+    });
     if (False(image.flags & ImageFlagBits::Sparse)) {
         auto map_id =
             slot_map_views.insert(image.gpu_addr, image.cpu_addr, image.guest_size_bytes, image_id);
@@ -1486,9 +1488,9 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
     image.flags &= ~ImageFlagBits::BadOverlap;
     lru_cache.Free(image.lru_index);
     const auto& clear_page_table =
-        [this, image_id](
-            u64 page,
-            std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>>& selected_page_table) {
+        [this, image_id](u64 page,
+                         std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>&
+                             selected_page_table) {
             const auto page_it = selected_page_table.find(page);
             if (page_it == selected_page_table.end()) {
                 ASSERT_MSG(false, "Unregistering unregistered page=0x{:x}", page << YUZU_PAGEBITS);
@@ -1504,7 +1506,7 @@ void TextureCache<P>::UnregisterImage(ImageId image_id) {
             image_ids.erase(vector_it);
         };
     ForEachGPUPage(image.gpu_addr, image.guest_size_bytes, [this, &clear_page_table](u64 page) {
-        clear_page_table(page, state->gpu_page_table);
+        clear_page_table(page, (*channel_state->gpu_page_table));
     });
     if (False(image.flags & ImageFlagBits::Sparse)) {
         const auto map_id = image.map_view_id;
@@ -1701,11 +1703,11 @@ void TextureCache<P>::DeleteImage(ImageId image_id, bool immediate_delete) {
 
 template <class P>
 void TextureCache<P>::RemoveImageViewReferences(std::span<const ImageViewId> removed_views) {
-    auto it = state->image_views.begin();
-    while (it != state->image_views.end()) {
+    auto it = channel_state->image_views.begin();
+    while (it != channel_state->image_views.end()) {
         const auto found = std::ranges::find(removed_views, it->second);
         if (found != removed_views.end()) {
-            it = state->image_views.erase(it);
+            it = channel_state->image_views.erase(it);
         } else {
             ++it;
         }
@@ -1967,61 +1969,19 @@ bool TextureCache<P>::IsFullClear(ImageViewId id) {
            scissor.max_y >= size.height;
 }
 
-template <class P>
-TextureCache<P>::ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& state) noexcept
-    : maxwell3d{*state.maxwell_3d}, kepler_compute{*state.kepler_compute},
-      gpu_memory{*state.memory_manager}, graphics_image_table{gpu_memory},
-      graphics_sampler_table{gpu_memory}, compute_image_table{gpu_memory}, compute_sampler_table{
-                                                                               gpu_memory} {}
-
 template <class P>
 void TextureCache<P>::CreateChannel(struct Tegra::Control::ChannelState& channel) {
-    ASSERT(channel_map.find(channel.bind_id) == channel_map.end() && channel.bind_id >= 0);
-    auto new_id = [this, &channel]() {
-        if (!free_channel_ids.empty()) {
-            auto id = free_channel_ids.front();
-            free_channel_ids.pop_front();
-            new (&channel_storage[id]) ChannelInfo(channel);
-            return id;
-        }
-        channel_storage.emplace_back(channel);
-        return channel_storage.size() - 1;
-    }();
-    channel_map.emplace(channel.bind_id, new_id);
-    if (current_channel_id != UNSET_CHANNEL) {
-        state = &channel_storage[current_channel_id];
-    }
+    VideoCommon::ChannelSetupCaches<TextureCacheChannelInfo>::CreateChannel(channel);
+    const auto it = channel_map.find(channel.bind_id);
+    auto* this_state = &channel_storage[it->second];
+    const auto& this_as_ref = address_spaces[channel.memory_manager->GetID()];
+    this_state->gpu_page_table = &gpu_page_table_storage[this_as_ref.storage_id];
 }
 
 /// Bind a channel for execution.
 template <class P>
-void TextureCache<P>::BindToChannel(s32 id) {
-    auto it = channel_map.find(id);
-    ASSERT(it != channel_map.end() && id >= 0);
-    current_channel_id = it->second;
-    state = &channel_storage[current_channel_id];
-    maxwell3d = &state->maxwell3d;
-    kepler_compute = &state->kepler_compute;
-    gpu_memory = &state->gpu_memory;
-}
-
-/// Erase channel's state.
-template <class P>
-void TextureCache<P>::EraseChannel(s32 id) {
-    const auto it = channel_map.find(id);
-    ASSERT(it != channel_map.end() && id >= 0);
-    const auto this_id = it->second;
-    free_channel_ids.push_back(this_id);
-    channel_map.erase(it);
-    if (this_id == current_channel_id) {
-        current_channel_id = UNSET_CHANNEL;
-        state = nullptr;
-        maxwell3d = nullptr;
-        kepler_compute = nullptr;
-        gpu_memory = nullptr;
-    } else if (current_channel_id != UNSET_CHANNEL) {
-        state = &channel_storage[current_channel_id];
-    }
+void TextureCache<P>::OnGPUASRegister([[maybe_unused]] size_t map_id) {
+    gpu_page_table_storage.emplace_back();
 }
 
 } // namespace VideoCommon

+ 40 - 56
src/video_core/texture_cache/texture_cache_base.h

@@ -1,5 +1,7 @@
-// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
-// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2021 yuzu emulator team
+// (https://github.com/skyline-emu/)
+// SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3
+// or any later version Refer to the license.txt file included.
 
 #pragma once
 
@@ -13,9 +15,11 @@
 #include <queue>
 
 #include "common/common_types.h"
+#include "common/hash.h"
 #include "common/literals.h"
 #include "common/lru_cache.h"
 #include "video_core/compatible_formats.h"
+#include "video_core/control/channel_state_cache.h"
 #include "video_core/delayed_destruction_ring.h"
 #include "video_core/engines/fermi_2d.h"
 #include "video_core/surface.h"
@@ -50,8 +54,35 @@ struct ImageViewInOut {
     ImageViewId id{};
 };
 
+using TextureCacheGPUMap = std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>>;
+
+class TextureCacheChannelInfo : public ChannelInfo {
+public:
+    TextureCacheChannelInfo() = delete;
+    TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept;
+    TextureCacheChannelInfo(const TextureCacheChannelInfo& state) = delete;
+    TextureCacheChannelInfo& operator=(const TextureCacheChannelInfo&) = delete;
+    TextureCacheChannelInfo(TextureCacheChannelInfo&& other) noexcept = default;
+    TextureCacheChannelInfo& operator=(TextureCacheChannelInfo&& other) noexcept = default;
+
+    DescriptorTable<TICEntry> graphics_image_table{gpu_memory};
+    DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory};
+    std::vector<SamplerId> graphics_sampler_ids;
+    std::vector<ImageViewId> graphics_image_view_ids;
+
+    DescriptorTable<TICEntry> compute_image_table{gpu_memory};
+    DescriptorTable<TSCEntry> compute_sampler_table{gpu_memory};
+    std::vector<SamplerId> compute_sampler_ids;
+    std::vector<ImageViewId> compute_image_view_ids;
+
+    std::unordered_map<TICEntry, ImageViewId> image_views;
+    std::unordered_map<TSCEntry, SamplerId> samplers;
+
+    TextureCacheGPUMap* gpu_page_table;
+};
+
 template <class P>
-class TextureCache {
+class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelInfo> {
     /// Address shift for caching images into a hash table
     static constexpr u64 YUZU_PAGEBITS = 20;
 
@@ -85,13 +116,6 @@ class TextureCache {
         PixelFormat src_format;
     };
 
-    template <typename T>
-    struct IdentityHash {
-        [[nodiscard]] size_t operator()(T value) const noexcept {
-            return static_cast<size_t>(value);
-        }
-    };
-
 public:
     explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&);
 
@@ -179,13 +203,7 @@ public:
     [[nodiscard]] bool IsRescaling(const ImageViewBase& image_view) const noexcept;
 
     /// Create channel state.
-    void CreateChannel(struct Tegra::Control::ChannelState& channel);
-
-    /// Bind a channel for execution.
-    void BindToChannel(s32 id);
-
-    /// Erase channel's state.
-    void EraseChannel(s32 id);
+    void CreateChannel(Tegra::Control::ChannelState& channel) final override;
 
     std::mutex mutex;
 
@@ -221,6 +239,8 @@ private:
         }
     }
 
+    void OnGPUASRegister(size_t map_id) final override;
+
     /// Runs the Garbage Collector.
     void RunGarbageCollector();
 
@@ -355,51 +375,15 @@ private:
 
     Runtime& runtime;
 
-    struct ChannelInfo {
-        ChannelInfo() = delete;
-        ChannelInfo(struct Tegra::Control::ChannelState& state) noexcept;
-        ChannelInfo(const ChannelInfo& state) = delete;
-        ChannelInfo& operator=(const ChannelInfo&) = delete;
-        ChannelInfo(ChannelInfo&& other) noexcept = default;
-        ChannelInfo& operator=(ChannelInfo&& other) noexcept = default;
-
-        Tegra::Engines::Maxwell3D& maxwell3d;
-        Tegra::Engines::KeplerCompute& kepler_compute;
-        Tegra::MemoryManager& gpu_memory;
-
-        DescriptorTable<TICEntry> graphics_image_table{gpu_memory};
-        DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory};
-        std::vector<SamplerId> graphics_sampler_ids;
-        std::vector<ImageViewId> graphics_image_view_ids;
-
-        DescriptorTable<TICEntry> compute_image_table{gpu_memory};
-        DescriptorTable<TSCEntry> compute_sampler_table{gpu_memory};
-        std::vector<SamplerId> compute_sampler_ids;
-        std::vector<ImageViewId> compute_image_view_ids;
-
-        std::unordered_map<TICEntry, ImageViewId> image_views;
-        std::unordered_map<TSCEntry, SamplerId> samplers;
-
-        std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> gpu_page_table;
-    };
-
-    std::deque<ChannelInfo> channel_storage;
-    std::deque<size_t> free_channel_ids;
-    std::unordered_map<s32, size_t> channel_map;
-
-    ChannelInfo* state;
-    size_t current_channel_id{UNSET_CHANNEL};
     VideoCore::RasterizerInterface& rasterizer;
-    Tegra::Engines::Maxwell3D* maxwell3d;
-    Tegra::Engines::KeplerCompute* kepler_compute;
-    Tegra::MemoryManager* gpu_memory;
+    std::deque<TextureCacheGPUMap> gpu_page_table_storage;
 
     RenderTargets render_targets;
 
     std::unordered_map<RenderTargets, FramebufferId> framebuffers;
 
-    std::unordered_map<u64, std::vector<ImageMapId>, IdentityHash<u64>> page_table;
-    std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> sparse_page_table;
+    std::unordered_map<u64, std::vector<ImageMapId>, Common::IdentityHash<u64>> page_table;
+    std::unordered_map<u64, std::vector<ImageId>, Common::IdentityHash<u64>> sparse_page_table;
     std::unordered_map<ImageId, std::vector<ImageViewId>> sparse_views;
 
     VAddr virtual_invalid_space{};