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 <mutex>
#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<std::mutex> 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<std::mutex> lock(m_mutex);
m_cond.wait(lock, [this] { return m_queue.empty(); });
m_queue.WaitForEmpty();
}
void AsyncRequests::SetPassthrough(bool enable)
{
std::unique_lock<std::mutex> lock(m_mutex);
m_passthrough = enable;
}

View File

@ -3,14 +3,12 @@
#pragma once
#include <condition_variable>
#include <concepts>
#include <functional>
#include <future>
#include <mutex>
#include <queue>
#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 <typename F>
// The following are called from the CPU thread.
void WaitForEmptyQueue();
template <std::invocable<> F>
void PushEvent(F&& callback)
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_passthrough)
{
std::invoke(callback);
std::invoke(std::forward<F>(callback));
return;
}
QueueEvent(Event{std::forward<F>(callback)});
}
template <typename F>
template <std::invocable<> F>
auto PushBlockingEvent(F&& callback) -> std::invoke_result_t<F>
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_passthrough)
return std::invoke(callback);
return std::invoke(std::forward<F>(callback));
std::packaged_task task{std::forward<F>(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<Event> m_queue;
std::mutex m_mutex;
std::condition_variable m_cond;
Common::WaitableSPSCQueue<Event> m_queue;
bool m_passthrough = true;
};