2012-12-28 21:58:00 +01:00
|
|
|
#pragma once
|
|
|
|
|
2013-08-07 23:27:29 -07:00
|
|
|
#include "native/base/mutex.h"
|
|
|
|
#include "GPU/GPUInterface.h"
|
2013-08-10 03:33:09 -07:00
|
|
|
#include "Core/CoreTiming.h"
|
2013-08-07 23:59:32 -07:00
|
|
|
#include <deque>
|
2012-12-28 21:58:00 +01:00
|
|
|
|
2013-08-10 03:33:09 -07:00
|
|
|
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
|
2012-12-28 21:58:00 +01:00
|
|
|
{
|
|
|
|
public:
|
2013-04-04 23:19:28 -07:00
|
|
|
GPUCommon();
|
|
|
|
virtual ~GPUCommon() {}
|
2012-12-29 02:10:29 +01:00
|
|
|
|
2013-04-06 08:19:54 -07:00
|
|
|
virtual void InterruptStart(int listid);
|
|
|
|
virtual void InterruptEnd(int listid);
|
2013-04-07 12:45:42 -07:00
|
|
|
virtual void SyncEnd(WaitType waitType, int listid, bool wokeThreads);
|
2013-03-31 23:02:46 -07:00
|
|
|
virtual void EnableInterrupts(bool enable) {
|
|
|
|
interruptsEnabled_ = enable;
|
|
|
|
}
|
2013-02-04 00:41:16 +01:00
|
|
|
|
2013-03-31 23:02:46 -07:00
|
|
|
virtual void ExecuteOp(u32 op, u32 diff);
|
2012-12-29 02:10:29 +01:00
|
|
|
virtual void PreExecuteOp(u32 op, u32 diff);
|
|
|
|
virtual bool InterpretList(DisplayList &list);
|
|
|
|
virtual bool ProcessDLQueue();
|
2013-03-31 23:23:03 -07:00
|
|
|
virtual u32 UpdateStall(int listid, u32 newstall);
|
2012-12-21 22:50:35 -08:00
|
|
|
virtual u32 EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head);
|
2013-03-31 23:23:03 -07:00
|
|
|
virtual u32 DequeueList(int listid);
|
|
|
|
virtual int ListSync(int listid, int mode);
|
|
|
|
virtual u32 DrawSync(int mode);
|
2012-12-29 11:41:33 -08:00
|
|
|
virtual void DoState(PointerWrap &p);
|
2013-03-03 13:00:21 +01:00
|
|
|
virtual bool FramebufferDirty() { return true; }
|
2013-03-31 23:23:03 -07:00
|
|
|
virtual u32 Continue();
|
|
|
|
virtual u32 Break(int mode);
|
2013-08-04 15:15:50 -07:00
|
|
|
virtual void ReapplyGfxState();
|
2012-12-28 21:58:00 +01:00
|
|
|
|
|
|
|
protected:
|
2013-04-28 14:23:30 -07:00
|
|
|
// To avoid virtual calls to PreExecuteOp().
|
|
|
|
virtual void FastRunLoop(DisplayList &list) = 0;
|
|
|
|
void SlowRunLoop(DisplayList &list);
|
2013-04-28 13:34:29 -07:00
|
|
|
void UpdatePC(u32 currentPC, u32 newPC = 0);
|
|
|
|
void UpdateState(GPUState state);
|
2013-04-04 23:19:28 -07:00
|
|
|
void PopDLQueue();
|
|
|
|
void CheckDrawSync();
|
2013-08-04 16:31:11 -07:00
|
|
|
int GetNextListIndex();
|
2013-08-07 23:59:32 -07:00
|
|
|
void ProcessDLQueueInternal();
|
|
|
|
void ReapplyGfxStateInternal();
|
2013-08-10 03:33:09 -07:00
|
|
|
virtual void ProcessEvent(GPUEvent ev);
|
2013-04-03 08:10:35 -07:00
|
|
|
|
2013-08-09 01:03:54 -07:00
|
|
|
// Allows early unlocking with a guard. Do not double unlock.
|
2013-08-09 00:32:40 -07:00
|
|
|
class easy_guard {
|
|
|
|
public:
|
|
|
|
easy_guard(recursive_mutex &mtx) : mtx_(mtx), locked_(true) { mtx_.lock(); }
|
|
|
|
~easy_guard() { if (locked_) mtx_.unlock(); }
|
2013-08-09 01:03:54 -07:00
|
|
|
void unlock() { if (locked_) mtx_.unlock(); else Crash(); locked_ = false; }
|
2013-08-09 00:32:40 -07:00
|
|
|
|
|
|
|
private:
|
|
|
|
bool locked_;
|
|
|
|
recursive_mutex &mtx_;
|
|
|
|
};
|
|
|
|
|
2013-04-04 23:19:28 -07:00
|
|
|
typedef std::list<int> DisplayListQueue;
|
2012-12-29 02:10:29 +01:00
|
|
|
|
2013-04-04 23:19:28 -07:00
|
|
|
DisplayList dls[DisplayListMaxCount];
|
2012-12-28 21:58:00 +01:00
|
|
|
DisplayList *currentList;
|
|
|
|
DisplayListQueue dlQueue;
|
2013-08-07 23:27:29 -07:00
|
|
|
recursive_mutex listLock;
|
2012-12-29 02:10:29 +01:00
|
|
|
|
2013-08-07 23:59:32 -07:00
|
|
|
std::deque<GPUEvent> events;
|
|
|
|
recursive_mutex eventsLock;
|
2013-08-08 23:22:05 -07:00
|
|
|
recursive_mutex eventsWaitLock;
|
|
|
|
recursive_mutex eventsDrainLock;
|
|
|
|
condition_variable eventsWait;
|
|
|
|
condition_variable eventsDrain;
|
2013-08-07 23:59:32 -07:00
|
|
|
|
2013-02-04 00:41:16 +01:00
|
|
|
bool interruptRunning;
|
2013-04-04 01:07:30 -07:00
|
|
|
GPUState gpuState;
|
2013-04-04 00:35:38 -07:00
|
|
|
bool isbreak;
|
2013-04-07 12:45:42 -07:00
|
|
|
u64 drawCompleteTicks;
|
2013-04-09 00:56:04 -07:00
|
|
|
u64 busyTicks;
|
2012-12-29 02:10:29 +01:00
|
|
|
|
2013-04-28 14:23:30 -07:00
|
|
|
int downcount;
|
2013-04-03 08:10:35 -07:00
|
|
|
u64 startingTicks;
|
|
|
|
u32 cycleLastPC;
|
|
|
|
int cyclesExecuted;
|
|
|
|
|
2012-12-29 02:10:29 +01:00
|
|
|
bool dumpNextFrame_;
|
|
|
|
bool dumpThisFrame_;
|
2013-03-31 23:02:46 -07:00
|
|
|
bool interruptsEnabled_;
|
2013-02-04 00:41:16 +01:00
|
|
|
|
|
|
|
public:
|
|
|
|
virtual DisplayList* getList(int listid)
|
|
|
|
{
|
2013-04-04 23:19:28 -07:00
|
|
|
return &dls[listid];
|
2013-02-04 00:41:16 +01:00
|
|
|
}
|
2013-02-10 16:36:06 +01:00
|
|
|
|
2013-04-04 23:19:28 -07:00
|
|
|
const std::list<int>& GetDisplayLists()
|
2013-02-10 16:36:06 +01:00
|
|
|
{
|
|
|
|
return dlQueue;
|
|
|
|
}
|
|
|
|
DisplayList* GetCurrentDisplayList()
|
|
|
|
{
|
|
|
|
return currentList;
|
|
|
|
}
|
2013-02-17 01:06:06 +01:00
|
|
|
virtual bool DecodeTexture(u8* dest, GPUgstate state)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
std::vector<FramebufferInfo> GetFramebufferList()
|
|
|
|
{
|
|
|
|
return std::vector<FramebufferInfo>();
|
|
|
|
}
|
|
|
|
|
2013-02-04 00:41:16 +01:00
|
|
|
};
|