Factor out event queue logic to a template mixin.

This commit is contained in:
Unknown W. Brackets 2013-08-10 03:33:09 -07:00
parent c6956c299a
commit 450e4c7fea
5 changed files with 116 additions and 94 deletions

View File

@ -373,7 +373,7 @@ void GLES_GPU::ProcessEvent(GPUEvent ev) {
break;
default:
ERROR_LOG(G3D, "Unexpected GPU event type: %d", ev);
GPUCommon::ProcessEvent(ev);
}
}

View File

@ -29,6 +29,7 @@ GPUCommon::GPUCommon() :
dls[i].state = PSP_GE_DL_STATE_NONE;
dls[i].waitTicks = 0;
}
SetThreadEnabled(g_Config.bSeparateCPUThread);
}
void GPUCommon::PopDLQueue() {
@ -539,90 +540,19 @@ inline void GPUCommon::UpdateState(GPUState state) {
downcount = 0;
}
GPUEvent GPUCommon::GetNextEvent() {
easy_guard guard(eventsLock);
if (events.empty()) {
eventsDrain.notify_one();
return GPU_EVENT_INVALID;
void GPUCommon::ProcessEvent(GPUEvent ev) {
switch (ev.type) {
case GPU_EVENT_PROCESS_QUEUE:
ProcessDLQueueInternal();
break;
case GPU_EVENT_REAPPLY_GFX_STATE:
ReapplyGfxStateInternal();
break;
default:
ERROR_LOG(G3D, "Unexpected GPU event type: %d", ev);
}
GPUEvent ev = events.front();
events.pop_front();
return ev;
}
bool GPUCommon::HasEvents() {
easy_guard guard(eventsLock);
return !events.empty();
}
void GPUCommon::ScheduleEvent(GPUEvent ev) {
easy_guard guard(eventsLock);
events.push_back(ev);
eventsWait.notify_one();
guard.unlock();
if (!g_Config.bSeparateCPUThread) {
RunEventsUntil(0);
}
}
void GPUCommon::RunEventsUntil(u64 globalticks) {
do {
for (GPUEvent ev = GetNextEvent(); ev.type != GPU_EVENT_INVALID; ev = GetNextEvent()) {
switch (ev.type) {
case GPU_EVENT_PROCESS_QUEUE:
ProcessDLQueueInternal();
break;
case GPU_EVENT_REAPPLY_GFX_STATE:
ReapplyGfxStateInternal();
break;
case GPU_EVENT_FINISH_EVENT_LOOP:
// Stop waiting.
globalticks = 0;
break;
case GPU_EVENT_SYNC_THREAD:
break;
default:
ProcessEvent(ev);
}
}
// Quit the loop if the queue is drained and coreState has tripped.
if (coreState != CORE_RUNNING) {
return;
}
// coreState changes won't wake us, so recheck periodically.
if (!g_Config.bSeparateCPUThread) {
return;
}
eventsWait.wait_for(eventsWaitLock, 1);
} while (CoreTiming::GetTicks() < globalticks);
}
void GPUCommon::FinishEventLoop() {
if (g_Config.bSeparateCPUThread) {
ScheduleEvent(GPU_EVENT_FINISH_EVENT_LOOP);
}
}
void GPUCommon::SyncThread() {
if (!g_Config.bSeparateCPUThread) {
return;
}
// It could be that we are *currently* processing the last event.
// The events queue will be empty (HasEvents() = false), but it's not done.
// So we schedule a nothing event and wait for that to finish.
ScheduleEvent(GPU_EVENT_SYNC_THREAD);
while (HasEvents() && coreState == CORE_RUNNING) {
eventsDrain.wait_for(eventsDrainLock, 1);
};
}
int GPUCommon::GetNextListIndex() {

View File

@ -2,17 +2,110 @@
#include "native/base/mutex.h"
#include "GPU/GPUInterface.h"
#include "Core/CoreTiming.h"
#include <deque>
class GPUCommon : public GPUInterface
template <typename B, typename Event, typename EventType, EventType EVENT_INVALID, EventType EVENT_SYNC, EventType EVENT_FINISH>
struct ThreadEventQueue : public B {
void SetThreadEnabled(bool threadEnabled) {
threadEnabled_ = threadEnabled;
}
void ScheduleEvent(Event ev) {
{
lock_guard guard(eventsLock_);
events_.push_back(ev);
eventsWait_.notify_one();
}
if (!threadEnabled_) {
RunEventsUntil(0);
}
}
bool HasEvents() {
lock_guard guard(eventsLock_);
return !events_.empty();
}
Event GetNextEvent() {
lock_guard guard(eventsLock_);
if (events_.empty()) {
eventsDrain_.notify_one();
return EVENT_INVALID;
}
Event ev = events_.front();
events_.pop_front();
return ev;
}
void RunEventsUntil(u64 globalticks) {
do {
for (Event ev = GetNextEvent(); EventType(ev) != EVENT_INVALID; ev = GetNextEvent()) {
switch (EventType(ev)) {
case EVENT_FINISH:
// Stop waiting.
globalticks = 0;
break;
case EVENT_SYNC:
break;
default:
ProcessEvent(ev);
}
}
// Quit the loop if the queue is drained and coreState has tripped, or threading is disabled.
if (coreState != CORE_RUNNING || !threadEnabled_) {
return;
}
// coreState changes won't wake us, so recheck periodically.
eventsWait_.wait_for(eventsWaitLock_, 1);
} while (CoreTiming::GetTicks() < globalticks);
}
void SyncThread() {
if (!threadEnabled_) {
return;
}
// While processing the last event, HasEvents() will be false even while not done.
// So we schedule a nothing event and wait for that to finish.
ScheduleEvent(EVENT_SYNC);
while (HasEvents() && coreState == CORE_RUNNING) {
eventsDrain_.wait_for(eventsDrainLock_, 1);
}
}
void FinishEventLoop() {
if (threadEnabled_) {
ScheduleEvent(EVENT_FINISH);
}
}
protected:
virtual void ProcessEvent(Event ev) = 0;
private:
bool threadEnabled_;
std::deque<Event> events_;
recursive_mutex eventsLock_;
recursive_mutex eventsWaitLock_;
recursive_mutex eventsDrainLock_;
condition_variable eventsWait_;
condition_variable eventsDrain_;
};
typedef ThreadEventQueue<GPUInterface, GPUEvent, GPUEventType, GPU_EVENT_INVALID, GPU_EVENT_SYNC_THREAD, GPU_EVENT_FINISH_EVENT_LOOP> GPUThreadEventQueue;
class GPUCommon : public GPUThreadEventQueue
{
public:
GPUCommon();
virtual ~GPUCommon() {}
virtual void RunEventsUntil(u64 globalticks);
virtual void FinishEventLoop();
virtual void InterruptStart(int listid);
virtual void InterruptEnd(int listid);
virtual void SyncEnd(WaitType waitType, int listid, bool wokeThreads);
@ -34,7 +127,6 @@ public:
virtual u32 Continue();
virtual u32 Break(int mode);
virtual void ReapplyGfxState();
virtual void SyncThread();
protected:
// To avoid virtual calls to PreExecuteOp().
@ -45,12 +137,9 @@ protected:
void PopDLQueue();
void CheckDrawSync();
int GetNextListIndex();
GPUEvent GetNextEvent();
bool HasEvents();
void ScheduleEvent(GPUEvent ev);
void ProcessDLQueueInternal();
void ReapplyGfxStateInternal();
virtual void ProcessEvent(GPUEvent ev) = 0;
virtual void ProcessEvent(GPUEvent ev);
// Allows early unlocking with a guard. Do not double unlock.
class easy_guard {

View File

@ -163,6 +163,10 @@ struct GPUEvent {
GPUInvalidationType type;
} invalidate_cache;
};
operator GPUEventType() const {
return type;
}
};
class GPUInterface

View File

@ -49,5 +49,4 @@ public:
protected:
virtual void FastRunLoop(DisplayList &list);
virtual void ProcessEvent(GPUEvent ev) {}
};