VideoCommon: Clean up and eliminate the mutex in AsyncRequests using WaitableSPSCQueue.

This commit is contained in:
Jordan Woyak 2025-10-25 16:40:56 -05:00
parent 3d764f7b42
commit 09a125fec4
2 changed files with 24 additions and 47 deletions

View File

@ -3,8 +3,6 @@
#include "VideoCommon/AsyncRequests.h" #include "VideoCommon/AsyncRequests.h"
#include <mutex>
#include "Core/System.h" #include "Core/System.h"
#include "VideoCommon/Fifo.h" #include "VideoCommon/Fifo.h"
@ -16,33 +14,25 @@ AsyncRequests AsyncRequests::s_singleton;
AsyncRequests::AsyncRequests() = default; AsyncRequests::AsyncRequests() = default;
void AsyncRequests::PullEventsInternal() void AsyncRequests::PullEvents()
{ {
if (m_queue.Empty())
return;
// This is only called if the queue isn't empty. // This is only called if the queue isn't empty.
// So just flush the pipeline to get accurate results. // So just flush the pipeline to get accurate results.
g_vertex_manager->Flush(); g_vertex_manager->Flush();
std::unique_lock<std::mutex> lock(m_mutex); while (!m_queue.Empty())
m_empty.Set();
while (!m_queue.empty())
{ {
Event e = std::move(m_queue.front()); std::invoke(std::move(m_queue.Front()));
lock.unlock(); m_queue.Pop();
std::invoke(e);
lock.lock();
m_queue.pop();
} }
m_cond.notify_one();
} }
void AsyncRequests::QueueEvent(Event&& event) 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(); auto& system = Core::System::GetInstance();
system.GetFifo().RunGpu(); system.GetFifo().RunGpu();
@ -50,12 +40,10 @@ void AsyncRequests::QueueEvent(Event&& event)
void AsyncRequests::WaitForEmptyQueue() void AsyncRequests::WaitForEmptyQueue()
{ {
std::unique_lock<std::mutex> lock(m_mutex); m_queue.WaitForEmpty();
m_cond.wait(lock, [this] { return m_queue.empty(); });
} }
void AsyncRequests::SetPassthrough(bool enable) void AsyncRequests::SetPassthrough(bool enable)
{ {
std::unique_lock<std::mutex> lock(m_mutex);
m_passthrough = enable; m_passthrough = enable;
} }

View File

@ -3,14 +3,12 @@
#pragma once #pragma once
#include <condition_variable> #include <concepts>
#include <functional> #include <functional>
#include <future> #include <future>
#include <mutex>
#include <queue>
#include "Common/Flag.h"
#include "Common/Functional.h" #include "Common/Functional.h"
#include "Common/SPSCQueue.h"
struct EfbPokeData; struct EfbPokeData;
class PointerWrap; class PointerWrap;
@ -20,43 +18,39 @@ class AsyncRequests
public: public:
AsyncRequests(); AsyncRequests();
void PullEvents() // Called from the Video thread.
{ void PullEvents();
if (!m_empty.IsSet())
PullEventsInternal();
}
void WaitForEmptyQueue();
void SetPassthrough(bool enable);
template <typename F> // The following are called from the CPU thread.
void WaitForEmptyQueue();
template <std::invocable<> F>
void PushEvent(F&& callback) void PushEvent(F&& callback)
{ {
std::unique_lock<std::mutex> lock(m_mutex);
if (m_passthrough) if (m_passthrough)
{ {
std::invoke(callback); std::invoke(std::forward<F>(callback));
return; return;
} }
QueueEvent(Event{std::forward<F>(callback)}); QueueEvent(Event{std::forward<F>(callback)});
} }
template <typename F> template <std::invocable<> F>
auto PushBlockingEvent(F&& callback) -> std::invoke_result_t<F> auto PushBlockingEvent(F&& callback) -> std::invoke_result_t<F>
{ {
std::unique_lock<std::mutex> lock(m_mutex);
if (m_passthrough) if (m_passthrough)
return std::invoke(callback); return std::invoke(std::forward<F>(callback));
std::packaged_task task{std::forward<F>(callback)}; std::packaged_task task{std::forward<F>(callback)};
QueueEvent(Event{[&] { task(); }}); QueueEvent(Event{[&] { task(); }});
lock.unlock();
return task.get_future().get(); return task.get_future().get();
} }
// Not thread-safe. Only set during initialization.
void SetPassthrough(bool enable);
static AsyncRequests* GetInstance() { return &s_singleton; } static AsyncRequests* GetInstance() { return &s_singleton; }
private: private:
@ -64,14 +58,9 @@ private:
void QueueEvent(Event&& event); void QueueEvent(Event&& event);
void PullEventsInternal();
static AsyncRequests s_singleton; static AsyncRequests s_singleton;
Common::Flag m_empty; Common::WaitableSPSCQueue<Event> m_queue;
std::queue<Event> m_queue;
std::mutex m_mutex;
std::condition_variable m_cond;
bool m_passthrough = true; bool m_passthrough = true;
}; };