From cf7c718706ede98357fd4e50218980a14f538c48 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 11 Aug 2013 13:41:42 -0700 Subject: [PATCH] Use a timer to keep gpu/cpu in sync periodically. Fixes Diva Extend demo, at least, losing FPS. --- Core/HLE/sceGe.cpp | 27 +++++++++++++++++++++++++++ GPU/GPUCommon.cpp | 7 ++++++- GPU/GPUCommon.h | 14 ++++++++++++++ GPU/GPUInterface.h | 1 + 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Core/HLE/sceGe.cpp b/Core/HLE/sceGe.cpp index 6a6dd271e2..405682b57a 100644 --- a/Core/HLE/sceGe.cpp +++ b/Core/HLE/sceGe.cpp @@ -40,6 +40,11 @@ struct GeInterruptData static std::list ge_pending_cb; static int geSyncEvent; static int geInterruptEvent; +static int geCycleEvent; + +// Let's try updating 10 times per vblank. +const int geIntervalUs = 1000000 / (60 * 10); +const int geBehindThresholdUs = 1000000 / (60 * 10); class GeIntrHandler : public IntrHandler { @@ -162,6 +167,20 @@ void __GeExecuteInterrupt(u64 userdata, int cyclesLate) __TriggerInterrupt(PSP_INTR_IMMEDIATE, PSP_GE_INTR, PSP_INTR_SUB_NONE); } +void __GeCheckCycles(u64 userdata, int cyclesLate) +{ + u64 geTicks = gpu->GetTickEstimate(); + if (geTicks != 0) + { + if (CoreTiming::GetTicks() > geTicks + usToCycles(geBehindThresholdUs)) { + u64 diff = CoreTiming::GetTicks() - geTicks; + gpu->SyncThread(); + CoreTiming::Advance(); + } + } + CoreTiming::ScheduleEvent(usToCycles(geIntervalUs), geCycleEvent, 0); +} + void __GeInit() { memset(&ge_used_callbacks, 0, sizeof(ge_used_callbacks)); @@ -170,6 +189,12 @@ void __GeInit() geSyncEvent = CoreTiming::RegisterEvent("GeSyncEvent", &__GeExecuteSync); geInterruptEvent = CoreTiming::RegisterEvent("GeInterruptEvent", &__GeExecuteInterrupt); + geCycleEvent = CoreTiming::RegisterEvent("GeCycleEvent", &__GeCheckCycles); + + // When we're using separate CPU/GPU threads, we need to keep them in sync. + if (IsOnSeparateCPUThread()) { + CoreTiming::ScheduleEvent(usToCycles(geIntervalUs), geCycleEvent, 0); + } } void __GeDoState(PointerWrap &p) @@ -182,6 +207,8 @@ void __GeDoState(PointerWrap &p) CoreTiming::RestoreRegisterEvent(geSyncEvent, "GeSyncEvent", &__GeExecuteSync); p.Do(geInterruptEvent); CoreTiming::RestoreRegisterEvent(geInterruptEvent, "GeInterruptEvent", &__GeExecuteInterrupt); + p.Do(geCycleEvent); + CoreTiming::RestoreRegisterEvent(geCycleEvent, "GeCycleEvent", &__GeCheckCycles); // Everything else is done in sceDisplay. p.DoMarker("sceGe"); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 6d610b5764..b4001a338f 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -20,7 +20,8 @@ GPUCommon::GPUCommon() : busyTicks(0), dumpNextFrame_(false), dumpThisFrame_(false), - interruptsEnabled_(true) + interruptsEnabled_(true), + curTickEst_(0) { memset(dls, 0, sizeof(dls)); for (int i = 0; i < DisplayListMaxCount; ++i) { @@ -579,6 +580,7 @@ bool GPUCommon::ProcessDLQueue() { void GPUCommon::ProcessDLQueueInternal() { startingTicks = CoreTiming::GetTicks(); cyclesExecuted = 0; + UpdateTickEstimate(std::max(busyTicks, startingTicks + cyclesExecuted)); if (startingTicks < busyTicks) { DEBUG_LOG(HLE, "Can't execute a list yet, still busy for %lld ticks", busyTicks - startingTicks); @@ -594,6 +596,7 @@ void GPUCommon::ProcessDLQueueInternal() { easy_guard guard(listLock); // At the end, we can remove it from the queue and continue. dlQueue.erase(std::remove(dlQueue.begin(), dlQueue.end(), listIndex), dlQueue.end()); + UpdateTickEstimate(std::max(busyTicks, startingTicks + cyclesExecuted)); } } @@ -603,6 +606,8 @@ void GPUCommon::ProcessDLQueueInternal() { drawCompleteTicks = startingTicks + cyclesExecuted; busyTicks = std::max(busyTicks, drawCompleteTicks); __GeTriggerSync(WAITTYPE_GEDRAWSYNC, 1, drawCompleteTicks); + // Since the event is in CoreTiming, we're in sync. Just set 0 now. + UpdateTickEstimate(0); } void GPUCommon::PreExecuteOp(u32 op, u32 diff) { diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index dc32975698..4b4692271f 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -36,6 +36,11 @@ public: virtual u32 Break(int mode); virtual void ReapplyGfxState(); + virtual u64 GetTickEstimate() { + lock_guard guard(curTickEstLock_); + return curTickEst_; + } + protected: // To avoid virtual calls to PreExecuteOp(). virtual void FastRunLoop(DisplayList &list) = 0; @@ -86,6 +91,15 @@ protected: bool dumpThisFrame_; bool interruptsEnabled_; + // For CPU/GPU sync. + volatile u64 curTickEst_; + recursive_mutex curTickEstLock_; + + virtual void UpdateTickEstimate(u64 value) { + lock_guard guard(curTickEstLock_); + curTickEst_ = value; + } + public: virtual DisplayList* getList(int listid) { diff --git a/GPU/GPUInterface.h b/GPU/GPUInterface.h index f98a16a923..9da055b53a 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -224,6 +224,7 @@ public: virtual void DeviceLost() = 0; virtual void ReapplyGfxState() = 0; virtual void SyncThread() = 0; + virtual u64 GetTickEstimate() = 0; virtual void DoState(PointerWrap &p) = 0; // Called by the window system if the window size changed. This will be reflected in PSPCoreParam.pixel*.