mirror of
https://github.com/libretro/ppsspp.git
synced 2025-02-21 09:21:02 +00:00
Factor out event queue logic to a template mixin.
This commit is contained in:
parent
c6956c299a
commit
450e4c7fea
@ -373,7 +373,7 @@ void GLES_GPU::ProcessEvent(GPUEvent ev) {
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR_LOG(G3D, "Unexpected GPU event type: %d", ev);
|
||||
GPUCommon::ProcessEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
107
GPU/GPUCommon.h
107
GPU/GPUCommon.h
@ -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 {
|
||||
|
@ -163,6 +163,10 @@ struct GPUEvent {
|
||||
GPUInvalidationType type;
|
||||
} invalidate_cache;
|
||||
};
|
||||
|
||||
operator GPUEventType() const {
|
||||
return type;
|
||||
}
|
||||
};
|
||||
|
||||
class GPUInterface
|
||||
|
@ -49,5 +49,4 @@ public:
|
||||
|
||||
protected:
|
||||
virtual void FastRunLoop(DisplayList &list);
|
||||
virtual void ProcessEvent(GPUEvent ev) {}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user