Просмотр исходного кода

vk_scheduler: Use locks instead of SPSC a queue

This tries to fix a data race where we'd wait forever for the GPU.
ReinUsesLisp 5 лет назад
Родитель
Сommit
36f1586267

+ 33 - 25
src/video_core/renderer_vulkan/vk_scheduler.cpp

@@ -47,8 +47,11 @@ VKScheduler::VKScheduler(const Device& device_, StateTracker& state_tracker_)
 }
 
 VKScheduler::~VKScheduler() {
-    quit = true;
-    cv.notify_all();
+    {
+        std::lock_guard lock{work_mutex};
+        quit = true;
+    }
+    work_cv.notify_all();
     worker_thread.join();
 }
 
@@ -69,20 +72,19 @@ void VKScheduler::WaitWorker() {
     MICROPROFILE_SCOPE(Vulkan_WaitForWorker);
     DispatchWork();
 
-    bool finished = false;
-    do {
-        cv.notify_all();
-        std::unique_lock lock{mutex};
-        finished = chunk_queue.Empty();
-    } while (!finished);
+    std::unique_lock lock{work_mutex};
+    wait_cv.wait(lock, [this] { return work_queue.empty(); });
 }
 
 void VKScheduler::DispatchWork() {
     if (chunk->Empty()) {
         return;
     }
-    chunk_queue.Push(std::move(chunk));
-    cv.notify_all();
+    {
+        std::lock_guard lock{work_mutex};
+        work_queue.push(std::move(chunk));
+    }
+    work_cv.notify_one();
     AcquireNewChunk();
 }
 
@@ -135,22 +137,27 @@ bool VKScheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
 
 void VKScheduler::WorkerThread() {
     Common::SetCurrentThreadName("yuzu:VulkanWorker");
-    std::unique_lock lock{mutex};
     do {
-        cv.wait(lock, [this] { return !chunk_queue.Empty() || quit; });
-        if (quit) {
-            continue;
+        if (work_queue.empty()) {
+            wait_cv.notify_all();
         }
-        while (!chunk_queue.Empty()) {
-            auto extracted_chunk = std::move(chunk_queue.Front());
-            chunk_queue.Pop();
-            const bool has_submit = extracted_chunk->HasSubmit();
-            extracted_chunk->ExecuteAll(current_cmdbuf);
-            if (has_submit) {
-                AllocateWorkerCommandBuffer();
+        std::unique_ptr<CommandChunk> work;
+        {
+            std::unique_lock lock{work_mutex};
+            work_cv.wait(lock, [this] { return !work_queue.empty() || quit; });
+            if (quit) {
+                continue;
             }
-            chunk_reserve.Push(std::move(extracted_chunk));
+            work = std::move(work_queue.front());
+            work_queue.pop();
+        }
+        const bool has_submit = work->HasSubmit();
+        work->ExecuteAll(current_cmdbuf);
+        if (has_submit) {
+            AllocateWorkerCommandBuffer();
         }
+        std::lock_guard reserve_lock{reserve_mutex};
+        chunk_reserve.push_back(std::move(work));
     } while (!quit);
 }
 
@@ -269,12 +276,13 @@ void VKScheduler::EndRenderPass() {
 }
 
 void VKScheduler::AcquireNewChunk() {
-    if (chunk_reserve.Empty()) {
+    std::lock_guard lock{reserve_mutex};
+    if (chunk_reserve.empty()) {
         chunk = std::make_unique<CommandChunk>();
         return;
     }
-    chunk = std::move(chunk_reserve.Front());
-    chunk_reserve.Pop();
+    chunk = std::move(chunk_reserve.back());
+    chunk_reserve.pop_back();
 }
 
 } // namespace Vulkan

+ 9 - 7
src/video_core/renderer_vulkan/vk_scheduler.h

@@ -6,14 +6,14 @@
 
 #include <atomic>
 #include <condition_variable>
+#include <queue>
 #include <cstddef>
 #include <memory>
-#include <stack>
 #include <thread>
 #include <utility>
+
 #include "common/alignment.h"
 #include "common/common_types.h"
-#include "common/threadsafe_queue.h"
 #include "video_core/renderer_vulkan/vk_master_semaphore.h"
 #include "video_core/vulkan_common/vulkan_wrapper.h"
 
@@ -220,11 +220,13 @@ private:
     std::array<VkImage, 9> renderpass_images{};
     std::array<VkImageSubresourceRange, 9> renderpass_image_ranges{};
 
-    Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_queue;
-    Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_reserve;
-    std::mutex mutex;
-    std::condition_variable cv;
-    bool quit = false;
+    std::queue<std::unique_ptr<CommandChunk>> work_queue;
+    std::vector<std::unique_ptr<CommandChunk>> chunk_reserve;
+    std::mutex reserve_mutex;
+    std::mutex work_mutex;
+    std::condition_variable work_cv;
+    std::condition_variable wait_cv;
+    std::atomic_bool quit{};
 };
 
 } // namespace Vulkan