From e8bd9aed8bf0f60455d0ae6a8f6f3abf92dd8305 Mon Sep 17 00:00:00 2001
From: Markus Wick <markus@selfnet.de>
Date: Wed, 7 Apr 2021 11:41:31 +0200
Subject: video_core: Use a CV for blocking commands.

There is no need for a busy loop here. Let's just use a condition variable to save some power.
---
 src/video_core/gpu_thread.cpp | 43 ++++++++++++++++++++++++++-----------------
 1 file changed, 26 insertions(+), 17 deletions(-)

(limited to 'src/video_core/gpu_thread.cpp')

diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index 9488bf5444..7addfbc7bb 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -56,11 +56,17 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
         } else if (const auto* invalidate = std::get_if<InvalidateRegionCommand>(&next.data)) {
             rasterizer->OnCPUWrite(invalidate->addr, invalidate->size);
         } else if (std::holds_alternative<EndProcessingCommand>(next.data)) {
-            return;
+            ASSERT(state.is_running == false);
         } else {
             UNREACHABLE();
         }
         state.signaled_fence.store(next.fence);
+        if (next.block) {
+            // We have to lock the write_lock to ensure that the condition_variable wait not get a
+            // race between the check and the lock itself.
+            std::lock_guard lk(state.write_lock);
+            state.cv.notify_all();
+        }
     }
 }
 
@@ -105,9 +111,8 @@ void ThreadManager::FlushRegion(VAddr addr, u64 size) {
     case Settings::GPUAccuracy::Extreme: {
         auto& gpu = system.GPU();
         u64 fence = gpu.RequestFlush(addr, size);
-        PushCommand(GPUTickCommand());
-        while (fence > gpu.CurrentFlushRequestFence()) {
-        }
+        PushCommand(GPUTickCommand(), true);
+        ASSERT(fence <= gpu.CurrentFlushRequestFence());
         break;
     }
     default:
@@ -124,18 +129,16 @@ void ThreadManager::FlushAndInvalidateRegion(VAddr addr, u64 size) {
     rasterizer->OnCPUWrite(addr, size);
 }
 
-void ThreadManager::WaitIdle() const {
-    while (state.last_fence > state.signaled_fence.load(std::memory_order_relaxed) &&
-           state.is_running) {
-    }
-}
-
 void ThreadManager::ShutDown() {
     if (!state.is_running) {
         return;
     }
 
-    state.is_running = false;
+    {
+        std::lock_guard lk(state.write_lock);
+        state.is_running = false;
+        state.cv.notify_all();
+    }
 
     if (!thread.joinable()) {
         return;
@@ -150,15 +153,21 @@ void ThreadManager::OnCommandListEnd() {
     PushCommand(OnCommandListEndCommand());
 }
 
-u64 ThreadManager::PushCommand(CommandData&& command_data) {
+u64 ThreadManager::PushCommand(CommandData&& command_data, bool block) {
+    if (!is_async) {
+        // In synchronous GPU mode, block the caller until the command has executed
+        block = true;
+    }
+
     std::unique_lock lk(state.write_lock);
     const u64 fence{++state.last_fence};
-    state.queue.Push(CommandDataContainer(std::move(command_data), fence));
+    state.queue.Push(CommandDataContainer(std::move(command_data), fence, block));
 
-    if (!is_async) {
-        // In synchronous GPU mode, block the caller until the command has executed
-        lk.unlock();
-        WaitIdle();
+    if (block) {
+        state.cv.wait(lk, [this, fence] {
+            return fence <= state.signaled_fence.load(std::memory_order_relaxed) ||
+                   !state.is_running;
+        });
     }
 
     return fence;
-- 
cgit v1.2.3-70-g09d2