mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-12-16 04:09:39 +00:00
VideoCommon: Clean up and eliminate the mutex in AsyncRequests using WaitableSPSCQueue.
This commit is contained in:
parent
3d764f7b42
commit
09a125fec4
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user