diff options
Diffstat (limited to 'src/video_core/shader_cache.h')
| -rw-r--r-- | src/video_core/shader_cache.h | 215 |
1 files changed, 68 insertions, 147 deletions
diff --git a/src/video_core/shader_cache.h b/src/video_core/shader_cache.h index 015a789d6..136fe294c 100644 --- a/src/video_core/shader_cache.h +++ b/src/video_core/shader_cache.h | |||
| @@ -5,226 +5,147 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <algorithm> | 7 | #include <algorithm> |
| 8 | #include <array> | ||
| 8 | #include <memory> | 9 | #include <memory> |
| 9 | #include <mutex> | 10 | #include <mutex> |
| 11 | #include <span> | ||
| 10 | #include <unordered_map> | 12 | #include <unordered_map> |
| 11 | #include <utility> | 13 | #include <utility> |
| 12 | #include <vector> | 14 | #include <vector> |
| 13 | 15 | ||
| 14 | #include "common/assert.h" | ||
| 15 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 16 | #include "video_core/rasterizer_interface.h" | 17 | #include "video_core/rasterizer_interface.h" |
| 18 | #include "video_core/shader_environment.h" | ||
| 19 | |||
| 20 | namespace Tegra { | ||
| 21 | class MemoryManager; | ||
| 22 | } | ||
| 17 | 23 | ||
| 18 | namespace VideoCommon { | 24 | namespace VideoCommon { |
| 19 | 25 | ||
| 20 | template <class T> | 26 | class GenericEnvironment; |
| 27 | |||
| 28 | struct ShaderInfo { | ||
| 29 | u64 unique_hash{}; | ||
| 30 | size_t size_bytes{}; | ||
| 31 | }; | ||
| 32 | |||
| 21 | class ShaderCache { | 33 | class ShaderCache { |
| 22 | static constexpr u64 PAGE_BITS = 14; | 34 | static constexpr u64 PAGE_BITS = 14; |
| 23 | static constexpr u64 PAGE_SIZE = u64(1) << PAGE_BITS; | 35 | static constexpr u64 PAGE_SIZE = u64(1) << PAGE_BITS; |
| 24 | 36 | ||
| 37 | static constexpr size_t NUM_PROGRAMS = 6; | ||
| 38 | |||
| 25 | struct Entry { | 39 | struct Entry { |
| 26 | VAddr addr_start; | 40 | VAddr addr_start; |
| 27 | VAddr addr_end; | 41 | VAddr addr_end; |
| 28 | T* data; | 42 | ShaderInfo* data; |
| 29 | 43 | ||
| 30 | bool is_memory_marked = true; | 44 | bool is_memory_marked = true; |
| 31 | 45 | ||
| 32 | constexpr bool Overlaps(VAddr start, VAddr end) const noexcept { | 46 | bool Overlaps(VAddr start, VAddr end) const noexcept { |
| 33 | return start < addr_end && addr_start < end; | 47 | return start < addr_end && addr_start < end; |
| 34 | } | 48 | } |
| 35 | }; | 49 | }; |
| 36 | 50 | ||
| 37 | public: | 51 | public: |
| 38 | virtual ~ShaderCache() = default; | ||
| 39 | |||
| 40 | /// @brief Removes shaders inside a given region | 52 | /// @brief Removes shaders inside a given region |
| 41 | /// @note Checks for ranges | 53 | /// @note Checks for ranges |
| 42 | /// @param addr Start address of the invalidation | 54 | /// @param addr Start address of the invalidation |
| 43 | /// @param size Number of bytes of the invalidation | 55 | /// @param size Number of bytes of the invalidation |
| 44 | void InvalidateRegion(VAddr addr, std::size_t size) { | 56 | void InvalidateRegion(VAddr addr, size_t size); |
| 45 | std::scoped_lock lock{invalidation_mutex}; | ||
| 46 | InvalidatePagesInRegion(addr, size); | ||
| 47 | RemovePendingShaders(); | ||
| 48 | } | ||
| 49 | 57 | ||
| 50 | /// @brief Unmarks a memory region as cached and marks it for removal | 58 | /// @brief Unmarks a memory region as cached and marks it for removal |
| 51 | /// @param addr Start address of the CPU write operation | 59 | /// @param addr Start address of the CPU write operation |
| 52 | /// @param size Number of bytes of the CPU write operation | 60 | /// @param size Number of bytes of the CPU write operation |
| 53 | void OnCPUWrite(VAddr addr, std::size_t size) { | 61 | void OnCPUWrite(VAddr addr, size_t size); |
| 54 | std::lock_guard lock{invalidation_mutex}; | ||
| 55 | InvalidatePagesInRegion(addr, size); | ||
| 56 | } | ||
| 57 | 62 | ||
| 58 | /// @brief Flushes delayed removal operations | 63 | /// @brief Flushes delayed removal operations |
| 59 | void SyncGuestHost() { | 64 | void SyncGuestHost(); |
| 60 | std::scoped_lock lock{invalidation_mutex}; | ||
| 61 | RemovePendingShaders(); | ||
| 62 | } | ||
| 63 | 65 | ||
| 64 | /// @brief Tries to obtain a cached shader starting in a given address | 66 | protected: |
| 65 | /// @note Doesn't check for ranges, the given address has to be the start of the shader | 67 | struct GraphicsEnvironments { |
| 66 | /// @param addr Start address of the shader, this doesn't cache for region | 68 | std::array<GraphicsEnvironment, NUM_PROGRAMS> envs; |
| 67 | /// @return Pointer to a valid shader, nullptr when nothing is found | 69 | std::array<Shader::Environment*, NUM_PROGRAMS> env_ptrs; |
| 68 | T* TryGet(VAddr addr) const { | ||
| 69 | std::scoped_lock lock{lookup_mutex}; | ||
| 70 | 70 | ||
| 71 | const auto it = lookup_cache.find(addr); | 71 | std::span<Shader::Environment* const> Span() const noexcept { |
| 72 | if (it == lookup_cache.end()) { | 72 | return std::span(env_ptrs.begin(), std::ranges::find(env_ptrs, nullptr)); |
| 73 | return nullptr; | ||
| 74 | } | 73 | } |
| 75 | return it->second->data; | 74 | }; |
| 76 | } | ||
| 77 | |||
| 78 | protected: | ||
| 79 | explicit ShaderCache(VideoCore::RasterizerInterface& rasterizer_) : rasterizer{rasterizer_} {} | ||
| 80 | 75 | ||
| 81 | /// @brief Register in the cache a given entry | 76 | explicit ShaderCache(VideoCore::RasterizerInterface& rasterizer_, |
| 82 | /// @param data Shader to store in the cache | 77 | Tegra::MemoryManager& gpu_memory_, Tegra::Engines::Maxwell3D& maxwell3d_, |
| 83 | /// @param addr Start address of the shader that will be registered | 78 | Tegra::Engines::KeplerCompute& kepler_compute_); |
| 84 | /// @param size Size in bytes of the shader | ||
| 85 | void Register(std::unique_ptr<T> data, VAddr addr, std::size_t size) { | ||
| 86 | std::scoped_lock lock{invalidation_mutex, lookup_mutex}; | ||
| 87 | 79 | ||
| 88 | const VAddr addr_end = addr + size; | 80 | /// @brief Update the hashes and information of shader stages |
| 89 | Entry* const entry = NewEntry(addr, addr_end, data.get()); | 81 | /// @param unique_hashes Shader hashes to store into when a stage is enabled |
| 82 | /// @return True no success, false on error | ||
| 83 | bool RefreshStages(std::array<u64, NUM_PROGRAMS>& unique_hashes); | ||
| 90 | 84 | ||
| 91 | const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS; | 85 | /// @brief Returns information about the current compute shader |
| 92 | for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) { | 86 | /// @return Pointer to a valid shader, nullptr on error |
| 93 | invalidation_cache[page].push_back(entry); | 87 | const ShaderInfo* ComputeShader(); |
| 94 | } | ||
| 95 | 88 | ||
| 96 | storage.push_back(std::move(data)); | 89 | /// @brief Collect the current graphics environments |
| 90 | void GetGraphicsEnvironments(GraphicsEnvironments& result, | ||
| 91 | const std::array<u64, NUM_PROGRAMS>& unique_hashes); | ||
| 97 | 92 | ||
| 98 | rasterizer.UpdatePagesCachedCount(addr, size, 1); | 93 | Tegra::MemoryManager& gpu_memory; |
| 99 | } | 94 | Tegra::Engines::Maxwell3D& maxwell3d; |
| 95 | Tegra::Engines::KeplerCompute& kepler_compute; | ||
| 100 | 96 | ||
| 101 | /// @brief Called when a shader is going to be removed | 97 | std::array<const ShaderInfo*, NUM_PROGRAMS> shader_infos{}; |
| 102 | /// @param shader Shader that will be removed | 98 | bool last_shaders_valid = false; |
| 103 | /// @pre invalidation_cache is locked | ||
| 104 | /// @pre lookup_mutex is locked | ||
| 105 | virtual void OnShaderRemoval([[maybe_unused]] T* shader) {} | ||
| 106 | 99 | ||
| 107 | private: | 100 | private: |
| 101 | /// @brief Tries to obtain a cached shader starting in a given address | ||
| 102 | /// @note Doesn't check for ranges, the given address has to be the start of the shader | ||
| 103 | /// @param addr Start address of the shader, this doesn't cache for region | ||
| 104 | /// @return Pointer to a valid shader, nullptr when nothing is found | ||
| 105 | ShaderInfo* TryGet(VAddr addr) const; | ||
| 106 | |||
| 107 | /// @brief Register in the cache a given entry | ||
| 108 | /// @param data Shader to store in the cache | ||
| 109 | /// @param addr Start address of the shader that will be registered | ||
| 110 | /// @param size Size in bytes of the shader | ||
| 111 | void Register(std::unique_ptr<ShaderInfo> data, VAddr addr, size_t size); | ||
| 112 | |||
| 108 | /// @brief Invalidate pages in a given region | 113 | /// @brief Invalidate pages in a given region |
| 109 | /// @pre invalidation_mutex is locked | 114 | /// @pre invalidation_mutex is locked |
| 110 | void InvalidatePagesInRegion(VAddr addr, std::size_t size) { | 115 | void InvalidatePagesInRegion(VAddr addr, size_t size); |
| 111 | const VAddr addr_end = addr + size; | ||
| 112 | const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS; | ||
| 113 | for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) { | ||
| 114 | auto it = invalidation_cache.find(page); | ||
| 115 | if (it == invalidation_cache.end()) { | ||
| 116 | continue; | ||
| 117 | } | ||
| 118 | InvalidatePageEntries(it->second, addr, addr_end); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | 116 | ||
| 122 | /// @brief Remove shaders marked for deletion | 117 | /// @brief Remove shaders marked for deletion |
| 123 | /// @pre invalidation_mutex is locked | 118 | /// @pre invalidation_mutex is locked |
| 124 | void RemovePendingShaders() { | 119 | void RemovePendingShaders(); |
| 125 | if (marked_for_removal.empty()) { | ||
| 126 | return; | ||
| 127 | } | ||
| 128 | // Remove duplicates | ||
| 129 | std::sort(marked_for_removal.begin(), marked_for_removal.end()); | ||
| 130 | marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()), | ||
| 131 | marked_for_removal.end()); | ||
| 132 | |||
| 133 | std::vector<T*> removed_shaders; | ||
| 134 | removed_shaders.reserve(marked_for_removal.size()); | ||
| 135 | |||
| 136 | std::scoped_lock lock{lookup_mutex}; | ||
| 137 | |||
| 138 | for (Entry* const entry : marked_for_removal) { | ||
| 139 | removed_shaders.push_back(entry->data); | ||
| 140 | |||
| 141 | const auto it = lookup_cache.find(entry->addr_start); | ||
| 142 | ASSERT(it != lookup_cache.end()); | ||
| 143 | lookup_cache.erase(it); | ||
| 144 | } | ||
| 145 | marked_for_removal.clear(); | ||
| 146 | |||
| 147 | if (!removed_shaders.empty()) { | ||
| 148 | RemoveShadersFromStorage(std::move(removed_shaders)); | ||
| 149 | } | ||
| 150 | } | ||
| 151 | 120 | ||
| 152 | /// @brief Invalidates entries in a given range for the passed page | 121 | /// @brief Invalidates entries in a given range for the passed page |
| 153 | /// @param entries Vector of entries in the page, it will be modified on overlaps | 122 | /// @param entries Vector of entries in the page, it will be modified on overlaps |
| 154 | /// @param addr Start address of the invalidation | 123 | /// @param addr Start address of the invalidation |
| 155 | /// @param addr_end Non-inclusive end address of the invalidation | 124 | /// @param addr_end Non-inclusive end address of the invalidation |
| 156 | /// @pre invalidation_mutex is locked | 125 | /// @pre invalidation_mutex is locked |
| 157 | void InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end) { | 126 | void InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end); |
| 158 | std::size_t index = 0; | ||
| 159 | while (index < entries.size()) { | ||
| 160 | Entry* const entry = entries[index]; | ||
| 161 | if (!entry->Overlaps(addr, addr_end)) { | ||
| 162 | ++index; | ||
| 163 | continue; | ||
| 164 | } | ||
| 165 | |||
| 166 | UnmarkMemory(entry); | ||
| 167 | RemoveEntryFromInvalidationCache(entry); | ||
| 168 | marked_for_removal.push_back(entry); | ||
| 169 | } | ||
| 170 | } | ||
| 171 | 127 | ||
| 172 | /// @brief Removes all references to an entry in the invalidation cache | 128 | /// @brief Removes all references to an entry in the invalidation cache |
| 173 | /// @param entry Entry to remove from the invalidation cache | 129 | /// @param entry Entry to remove from the invalidation cache |
| 174 | /// @pre invalidation_mutex is locked | 130 | /// @pre invalidation_mutex is locked |
| 175 | void RemoveEntryFromInvalidationCache(const Entry* entry) { | 131 | void RemoveEntryFromInvalidationCache(const Entry* entry); |
| 176 | const u64 page_end = (entry->addr_end + PAGE_SIZE - 1) >> PAGE_BITS; | ||
| 177 | for (u64 page = entry->addr_start >> PAGE_BITS; page < page_end; ++page) { | ||
| 178 | const auto entries_it = invalidation_cache.find(page); | ||
| 179 | ASSERT(entries_it != invalidation_cache.end()); | ||
| 180 | std::vector<Entry*>& entries = entries_it->second; | ||
| 181 | |||
| 182 | const auto entry_it = std::find(entries.begin(), entries.end(), entry); | ||
| 183 | ASSERT(entry_it != entries.end()); | ||
| 184 | entries.erase(entry_it); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | 132 | ||
| 188 | /// @brief Unmarks an entry from the rasterizer cache | 133 | /// @brief Unmarks an entry from the rasterizer cache |
| 189 | /// @param entry Entry to unmark from memory | 134 | /// @param entry Entry to unmark from memory |
| 190 | void UnmarkMemory(Entry* entry) { | 135 | void UnmarkMemory(Entry* entry); |
| 191 | if (!entry->is_memory_marked) { | ||
| 192 | return; | ||
| 193 | } | ||
| 194 | entry->is_memory_marked = false; | ||
| 195 | |||
| 196 | const VAddr addr = entry->addr_start; | ||
| 197 | const std::size_t size = entry->addr_end - addr; | ||
| 198 | rasterizer.UpdatePagesCachedCount(addr, size, -1); | ||
| 199 | } | ||
| 200 | 136 | ||
| 201 | /// @brief Removes a vector of shaders from a list | 137 | /// @brief Removes a vector of shaders from a list |
| 202 | /// @param removed_shaders Shaders to be removed from the storage | 138 | /// @param removed_shaders Shaders to be removed from the storage |
| 203 | /// @pre invalidation_mutex is locked | 139 | /// @pre invalidation_mutex is locked |
| 204 | /// @pre lookup_mutex is locked | 140 | /// @pre lookup_mutex is locked |
| 205 | void RemoveShadersFromStorage(std::vector<T*> removed_shaders) { | 141 | void RemoveShadersFromStorage(std::vector<ShaderInfo*> removed_shaders); |
| 206 | // Notify removals | ||
| 207 | for (T* const shader : removed_shaders) { | ||
| 208 | OnShaderRemoval(shader); | ||
| 209 | } | ||
| 210 | |||
| 211 | // Remove them from the cache | ||
| 212 | const auto is_removed = [&removed_shaders](const std::unique_ptr<T>& shader) { | ||
| 213 | return std::find(removed_shaders.begin(), removed_shaders.end(), shader.get()) != | ||
| 214 | removed_shaders.end(); | ||
| 215 | }; | ||
| 216 | std::erase_if(storage, is_removed); | ||
| 217 | } | ||
| 218 | 142 | ||
| 219 | /// @brief Creates a new entry in the lookup cache and returns its pointer | 143 | /// @brief Creates a new entry in the lookup cache and returns its pointer |
| 220 | /// @pre lookup_mutex is locked | 144 | /// @pre lookup_mutex is locked |
| 221 | Entry* NewEntry(VAddr addr, VAddr addr_end, T* data) { | 145 | Entry* NewEntry(VAddr addr, VAddr addr_end, ShaderInfo* data); |
| 222 | auto entry = std::make_unique<Entry>(Entry{addr, addr_end, data}); | ||
| 223 | Entry* const entry_pointer = entry.get(); | ||
| 224 | 146 | ||
| 225 | lookup_cache.emplace(addr, std::move(entry)); | 147 | /// @brief Create a new shader entry and register it |
| 226 | return entry_pointer; | 148 | const ShaderInfo* MakeShaderInfo(GenericEnvironment& env, VAddr cpu_addr); |
| 227 | } | ||
| 228 | 149 | ||
| 229 | VideoCore::RasterizerInterface& rasterizer; | 150 | VideoCore::RasterizerInterface& rasterizer; |
| 230 | 151 | ||
| @@ -233,7 +154,7 @@ private: | |||
| 233 | 154 | ||
| 234 | std::unordered_map<u64, std::unique_ptr<Entry>> lookup_cache; | 155 | std::unordered_map<u64, std::unique_ptr<Entry>> lookup_cache; |
| 235 | std::unordered_map<u64, std::vector<Entry*>> invalidation_cache; | 156 | std::unordered_map<u64, std::vector<Entry*>> invalidation_cache; |
| 236 | std::vector<std::unique_ptr<T>> storage; | 157 | std::vector<std::unique_ptr<ShaderInfo>> storage; |
| 237 | std::vector<Entry*> marked_for_removal; | 158 | std::vector<Entry*> marked_for_removal; |
| 238 | }; | 159 | }; |
| 239 | 160 | ||