summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp84
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h6
2 files changed, 62 insertions, 28 deletions
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index e03685af1..c636a1625 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -47,14 +47,15 @@ Scheduler::Scheduler(const Device& device_, StateTracker& state_tracker_)
47Scheduler::~Scheduler() = default; 47Scheduler::~Scheduler() = default;
48 48
49void Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { 49void Scheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
50 // When flushing, we only send data to the worker thread; no waiting is necessary.
50 SubmitExecution(signal_semaphore, wait_semaphore); 51 SubmitExecution(signal_semaphore, wait_semaphore);
51 AllocateNewContext(); 52 AllocateNewContext();
52} 53}
53 54
54void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) { 55void Scheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
56 // When finishing, we need to wait for the submission to have executed on the device.
55 const u64 presubmit_tick = CurrentTick(); 57 const u64 presubmit_tick = CurrentTick();
56 SubmitExecution(signal_semaphore, wait_semaphore); 58 SubmitExecution(signal_semaphore, wait_semaphore);
57 WaitWorker();
58 Wait(presubmit_tick); 59 Wait(presubmit_tick);
59 AllocateNewContext(); 60 AllocateNewContext();
60} 61}
@@ -63,8 +64,13 @@ void Scheduler::WaitWorker() {
63 MICROPROFILE_SCOPE(Vulkan_WaitForWorker); 64 MICROPROFILE_SCOPE(Vulkan_WaitForWorker);
64 DispatchWork(); 65 DispatchWork();
65 66
66 std::unique_lock lock{work_mutex}; 67 // Ensure the queue is drained.
67 wait_cv.wait(lock, [this] { return work_queue.empty(); }); 68 std::unique_lock ql{queue_mutex};
69 event_cv.wait(ql, [this] { return work_queue.empty(); });
70
71 // Now wait for execution to finish.
72 // This needs to be done in the same order as WorkerThread.
73 std::unique_lock el{execution_mutex};
68} 74}
69 75
70void Scheduler::DispatchWork() { 76void Scheduler::DispatchWork() {
@@ -72,10 +78,10 @@ void Scheduler::DispatchWork() {
72 return; 78 return;
73 } 79 }
74 { 80 {
75 std::scoped_lock lock{work_mutex}; 81 std::scoped_lock ql{queue_mutex};
76 work_queue.push(std::move(chunk)); 82 work_queue.push(std::move(chunk));
77 } 83 }
78 work_cv.notify_one(); 84 event_cv.notify_all();
79 AcquireNewChunk(); 85 AcquireNewChunk();
80} 86}
81 87
@@ -137,30 +143,55 @@ bool Scheduler::UpdateRescaling(bool is_rescaling) {
137 143
138void Scheduler::WorkerThread(std::stop_token stop_token) { 144void Scheduler::WorkerThread(std::stop_token stop_token) {
139 Common::SetCurrentThreadName("VulkanWorker"); 145 Common::SetCurrentThreadName("VulkanWorker");
140 do { 146
147 const auto TryPopQueue{[this](auto& work) -> bool {
148 if (work_queue.empty()) {
149 return false;
150 }
151
152 work = std::move(work_queue.front());
153 work_queue.pop();
154 event_cv.notify_all();
155 return true;
156 }};
157
158 while (!stop_token.stop_requested()) {
141 std::unique_ptr<CommandChunk> work; 159 std::unique_ptr<CommandChunk> work;
142 bool has_submit{false}; 160
143 { 161 {
144 std::unique_lock lock{work_mutex}; 162 std::unique_lock lk{queue_mutex};
145 if (work_queue.empty()) { 163
146 wait_cv.notify_all(); 164 // Wait for work.
147 } 165 Common::CondvarWait(event_cv, lk, stop_token, [&] { return TryPopQueue(work); });
148 Common::CondvarWait(work_cv, lock, stop_token, [&] { return !work_queue.empty(); }); 166
167 // If we've been asked to stop, we're done.
149 if (stop_token.stop_requested()) { 168 if (stop_token.stop_requested()) {
150 continue; 169 return;
151 } 170 }
152 work = std::move(work_queue.front());
153 work_queue.pop();
154 171
155 has_submit = work->HasSubmit(); 172 // Exchange lock ownership so that we take the execution lock before
173 // the queue lock goes out of scope. This allows us to force execution
174 // to complete in the next step.
175 std::exchange(lk, std::unique_lock{execution_mutex});
176
177 // Perform the work, tracking whether the chunk was a submission
178 // before executing.
179 const bool has_submit = work->HasSubmit();
156 work->ExecuteAll(current_cmdbuf); 180 work->ExecuteAll(current_cmdbuf);
181
182 // If the chunk was a submission, reallocate the command buffer.
183 if (has_submit) {
184 AllocateWorkerCommandBuffer();
185 }
157 } 186 }
158 if (has_submit) { 187
159 AllocateWorkerCommandBuffer(); 188 {
189 std::scoped_lock rl{reserve_mutex};
190
191 // Recycle the chunk back to the reserve.
192 chunk_reserve.emplace_back(std::move(work));
160 } 193 }
161 std::scoped_lock reserve_lock{reserve_mutex}; 194 }
162 chunk_reserve.push_back(std::move(work));
163 } while (!stop_token.stop_requested());
164} 195}
165 196
166void Scheduler::AllocateWorkerCommandBuffer() { 197void Scheduler::AllocateWorkerCommandBuffer() {
@@ -289,13 +320,16 @@ void Scheduler::EndRenderPass() {
289} 320}
290 321
291void Scheduler::AcquireNewChunk() { 322void Scheduler::AcquireNewChunk() {
292 std::scoped_lock lock{reserve_mutex}; 323 std::scoped_lock rl{reserve_mutex};
324
293 if (chunk_reserve.empty()) { 325 if (chunk_reserve.empty()) {
326 // If we don't have anything reserved, we need to make a new chunk.
294 chunk = std::make_unique<CommandChunk>(); 327 chunk = std::make_unique<CommandChunk>();
295 return; 328 } else {
329 // Otherwise, we can just take from the reserve.
330 chunk = std::make_unique<CommandChunk>();
331 chunk_reserve.pop_back();
296 } 332 }
297 chunk = std::move(chunk_reserve.back());
298 chunk_reserve.pop_back();
299} 333}
300 334
301} // namespace Vulkan 335} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index bd4cb0f7e..8d75ce987 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -232,10 +232,10 @@ private:
232 232
233 std::queue<std::unique_ptr<CommandChunk>> work_queue; 233 std::queue<std::unique_ptr<CommandChunk>> work_queue;
234 std::vector<std::unique_ptr<CommandChunk>> chunk_reserve; 234 std::vector<std::unique_ptr<CommandChunk>> chunk_reserve;
235 std::mutex execution_mutex;
235 std::mutex reserve_mutex; 236 std::mutex reserve_mutex;
236 std::mutex work_mutex; 237 std::mutex queue_mutex;
237 std::condition_variable_any work_cv; 238 std::condition_variable_any event_cv;
238 std::condition_variable wait_cv;
239 std::jthread worker_thread; 239 std::jthread worker_thread;
240}; 240};
241 241