fence_manager.h 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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(bool force) {
  93. if constexpr (!can_async_check) {
  94. if (force) {
  95. TryReleasePendingFences<true>();
  96. } else {
  97. TryReleasePendingFences<false>();
  98. }
  99. } else {
  100. if (!force) {
  101. return;
  102. }
  103. std::mutex wait_mutex;
  104. std::condition_variable wait_cv;
  105. std::atomic<bool> wait_finished{};
  106. std::function<void()> func([&] {
  107. std::scoped_lock lk(wait_mutex);
  108. wait_finished.store(true, std::memory_order_relaxed);
  109. wait_cv.notify_all();
  110. });
  111. SignalFence(std::move(func));
  112. std::unique_lock lk(wait_mutex);
  113. wait_cv.wait(lk, [&wait_finished] { return wait_finished.load(std::memory_order_relaxed); });
  114. }
  115. }
  116. protected:
  117. explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
  118. TTextureCache& texture_cache_, TBufferCache& buffer_cache_,
  119. TQueryCache& query_cache_)
  120. : rasterizer{rasterizer_}, gpu{gpu_}, syncpoint_manager{gpu.Host1x().GetSyncpointManager()},
  121. texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {
  122. if constexpr (can_async_check) {
  123. fence_thread =
  124. std::jthread([this](std::stop_token token) { ReleaseThreadFunc(token); });
  125. }
  126. }
  127. virtual ~FenceManager() {
  128. if constexpr (can_async_check) {
  129. fence_thread.request_stop();
  130. cv.notify_all();
  131. fence_thread.join();
  132. }
  133. }
  134. /// Creates a Fence Interface, does not create a backend fence if 'is_stubbed' is
  135. /// true
  136. virtual TFence CreateFence(bool is_stubbed) = 0;
  137. /// Queues a fence into the backend if the fence isn't stubbed.
  138. virtual void QueueFence(TFence& fence) = 0;
  139. /// Notifies that the backend fence has been signaled/reached in host GPU.
  140. virtual bool IsFenceSignaled(TFence& fence) const = 0;
  141. /// Waits until a fence has been signalled by the host GPU.
  142. virtual void WaitFence(TFence& fence) = 0;
  143. VideoCore::RasterizerInterface& rasterizer;
  144. Tegra::GPU& gpu;
  145. Tegra::Host1x::SyncpointManager& syncpoint_manager;
  146. TTextureCache& texture_cache;
  147. TBufferCache& buffer_cache;
  148. TQueryCache& query_cache;
  149. private:
  150. template <bool force_wait>
  151. void TryReleasePendingFences() {
  152. while (!fences.empty()) {
  153. TFence& current_fence = fences.front();
  154. if (ShouldWait() && !IsFenceSignaled(current_fence)) {
  155. if constexpr (force_wait) {
  156. WaitFence(current_fence);
  157. } else {
  158. return;
  159. }
  160. }
  161. PopAsyncFlushes();
  162. auto operations = std::move(pending_operations.front());
  163. pending_operations.pop_front();
  164. for (auto& operation : operations) {
  165. operation();
  166. }
  167. {
  168. std::unique_lock lock(ring_guard);
  169. delayed_destruction_ring.Push(std::move(current_fence));
  170. }
  171. fences.pop();
  172. }
  173. }
  174. void ReleaseThreadFunc(std::stop_token stop_token) {
  175. std::string name = "GPUFencingThread";
  176. MicroProfileOnThreadCreate(name.c_str());
  177. // Cleanup
  178. SCOPE_EXIT({ MicroProfileOnThreadExit(); });
  179. Common::SetCurrentThreadName(name.c_str());
  180. Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
  181. TFence current_fence;
  182. std::deque<std::function<void()>> current_operations;
  183. while (!stop_token.stop_requested()) {
  184. {
  185. std::unique_lock lock(guard);
  186. cv.wait(lock, [&] { return stop_token.stop_requested() || !fences.empty(); });
  187. if (stop_token.stop_requested()) [[unlikely]] {
  188. return;
  189. }
  190. current_fence = std::move(fences.front());
  191. current_operations = std::move(pending_operations.front());
  192. fences.pop();
  193. pending_operations.pop_front();
  194. }
  195. if (!current_fence->IsStubbed()) {
  196. WaitFence(current_fence);
  197. }
  198. PopAsyncFlushes();
  199. for (auto& operation : current_operations) {
  200. operation();
  201. }
  202. {
  203. std::unique_lock lock(ring_guard);
  204. delayed_destruction_ring.Push(std::move(current_fence));
  205. }
  206. }
  207. }
  208. bool ShouldWait() const {
  209. std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
  210. return texture_cache.ShouldWaitAsyncFlushes() || buffer_cache.ShouldWaitAsyncFlushes() ||
  211. query_cache.ShouldWaitAsyncFlushes();
  212. }
  213. bool ShouldFlush() const {
  214. std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
  215. return texture_cache.HasUncommittedFlushes() || buffer_cache.HasUncommittedFlushes() ||
  216. query_cache.HasUncommittedFlushes();
  217. }
  218. void PopAsyncFlushes() {
  219. {
  220. std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
  221. texture_cache.PopAsyncFlushes();
  222. buffer_cache.PopAsyncFlushes();
  223. }
  224. query_cache.PopAsyncFlushes();
  225. }
  226. void CommitAsyncFlushes() {
  227. {
  228. std::scoped_lock lock{buffer_cache.mutex, texture_cache.mutex};
  229. texture_cache.CommitAsyncFlushes();
  230. buffer_cache.CommitAsyncFlushes();
  231. }
  232. query_cache.CommitAsyncFlushes();
  233. }
  234. std::queue<TFence> fences;
  235. std::deque<std::function<void()>> uncommitted_operations;
  236. std::deque<std::deque<std::function<void()>>> pending_operations;
  237. std::mutex guard;
  238. std::mutex ring_guard;
  239. std::condition_variable cv;
  240. std::jthread fence_thread;
  241. DelayedDestructionRing<TFence, 6> delayed_destruction_ring;
  242. };
  243. } // namespace VideoCommon