texture_cache.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. // Copyright 2019 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <list>
  6. #include <memory>
  7. #include <set>
  8. #include <tuple>
  9. #include <unordered_map>
  10. #include <boost/icl/interval_map.hpp>
  11. #include <boost/range/iterator_range.hpp>
  12. #include "common/assert.h"
  13. #include "common/common_types.h"
  14. #include "core/memory.h"
  15. #include "video_core/engines/fermi_2d.h"
  16. #include "video_core/engines/maxwell_3d.h"
  17. #include "video_core/gpu.h"
  18. #include "video_core/memory_manager.h"
  19. #include "video_core/rasterizer_interface.h"
  20. #include "video_core/surface.h"
  21. #include "video_core/texture_cache/copy_params.h"
  22. #include "video_core/texture_cache/surface_base.h"
  23. #include "video_core/texture_cache/surface_params.h"
  24. #include "video_core/texture_cache/surface_view.h"
  25. namespace Core {
  26. class System;
  27. }
  28. namespace Tegra::Texture {
  29. struct FullTextureInfo;
  30. }
  31. namespace VideoCore {
  32. class RasterizerInterface;
  33. }
  34. namespace VideoCommon {
  35. using VideoCore::Surface::SurfaceTarget;
  36. using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig;
  37. template <typename TSurface, typename TView>
  38. class TextureCache {
  39. using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface>>;
  40. using IntervalType = typename IntervalMap::interval_type;
  41. public:
  42. void InitMemoryMananger(Tegra::MemoryManager& memory_manager) {
  43. this->memory_manager = &memory_manager;
  44. }
  45. void InvalidateRegion(CacheAddr addr, std::size_t size) {
  46. for (const auto& surface : GetSurfacesInRegion(addr, size)) {
  47. Unregister(surface);
  48. }
  49. }
  50. TView GetTextureSurface(const Tegra::Texture::FullTextureInfo& config,
  51. const VideoCommon::Shader::Sampler& entry) {
  52. const auto gpu_addr{config.tic.Address()};
  53. if (!gpu_addr) {
  54. return {};
  55. }
  56. const auto params{SurfaceParams::CreateForTexture(system, config, entry)};
  57. return GetSurface(gpu_addr, params, true).second;
  58. }
  59. TView GetDepthBufferSurface(bool preserve_contents) {
  60. const auto& regs{system.GPU().Maxwell3D().regs};
  61. const auto gpu_addr{regs.zeta.Address()};
  62. if (!gpu_addr || !regs.zeta_enable) {
  63. return {};
  64. }
  65. const auto depth_params{SurfaceParams::CreateForDepthBuffer(
  66. system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
  67. regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
  68. regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
  69. auto surface_view = GetSurface(gpu_addr, depth_params, preserve_contents);
  70. if (depth_buffer.target)
  71. depth_buffer.target->MarkAsProtected(false);
  72. if (depth_buffer.target)
  73. depth_buffer.target->MarkAsProtected(true);
  74. return surface_view.second;
  75. }
  76. TView GetColorBufferSurface(std::size_t index, bool preserve_contents) {
  77. ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
  78. const auto& regs{system.GPU().Maxwell3D().regs};
  79. if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
  80. regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
  81. SetEmptyColorBuffer(index);
  82. return {};
  83. }
  84. const auto& config{regs.rt[index]};
  85. const auto gpu_addr{config.Address()};
  86. if (!gpu_addr) {
  87. SetEmptyColorBuffer(index);
  88. return {};
  89. }
  90. auto surface_view = GetSurface(gpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
  91. preserve_contents);
  92. if (render_targets[index].target)
  93. render_targets[index].target->MarkAsProtected(false);
  94. render_targets[index].target = surface_view.first;
  95. if (render_targets[index].target)
  96. render_targets[index].target->MarkAsProtected(true);
  97. return surface_view.second;
  98. }
  99. void MarkColorBufferInUse(std::size_t index) {
  100. if (render_targets[index].target)
  101. render_targets[index].target->MarkAsModified(true, Tick());
  102. }
  103. void MarkDepthBufferInUse() {
  104. if (depth_buffer.target)
  105. depth_buffer.target->MarkAsModified(true, Tick());
  106. }
  107. void SetEmptyDepthBuffer() {
  108. if (depth_buffer.target != nullptr) {
  109. depth_buffer.target->MarkAsProtected(false);
  110. depth_buffer.target = nullptr;
  111. depth_buffer.view = nullptr;
  112. }
  113. }
  114. void SetEmptyColorBuffer(std::size_t index) {
  115. if (render_targets[index].target != nullptr) {
  116. render_targets[index].target->MarkAsProtected(false);
  117. std::memset(&render_targets[index].config, sizeof(RenderTargetConfig), 0);
  118. render_targets[index].target = nullptr;
  119. render_targets[index].view = nullptr;
  120. }
  121. }
  122. TView GetFermiSurface(const Tegra::Engines::Fermi2D::Regs::Surface& config) {
  123. SurfaceParams params = SurfaceParams::CreateForFermiCopySurface(config);
  124. const GPUVAddr gpu_addr = config.Address();
  125. return GetSurface(gpu_addr, params, true).second;
  126. }
  127. TSurface TryFindFramebufferSurface(const u8* host_ptr) {
  128. const CacheAddr cache_addr = ToCacheAddr(host_ptr);
  129. if (!cache_addr) {
  130. return nullptr;
  131. }
  132. const CacheAddr page = cache_addr >> registry_page_bits;
  133. std::list<TSurface>& list = registry[page];
  134. for (auto& s : list) {
  135. if (s->GetCacheAddr() == cache_addr) {
  136. return s;
  137. }
  138. }
  139. return nullptr;
  140. }
  141. u64 Tick() {
  142. return ++ticks;
  143. }
  144. protected:
  145. TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
  146. : system{system}, rasterizer{rasterizer} {
  147. for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
  148. SetEmptyColorBuffer(i);
  149. }
  150. SetEmptyDepthBuffer();
  151. }
  152. ~TextureCache() = default;
  153. virtual TSurface CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) = 0;
  154. virtual void ImageCopy(TSurface src_surface, TSurface dst_surface,
  155. const CopyParams& copy_params) = 0;
  156. void Register(TSurface surface) {
  157. const GPUVAddr gpu_addr = surface->GetGpuAddr();
  158. const CacheAddr cache_ptr = ToCacheAddr(memory_manager->GetPointer(gpu_addr));
  159. const std::size_t size = surface->GetSizeInBytes();
  160. const std::optional<VAddr> cpu_addr = memory_manager->GpuToCpuAddress(gpu_addr);
  161. if (!cache_ptr || !cpu_addr) {
  162. LOG_CRITICAL(HW_GPU, "Failed to register surface with unmapped gpu_address 0x{:016x}",
  163. gpu_addr);
  164. return;
  165. }
  166. surface->SetCacheAddr(cache_ptr);
  167. surface->SetCpuAddr(*cpu_addr);
  168. RegisterInnerCache(surface);
  169. surface->MarkAsRegistered(true);
  170. rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1);
  171. }
  172. void Unregister(TSurface surface) {
  173. if (surface->IsProtected())
  174. return;
  175. const GPUVAddr gpu_addr = surface->GetGpuAddr();
  176. const CacheAddr cache_ptr = surface->GetCacheAddr();
  177. const std::size_t size = surface->GetSizeInBytes();
  178. const VAddr cpu_addr = surface->GetCpuAddr();
  179. rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1);
  180. UnregisterInnerCache(surface);
  181. surface->MarkAsRegistered(false);
  182. ReserveSurface(surface->GetSurfaceParams(), surface);
  183. }
  184. TSurface GetUncachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) {
  185. if (const auto surface = TryGetReservedSurface(params); surface) {
  186. surface->SetGpuAddr(gpu_addr);
  187. return surface;
  188. }
  189. // No reserved surface available, create a new one and reserve it
  190. auto new_surface{CreateSurface(gpu_addr, params)};
  191. return new_surface;
  192. }
  193. Core::System& system;
  194. private:
  195. enum class RecycleStrategy : u32 {
  196. Ignore = 0,
  197. Flush = 1,
  198. BufferCopy = 3,
  199. };
  200. RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params,
  201. const GPUVAddr gpu_addr, const bool untopological) {
  202. // Untopological decision
  203. if (untopological) {
  204. return RecycleStrategy::Ignore;
  205. }
  206. // 3D Textures decision
  207. if (params.block_depth > 1 || params.target == SurfaceTarget::Texture3D) {
  208. return RecycleStrategy::Flush;
  209. }
  210. for (auto s : overlaps) {
  211. const auto& s_params = s->GetSurfaceParams();
  212. if (s_params.block_depth > 1 || s_params.target == SurfaceTarget::Texture3D) {
  213. return RecycleStrategy::Flush;
  214. }
  215. }
  216. return RecycleStrategy::Ignore;
  217. }
  218. std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps,
  219. const SurfaceParams& params, const GPUVAddr gpu_addr,
  220. const u8* host_ptr, const bool preserve_contents,
  221. const bool untopological) {
  222. for (auto surface : overlaps) {
  223. Unregister(surface);
  224. }
  225. RecycleStrategy strategy = !Settings::values.use_accurate_gpu_emulation
  226. ? PickStrategy(overlaps, params, gpu_addr, untopological)
  227. : RecycleStrategy::Flush;
  228. switch (strategy) {
  229. case RecycleStrategy::Ignore: {
  230. return InitializeSurface(gpu_addr, params, preserve_contents);
  231. }
  232. case RecycleStrategy::Flush: {
  233. std::sort(overlaps.begin(), overlaps.end(),
  234. [](const TSurface& a, const TSurface& b) -> bool {
  235. return a->GetModificationTick() < b->GetModificationTick();
  236. });
  237. for (auto surface : overlaps) {
  238. FlushSurface(surface);
  239. }
  240. return InitializeSurface(gpu_addr, params, preserve_contents);
  241. }
  242. default: {
  243. UNIMPLEMENTED_MSG("Unimplemented Texture Cache Recycling Strategy!");
  244. return InitializeSurface(gpu_addr, params, preserve_contents);
  245. }
  246. }
  247. }
  248. std::pair<TSurface, TView> RebuildSurface(TSurface current_surface,
  249. const SurfaceParams& params) {
  250. const auto gpu_addr = current_surface->GetGpuAddr();
  251. TSurface new_surface = GetUncachedSurface(gpu_addr, params);
  252. std::vector<CopyParams> bricks = current_surface->BreakDown(params);
  253. for (auto& brick : bricks) {
  254. ImageCopy(current_surface, new_surface, brick);
  255. }
  256. Unregister(current_surface);
  257. Register(new_surface);
  258. return {new_surface, new_surface->GetMainView()};
  259. }
  260. std::pair<TSurface, TView> ManageStructuralMatch(TSurface current_surface,
  261. const SurfaceParams& params) {
  262. const bool is_mirage = !current_surface->MatchFormat(params.pixel_format);
  263. if (is_mirage) {
  264. return RebuildSurface(current_surface, params);
  265. }
  266. const bool matches_target = current_surface->MatchTarget(params.target);
  267. if (matches_target) {
  268. return {current_surface, current_surface->GetMainView()};
  269. }
  270. return {current_surface, current_surface->EmplaceOverview(params)};
  271. }
  272. std::optional<std::pair<TSurface, TView>> ReconstructSurface(std::vector<TSurface>& overlaps,
  273. const SurfaceParams& params,
  274. const GPUVAddr gpu_addr,
  275. const u8* host_ptr) {
  276. if (!params.is_layered || params.target == SurfaceTarget::Texture3D) {
  277. return {};
  278. }
  279. TSurface new_surface = GetUncachedSurface(gpu_addr, params);
  280. for (auto surface : overlaps) {
  281. const SurfaceParams& src_params = surface->GetSurfaceParams();
  282. if (src_params.is_layered || src_params.num_levels > 1) {
  283. // We send this cases to recycle as they are more complex to handle
  284. return {};
  285. }
  286. const std::size_t candidate_size = src_params.GetGuestSizeInBytes();
  287. auto mipmap_layer{new_surface->GetLayerMipmap(surface->GetGpuAddr())};
  288. if (!mipmap_layer) {
  289. return {};
  290. }
  291. const u32 layer{mipmap_layer->first};
  292. const u32 mipmap{mipmap_layer->second};
  293. if (new_surface->GetMipmapSize(mipmap) != candidate_size) {
  294. return {};
  295. }
  296. // Now we got all the data set up
  297. const u32 dst_width{params.GetMipWidth(mipmap)};
  298. const u32 dst_height{params.GetMipHeight(mipmap)};
  299. const CopyParams copy_params(0, 0, 0, 0, 0, layer, 0, mipmap,
  300. std::min(src_params.width, dst_width),
  301. std::min(src_params.height, dst_height), 1);
  302. ImageCopy(surface, new_surface, copy_params);
  303. }
  304. for (auto surface : overlaps) {
  305. Unregister(surface);
  306. }
  307. Register(new_surface);
  308. return {{new_surface, new_surface->GetMainView()}};
  309. }
  310. std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const SurfaceParams& params,
  311. bool preserve_contents) {
  312. const auto host_ptr{memory_manager->GetPointer(gpu_addr)};
  313. const auto cache_addr{ToCacheAddr(host_ptr)};
  314. const std::size_t candidate_size = params.GetGuestSizeInBytes();
  315. auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)};
  316. if (overlaps.empty()) {
  317. return InitializeSurface(gpu_addr, params, preserve_contents);
  318. }
  319. for (auto surface : overlaps) {
  320. if (!surface->MatchesTopology(params)) {
  321. return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents,
  322. true);
  323. }
  324. }
  325. if (overlaps.size() == 1) {
  326. TSurface current_surface = overlaps[0];
  327. MatchStructureResult s_result = current_surface->MatchesStructure(params);
  328. if (s_result != MatchStructureResult::None &&
  329. current_surface->GetGpuAddr() == gpu_addr &&
  330. (params.target != SurfaceTarget::Texture3D ||
  331. current_surface->MatchTarget(params.target))) {
  332. if (s_result == MatchStructureResult::FullMatch) {
  333. return ManageStructuralMatch(current_surface, params);
  334. } else {
  335. return RebuildSurface(current_surface, params);
  336. }
  337. }
  338. if (current_surface->GetSizeInBytes() <= candidate_size) {
  339. return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents,
  340. false);
  341. }
  342. std::optional<TView> view = current_surface->EmplaceView(params, gpu_addr);
  343. if (view.has_value()) {
  344. const bool is_mirage = !current_surface->MatchFormat(params.pixel_format);
  345. if (is_mirage) {
  346. LOG_CRITICAL(HW_GPU, "Mirage View Unsupported");
  347. return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents,
  348. false);
  349. }
  350. return {current_surface, *view};
  351. }
  352. return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents, false);
  353. } else {
  354. std::optional<std::pair<TSurface, TView>> view =
  355. ReconstructSurface(overlaps, params, gpu_addr, host_ptr);
  356. if (view.has_value()) {
  357. return *view;
  358. }
  359. return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents, false);
  360. }
  361. }
  362. std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params,
  363. bool preserve_contents) {
  364. auto new_surface{GetUncachedSurface(gpu_addr, params)};
  365. Register(new_surface);
  366. if (preserve_contents) {
  367. LoadSurface(new_surface);
  368. }
  369. return {new_surface, new_surface->GetMainView()};
  370. }
  371. void LoadSurface(const TSurface& surface) {
  372. staging_buffer.resize(surface->GetHostSizeInBytes());
  373. surface->LoadBuffer(*memory_manager, staging_buffer);
  374. surface->UploadTexture(staging_buffer);
  375. surface->MarkAsModified(false, Tick());
  376. }
  377. void FlushSurface(const TSurface& surface) {
  378. if (!surface->IsModified()) {
  379. return;
  380. }
  381. staging_buffer.resize(surface->GetHostSizeInBytes());
  382. surface->DownloadTexture(staging_buffer);
  383. surface->FlushBuffer(*memory_manager, staging_buffer);
  384. surface->MarkAsModified(false, Tick());
  385. }
  386. void RegisterInnerCache(TSurface& surface) {
  387. CacheAddr start = surface->GetCacheAddr() >> registry_page_bits;
  388. const CacheAddr end = (surface->GetCacheAddrEnd() - 1) >> registry_page_bits;
  389. while (start <= end) {
  390. registry[start].push_back(surface);
  391. start++;
  392. }
  393. }
  394. void UnregisterInnerCache(TSurface& surface) {
  395. CacheAddr start = surface->GetCacheAddr() >> registry_page_bits;
  396. const CacheAddr end = (surface->GetCacheAddrEnd() - 1) >> registry_page_bits;
  397. while (start <= end) {
  398. registry[start].remove(surface);
  399. start++;
  400. }
  401. }
  402. std::vector<TSurface> GetSurfacesInRegion(const CacheAddr cache_addr, const std::size_t size) {
  403. if (size == 0) {
  404. return {};
  405. }
  406. const CacheAddr cache_addr_end = cache_addr + size;
  407. CacheAddr start = cache_addr >> registry_page_bits;
  408. const CacheAddr end = (cache_addr_end - 1) >> registry_page_bits;
  409. std::vector<TSurface> surfaces;
  410. while (start <= end) {
  411. std::list<TSurface>& list = registry[start];
  412. for (auto& s : list) {
  413. if (!s->IsPicked() && s->Overlaps(cache_addr, cache_addr_end)) {
  414. s->MarkAsPicked(true);
  415. surfaces.push_back(s);
  416. }
  417. }
  418. start++;
  419. }
  420. for (auto& s : surfaces) {
  421. s->MarkAsPicked(false);
  422. }
  423. return surfaces;
  424. }
  425. void ReserveSurface(const SurfaceParams& params, TSurface surface) {
  426. surface_reserve[params].push_back(std::move(surface));
  427. }
  428. TSurface TryGetReservedSurface(const SurfaceParams& params) {
  429. auto search{surface_reserve.find(params)};
  430. if (search == surface_reserve.end()) {
  431. return {};
  432. }
  433. for (auto& surface : search->second) {
  434. if (!surface->IsRegistered()) {
  435. return surface;
  436. }
  437. }
  438. return {};
  439. }
  440. struct RenderInfo {
  441. RenderTargetConfig config;
  442. TSurface target;
  443. TView view;
  444. };
  445. struct DepthBufferInfo {
  446. TSurface target;
  447. TView view;
  448. };
  449. VideoCore::RasterizerInterface& rasterizer;
  450. Tegra::MemoryManager* memory_manager;
  451. u64 ticks{};
  452. // The internal Cache is different for the Texture Cache. It's based on buckets
  453. // of 1MB. This fits better for the purpose of this cache as textures are normaly
  454. // large in size.
  455. static constexpr u64 registry_page_bits{20};
  456. static constexpr u64 registry_page_size{1 << registry_page_bits};
  457. std::unordered_map<CacheAddr, std::list<TSurface>> registry;
  458. /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
  459. /// previously been used. This is to prevent surfaces from being constantly created and
  460. /// destroyed when used with different surface parameters.
  461. std::unordered_map<SurfaceParams, std::list<TSurface>> surface_reserve;
  462. std::array<RenderInfo, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> render_targets;
  463. DepthBufferInfo depth_buffer;
  464. std::vector<u8> staging_buffer;
  465. };
  466. } // namespace VideoCommon