shader_cache.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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::OnCPUWrite(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. const GPUVAddr shader_addr{base_addr + shader_config.offset};
  46. const std::optional<VAddr> cpu_shader_addr{gpu_memory->GpuToCpuAddress(shader_addr)};
  47. if (!cpu_shader_addr) {
  48. LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr);
  49. last_shaders_valid = false;
  50. return false;
  51. }
  52. const ShaderInfo* shader_info{TryGet(*cpu_shader_addr)};
  53. if (!shader_info) {
  54. const u32 start_address{shader_config.offset};
  55. GraphicsEnvironment env{*maxwell3d, *gpu_memory, program, base_addr, start_address};
  56. shader_info = MakeShaderInfo(env, *cpu_shader_addr);
  57. }
  58. shader_infos[index] = shader_info;
  59. unique_hashes[index] = shader_info->unique_hash;
  60. }
  61. last_shaders_valid = true;
  62. return true;
  63. }
  64. const ShaderInfo* ShaderCache::ComputeShader() {
  65. const GPUVAddr program_base{kepler_compute->regs.code_loc.Address()};
  66. const auto& qmd{kepler_compute->launch_description};
  67. const GPUVAddr shader_addr{program_base + qmd.program_start};
  68. const std::optional<VAddr> cpu_shader_addr{gpu_memory->GpuToCpuAddress(shader_addr)};
  69. if (!cpu_shader_addr) {
  70. LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr);
  71. return nullptr;
  72. }
  73. if (const ShaderInfo* const shader = TryGet(*cpu_shader_addr)) {
  74. return shader;
  75. }
  76. ComputeEnvironment env{*kepler_compute, *gpu_memory, program_base, qmd.program_start};
  77. return MakeShaderInfo(env, *cpu_shader_addr);
  78. }
  79. void ShaderCache::GetGraphicsEnvironments(GraphicsEnvironments& result,
  80. const std::array<u64, NUM_PROGRAMS>& unique_hashes) {
  81. size_t env_index{};
  82. const GPUVAddr base_addr{maxwell3d->regs.program_region.Address()};
  83. for (size_t index = 0; index < NUM_PROGRAMS; ++index) {
  84. if (unique_hashes[index] == 0) {
  85. continue;
  86. }
  87. const auto program{static_cast<Tegra::Engines::Maxwell3D::Regs::ShaderType>(index)};
  88. auto& env{result.envs[index]};
  89. const u32 start_address{maxwell3d->regs.pipelines[index].offset};
  90. env = GraphicsEnvironment{*maxwell3d, *gpu_memory, program, base_addr, start_address};
  91. env.SetCachedSize(shader_infos[index]->size_bytes);
  92. result.env_ptrs[env_index++] = &env;
  93. }
  94. }
  95. ShaderInfo* ShaderCache::TryGet(VAddr addr) const {
  96. std::scoped_lock lock{lookup_mutex};
  97. const auto it = lookup_cache.find(addr);
  98. if (it == lookup_cache.end()) {
  99. return nullptr;
  100. }
  101. return it->second->data;
  102. }
  103. void ShaderCache::Register(std::unique_ptr<ShaderInfo> data, VAddr addr, size_t size) {
  104. std::scoped_lock lock{invalidation_mutex, lookup_mutex};
  105. const VAddr addr_end = addr + size;
  106. Entry* const entry = NewEntry(addr, addr_end, data.get());
  107. const u64 page_end = (addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS;
  108. for (u64 page = addr >> YUZU_PAGEBITS; page < page_end; ++page) {
  109. invalidation_cache[page].push_back(entry);
  110. }
  111. storage.push_back(std::move(data));
  112. rasterizer.UpdatePagesCachedCount(addr, size, 1);
  113. }
  114. void ShaderCache::InvalidatePagesInRegion(VAddr addr, size_t size) {
  115. const VAddr addr_end = addr + size;
  116. const u64 page_end = (addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS;
  117. for (u64 page = addr >> YUZU_PAGEBITS; page < page_end; ++page) {
  118. auto it = invalidation_cache.find(page);
  119. if (it == invalidation_cache.end()) {
  120. continue;
  121. }
  122. InvalidatePageEntries(it->second, addr, addr_end);
  123. }
  124. }
  125. void ShaderCache::RemovePendingShaders() {
  126. if (marked_for_removal.empty()) {
  127. return;
  128. }
  129. // Remove duplicates
  130. std::ranges::sort(marked_for_removal);
  131. marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()),
  132. marked_for_removal.end());
  133. std::vector<ShaderInfo*> removed_shaders;
  134. removed_shaders.reserve(marked_for_removal.size());
  135. std::scoped_lock lock{lookup_mutex};
  136. for (Entry* const entry : marked_for_removal) {
  137. removed_shaders.push_back(entry->data);
  138. const auto it = lookup_cache.find(entry->addr_start);
  139. ASSERT(it != lookup_cache.end());
  140. lookup_cache.erase(it);
  141. }
  142. marked_for_removal.clear();
  143. if (!removed_shaders.empty()) {
  144. RemoveShadersFromStorage(removed_shaders);
  145. }
  146. }
  147. void ShaderCache::InvalidatePageEntries(std::vector<Entry*>& entries, VAddr addr, VAddr addr_end) {
  148. size_t index = 0;
  149. while (index < entries.size()) {
  150. Entry* const entry = entries[index];
  151. if (!entry->Overlaps(addr, addr_end)) {
  152. ++index;
  153. continue;
  154. }
  155. UnmarkMemory(entry);
  156. RemoveEntryFromInvalidationCache(entry);
  157. marked_for_removal.push_back(entry);
  158. }
  159. }
  160. void ShaderCache::RemoveEntryFromInvalidationCache(const Entry* entry) {
  161. const u64 page_end = (entry->addr_end + YUZU_PAGESIZE - 1) >> YUZU_PAGEBITS;
  162. for (u64 page = entry->addr_start >> YUZU_PAGEBITS; page < page_end; ++page) {
  163. const auto entries_it = invalidation_cache.find(page);
  164. ASSERT(entries_it != invalidation_cache.end());
  165. std::vector<Entry*>& entries = entries_it->second;
  166. const auto entry_it = std::ranges::find(entries, entry);
  167. ASSERT(entry_it != entries.end());
  168. entries.erase(entry_it);
  169. }
  170. }
  171. void ShaderCache::UnmarkMemory(Entry* entry) {
  172. if (!entry->is_memory_marked) {
  173. return;
  174. }
  175. entry->is_memory_marked = false;
  176. const VAddr addr = entry->addr_start;
  177. const size_t size = entry->addr_end - addr;
  178. rasterizer.UpdatePagesCachedCount(addr, size, -1);
  179. }
  180. void ShaderCache::RemoveShadersFromStorage(std::span<ShaderInfo*> removed_shaders) {
  181. // Remove them from the cache
  182. std::erase_if(storage, [&removed_shaders](const std::unique_ptr<ShaderInfo>& shader) {
  183. return std::ranges::find(removed_shaders, shader.get()) != removed_shaders.end();
  184. });
  185. }
  186. ShaderCache::Entry* ShaderCache::NewEntry(VAddr addr, VAddr addr_end, ShaderInfo* data) {
  187. auto entry = std::make_unique<Entry>(Entry{addr, addr_end, data});
  188. Entry* const entry_pointer = entry.get();
  189. lookup_cache.emplace(addr, std::move(entry));
  190. return entry_pointer;
  191. }
  192. const ShaderInfo* ShaderCache::MakeShaderInfo(GenericEnvironment& env, VAddr cpu_addr) {
  193. auto info = std::make_unique<ShaderInfo>();
  194. if (const std::optional<u64> cached_hash{env.Analyze()}) {
  195. info->unique_hash = *cached_hash;
  196. info->size_bytes = env.CachedSizeBytes();
  197. } else {
  198. // Slow path, not really hit on commercial games
  199. // Build a control flow graph to get the real shader size
  200. Shader::ObjectPool<Shader::Maxwell::Flow::Block> flow_block;
  201. Shader::Maxwell::Flow::CFG cfg{env, flow_block, env.StartAddress()};
  202. info->unique_hash = env.CalculateHash();
  203. info->size_bytes = env.ReadSizeBytes();
  204. }
  205. const size_t size_bytes{info->size_bytes};
  206. const ShaderInfo* const result{info.get()};
  207. Register(std::move(info), cpu_addr, size_bytes);
  208. return result;
  209. }
  210. } // namespace VideoCommon