shader_cache.cpp 9.1 KB

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