rasterizer_cache.h 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  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 <set>
  6. #include <boost/icl/interval_map.hpp>
  7. #include <boost/range/iterator_range_core.hpp>
  8. #include "common/common_types.h"
  9. #include "core/core.h"
  10. #include "core/settings.h"
  11. #include "video_core/rasterizer_interface.h"
  12. #include "video_core/renderer_base.h"
  13. class RasterizerCacheObject {
  14. public:
  15. virtual ~RasterizerCacheObject();
  16. /// Gets the address of the shader in guest memory, required for cache management
  17. virtual VAddr GetAddr() const = 0;
  18. /// Gets the size of the shader in guest memory, required for cache management
  19. virtual std::size_t GetSizeInBytes() const = 0;
  20. /// Wriets any cached resources back to memory
  21. virtual void Flush() = 0;
  22. /// Sets whether the cached object should be considered registered
  23. void SetIsRegistered(bool registered) {
  24. is_registered = registered;
  25. }
  26. /// Returns true if the cached object is registered
  27. bool IsRegistered() const {
  28. return is_registered;
  29. }
  30. /// Returns true if the cached object is dirty
  31. bool IsDirty() const {
  32. return is_dirty;
  33. }
  34. /// Returns ticks from when this cached object was last modified
  35. u64 GetLastModifiedTicks() const {
  36. return last_modified_ticks;
  37. }
  38. /// Marks an object as recently modified, used to specify whether it is clean or dirty
  39. template <class T>
  40. void MarkAsModified(bool dirty, T& cache) {
  41. is_dirty = dirty;
  42. last_modified_ticks = cache.GetModifiedTicks();
  43. }
  44. private:
  45. bool is_registered{}; ///< Whether the object is currently registered with the cache
  46. bool is_dirty{}; ///< Whether the object is dirty (out of sync with guest memory)
  47. u64 last_modified_ticks{}; ///< When the object was last modified, used for in-order flushing
  48. };
  49. template <class T>
  50. class RasterizerCache : NonCopyable {
  51. friend class RasterizerCacheObject;
  52. public:
  53. /// Write any cached resources overlapping the specified region back to memory
  54. void FlushRegion(Tegra::GPUVAddr addr, size_t size) {
  55. const auto& objects{GetSortedObjectsFromRegion(addr, size)};
  56. for (auto& object : objects) {
  57. FlushObject(object);
  58. }
  59. }
  60. /// Mark the specified region as being invalidated
  61. void InvalidateRegion(VAddr addr, u64 size) {
  62. const auto& objects{GetSortedObjectsFromRegion(addr, size)};
  63. for (auto& object : objects) {
  64. if (!object->IsRegistered()) {
  65. // Skip duplicates
  66. continue;
  67. }
  68. Unregister(object);
  69. }
  70. }
  71. /// Invalidates everything in the cache
  72. void InvalidateAll() {
  73. while (object_cache.begin() != object_cache.end()) {
  74. Unregister(*object_cache.begin()->second.begin());
  75. }
  76. }
  77. protected:
  78. /// Tries to get an object from the cache with the specified address
  79. T TryGet(VAddr addr) const {
  80. const ObjectInterval interval{addr};
  81. for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
  82. for (auto& cached_object : pair.second) {
  83. if (cached_object->GetAddr() == addr) {
  84. return cached_object;
  85. }
  86. }
  87. }
  88. return nullptr;
  89. }
  90. /// Register an object into the cache
  91. void Register(const T& object) {
  92. object->SetIsRegistered(true);
  93. object_cache.add({GetInterval(object), ObjectSet{object}});
  94. auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
  95. rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1);
  96. }
  97. /// Unregisters an object from the cache
  98. void Unregister(const T& object) {
  99. object->SetIsRegistered(false);
  100. auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
  101. rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
  102. // Only flush if use_accurate_gpu_emulation is enabled, as it incurs a performance hit
  103. if (Settings::values.use_accurate_gpu_emulation) {
  104. FlushObject(object);
  105. }
  106. object_cache.subtract({GetInterval(object), ObjectSet{object}});
  107. }
  108. /// Returns a ticks counter used for tracking when cached objects were last modified
  109. u64 GetModifiedTicks() {
  110. return ++modified_ticks;
  111. }
  112. private:
  113. /// Returns a list of cached objects from the specified memory region, ordered by access time
  114. std::vector<T> GetSortedObjectsFromRegion(VAddr addr, u64 size) {
  115. if (size == 0) {
  116. return {};
  117. }
  118. std::vector<T> objects;
  119. const ObjectInterval interval{addr, addr + size};
  120. for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
  121. for (auto& cached_object : pair.second) {
  122. if (!cached_object) {
  123. continue;
  124. }
  125. objects.push_back(cached_object);
  126. }
  127. }
  128. std::sort(objects.begin(), objects.end(), [](const T& a, const T& b) -> bool {
  129. return a->GetLastModifiedTicks() < b->GetLastModifiedTicks();
  130. });
  131. return objects;
  132. }
  133. /// Flushes the specified object, updating appropriate cache state as needed
  134. void FlushObject(const T& object) {
  135. if (!object->IsDirty()) {
  136. return;
  137. }
  138. object->Flush();
  139. object->MarkAsModified(false, *this);
  140. }
  141. using ObjectSet = std::set<T>;
  142. using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>;
  143. using ObjectInterval = typename ObjectCache::interval_type;
  144. static auto GetInterval(const T& object) {
  145. return ObjectInterval::right_open(object->GetAddr(),
  146. object->GetAddr() + object->GetSizeInBytes());
  147. }
  148. ObjectCache object_cache; ///< Cache of objects
  149. u64 modified_ticks{}; ///< Counter of cache state ticks, used for in-order flushing
  150. };