summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2021-09-18 20:37:40 -0700
committerGravatar GitHub2021-09-18 20:37:40 -0700
commita9c3619d26beaaae338c5e902635147b9237350a (patch)
treeb86f29a288fa5bc5a337ba61a6833fa1c89c2270
parentMerge pull request #6485 from MonsterDruide1/tas (diff)
parentvk_scheduler: Use std::jthread (diff)
downloadyuzu-a9c3619d26beaaae338c5e902635147b9237350a.tar.gz
yuzu-a9c3619d26beaaae338c5e902635147b9237350a.tar.xz
yuzu-a9c3619d26beaaae338c5e902635147b9237350a.zip
Merge pull request #7019 from ameerj/videocore-jthread
videocore: Use std::jthread for worker threads
Diffstat (limited to '')
-rw-r--r--src/common/threadsafe_queue.h27
-rw-r--r--src/core/core.cpp6
-rw-r--r--src/video_core/gpu.cpp8
-rw-r--r--src/video_core/gpu.h3
-rw-r--r--src/video_core/gpu_thread.cpp57
-rw-r--r--src/video_core/gpu_thread.h13
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp19
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h7
8 files changed, 49 insertions, 91 deletions
diff --git a/src/common/threadsafe_queue.h b/src/common/threadsafe_queue.h
index 8430b9778..2c8c2b90e 100644
--- a/src/common/threadsafe_queue.h
+++ b/src/common/threadsafe_queue.h
@@ -14,7 +14,7 @@
14#include <utility> 14#include <utility>
15 15
16namespace Common { 16namespace Common {
17template <typename T> 17template <typename T, bool with_stop_token = false>
18class SPSCQueue { 18class SPSCQueue {
19public: 19public:
20 SPSCQueue() { 20 SPSCQueue() {
@@ -84,7 +84,7 @@ public:
84 void Wait() { 84 void Wait() {
85 if (Empty()) { 85 if (Empty()) {
86 std::unique_lock lock{cv_mutex}; 86 std::unique_lock lock{cv_mutex};
87 cv.wait(lock, [this]() { return !Empty(); }); 87 cv.wait(lock, [this] { return !Empty(); });
88 } 88 }
89 } 89 }
90 90
@@ -95,6 +95,19 @@ public:
95 return t; 95 return t;
96 } 96 }
97 97
98 T PopWait(std::stop_token stop_token) {
99 if (Empty()) {
100 std::unique_lock lock{cv_mutex};
101 cv.wait(lock, stop_token, [this] { return !Empty(); });
102 }
103 if (stop_token.stop_requested()) {
104 return T{};
105 }
106 T t;
107 Pop(t);
108 return t;
109 }
110
98 // not thread-safe 111 // not thread-safe
99 void Clear() { 112 void Clear() {
100 size.store(0); 113 size.store(0);
@@ -123,13 +136,13 @@ private:
123 ElementPtr* read_ptr; 136 ElementPtr* read_ptr;
124 std::atomic_size_t size{0}; 137 std::atomic_size_t size{0};
125 std::mutex cv_mutex; 138 std::mutex cv_mutex;
126 std::condition_variable cv; 139 std::conditional_t<with_stop_token, std::condition_variable_any, std::condition_variable> cv;
127}; 140};
128 141
129// a simple thread-safe, 142// a simple thread-safe,
130// single reader, multiple writer queue 143// single reader, multiple writer queue
131 144
132template <typename T> 145template <typename T, bool with_stop_token = false>
133class MPSCQueue { 146class MPSCQueue {
134public: 147public:
135 [[nodiscard]] std::size_t Size() const { 148 [[nodiscard]] std::size_t Size() const {
@@ -166,13 +179,17 @@ public:
166 return spsc_queue.PopWait(); 179 return spsc_queue.PopWait();
167 } 180 }
168 181
182 T PopWait(std::stop_token stop_token) {
183 return spsc_queue.PopWait(stop_token);
184 }
185
169 // not thread-safe 186 // not thread-safe
170 void Clear() { 187 void Clear() {
171 spsc_queue.Clear(); 188 spsc_queue.Clear();
172 } 189 }
173 190
174private: 191private:
175 SPSCQueue<T> spsc_queue; 192 SPSCQueue<T, with_stop_token> spsc_queue;
176 std::mutex write_lock; 193 std::mutex write_lock;
177}; 194};
178} // namespace Common 195} // namespace Common
diff --git a/src/core/core.cpp b/src/core/core.cpp
index b13350f6e..54ebed2c1 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -305,10 +305,7 @@ struct System::Impl {
305 is_powered_on = false; 305 is_powered_on = false;
306 exit_lock = false; 306 exit_lock = false;
307 307
308 if (gpu_core) { 308 gpu_core.reset();
309 gpu_core->ShutDown();
310 }
311
312 services.reset(); 309 services.reset();
313 service_manager.reset(); 310 service_manager.reset();
314 cheat_engine.reset(); 311 cheat_engine.reset();
@@ -317,7 +314,6 @@ struct System::Impl {
317 time_manager.Shutdown(); 314 time_manager.Shutdown();
318 core_timing.Shutdown(); 315 core_timing.Shutdown();
319 app_loader.reset(); 316 app_loader.reset();
320 gpu_core.reset();
321 perf_stats.reset(); 317 perf_stats.reset();
322 kernel.Shutdown(); 318 kernel.Shutdown();
323 memory.Reset(); 319 memory.Reset();
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index ff024f530..2ae3639b5 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -531,14 +531,6 @@ void GPU::TriggerCpuInterrupt(const u32 syncpoint_id, const u32 value) const {
531 interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value); 531 interrupt_manager.GPUInterruptSyncpt(syncpoint_id, value);
532} 532}
533 533
534void GPU::ShutDown() {
535 // Signal that threads should no longer block on syncpoint fences
536 shutting_down.store(true, std::memory_order_relaxed);
537 sync_cv.notify_all();
538
539 gpu_thread.ShutDown();
540}
541
542void GPU::OnCommandListEnd() { 534void GPU::OnCommandListEnd() {
543 if (is_async) { 535 if (is_async) {
544 // This command only applies to asynchronous GPU mode 536 // This command only applies to asynchronous GPU mode
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index a8e98e51b..e6a02a71b 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -219,9 +219,6 @@ public:
219 return *shader_notify; 219 return *shader_notify;
220 } 220 }
221 221
222 // Stops the GPU execution and waits for the GPU to finish working
223 void ShutDown();
224
225 /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame. 222 /// Allows the CPU/NvFlinger to wait on the GPU before presenting a frame.
226 void WaitFence(u32 syncpoint_id, u32 value); 223 void WaitFence(u32 syncpoint_id, u32 value);
227 224
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 46f642b19..9547f277a 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -17,9 +17,9 @@
17namespace VideoCommon::GPUThread { 17namespace VideoCommon::GPUThread {
18 18
19/// Runs the GPU thread 19/// Runs the GPU thread
20static void RunThread(Core::System& system, VideoCore::RendererBase& renderer, 20static void RunThread(std::stop_token stop_token, Core::System& system,
21 Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher, 21 VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
22 SynchState& state) { 22 Tegra::DmaPusher& dma_pusher, SynchState& state) {
23 std::string name = "yuzu:GPU"; 23 std::string name = "yuzu:GPU";
24 MicroProfileOnThreadCreate(name.c_str()); 24 MicroProfileOnThreadCreate(name.c_str());
25 SCOPE_EXIT({ MicroProfileOnThreadExit(); }); 25 SCOPE_EXIT({ MicroProfileOnThreadExit(); });
@@ -28,20 +28,14 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
28 Common::SetCurrentThreadPriority(Common::ThreadPriority::High); 28 Common::SetCurrentThreadPriority(Common::ThreadPriority::High);
29 system.RegisterHostThread(); 29 system.RegisterHostThread();
30 30
31 // Wait for first GPU command before acquiring the window context
32 state.queue.Wait();
33
34 // If emulation was stopped during disk shader loading, abort before trying to acquire context
35 if (!state.is_running) {
36 return;
37 }
38
39 auto current_context = context.Acquire(); 31 auto current_context = context.Acquire();
40 VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer(); 32 VideoCore::RasterizerInterface* const rasterizer = renderer.ReadRasterizer();
41 33
42 CommandDataContainer next; 34 while (!stop_token.stop_requested()) {
43 while (state.is_running) { 35 CommandDataContainer next = state.queue.PopWait(stop_token);
44 next = state.queue.PopWait(); 36 if (stop_token.stop_requested()) {
37 break;
38 }
45 if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { 39 if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) {
46 dma_pusher.Push(std::move(submit_list->entries)); 40 dma_pusher.Push(std::move(submit_list->entries));
47 dma_pusher.DispatchCalls(); 41 dma_pusher.DispatchCalls();
@@ -55,8 +49,6 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
55 rasterizer->FlushRegion(flush->addr, flush->size); 49 rasterizer->FlushRegion(flush->addr, flush->size);
56 } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) { 50 } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) {
57 rasterizer->OnCPUWrite(invalidate->addr, invalidate->size); 51 rasterizer->OnCPUWrite(invalidate->addr, invalidate->size);
58 } else if (std::holds_alternative<EndProcessingCommand>(next.data)) {
59 ASSERT(state.is_running == false);
60 } else { 52 } else {
61 UNREACHABLE(); 53 UNREACHABLE();
62 } 54 }
@@ -73,16 +65,14 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
73ThreadManager::ThreadManager(Core::System& system_, bool is_async_) 65ThreadManager::ThreadManager(Core::System& system_, bool is_async_)
74 : system{system_}, is_async{is_async_} {} 66 : system{system_}, is_async{is_async_} {}
75 67
76ThreadManager::~ThreadManager() { 68ThreadManager::~ThreadManager() = default;
77 ShutDown();
78}
79 69
80void ThreadManager::StartThread(VideoCore::RendererBase& renderer, 70void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
81 Core::Frontend::GraphicsContext& context, 71 Core::Frontend::GraphicsContext& context,
82 Tegra::DmaPusher& dma_pusher) { 72 Tegra::DmaPusher& dma_pusher) {
83 rasterizer = renderer.ReadRasterizer(); 73 rasterizer = renderer.ReadRasterizer();
84 thread = std::thread(RunThread, std::ref(system), std::ref(renderer), std::ref(context), 74 thread = std::jthread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
85 std::ref(dma_pusher), std::ref(state)); 75 std::ref(dma_pusher), std::ref(state));
86} 76}
87 77
88void ThreadManager::SubmitList(Tegra::CommandList&& entries) { 78void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
@@ -117,26 +107,6 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
117 rasterizer->OnCPUWrite(addr, size); 107 rasterizer->OnCPUWrite(addr, size);
118} 108}
119 109
120void ThreadManager::ShutDown() {
121 if (!state.is_running) {
122 return;
123 }
124
125 {
126 std::lock_guard lk(state.write_lock);
127 state.is_running = false;
128 state.cv.notify_all();
129 }
130
131 if (!thread.joinable()) {
132 return;
133 }
134
135 // Notify GPU thread that a shutdown is pending
136 PushCommand(EndProcessingCommand());
137 thread.join();
138}
139
140void ThreadManager::OnCommandListEnd() { 110void ThreadManager::OnCommandListEnd() {
141 PushCommand(OnCommandListEndCommand()); 111 PushCommand(OnCommandListEndCommand());
142} 112}
@@ -152,9 +122,8 @@ u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
152 state.queue.Push(CommandDataContainer(std::move(command_data), fence, block)); 122 state.queue.Push(CommandDataContainer(std::move(command_data), fence, block));
153 123
154 if (block) { 124 if (block) {
155 state.cv.wait(lk, [this, fence] { 125 state.cv.wait(lk, thread.get_stop_token(), [this, fence] {
156 return fence <= state.signaled_fence.load(std::memory_order_relaxed) || 126 return fence <= state.signaled_fence.load(std::memory_order_relaxed);
157 !state.is_running;
158 }); 127 });
159 } 128 }
160 129
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 11a648f38..91bada925 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -33,9 +33,6 @@ class RendererBase;
33 33
34namespace VideoCommon::GPUThread { 34namespace VideoCommon::GPUThread {
35 35
36/// Command to signal to the GPU thread that processing has ended
37struct EndProcessingCommand final {};
38
39/// Command to signal to the GPU thread that a command list is ready for processing 36/// Command to signal to the GPU thread that a command list is ready for processing
40struct SubmitListCommand final { 37struct SubmitListCommand final {
41 explicit SubmitListCommand(Tegra::CommandList&& entries_) : entries{std::move(entries_)} {} 38 explicit SubmitListCommand(Tegra::CommandList&& entries_) : entries{std::move(entries_)} {}
@@ -83,7 +80,7 @@ struct OnCommandListEndCommand final {};
83struct GPUTickCommand final {}; 80struct GPUTickCommand final {};
84 81
85using CommandData = 82using CommandData =
86 std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, 83 std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
87 InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand, 84 InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand,
88 GPUTickCommand>; 85 GPUTickCommand>;
89 86
@@ -100,14 +97,12 @@ struct CommandDataContainer {
100 97
101/// Struct used to synchronize the GPU thread 98/// Struct used to synchronize the GPU thread
102struct SynchState final { 99struct SynchState final {
103 std::atomic_bool is_running{true}; 100 using CommandQueue = Common::SPSCQueue<CommandDataContainer, true>;
104
105 using CommandQueue = Common::SPSCQueue<CommandDataContainer>;
106 std::mutex write_lock; 101 std::mutex write_lock;
107 CommandQueue queue; 102 CommandQueue queue;
108 u64 last_fence{}; 103 u64 last_fence{};
109 std::atomic<u64> signaled_fence{}; 104 std::atomic<u64> signaled_fence{};
110 std::condition_variable cv; 105 std::condition_variable_any cv;
111}; 106};
112 107
113/// Class used to manage the GPU thread 108/// Class used to manage the GPU thread
@@ -149,7 +144,7 @@ private:
149 VideoCore::RasterizerInterface* rasterizer = nullptr; 144 VideoCore::RasterizerInterface* rasterizer = nullptr;
150 145
151 SynchState state; 146 SynchState state;
152 std::thread thread; 147 std::jthread thread;
153}; 148};
154 149
155} // namespace VideoCommon::GPUThread 150} // namespace VideoCommon::GPUThread
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index 1d438787a..0c11c814f 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -43,17 +43,10 @@ VKScheduler::VKScheduler(const Device& device_, StateTracker& state_tracker_)
43 command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} { 43 command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
44 AcquireNewChunk(); 44 AcquireNewChunk();
45 AllocateWorkerCommandBuffer(); 45 AllocateWorkerCommandBuffer();
46 worker_thread = std::thread(&VKScheduler::WorkerThread, this); 46 worker_thread = std::jthread([this](std::stop_token token) { WorkerThread(token); });
47} 47}
48 48
49VKScheduler::~VKScheduler() { 49VKScheduler::~VKScheduler() = default;
50 {
51 std::lock_guard lock{work_mutex};
52 quit = true;
53 }
54 work_cv.notify_all();
55 worker_thread.join();
56}
57 50
58void VKScheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { 51void VKScheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
59 SubmitExecution(signal_semaphore, wait_semaphore); 52 SubmitExecution(signal_semaphore, wait_semaphore);
@@ -135,7 +128,7 @@ bool VKScheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
135 return true; 128 return true;
136} 129}
137 130
138void VKScheduler::WorkerThread() { 131void VKScheduler::WorkerThread(std::stop_token stop_token) {
139 Common::SetCurrentThreadName("yuzu:VulkanWorker"); 132 Common::SetCurrentThreadName("yuzu:VulkanWorker");
140 do { 133 do {
141 if (work_queue.empty()) { 134 if (work_queue.empty()) {
@@ -144,8 +137,8 @@ void VKScheduler::WorkerThread() {
144 std::unique_ptr<CommandChunk> work; 137 std::unique_ptr<CommandChunk> work;
145 { 138 {
146 std::unique_lock lock{work_mutex}; 139 std::unique_lock lock{work_mutex};
147 work_cv.wait(lock, [this] { return !work_queue.empty() || quit; }); 140 work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); });
148 if (quit) { 141 if (stop_token.stop_requested()) {
149 continue; 142 continue;
150 } 143 }
151 work = std::move(work_queue.front()); 144 work = std::move(work_queue.front());
@@ -158,7 +151,7 @@ void VKScheduler::WorkerThread() {
158 } 151 }
159 std::lock_guard reserve_lock{reserve_mutex}; 152 std::lock_guard reserve_lock{reserve_mutex};
160 chunk_reserve.push_back(std::move(work)); 153 chunk_reserve.push_back(std::move(work));
161 } while (!quit); 154 } while (!stop_token.stop_requested());
162} 155}
163 156
164void VKScheduler::AllocateWorkerCommandBuffer() { 157void VKScheduler::AllocateWorkerCommandBuffer() {
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 759ed5a48..bd22e4e83 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -187,7 +187,7 @@ private:
187 GraphicsPipeline* graphics_pipeline = nullptr; 187 GraphicsPipeline* graphics_pipeline = nullptr;
188 }; 188 };
189 189
190 void WorkerThread(); 190 void WorkerThread(std::stop_token stop_token);
191 191
192 void AllocateWorkerCommandBuffer(); 192 void AllocateWorkerCommandBuffer();
193 193
@@ -212,7 +212,7 @@ private:
212 vk::CommandBuffer current_cmdbuf; 212 vk::CommandBuffer current_cmdbuf;
213 213
214 std::unique_ptr<CommandChunk> chunk; 214 std::unique_ptr<CommandChunk> chunk;
215 std::thread worker_thread; 215 std::jthread worker_thread;
216 216
217 State state; 217 State state;
218 218
@@ -224,9 +224,8 @@ private:
224 std::vector<std::unique_ptr<CommandChunk>> chunk_reserve; 224 std::vector<std::unique_ptr<CommandChunk>> chunk_reserve;
225 std::mutex reserve_mutex; 225 std::mutex reserve_mutex;
226 std::mutex work_mutex; 226 std::mutex work_mutex;
227 std::condition_variable work_cv; 227 std::condition_variable_any work_cv;
228 std::condition_variable wait_cv; 228 std::condition_variable wait_cv;
229 std::atomic_bool quit{};
230}; 229};
231 230
232} // namespace Vulkan 231} // namespace Vulkan