texture_cache.h 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  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 <type_traits>
  10. #include <unordered_map>
  11. #include <boost/icl/interval_map.hpp>
  12. #include <boost/range/iterator_range.hpp>
  13. #include "common/assert.h"
  14. #include "common/common_types.h"
  15. #include "core/memory.h"
  16. #include "video_core/engines/fermi_2d.h"
  17. #include "video_core/engines/maxwell_3d.h"
  18. #include "video_core/gpu.h"
  19. #include "video_core/rasterizer_interface.h"
  20. #include "video_core/surface.h"
  21. namespace Core {
  22. class System;
  23. }
  24. namespace Tegra::Texture {
  25. struct FullTextureInfo;
  26. }
  27. namespace VideoCore {
  28. class RasterizerInterface;
  29. }
  30. namespace VideoCommon {
  31. class HasheableSurfaceParams {
  32. public:
  33. std::size_t Hash() const;
  34. bool operator==(const HasheableSurfaceParams& rhs) const;
  35. protected:
  36. // Avoid creation outside of a managed environment.
  37. HasheableSurfaceParams() = default;
  38. bool is_tiled;
  39. u32 block_width;
  40. u32 block_height;
  41. u32 block_depth;
  42. u32 tile_width_spacing;
  43. u32 width;
  44. u32 height;
  45. u32 depth;
  46. u32 pitch;
  47. u32 unaligned_height;
  48. u32 num_levels;
  49. VideoCore::Surface::PixelFormat pixel_format;
  50. VideoCore::Surface::ComponentType component_type;
  51. VideoCore::Surface::SurfaceType type;
  52. VideoCore::Surface::SurfaceTarget target;
  53. };
  54. class SurfaceParams final : public HasheableSurfaceParams {
  55. public:
  56. /// Creates SurfaceCachedParams from a texture configuration.
  57. static SurfaceParams CreateForTexture(Core::System& system,
  58. const Tegra::Texture::FullTextureInfo& config);
  59. /// Creates SurfaceCachedParams for a depth buffer configuration.
  60. static SurfaceParams CreateForDepthBuffer(
  61. Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
  62. u32 block_width, u32 block_height, u32 block_depth,
  63. Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
  64. /// Creates SurfaceCachedParams from a framebuffer configuration.
  65. static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
  66. /// Creates SurfaceCachedParams from a Fermi2D surface configuration.
  67. static SurfaceParams CreateForFermiCopySurface(
  68. const Tegra::Engines::Fermi2D::Regs::Surface& config);
  69. bool IsTiled() const {
  70. return is_tiled;
  71. }
  72. u32 GetBlockWidth() const {
  73. return block_width;
  74. }
  75. u32 GetTileWidthSpacing() const {
  76. return tile_width_spacing;
  77. }
  78. u32 GetWidth() const {
  79. return width;
  80. }
  81. u32 GetHeight() const {
  82. return height;
  83. }
  84. u32 GetDepth() const {
  85. return depth;
  86. }
  87. u32 GetPitch() const {
  88. return pitch;
  89. }
  90. u32 GetNumLevels() const {
  91. return num_levels;
  92. }
  93. VideoCore::Surface::PixelFormat GetPixelFormat() const {
  94. return pixel_format;
  95. }
  96. VideoCore::Surface::ComponentType GetComponentType() const {
  97. return component_type;
  98. }
  99. VideoCore::Surface::SurfaceTarget GetTarget() const {
  100. return target;
  101. }
  102. VideoCore::Surface::SurfaceType GetType() const {
  103. return type;
  104. }
  105. std::size_t GetGuestSizeInBytes() const {
  106. return guest_size_in_bytes;
  107. }
  108. std::size_t GetHostSizeInBytes() const {
  109. return host_size_in_bytes;
  110. }
  111. u32 GetNumLayers() const {
  112. return num_layers;
  113. }
  114. /// Returns the width of a given mipmap level.
  115. u32 GetMipWidth(u32 level) const;
  116. /// Returns the height of a given mipmap level.
  117. u32 GetMipHeight(u32 level) const;
  118. /// Returns the depth of a given mipmap level.
  119. u32 GetMipDepth(u32 level) const;
  120. /// Returns true if these parameters are from a layered surface.
  121. bool IsLayered() const;
  122. /// Returns the block height of a given mipmap level.
  123. u32 GetMipBlockHeight(u32 level) const;
  124. /// Returns the block depth of a given mipmap level.
  125. u32 GetMipBlockDepth(u32 level) const;
  126. /// Returns the offset in bytes in guest memory of a given mipmap level.
  127. std::size_t GetGuestMipmapLevelOffset(u32 level) const;
  128. /// Returns the offset in bytes in host memory (linear) of a given mipmap level.
  129. std::size_t GetHostMipmapLevelOffset(u32 level) const;
  130. /// Returns the size of a layer in bytes in guest memory.
  131. std::size_t GetGuestLayerSize() const;
  132. /// Returns the size of a layer in bytes in host memory for a given mipmap level.
  133. std::size_t GetHostLayerSize(u32 level) const;
  134. /// Returns true if another surface can be familiar with this. This is a loosely defined term
  135. /// that reflects the possibility of these two surface parameters potentially being part of a
  136. /// bigger superset.
  137. bool IsFamiliar(const SurfaceParams& view_params) const;
  138. /// Returns true if the pixel format is a depth and/or stencil format.
  139. bool IsPixelFormatZeta() const;
  140. /// Creates a map that redirects an address difference to a layer and mipmap level.
  141. std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const;
  142. /// Returns true if the passed surface view parameters is equal or a valid subset of this.
  143. bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const;
  144. private:
  145. /// Calculates values that can be deduced from HasheableSurfaceParams.
  146. void CalculateCachedValues();
  147. /// Returns the size of a given mipmap level.
  148. std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only,
  149. bool uncompressed) const;
  150. /// Returns the size of all mipmap levels and aligns as needed.
  151. std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const;
  152. /// Returns true if the passed view width and height match the size of this params in a given
  153. /// mipmap level.
  154. bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const;
  155. /// Returns true if the passed view depth match the size of this params in a given mipmap level.
  156. bool IsDepthValid(const SurfaceParams& view_params, u32 level) const;
  157. /// Returns true if the passed view layers and mipmap levels are in bounds.
  158. bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const;
  159. std::size_t guest_size_in_bytes;
  160. std::size_t host_size_in_bytes;
  161. u32 num_layers;
  162. };
  163. struct ViewKey {
  164. std::size_t Hash() const;
  165. bool operator==(const ViewKey& rhs) const;
  166. u32 base_layer{};
  167. u32 num_layers{};
  168. u32 base_level{};
  169. u32 num_levels{};
  170. };
  171. } // namespace VideoCommon
  172. namespace std {
  173. template <>
  174. struct hash<VideoCommon::SurfaceParams> {
  175. std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept {
  176. return k.Hash();
  177. }
  178. };
  179. template <>
  180. struct hash<VideoCommon::ViewKey> {
  181. std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept {
  182. return k.Hash();
  183. }
  184. };
  185. } // namespace std
  186. namespace VideoCommon {
  187. template <typename TView, typename TExecutionContext>
  188. class SurfaceBase {
  189. static_assert(std::is_trivially_copyable_v<TExecutionContext>);
  190. public:
  191. virtual void LoadBuffer() = 0;
  192. virtual TExecutionContext FlushBuffer(TExecutionContext exctx) = 0;
  193. virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0;
  194. TView* TryGetView(VAddr view_addr, const SurfaceParams& view_params) {
  195. if (view_addr < cpu_addr || !params.IsFamiliar(view_params)) {
  196. // It can't be a view if it's in a prior address.
  197. return {};
  198. }
  199. const auto relative_offset{static_cast<u64>(view_addr - cpu_addr)};
  200. const auto it{view_offset_map.find(relative_offset)};
  201. if (it == view_offset_map.end()) {
  202. // Couldn't find an aligned view.
  203. return {};
  204. }
  205. const auto [layer, level] = it->second;
  206. if (!params.IsViewValid(view_params, layer, level)) {
  207. return {};
  208. }
  209. return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels());
  210. }
  211. VAddr GetCpuAddr() const {
  212. ASSERT(is_registered);
  213. return cpu_addr;
  214. }
  215. u8* GetHostPtr() const {
  216. ASSERT(is_registered);
  217. return host_ptr;
  218. }
  219. CacheAddr GetCacheAddr() const {
  220. ASSERT(is_registered);
  221. return cache_addr;
  222. }
  223. std::size_t GetSizeInBytes() const {
  224. return params.GetGuestSizeInBytes();
  225. }
  226. void MarkAsModified(bool is_modified_) {
  227. is_modified = is_modified_;
  228. }
  229. const SurfaceParams& GetSurfaceParams() const {
  230. return params;
  231. }
  232. TView* GetView(VAddr view_addr, const SurfaceParams& view_params) {
  233. TView* view{TryGetView(view_addr, view_params)};
  234. ASSERT(view != nullptr);
  235. return view;
  236. }
  237. void Register(VAddr cpu_addr_, u8* host_ptr_) {
  238. ASSERT(!is_registered);
  239. is_registered = true;
  240. cpu_addr = cpu_addr_;
  241. host_ptr = host_ptr_;
  242. cache_addr = ToCacheAddr(host_ptr_);
  243. }
  244. void Register(VAddr cpu_addr_) {
  245. Register(cpu_addr_, Memory::GetPointer(cpu_addr_));
  246. }
  247. void Unregister() {
  248. ASSERT(is_registered);
  249. is_registered = false;
  250. }
  251. bool IsRegistered() const {
  252. return is_registered;
  253. }
  254. protected:
  255. explicit SurfaceBase(const SurfaceParams& params)
  256. : params{params}, view_offset_map{params.CreateViewOffsetMap()} {}
  257. ~SurfaceBase() = default;
  258. virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0;
  259. bool IsModified() const {
  260. return is_modified;
  261. }
  262. const SurfaceParams params;
  263. private:
  264. TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) {
  265. const ViewKey key{base_layer, num_layers, base_level, num_levels};
  266. const auto [entry, is_cache_miss] = views.try_emplace(key);
  267. auto& view{entry->second};
  268. if (is_cache_miss) {
  269. view = CreateView(key);
  270. }
  271. return view.get();
  272. }
  273. const std::map<u64, std::pair<u32, u32>> view_offset_map;
  274. VAddr cpu_addr{};
  275. u8* host_ptr{};
  276. CacheAddr cache_addr{};
  277. bool is_modified{};
  278. bool is_registered{};
  279. std::unordered_map<ViewKey, std::unique_ptr<TView>> views;
  280. };
  281. template <typename TSurface, typename TView, typename TExecutionContext>
  282. class TextureCache {
  283. static_assert(std::is_trivially_copyable_v<TExecutionContext>);
  284. using ResultType = std::tuple<TView*, TExecutionContext>;
  285. using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface*>>;
  286. using IntervalType = typename IntervalMap::interval_type;
  287. public:
  288. void InvalidateRegion(CacheAddr addr, std::size_t size) {
  289. for (TSurface* surface : GetSurfacesInRegion(addr, size)) {
  290. if (!surface->IsRegistered()) {
  291. // Skip duplicates
  292. continue;
  293. }
  294. Unregister(surface);
  295. }
  296. }
  297. ResultType GetTextureSurface(TExecutionContext exctx,
  298. const Tegra::Texture::FullTextureInfo& config) {
  299. auto& memory_manager{system.GPU().MemoryManager()};
  300. const auto cpu_addr{memory_manager.GpuToCpuAddress(config.tic.Address())};
  301. if (!cpu_addr) {
  302. return {{}, exctx};
  303. }
  304. const auto params{SurfaceParams::CreateForTexture(system, config)};
  305. return GetSurfaceView(exctx, *cpu_addr, params, true);
  306. }
  307. ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) {
  308. const auto& regs{system.GPU().Maxwell3D().regs};
  309. if (!regs.zeta.Address() || !regs.zeta_enable) {
  310. return {{}, exctx};
  311. }
  312. auto& memory_manager{system.GPU().MemoryManager()};
  313. const auto cpu_addr{memory_manager.GpuToCpuAddress(regs.zeta.Address())};
  314. if (!cpu_addr) {
  315. return {{}, exctx};
  316. }
  317. const auto depth_params{SurfaceParams::CreateForDepthBuffer(
  318. system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
  319. regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
  320. regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
  321. return GetSurfaceView(exctx, *cpu_addr, depth_params, preserve_contents);
  322. }
  323. ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index,
  324. bool preserve_contents) {
  325. ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
  326. const auto& regs{system.GPU().Maxwell3D().regs};
  327. if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
  328. regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
  329. return {{}, exctx};
  330. }
  331. auto& memory_manager{system.GPU().MemoryManager()};
  332. const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
  333. const auto cpu_addr{memory_manager.GpuToCpuAddress(
  334. config.Address() + config.base_layer * config.layer_stride * sizeof(u32))};
  335. if (!cpu_addr) {
  336. return {{}, exctx};
  337. }
  338. return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
  339. preserve_contents);
  340. }
  341. ResultType GetFermiSurface(TExecutionContext exctx,
  342. const Tegra::Engines::Fermi2D::Regs::Surface& config) {
  343. const auto cpu_addr{system.GPU().MemoryManager().GpuToCpuAddress(config.Address())};
  344. ASSERT(cpu_addr);
  345. return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFermiCopySurface(config),
  346. true);
  347. }
  348. TSurface* TryFindFramebufferSurface(const u8* host_ptr) const {
  349. const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))};
  350. return it != registered_surfaces.end() ? *it->second.begin() : nullptr;
  351. }
  352. protected:
  353. TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
  354. : system{system}, rasterizer{rasterizer} {}
  355. ~TextureCache() = default;
  356. virtual ResultType TryFastGetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr,
  357. const SurfaceParams& params, bool preserve_contents,
  358. const std::vector<TSurface*>& overlaps) = 0;
  359. virtual std::unique_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0;
  360. void Register(TSurface* surface, VAddr cpu_addr, u8* host_ptr) {
  361. surface->Register(cpu_addr, host_ptr);
  362. registered_surfaces.add({GetSurfaceInterval(surface), {surface}});
  363. rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1);
  364. }
  365. void Unregister(TSurface* surface) {
  366. registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}});
  367. rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1);
  368. surface->Unregister();
  369. }
  370. TSurface* GetUncachedSurface(const SurfaceParams& params) {
  371. if (TSurface* surface = TryGetReservedSurface(params); surface)
  372. return surface;
  373. // No reserved surface available, create a new one and reserve it
  374. auto new_surface{CreateSurface(params)};
  375. TSurface* surface{new_surface.get()};
  376. ReserveSurface(params, std::move(new_surface));
  377. return surface;
  378. }
  379. Core::System& system;
  380. private:
  381. ResultType GetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, const SurfaceParams& params,
  382. bool preserve_contents) {
  383. const auto host_ptr{Memory::GetPointer(cpu_addr)};
  384. const auto cache_addr{ToCacheAddr(host_ptr)};
  385. const auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())};
  386. if (overlaps.empty()) {
  387. return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents);
  388. }
  389. if (overlaps.size() == 1) {
  390. if (TView* view = overlaps[0]->TryGetView(cpu_addr, params); view)
  391. return {view, exctx};
  392. }
  393. TView* fast_view;
  394. std::tie(fast_view, exctx) =
  395. TryFastGetSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents, overlaps);
  396. for (TSurface* surface : overlaps) {
  397. if (!fast_view) {
  398. // Flush even when we don't care about the contents, to preserve memory not written
  399. // by the new surface.
  400. exctx = surface->FlushBuffer(exctx);
  401. }
  402. Unregister(surface);
  403. }
  404. if (fast_view) {
  405. return {fast_view, exctx};
  406. }
  407. return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents);
  408. }
  409. ResultType LoadSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr,
  410. const SurfaceParams& params, bool preserve_contents) {
  411. TSurface* new_surface{GetUncachedSurface(params)};
  412. Register(new_surface, cpu_addr, host_ptr);
  413. if (preserve_contents) {
  414. exctx = LoadSurface(exctx, new_surface);
  415. }
  416. return {new_surface->GetView(cpu_addr, params), exctx};
  417. }
  418. TExecutionContext LoadSurface(TExecutionContext exctx, TSurface* surface) {
  419. surface->LoadBuffer();
  420. exctx = surface->UploadTexture(exctx);
  421. surface->MarkAsModified(false);
  422. return exctx;
  423. }
  424. std::vector<TSurface*> GetSurfacesInRegion(CacheAddr cache_addr, std::size_t size) const {
  425. if (size == 0) {
  426. return {};
  427. }
  428. const IntervalType interval{cache_addr, cache_addr + size};
  429. std::vector<TSurface*> surfaces;
  430. for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) {
  431. surfaces.push_back(*pair.second.begin());
  432. }
  433. return surfaces;
  434. }
  435. void ReserveSurface(const SurfaceParams& params, std::unique_ptr<TSurface> surface) {
  436. surface_reserve[params].push_back(std::move(surface));
  437. }
  438. TSurface* TryGetReservedSurface(const SurfaceParams& params) {
  439. auto search{surface_reserve.find(params)};
  440. if (search == surface_reserve.end()) {
  441. return {};
  442. }
  443. for (auto& surface : search->second) {
  444. if (!surface->IsRegistered()) {
  445. return surface.get();
  446. }
  447. }
  448. return {};
  449. }
  450. IntervalType GetSurfaceInterval(TSurface* surface) const {
  451. return IntervalType::right_open(surface->GetCacheAddr(),
  452. surface->GetCacheAddr() + surface->GetSizeInBytes());
  453. }
  454. VideoCore::RasterizerInterface& rasterizer;
  455. IntervalMap registered_surfaces;
  456. /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
  457. /// previously been used. This is to prevent surfaces from being constantly created and
  458. /// destroyed when used with different surface parameters.
  459. std::unordered_map<SurfaceParams, std::list<std::unique_ptr<TSurface>>> surface_reserve;
  460. };
  461. } // namespace VideoCommon