shader_cache.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <array>
  6. #include <vector>
  7. #include "common/assert.h"
  8. #include "shader_recompiler/frontend/maxwell/control_flow.h"
  9. #include "shader_recompiler/object_pool.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::lock_guard 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_,
  31. Tegra::MemoryManager& gpu_memory_, Tegra::Engines::Maxwell3D& maxwell3d_,
  32. Tegra::Engines::KeplerCompute& kepler_compute_)
  33. : gpu_memory{gpu_memory_}, maxwell3d{maxwell3d_}, kepler_compute{kepler_compute_},
  34. rasterizer{rasterizer_} {}
  35. bool ShaderCache::RefreshStages(std::array<u64, 6>& unique_hashes) {
  36. auto& dirty{maxwell3d.dirty.flags};
  37. if (!dirty[VideoCommon::Dirty::Shaders]) {
  38. return last_shaders_valid;
  39. }
  40. dirty[VideoCommon::Dirty::Shaders] = false;
  41. const GPUVAddr base_addr{maxwell3d.regs.code_address.CodeAddress()};
  42. for (size_t index = 0; index < Tegra::Engines::Maxwell3D::Regs::MaxShaderProgram; ++index) {
  43. if (!maxwell3d.regs.IsShaderConfigEnabled(index)) {
  44. unique_hashes[index] = 0;
  45. continue;
  46. }
  47. const auto& shader_config{maxwell3d.regs.shader_config[index]};
  48. const auto program{static_cast<Tegra::Engines::Maxwell3D::Regs::ShaderProgram>(index)};
  49. const GPUVAddr shader_addr{base_addr + shader_config.offset};
  50. const std::optional<VAddr> cpu_shader_addr{gpu_memory.GpuToCpuAddress(shader_addr)};
  51. if (!cpu_shader_addr) {
  52. LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr);
  53. last_shaders_valid = false;
  54. return false;
  55. }
  56. const ShaderInfo* shader_info{TryGet(*cpu_shader_addr)};
  57. if (!shader_info) {
  58. const u32 start_address{shader_config.offset};
  59. GraphicsEnvironment env{maxwell3d, gpu_memory, program, base_addr, start_address};
  60. shader_info = MakeShaderInfo(env, *cpu_shader_addr);
  61. }
  62. shader_infos[index] = shader_info;
  63. unique_hashes[index] = shader_info->unique_hash;
  64. }
  65. last_shaders_valid = true;
  66. return true;
  67. }
  68. const ShaderInfo* ShaderCache::ComputeShader() {
  69. const GPUVAddr program_base{kepler_compute.regs.code_loc.Address()};
  70. const auto& qmd{kepler_compute.launch_description};
  71. const GPUVAddr shader_addr{program_base + qmd.program_start};
  72. const std::optional<VAddr> cpu_shader_addr{gpu_memory.GpuToCpuAddress(shader_addr)};
  73. if (!cpu_shader_addr) {
  74. LOG_ERROR(HW_GPU, "Invalid GPU address for shader 0x{:016x}", shader_addr);
  75. return nullptr;
  76. }
  77. if (const ShaderInfo* const shader = TryGet(*cpu_shader_addr)) {
  78. return shader;
  79. }
  80. ComputeEnvironment env{kepler_compute, gpu_memory, program_base, qmd.program_start};
  81. return MakeShaderInfo(env, *cpu_shader_addr);
  82. }
  83. void ShaderCache::GetGraphicsEnvironments(GraphicsEnvironments& result,
  84. const std::array<u64, NUM_PROGRAMS>& unique_hashes) {
  85. size_t env_index{};
  86. const GPUVAddr base_addr{maxwell3d.regs.code_address.CodeAddress()};
  87. for (size_t index = 0; index < NUM_PROGRAMS; ++index) {
  88. if (unique_hashes[index] == 0) {
  89. continue;
  90. }
  91. const auto program{static_cast<Tegra::Engines::Maxwell3D::Regs::ShaderProgram>(index)};
  92. auto& env{result.envs[index]};
  93. const u32 start_address{maxwell3d.regs.shader_config[index].offset};
  94. env = GraphicsEnvironment{maxwell3d, gpu_memory, program, base_addr, start_address};
  95. env.SetCachedSize(shader_infos[index]->size_bytes);
  96. result.env_ptrs[env_index++] = &env;
  97. }
  98. }
  99. ShaderInfo* ShaderCache::TryGet(VAddr addr) const {
  100. std::scoped_lock lock{lookup_mutex};
  101. const auto it = lookup_cache.find(addr);
  102. if (it == lookup_cache.end()) {
  103. return nullptr;
  104. }
  105. return it->second->data;
  106. }
  107. void ShaderCache::Register(std::unique_ptr<ShaderInfo> data, VAddr addr, size_t size) {
  108. std::scoped_lock lock{invalidation_mutex, lookup_mutex};
  109. const VAddr addr_end = addr + size;
  110. Entry* const entry = NewEntry(addr, addr_end, data.get());
  111. const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
  112. for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) {
  113. invalidation_cache[page].push_back(entry);
  114. }
  115. storage.push_back(std::move(data));
  116. rasterizer.UpdatePagesCachedCount(addr, size, 1);
  117. }
  118. void ShaderCache::InvalidatePagesInRegion(VAddr addr, size_t size) {
  119. const VAddr addr_end = addr + size;
  120. const u64 page_end = (addr_end + PAGE_SIZE - 1) >> PAGE_BITS;
  121. for (u64 page = addr >> PAGE_BITS; page < page_end; ++page) {
  122. auto it = invalidation_cache.find(page);
  123. if (it == invalidation_cache.end()) {
  124. continue;
  125. }
  126. InvalidatePageEntries(it->second, addr, addr_end);
  127. }
  128. }
  129. void ShaderCache::RemovePendingShaders() {
  130. if (marked_for_removal.empty()) {
  131. return;
  132. }
  133. // Remove duplicates
  134. std::ranges::sort(marked_for_removal);
  135. marked_for_removal.erase(std::unique(marked_for_removal.begin(), marked_for_removal.end()),
  136. marked_for_removal.end());
  137. std::vector<ShaderInfo*> removed_shaders;
  138. removed_shaders.reserve(marked_for_removal.size());
  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(std::move(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 + PAGE_SIZE - 1) >> PAGE_BITS;
  166. for (u64 page = entry->addr_start >> PAGE_BITS; 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::vector<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.CachedSize();
  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.ReadSize();
  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