shader_cache.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <algorithm>
  4. #include <array>
  5. #include <vector>
  6. #include "common/assert.h"
  7. #include "shader_recompiler/frontend/maxwell/control_flow.h"
  8. #include "shader_recompiler/object_pool.h"
  9. #include "video_core/control/channel_state.h"
  10. #include "video_core/dirty_flags.h"
  11. #include "video_core/engines/kepler_compute.h"
  12. #include "video_core/engines/maxwell_3d.h"
  13. #include "video_core/memory_manager.h"
  14. #include "video_core/shader_cache.h"
  15. #include "video_core/shader_environment.h"
  16. namespace VideoCommon {
  17. void ShaderCache::InvalidateRegion(VAddr addr, size_t size) {
  18. std::scoped_lock lock{invalidation_mutex};
  19. InvalidatePagesInRegion(addr, size);
  20. RemovePendingShaders();
  21. }
  22. void ShaderCache::OnCacheInvalidation(VAddr addr, size_t size) {
  23. std::scoped_lock lock{invalidation_mutex};
  24. InvalidatePagesInRegion(addr, size);
  25. }
  26. void ShaderCache::SyncGuestHost() {
  27. std::scoped_lock lock{invalidation_mutex};
  28. RemovePendingShaders();
  29. }
  30. ShaderCache::ShaderCache(VideoCore::RasterizerInterface& rasterizer_) : rasterizer{rasterizer_} {}
  31. bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) {
  32. auto& dirty{maxwell3d->dirty.flags};
  33. if (!dirty[VideoCommon::Dirty::Shaders]) {
  34. return last_shaders_valid;
  35. }
  36. dirty[VideoCommon::Dirty::Shaders] = false;
  37. const GPUVAddr base_addr{maxwell3d->regs.program_region.Address()};
  38. for (size_t index = 0; index < Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; ++index) {
  39. if (!maxwell3d->regs.IsShaderConfigEnabled(index)) {
  40. unique_hashes[index] = 0;
  41. continue;
  42. }
  43. const auto& shader_config{maxwell3d->regs.pipelines[index]};
  44. const auto program{static_cast<Tegra::Engines::Maxwell3D::Regs::ShaderType>(index)};
  45. if (program == Tegra::Engines::Maxwell3D::Regs::ShaderType::Pixel &&
  46. !maxwell3d->regs.rasterize_enable) {
  47. unique_hashes[index] = 0;
  48. continue;
  49. }
  50. const GPUVAddr shader_addr{base_addr + shader_config.offset};
  51. const std::optional<VAddr> cpu_shader_addr{gpu_memory->GpuToCpuAddress(shader_addr)};
  52. if (!cpu_shader_addr) {
  53. LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr);
  54. last_shaders_valid = false;
  55. return false;
  56. }
  57. const ShaderInfo* shader_info{TryGet(*cpu_shader_addr)};
  58. if (!shader_info) {
  59. const u32 start_address{shader_config.offset};
  60. GraphicsEnvironment env{*maxwell3d, *gpu_memory, program, base_addr, start_address};
  61. shader_info = MakeShaderInfo(env, *cpu_shader_addr);
  62. }
  63. shader_infos[index] = shader_info;
  64. unique_hashes[index] = shader_info->unique_hash;
  65. }
  66. last_shaders_valid = true;
  67. return true;
  68. }
  69. const ShaderInfo* ShaderCache::ComputeShader() {
  70. const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()};
  71. const auto& qmd{kepler_compute->launch_description};
  72. const GPUVAddr shader_addr{program_base + qmd.program_start};
  73. const std::optional<VAddr> cpu_shader_addr{gpu_memory->GpuToCpuAddress(shader_addr)};
  74. if (!cpu_shader_addr) {
  75. LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr);
  76. return nullptr;
  77. }
  78. if (const ShaderInfo* const shader = TryGet(*cpu_shader_addr)) {
  79. return shader;
  80. }
  81. ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
  82. return MakeShaderInfo(env, *cpu_shader_addr);
  83. }
  84. void ShaderCache::GetGraphicsEnvironments(GraphicsEnvironments& result,
  85. const std::array<u64, NUM_PROGRAMS>& unique_hashes) {
  86. size_t env_index{};
  87. const GPUVAddr base_addr{maxwell3d->regs.program_region.Address()};
  88. for (size_t index = 0; index < NUM_PROGRAMS; ++index) {
  89. if (unique_hashes[index] == 0) {
  90. continue;
  91. }
  92. const auto program{static_cast<Tegra::Engines::Maxwell3D::Regs::ShaderType>(index)};
  93. auto& env{result.envs[index]};
  94. const u32 start_address{maxwell3d->regs.pipelines[index].offset};
  95. env = GraphicsEnvironment{*maxwell3d, *gpu_memory, program, base_addr, start_address};
  96. env.SetCachedSize(shader_infos[index]->size_bytes);
  97. result.env_ptrs[env_index++] = &env;
  98. }
  99. }
  100. ShaderInfo* ShaderCache::TryGet(VAddr addr) const {
  101. std::scoped_lock lock{lookup_mutex};
  102. const auto it = lookup_cache.find(addr);
  103. if (it == lookup_cache.end()) {
  104. return nullptr;
  105. }
  106. return it->second->data;
  107. }
  108. void ShaderCache::Register(std::unique_ptr<ShaderInfo> data, VAddr addr, size_t size) {
  109. std::scoped_lock lock{invalidation_mutex, lookup_mutex};
  110. const VAddr addr_end = addr + size;
  111. Entry* const entry = NewEntry(addr, addr_end, data.get());
  112. const u64 page_end = (addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS;
  113. for (u64 page = addr >> YUZU_PAGEBITS; page < page_end; ++page) {
  114. invalidation_cache[page].push_back(entry);
  115. }
  116. storage.push_back(std::move(data));
  117. rasterizer.UpdatePagesCachedCount(addr, size, 1);
  118. }
  119. void ShaderCache::InvalidatePagesInRegion(VAddr addr, size_t size) {
  120. const VAddr addr_end = addr + size;
  121. const u64 page_end = (addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS;
  122. for (u64 page = addr >> YUZU_PAGEBITS; page < page_end; ++page) {
  123. auto it = invalidation_cache.find(page);
  124. if (it == invalidation_cache.end()) {
  125. continue;
  126. }
  127. InvalidatePageEntries(it->second, addr, addr_end);
  128. }
  129. }
  130. void ShaderCache::RemovePendingShaders() {
  131. if (marked_for_removal.empty()) {
  132. return;
  133. }
  134. // Remove duplicates
  135. std::ranges::sort(marked_for_removal);
  136. marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()),
  137. marked_for_removal.end());
  138. boost::container::small_vector<ShaderInfo*, 16> removed_shaders;
  139. std::scoped_lock lock{lookup_mutex};
  140. for (Entry* const entry : marked_for_removal) {
  141. removed_shaders.push_back(entry->data);
  142. const auto it = lookup_cache.find(entry->addr_start);
  143. ASSERT(it != lookup_cache.end());
  144. lookup_cache.erase(it);
  145. }
  146. marked_for_removal.clear();
  147. if (!removed_shaders.empty()) {
  148. RemoveShadersFromStorage(removed_shaders);
  149. }
  150. }
  151. void ShaderCache::InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end) {
  152. size_t index = 0;
  153. while (index < entries.size()) {
  154. Entry* const entry = entries[index];
  155. if (!entry->Overlaps(addr, addr_end)) {
  156. ++index;
  157. continue;
  158. }
  159. UnmarkMemory(entry);
  160. RemoveEntryFromInvalidationCache(entry);
  161. marked_for_removal.push_back(entry);
  162. }
  163. }
  164. void ShaderCache::RemoveEntryFromInvalidationCache(const Entry* entry) {
  165. const u64 page_end = (entry->addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS;
  166. for (u64 page = entry->addr_start >> YUZU_PAGEBITS; page < page_end; ++page) {
  167. const auto entries_it = invalidation_cache.find(page);
  168. ASSERT(entries_it != invalidation_cache.end());
  169. std::vector<Entry*>& entries = entries_it->second;
  170. const auto entry_it = std::ranges::find(entries, entry);
  171. ASSERT(entry_it != entries.end());
  172. entries.erase(entry_it);
  173. }
  174. }
  175. void ShaderCache::UnmarkMemory(Entry* entry) {
  176. if (!entry->is_memory_marked) {
  177. return;
  178. }
  179. entry->is_memory_marked = false;
  180. const VAddr addr = entry->addr_start;
  181. const size_t size = entry->addr_end - addr;
  182. rasterizer.UpdatePagesCachedCount(addr, size, -1);
  183. }
  184. void ShaderCache::RemoveShadersFromStorage(std::span<ShaderInfo*> removed_shaders) {
  185. // Remove them from the cache
  186. std::erase_if(storage, [&removed_shaders](const std::unique_ptr<ShaderInfo>& shader) {
  187. return std::ranges::find(removed_shaders, shader.get()) != removed_shaders.end();
  188. });
  189. }
  190. ShaderCache::Entry* ShaderCache::NewEntry(VAddr addr, VAddr addr_end, ShaderInfo* data) {
  191. auto entry = std::make_unique<Entry>(Entry{addr, addr_end, data});
  192. Entry* const entry_pointer = entry.get();
  193. lookup_cache.emplace(addr, std::move(entry));
  194. return entry_pointer;
  195. }
  196. const ShaderInfo* ShaderCache::MakeShaderInfo(GenericEnvironment& env, VAddr cpu_addr) {
  197. auto info = std::make_unique<ShaderInfo>();
  198. if (const std::optional<u64> cached_hash{env.Analyze()}) {
  199. info->unique_hash = *cached_hash;
  200. info->size_bytes = env.CachedSizeBytes();
  201. } else {
  202. // Slow path, not really hit on commercial games
  203. // Build a control flow graph to get the real shader size
  204. Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block;
  205. Shader::Maxwell::Flow::CFG cfg{env, flow_block, env.StartAddress()};
  206. info->unique_hash = env.CalculateHash();
  207. info->size_bytes = env.ReadSizeBytes();
  208. }
  209. const size_t size_bytes{info->size_bytes};
  210. const ShaderInfo* const result{info.get()};
  211. Register(std::move(info), cpu_addr, size_bytes);
  212. return result;
  213. }
  214. } // namespace VideoCommon