rasterizer_cache.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // Copyright 2018 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #pragma once
  5. #include <mutex>
  6. #include <set>
  7. #include <unordered_map>
  8. #include <boost/icl/interval_map.hpp>
  9. #include <boost/range/iterator_range_core.hpp>
  10. #include "common/common_types.h"
  11. #include "core/settings.h"
  12. #include "video_core/gpu.h"
  13. #include "video_core/rasterizer_interface.h"
  14. class RasterizerCacheObject {
  15. public:
  16. explicit RasterizerCacheObject(const u8* host_ptr)
  17. : host_ptr{host_ptr}, cache_addr{ToCacheAddr(host_ptr)} {}
  18. virtual ~RasterizerCacheObject();
  19. CacheAddr GetCacheAddr() const {
  20. return cache_addr;
  21. }
  22. const u8* GetHostPtr() const {
  23. return host_ptr;
  24. }
  25. /// Gets the address of the shader in guest memory, required for cache management
  26. virtual VAddr GetCpuAddr() const = 0;
  27. /// Gets the size of the shader in guest memory, required for cache management
  28. virtual std::size_t GetSizeInBytes() const = 0;
  29. /// Sets whether the cached object should be considered registered
  30. void SetIsRegistered(bool registered) {
  31. is_registered = registered;
  32. }
  33. /// Returns true if the cached object is registered
  34. bool IsRegistered() const {
  35. return is_registered;
  36. }
  37. /// Returns true if the cached object is dirty
  38. bool IsDirty() const {
  39. return is_dirty;
  40. }
  41. /// Returns ticks from when this cached object was last modified
  42. u64 GetLastModifiedTicks() const {
  43. return last_modified_ticks;
  44. }
  45. /// Marks an object as recently modified, used to specify whether it is clean or dirty
  46. template <class T>
  47. void MarkAsModified(bool dirty, T& cache) {
  48. is_dirty = dirty;
  49. last_modified_ticks = cache.GetModifiedTicks();
  50. }
  51. private:
  52. bool is_registered{}; ///< Whether the object is currently registered with the cache
  53. bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory)
  54. u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing
  55. const u8* host_ptr{}; ///< Pointer to the memory backing this cached region
  56. CacheAddr cache_addr{}; ///< Cache address memory, unique from emulated virtual address space
  57. };
  58. template <class T>
  59. class RasterizerCache : NonCopyable {
  60. friend class RasterizerCacheObject;
  61. public:
  62. explicit RasterizerCache(VideoCore::RasterizerInterface& rasterizer) : rasterizer{rasterizer} {}
  63. /// Write any cached resources overlapping the specified region back to memory
  64. void FlushRegion(CacheAddr addr, std::size_t size) {
  65. std::lock_guard lock{mutex};
  66. const auto& objects{GetSortedObjectsFromRegion(addr, size)};
  67. for (auto& object : objects) {
  68. FlushObject(object);
  69. }
  70. }
  71. /// Mark the specified region as being invalidated
  72. void InvalidateRegion(CacheAddr addr, u64 size) {
  73. std::lock_guard lock{mutex};
  74. const auto& objects{GetSortedObjectsFromRegion(addr, size)};
  75. for (auto& object : objects) {
  76. if (!object->IsRegistered()) {
  77. // Skip duplicates
  78. continue;
  79. }
  80. Unregister(object);
  81. }
  82. }
  83. /// Invalidates everything in the cache
  84. void InvalidateAll() {
  85. std::lock_guard lock{mutex};
  86. while (interval_cache.begin() != interval_cache.end()) {
  87. Unregister(*interval_cache.begin()->second.begin());
  88. }
  89. }
  90. protected:
  91. /// Tries to get an object from the cache with the specified cache address
  92. T TryGet(CacheAddr addr) const {
  93. const auto iter = map_cache.find(addr);
  94. if (iter != map_cache.end())
  95. return iter->second;
  96. return nullptr;
  97. }
  98. T TryGet(const void* addr) const {
  99. const auto iter = map_cache.find(ToCacheAddr(addr));
  100. if (iter != map_cache.end())
  101. return iter->second;
  102. return nullptr;
  103. }
  104. /// Register an object into the cache
  105. virtual void Register(const T& object) {
  106. std::lock_guard lock{mutex};
  107. object->SetIsRegistered(true);
  108. interval_cache.add({GetInterval(object), ObjectSet{object}});
  109. map_cache.insert({object->GetCacheAddr(), object});
  110. rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), 1);
  111. }
  112. /// Unregisters an object from the cache
  113. virtual void Unregister(const T& object) {
  114. std::lock_guard lock{mutex};
  115. object->SetIsRegistered(false);
  116. rasterizer.UpdatePagesCachedCount(object->GetCpuAddr(), object->GetSizeInBytes(), -1);
  117. const CacheAddr addr = object->GetCacheAddr();
  118. interval_cache.subtract({GetInterval(object), ObjectSet{object}});
  119. map_cache.erase(addr);
  120. }
  121. /// Returns a ticks counter used for tracking when cached objects were last modified
  122. u64 GetModifiedTicks() {
  123. std::lock_guard lock{mutex};
  124. return ++modified_ticks;
  125. }
  126. virtual void FlushObjectInner(const T& object) = 0;
  127. /// Flushes the specified object, updating appropriate cache state as needed
  128. void FlushObject(const T& object) {
  129. std::lock_guard lock{mutex};
  130. if (!object->IsDirty()) {
  131. return;
  132. }
  133. FlushObjectInner(object);
  134. object->MarkAsModified(false, *this);
  135. }
  136. std::recursive_mutex mutex;
  137. private:
  138. /// Returns a list of cached objects from the specified memory region, ordered by access time
  139. std::vector<T> GetSortedObjectsFromRegion(CacheAddr addr, u64 size) {
  140. if (size == 0) {
  141. return {};
  142. }
  143. std::vector<T> objects;
  144. const ObjectInterval interval{addr, addr + size};
  145. for (auto& pair : boost::make_iterator_range(interval_cache.equal_range(interval))) {
  146. for (auto& cached_object : pair.second) {
  147. if (!cached_object) {
  148. continue;
  149. }
  150. objects.push_back(cached_object);
  151. }
  152. }
  153. std::sort(objects.begin(), objects.end(), [](const T& a, const T& b) -> bool {
  154. return a->GetLastModifiedTicks() < b->GetLastModifiedTicks();
  155. });
  156. return objects;
  157. }
  158. using ObjectSet = std::set<T>;
  159. using ObjectCache = std::unordered_map<CacheAddr, T>;
  160. using IntervalCache = boost::icl::interval_map<CacheAddr, ObjectSet>;
  161. using ObjectInterval = typename IntervalCache::interval_type;
  162. static auto GetInterval(const T& object) {
  163. return ObjectInterval::right_open(object->GetCacheAddr(),
  164. object->GetCacheAddr() + object->GetSizeInBytes());
  165. }
  166. ObjectCache map_cache;
  167. IntervalCache interval_cache; ///< Cache of objects
  168. u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
  169. VideoCore::RasterizerInterface& rasterizer;
  170. };