texture_cache.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  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. CopyParams copy_params{};
  298. const u32 dst_width = params.GetMipWidth(mipmap);
  299. const u32 dst_height = params.GetMipHeight(mipmap);
  300. copy_params.width = std::min(src_params.width, dst_width);
  301. copy_params.height = std::min(src_params.height, dst_height);
  302. copy_params.depth = 1;
  303. copy_params.source_level = 0;
  304. copy_params.dest_level = mipmap;
  305. copy_params.source_z = 0;
  306. copy_params.dest_z = layer;
  307. ImageCopy(surface, new_surface, copy_params);
  308. }
  309. for (auto surface : overlaps) {
  310. Unregister(surface);
  311. }
  312. Register(new_surface);
  313. return {{new_surface, new_surface->GetMainView()}};
  314. }
  315. std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const SurfaceParams& params,
  316. bool preserve_contents) {
  317. const auto host_ptr{memory_manager->GetPointer(gpu_addr)};
  318. const auto cache_addr{ToCacheAddr(host_ptr)};
  319. const std::size_t candidate_size = params.GetGuestSizeInBytes();
  320. auto overlaps{GetSurfacesInRegion(cache_addr, candidate_size)};
  321. if (overlaps.empty()) {
  322. return InitializeSurface(gpu_addr, params, preserve_contents);
  323. }
  324. for (auto surface : overlaps) {
  325. if (!surface->MatchesTopology(params)) {
  326. return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents,
  327. true);
  328. }
  329. }
  330. if (overlaps.size() == 1) {
  331. TSurface current_surface = overlaps[0];
  332. MatchStructureResult s_result = current_surface->MatchesStructure(params);
  333. if (s_result != MatchStructureResult::None &&
  334. current_surface->GetGpuAddr() == gpu_addr &&
  335. (params.target != SurfaceTarget::Texture3D ||
  336. current_surface->MatchTarget(params.target))) {
  337. if (s_result == MatchStructureResult::FullMatch) {
  338. return ManageStructuralMatch(current_surface, params);
  339. } else {
  340. return RebuildSurface(current_surface, params);
  341. }
  342. }
  343. if (current_surface->GetSizeInBytes() <= candidate_size) {
  344. return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents,
  345. false);
  346. }
  347. std::optional<TView> view = current_surface->EmplaceView(params, gpu_addr);
  348. if (view.has_value()) {
  349. const bool is_mirage = !current_surface->MatchFormat(params.pixel_format);
  350. if (is_mirage) {
  351. LOG_CRITICAL(HW_GPU, "Mirage View Unsupported");
  352. return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents,
  353. false);
  354. }
  355. return {current_surface, *view};
  356. }
  357. return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents, false);
  358. } else {
  359. std::optional<std::pair<TSurface, TView>> view =
  360. ReconstructSurface(overlaps, params, gpu_addr, host_ptr);
  361. if (view.has_value()) {
  362. return *view;
  363. }
  364. return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents, false);
  365. }
  366. }
  367. std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params,
  368. bool preserve_contents) {
  369. auto new_surface{GetUncachedSurface(gpu_addr, params)};
  370. Register(new_surface);
  371. if (preserve_contents) {
  372. LoadSurface(new_surface);
  373. }
  374. return {new_surface, new_surface->GetMainView()};
  375. }
  376. void LoadSurface(const TSurface& surface) {
  377. staging_buffer.resize(surface->GetHostSizeInBytes());
  378. surface->LoadBuffer(*memory_manager, staging_buffer);
  379. surface->UploadTexture(staging_buffer);
  380. surface->MarkAsModified(false, Tick());
  381. }
  382. void FlushSurface(const TSurface& surface) {
  383. if (!surface->IsModified()) {
  384. return;
  385. }
  386. staging_buffer.resize(surface->GetHostSizeInBytes());
  387. surface->DownloadTexture(staging_buffer);
  388. surface->FlushBuffer(*memory_manager, staging_buffer);
  389. surface->MarkAsModified(false, Tick());
  390. }
  391. void RegisterInnerCache(TSurface& surface) {
  392. CacheAddr start = surface->GetCacheAddr() >> registry_page_bits;
  393. const CacheAddr end = (surface->GetCacheAddrEnd() - 1) >> registry_page_bits;
  394. while (start <= end) {
  395. registry[start].push_back(surface);
  396. start++;
  397. }
  398. }
  399. void UnregisterInnerCache(TSurface& surface) {
  400. CacheAddr start = surface->GetCacheAddr() >> registry_page_bits;
  401. const CacheAddr end = (surface->GetCacheAddrEnd() - 1) >> registry_page_bits;
  402. while (start <= end) {
  403. registry[start].remove(surface);
  404. start++;
  405. }
  406. }
  407. std::vector<TSurface> GetSurfacesInRegion(const CacheAddr cache_addr, const std::size_t size) {
  408. if (size == 0) {
  409. return {};
  410. }
  411. const CacheAddr cache_addr_end = cache_addr + size;
  412. CacheAddr start = cache_addr >> registry_page_bits;
  413. const CacheAddr end = (cache_addr_end - 1) >> registry_page_bits;
  414. std::vector<TSurface> surfaces;
  415. while (start <= end) {
  416. std::list<TSurface>& list = registry[start];
  417. for (auto& s : list) {
  418. if (!s->IsPicked() && s->Overlaps(cache_addr, cache_addr_end)) {
  419. s->MarkAsPicked(true);
  420. surfaces.push_back(s);
  421. }
  422. }
  423. start++;
  424. }
  425. for (auto& s : surfaces) {
  426. s->MarkAsPicked(false);
  427. }
  428. return surfaces;
  429. }
  430. void ReserveSurface(const SurfaceParams& params, TSurface surface) {
  431. surface_reserve[params].push_back(std::move(surface));
  432. }
  433. TSurface TryGetReservedSurface(const SurfaceParams& params) {
  434. auto search{surface_reserve.find(params)};
  435. if (search == surface_reserve.end()) {
  436. return {};
  437. }
  438. for (auto& surface : search->second) {
  439. if (!surface->IsRegistered()) {
  440. return surface;
  441. }
  442. }
  443. return {};
  444. }
  445. struct RenderInfo {
  446. RenderTargetConfig config;
  447. TSurface target;
  448. TView view;
  449. };
  450. struct DepthBufferInfo {
  451. TSurface target;
  452. TView view;
  453. };
  454. VideoCore::RasterizerInterface& rasterizer;
  455. Tegra::MemoryManager* memory_manager;
  456. u64 ticks{};
  457. // The internal Cache is different for the Texture Cache. It's based on buckets
  458. // of 1MB. This fits better for the purpose of this cache as textures are normaly
  459. // large in size.
  460. static constexpr u64 registry_page_bits{20};
  461. static constexpr u64 registry_page_size{1 << registry_page_bits};
  462. std::unordered_map<CacheAddr, std::list<TSurface>> registry;
  463. /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
  464. /// previously been used. This is to prevent surfaces from being constantly created and
  465. /// destroyed when used with different surface parameters.
  466. std::unordered_map<SurfaceParams, std::list<TSurface>> surface_reserve;
  467. std::array<RenderInfo, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> render_targets;
  468. DepthBufferInfo depth_buffer;
  469. std::vector<u8> staging_buffer;
  470. };
  471. } // namespace VideoCommon