diff --git a/Source/Core/VideoCommon/AsyncRequests.cpp b/Source/Core/VideoCommon/AsyncRequests.cpp index 6671f93293a..7348724e43a 100644 --- a/Source/Core/VideoCommon/AsyncRequests.cpp +++ b/Source/Core/VideoCommon/AsyncRequests.cpp @@ -3,8 +3,6 @@ #include "VideoCommon/AsyncRequests.h" -#include - #include "Core/System.h" #include "VideoCommon/Fifo.h" @@ -16,33 +14,25 @@ AsyncRequests AsyncRequests::s_singleton; AsyncRequests::AsyncRequests() = default; -void AsyncRequests::PullEventsInternal() +void AsyncRequests::PullEvents() { + if (m_queue.Empty()) + return; + // This is only called if the queue isn't empty. // So just flush the pipeline to get accurate results. g_vertex_manager->Flush(); - std::unique_lock lock(m_mutex); - m_empty.Set(); - - while (!m_queue.empty()) + while (!m_queue.Empty()) { - Event e = std::move(m_queue.front()); - lock.unlock(); - std::invoke(e); - lock.lock(); - - m_queue.pop(); + std::invoke(std::move(m_queue.Front())); + m_queue.Pop(); } - - m_cond.notify_one(); } void AsyncRequests::QueueEvent(Event&& event) { - m_empty.Clear(); - - m_queue.push(std::move(event)); + m_queue.Push(std::move(event)); auto& system = Core::System::GetInstance(); system.GetFifo().RunGpu(); @@ -50,12 +40,10 @@ void AsyncRequests::QueueEvent(Event&& event) void AsyncRequests::WaitForEmptyQueue() { - std::unique_lock lock(m_mutex); - m_cond.wait(lock, [this] { return m_queue.empty(); }); + m_queue.WaitForEmpty(); } void AsyncRequests::SetPassthrough(bool enable) { - std::unique_lock lock(m_mutex); m_passthrough = enable; } diff --git a/Source/Core/VideoCommon/AsyncRequests.h b/Source/Core/VideoCommon/AsyncRequests.h index 7b8cdd036b1..2c3e84fd473 100644 --- a/Source/Core/VideoCommon/AsyncRequests.h +++ b/Source/Core/VideoCommon/AsyncRequests.h @@ -3,14 +3,12 @@ #pragma once -#include +#include #include #include -#include -#include -#include "Common/Flag.h" #include "Common/Functional.h" +#include "Common/SPSCQueue.h" struct EfbPokeData; class PointerWrap; @@ -20,43 +18,39 @@ class AsyncRequests public: AsyncRequests(); - void PullEvents() - { - if (!m_empty.IsSet()) - PullEventsInternal(); - } - void WaitForEmptyQueue(); - void SetPassthrough(bool enable); + // Called from the Video thread. + void PullEvents(); - template + // The following are called from the CPU thread. + void WaitForEmptyQueue(); + + template F> void PushEvent(F&& callback) { - std::unique_lock lock(m_mutex); - if (m_passthrough) { - std::invoke(callback); + std::invoke(std::forward(callback)); return; } QueueEvent(Event{std::forward(callback)}); } - template + template F> auto PushBlockingEvent(F&& callback) -> std::invoke_result_t { - std::unique_lock lock(m_mutex); - if (m_passthrough) - return std::invoke(callback); + return std::invoke(std::forward(callback)); std::packaged_task task{std::forward(callback)}; QueueEvent(Event{[&] { task(); }}); - lock.unlock(); return task.get_future().get(); } + // Not thread-safe. Only set during initialization. + void SetPassthrough(bool enable); + static AsyncRequests* GetInstance() { return &s_singleton; } private: @@ -64,14 +58,9 @@ private: void QueueEvent(Event&& event); - void PullEventsInternal(); - static AsyncRequests s_singleton; - Common::Flag m_empty; - std::queue m_queue; - std::mutex m_mutex; - std::condition_variable m_cond; + Common::WaitableSPSCQueue m_queue; bool m_passthrough = true; };