fence_manager.h 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include <algorithm>
  5. #include <condition_variable>
  6. #include <cstring>
  7. #include <deque>
  8. #include <functional>
  9. #include <memory>
  10. #include <mutex>
  11. #include <thread>
  12. #include <queue>
  13. #include "common/common_types.h"
  14. #include "common/microprofile.h"
  15. #include "common/scope_exit.h"
  16. #include "common/settings.h"
  17. #include "common/thread.h"
  18. #include "video_core/delayed_destruction_ring.h"
  19. #include "video_core/gpu.h"
  20. #include "video_core/host1x/host1x.h"
  21. #include "video_core/host1x/syncpoint_manager.h"
  22. #include "video_core/rasterizer_interface.h"
  23. namespace VideoCommon {
  24. class FenceBase {
  25. public:
  26. explicit FenceBase(bool is_stubbed_) : is_stubbed{is_stubbed_} {}
  27. bool IsStubbed() const {
  28. return is_stubbed;
  29. }
  30. protected:
  31. bool is_stubbed;
  32. };
  33. template <typename Traits>
  34. class FenceManager {
  35. using TFence = typename Traits::FenceType;
  36. using TTextureCache = typename Traits::TextureCacheType;
  37. using TBufferCache = typename Traits::BufferCacheType;
  38. using TQueryCache = typename Traits::QueryCacheType;
  39. static constexpr bool can_async_check = Traits::HAS_ASYNC_CHECK;
  40. public:
  41. /// Notify the fence manager about a new frame
  42. void TickFrame() {
  43. std::unique_lock lock(ring_guard);
  44. delayed_destruction_ring.Tick();
  45. }
  46. // Unlike other fences, this one doesn't
  47. void SignalOrdering() {
  48. std::scoped_lock lock{buffer_cache.mutex};
  49. buffer_cache.AccumulateFlushes();
  50. }
  51. void SignalReference() {
  52. std::function<void()> do_nothing([] {});
  53. SignalFence(std::move(do_nothing));
  54. }
  55. void SyncOperation(std::function<void()>&& func) {
  56. uncommitted_operations.emplace_back(std::move(func));
  57. }
  58. void SignalFence(std::function<void()>&& func) {
  59. bool delay_fence = Settings::IsGPULevelHigh();
  60. if constexpr (!can_async_check) {
  61. TryReleasePendingFences<false>();
  62. }
  63. const bool should_flush = ShouldFlush();
  64. CommitAsyncFlushes();
  65. TFence new_fence = CreateFence(!should_flush);
  66. if constexpr (can_async_check) {
  67. guard.lock();
  68. }
  69. if (delay_fence) {
  70. uncommitted_operations.emplace_back(std::move(func));
  71. }
  72. pending_operations.emplace_back(std::move(uncommitted_operations));
  73. QueueFence(new_fence);
  74. if (!delay_fence) {
  75. func();
  76. }
  77. fences.push(std::move(new_fence));
  78. if (should_flush) {
  79. rasterizer.FlushCommands();
  80. }
  81. if constexpr (can_async_check) {
  82. guard.unlock();
  83. cv.notify_all();
  84. }
  85. rasterizer.InvalidateGPUCache();
  86. }
  87. void SignalSyncPoint(u32 value) {
  88. syncpoint_manager.IncrementGuest(value);
  89. std::function<void()> func([this, value] { syncpoint_manager.IncrementHost(value); });
  90. SignalFence(std::move(func));
  91. }
  92. void WaitPendingFences() {
  93. if constexpr (!can_async_check) {
  94. TryReleasePendingFences<true>();
  95. }
  96. }
  97. protected:
  98. explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
  99. TTextureCache& texture_cache_, TBufferCache& buffer_cache_,
  100. TQueryCache& query_cache_)
  101. : rasterizer{rasterizer_}, gpu{gpu_}, syncpoint_manager{gpu.Host1x().GetSyncpointManager()},
  102. texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {
  103. if constexpr (can_async_check) {
  104. fence_thread =
  105. std::jthread([this](std::stop_token token) { ReleaseThreadFunc(token); });
  106. }
  107. }
  108. virtual ~FenceManager() {
  109. if constexpr (can_async_check) {
  110. fence_thread.request_stop();
  111. cv.notify_all();
  112. fence_thread.join();
  113. }
  114. }
  115. /// Creates a Fence Interface, does not create a backend fence if 'is_stubbed' is
  116. /// true
  117. virtual TFence CreateFence(bool is_stubbed) = 0;
  118. /// Queues a fence into the backend if the fence isn't stubbed.
  119. virtual void QueueFence(TFence& fence) = 0;
  120. /// Notifies that the backend fence has been signaled/reached in host GPU.
  121. virtual bool IsFenceSignaled(TFence& fence) const = 0;
  122. /// Waits until a fence has been signalled by the host GPU.
  123. virtual void WaitFence(TFence& fence) = 0;
  124. VideoCore::RasterizerInterface& rasterizer;
  125. Tegra::GPU& gpu;
  126. Tegra::Host1x::SyncpointManager& syncpoint_manager;
  127. TTextureCache& texture_cache;
  128. TBufferCache& buffer_cache;
  129. TQueryCache& query_cache;
  130. private:
  131. template <bool force_wait>
  132. void TryReleasePendingFences() {
  133. while (!fences.empty()) {
  134. TFence& current_fence = fences.front();
  135. if (ShouldWait() && !IsFenceSignaled(current_fence)) {
  136. if constexpr (force_wait) {
  137. WaitFence(current_fence);
  138. } else {
  139. return;
  140. }
  141. }
  142. PopAsyncFlushes();
  143. auto operations = std::move(pending_operations.front());
  144. pending_operations.pop_front();
  145. for (auto& operation : operations) {
  146. operation();
  147. }
  148. {
  149. std::unique_lock lock(ring_guard);
  150. delayed_destruction_ring.Push(std::move(current_fence));
  151. }
  152. fences.pop();
  153. }
  154. }
  155. void ReleaseThreadFunc(std::stop_token stop_token) {
  156. std::string name = "GPUFencingThread";
  157. MicroProfileOnThreadCreate(name.c_str());
  158. // Cleanup
  159. SCOPE_EXIT({ MicroProfileOnThreadExit(); });
  160. Common::SetCurrentThreadName(name.c_str());
  161. Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
  162. TFence current_fence;
  163. std::deque<std::function<void()>> current_operations;
  164. while (!stop_token.stop_requested()) {
  165. {
  166. std::unique_lock lock(guard);
  167. cv.wait(lock, [&] { return stop_token.stop_requested() || !fences.empty(); });
  168. if (stop_token.stop_requested()) [[unlikely]] {
  169. return;
  170. }
  171. current_fence = std::move(fences.front());
  172. current_operations = std::move(pending_operations.front());
  173. fences.pop();
  174. pending_operations.pop_front();
  175. }
  176. if (!current_fence->IsStubbed()) {
  177. WaitFence(current_fence);
  178. }
  179. PopAsyncFlushes();
  180. for (auto& operation : current_operations) {
  181. operation();
  182. }
  183. {
  184. std::unique_lock lock(ring_guard);
  185. delayed_destruction_ring.Push(std::move(current_fence));
  186. }
  187. }
  188. }
  189. bool ShouldWait() const {
  190. std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
  191. return texture_cache.ShouldWaitAsyncFlushes() || buffer_cache.ShouldWaitAsyncFlushes() ||
  192. query_cache.ShouldWaitAsyncFlushes();
  193. }
  194. bool ShouldFlush() const {
  195. std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
  196. return texture_cache.HasUncommittedFlushes() || buffer_cache.HasUncommittedFlushes() ||
  197. query_cache.HasUncommittedFlushes();
  198. }
  199. void PopAsyncFlushes() {
  200. {
  201. std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
  202. texture_cache.PopAsyncFlushes();
  203. buffer_cache.PopAsyncFlushes();
  204. }
  205. query_cache.PopAsyncFlushes();
  206. }
  207. void CommitAsyncFlushes() {
  208. {
  209. std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
  210. texture_cache.CommitAsyncFlushes();
  211. buffer_cache.CommitAsyncFlushes();
  212. }
  213. query_cache.CommitAsyncFlushes();
  214. }
  215. std::queue<TFence> fences;
  216. std::deque<std::function<void()>> uncommitted_operations;
  217. std::deque<std::deque<std::function<void()>>> pending_operations;
  218. std::mutex guard;
  219. std::mutex ring_guard;
  220. std::condition_variable cv;
  221. std::jthread fence_thread;
  222. DelayedDestructionRing<TFence, 6> delayed_destruction_ring;
  223. };
  224. } // namespace VideoCommon