From 879060f008d6322b759c4c3ba52a9a0155b431c4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Nov 2013 20:56:11 -0800 Subject: [PATCH 1/6] Use a CORE_POWERUP state when starting. Otherwise, we could startup in CORE_POWERDOWN in a second game, which is confusing. --- Core/Core.cpp | 1 + Core/HLE/sceIo.cpp | 2 +- Core/HW/AsyncIOManager.cpp | 2 +- Core/System.cpp | 1 + Core/System.h | 1 + Core/ThreadEventQueue.h | 4 ++++ 6 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 4bbc10b6a6..53db613311 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -267,6 +267,7 @@ reswitch: #endif break; + case CORE_POWERUP: case CORE_POWERDOWN: case CORE_ERROR: // Exit loop!! diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 963836a392..93bfe00f2d 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -429,7 +429,7 @@ static DirectoryFileSystem *flash0System = NULL; void __IoManagerThread() { setCurrentThreadName("IOThread"); - while (ioManagerThreadEnabled) { + while (ioManagerThreadEnabled && coreState != CORE_ERROR && coreState != CORE_POWERDOWN) { ioManager.RunEventsUntil(CoreTiming::GetTicks() + msToCycles(1000)); } } diff --git a/Core/HW/AsyncIOManager.cpp b/Core/HW/AsyncIOManager.cpp index 53b72308c9..9623a5f621 100644 --- a/Core/HW/AsyncIOManager.cpp +++ b/Core/HW/AsyncIOManager.cpp @@ -41,7 +41,7 @@ bool AsyncIOManager::PopResult(u32 handle, AsyncIOResult &result) { bool AsyncIOManager::WaitResult(u32 handle, AsyncIOResult &result) { lock_guard guard(resultsLock_); ScheduleEvent(IO_EVENT_SYNC); - while (HasEvents() && resultsPending_.find(handle) != resultsPending_.end()) { + while (HasEvents() && ThreadEnabled() && resultsPending_.find(handle) != resultsPending_.end()) { if (PopResult(handle, result)) { return true; } diff --git a/Core/System.cpp b/Core/System.cpp index 4cfae68b9a..285ce3a2a9 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -147,6 +147,7 @@ void CPU_WaitStatus(condition_variable &cond, bool (*pred)()) { void CPU_Shutdown(); void CPU_Init() { + coreState = CORE_POWERUP; currentCPU = &mipsr4k; numCPUs = 1; diff --git a/Core/System.h b/Core/System.h index 300c8d03c7..4700e13870 100644 --- a/Core/System.h +++ b/Core/System.h @@ -78,6 +78,7 @@ enum CoreState CORE_RUNNING = 0, CORE_NEXTFRAME = 1, CORE_STEPPING, + CORE_POWERUP, CORE_POWERDOWN, CORE_ERROR, }; diff --git a/Core/ThreadEventQueue.h b/Core/ThreadEventQueue.h index b7c2f6ad2f..110e63600c 100644 --- a/Core/ThreadEventQueue.h +++ b/Core/ThreadEventQueue.h @@ -31,6 +31,10 @@ struct ThreadEventQueue : public B { threadEnabled_ = threadEnabled; } + bool ThreadEnabled() { + return threadEnabled_; + } + void ScheduleEvent(Event ev) { { lock_guard guard(eventsLock_); From afafa5fcd6975366d5ac9be9a20e01eaed00f55b Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Nov 2013 20:58:42 -0800 Subject: [PATCH 2/6] Make sure the gpu thread has a chance to run. If the scheduler puts it on the same core, it may not even do anything before we check if the framebuffer is dirty, so SyncThread will quit since it's not even running. Instead, let's wait until it's at least done something. --- Core/System.cpp | 3 +++ Core/ThreadEventQueue.h | 13 ++++++++++--- GPU/GPUInterface.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Core/System.cpp b/Core/System.cpp index 285ce3a2a9..a073bb7b67 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -336,6 +336,9 @@ void PSP_RunLoopUntil(u64 globalticks) { } if (cpuThread != NULL) { + // Tell the gpu a new frame is about to begin, before we start the CPU. + gpu->SyncBeginFrame(); + cpuThreadUntil = globalticks; if (CPU_NextState(CPU_THREAD_RUNNING, CPU_THREAD_EXECUTE)) { // The CPU doesn't actually respect cpuThreadUntil well, especially when skipping frames. diff --git a/Core/ThreadEventQueue.h b/Core/ThreadEventQueue.h index 110e63600c..1cfd805901 100644 --- a/Core/ThreadEventQueue.h +++ b/Core/ThreadEventQueue.h @@ -24,7 +24,7 @@ template struct ThreadEventQueue : public B { - ThreadEventQueue() : threadEnabled_(false), eventsRunning_(false) { + ThreadEventQueue() : threadEnabled_(false), eventsRunning_(false), eventsHaveRun_(false) { } void SetThreadEnabled(bool threadEnabled) { @@ -72,6 +72,7 @@ struct ThreadEventQueue : public B { void RunEventsUntil(u64 globalticks) { lock_guard guard(eventsLock_); eventsRunning_ = true; + eventsHaveRun_ = true; do { for (Event ev = GetNextEvent(); EventType(ev) != EVENT_INVALID; ev = GetNextEvent()) { @@ -107,6 +108,11 @@ struct ThreadEventQueue : public B { eventsRunning_ = false; } + void SyncBeginFrame() { + lock_guard guard(eventsLock_); + eventsHaveRun_ = false; + } + // Force ignores coreState. void SyncThread(bool force = false) { if (!threadEnabled_) { @@ -117,8 +123,8 @@ struct ThreadEventQueue : public B { // 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() && eventsRunning_ && (force || coreState == CORE_RUNNING)) { - eventsDrain_.wait_for(eventsLock_, 1); + while (HasEvents() && (eventsRunning_ || !eventsHaveRun_) && (force || coreState == CORE_RUNNING)) { + eventsDrain_.wait_for(eventsLock_, 1000); } } @@ -135,6 +141,7 @@ protected: private: bool threadEnabled_; bool eventsRunning_; + bool eventsHaveRun_; std::deque events_; recursive_mutex eventsLock_; condition_variable eventsWait_; diff --git a/GPU/GPUInterface.h b/GPU/GPUInterface.h index 82f812ec4d..1e3e2e6796 100644 --- a/GPU/GPUInterface.h +++ b/GPU/GPUInterface.h @@ -231,6 +231,7 @@ public: virtual void DeviceLost() = 0; virtual void ReapplyGfxState() = 0; virtual void SyncThread(bool force = false) = 0; + virtual void SyncBeginFrame() = 0; virtual u64 GetTickEstimate() = 0; virtual void DoState(PointerWrap &p) = 0; From 1633aa689ca59c9abe22d72389385552e9e2c83e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Nov 2013 21:00:02 -0800 Subject: [PATCH 3/6] Remove the extra process queues hack. It seems like it's not helping anymore so it could be hurting. --- GPU/GLES/GLES_GPU.cpp | 6 ------ GPU/GPUCommon.cpp | 5 ----- 2 files changed, 11 deletions(-) diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index 4f09ec1fe6..6bdf122d65 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -539,10 +539,7 @@ void GLES_GPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, GEBufferFormat fo } bool GLES_GPU::FramebufferDirty() { - // FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause. if (g_Config.bSeparateCPUThread) { - // FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause. - ScheduleEvent(GPU_EVENT_PROCESS_QUEUE); // Allow it to process fully before deciding if it's dirty. SyncThread(); } @@ -557,10 +554,7 @@ bool GLES_GPU::FramebufferDirty() { } bool GLES_GPU::FramebufferReallyDirty() { - // FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause. if (g_Config.bSeparateCPUThread) { - // FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause. - ScheduleEvent(GPU_EVENT_PROCESS_QUEUE); // Allow it to process fully before deciding if it's dirty. SyncThread(); } diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 8fa6751ef1..7293d3bf8e 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -60,10 +60,7 @@ bool GPUCommon::BusyDrawing() { } u32 GPUCommon::DrawSync(int mode) { - // FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause. if (g_Config.bSeparateCPUThread) { - // FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause. - ScheduleEvent(GPU_EVENT_PROCESS_QUEUE); // Sync first, because the CPU is usually faster than the emulated GPU. SyncThread(); } @@ -119,8 +116,6 @@ void GPUCommon::CheckDrawSync() { int GPUCommon::ListSync(int listid, int mode) { if (g_Config.bSeparateCPUThread) { - // FIXME: Workaround for displaylists sometimes hanging unprocessed. Not yet sure of the cause. - ScheduleEvent(GPU_EVENT_PROCESS_QUEUE); // Sync first, because the CPU is usually faster than the emulated GPU. SyncThread(); } From 113146302b9584c3f39cee20f6d6d162da94b949 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Nov 2013 21:11:50 -0800 Subject: [PATCH 4/6] Don't schedule a finish event when not running. Otherwise it'll get it when it does run, and stop early. --- Core/System.cpp | 2 +- Core/ThreadEventQueue.h | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Core/System.cpp b/Core/System.cpp index a073bb7b67..81de7a3db5 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -344,7 +344,7 @@ void PSP_RunLoopUntil(u64 globalticks) { // The CPU doesn't actually respect cpuThreadUntil well, especially when skipping frames. // TODO: Something smarter? Or force CPU to bail periodically? while (!CPU_IsReady()) { - gpu->RunEventsUntil(CoreTiming::GetTicks() + msToCycles(100)); + gpu->RunEventsUntil(CoreTiming::GetTicks() + msToCycles(1000)); } } else { ERROR_LOG(CPU, "Unable to execute CPU run loop, unexpected state: %d", cpuThreadState); diff --git a/Core/ThreadEventQueue.h b/Core/ThreadEventQueue.h index 1cfd805901..bbbf7276bd 100644 --- a/Core/ThreadEventQueue.h +++ b/Core/ThreadEventQueue.h @@ -130,7 +130,11 @@ struct ThreadEventQueue : public B { void FinishEventLoop() { if (threadEnabled_) { - ScheduleEvent(EVENT_FINISH); + lock_guard guard(eventsLock_); + // Don't schedule a finish if it's not even running. + if (eventsRunning_) { + ScheduleEvent(EVENT_FINISH); + } } } From 92d56434231afdd0288c9e5c01888eacc87faa8e Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Nov 2013 22:22:13 -0800 Subject: [PATCH 5/6] Implement SetCurrentThreadAffinity on Android. But, it doesn't really seem to improve performance. --- Common/Thread.cpp | 35 ++++++++++++++++++++++------------- Common/Thread.h | 4 ++-- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/Common/Thread.cpp b/Common/Thread.cpp index 38073a030d..2ca3ec08f8 100644 --- a/Common/Thread.cpp +++ b/Common/Thread.cpp @@ -20,6 +20,8 @@ #ifdef __APPLE__ #include +#elif defined(ANDROID) +#include #elif defined BSD4_4 #include #endif @@ -48,14 +50,14 @@ int CurrentThreadId() #ifdef _WIN32 -void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) +bool SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) { - SetThreadAffinityMask(thread, mask); + return SetThreadAffinityMask(thread, mask) != 0; } -void SetCurrentThreadAffinity(u32 mask) +bool SetCurrentThreadAffinity(u32 mask) { - SetThreadAffinityMask(GetCurrentThread(), mask); + return SetThreadAffinityMask(GetCurrentThread(), mask) != 0; } // Supporting functions @@ -127,29 +129,36 @@ void EnableCrashingOnCrashes() #else // !WIN32, so must be POSIX threads -void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) +bool SetThreadAffinity(std::thread::native_handle_type thread, u32 mask) { #ifdef __APPLE__ - thread_policy_set(pthread_mach_thread_np(thread), - THREAD_AFFINITY_POLICY, (integer_t *)&mask, 1); -#elif (defined __linux__ || defined BSD4_4) && !defined(ANDROID) + return thread_policy_set(pthread_mach_thread_np(thread), + THREAD_AFFINITY_POLICY, (integer_t *)&mask, 1) == 0; +#elif defined(ANDROID) || defined(BLACKBERRY) + return false; +#elif (defined __linux__ || defined BSD4_4) cpu_set_t cpu_set; CPU_ZERO(&cpu_set); - for (int i = 0; i != sizeof(mask) * 8; ++i) + for (int i = 0; i != sizeof(mask) * 8; ++i) { if ((mask >> i) & 1) CPU_SET(i, &cpu_set); + } - pthread_setaffinity_np(thread, sizeof(cpu_set), &cpu_set); + return pthread_setaffinity_np(thread, sizeof(cpu_set), &cpu_set) == 0; #endif + + return false; } -void SetCurrentThreadAffinity(u32 mask) +bool SetCurrentThreadAffinity(u32 mask) { #ifdef BLACKBERRY - ThreadCtl(_NTO_TCTL_RUNMASK, &mask); + return ThreadCtl(_NTO_TCTL_RUNMASK, &mask) != -1; +#elif defined(ANDROID) + return syscall(__NR_sched_setaffinity, gettid(), sizeof(mask), &mask) == 0; #else - SetThreadAffinity(pthread_self(), mask); + return SetThreadAffinity(pthread_self(), mask); #endif } diff --git a/Common/Thread.h b/Common/Thread.h index 0d1d02a0cb..0726bef34c 100644 --- a/Common/Thread.h +++ b/Common/Thread.h @@ -50,8 +50,8 @@ int CurrentThreadId(); // In Windows 7 SP1 or later, stops Windows from swallowing crashes in WndProcs and other callbacks. void EnableCrashingOnCrashes(); -void SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); -void SetCurrentThreadAffinity(u32 mask); +bool SetThreadAffinity(std::thread::native_handle_type thread, u32 mask); +bool SetCurrentThreadAffinity(u32 mask); class Event { From d6048dc646cd6940751ca1bdfdc53fe8a331b6cf Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 9 Nov 2013 22:31:12 -0800 Subject: [PATCH 6/6] Don't burn the cpu while idle in multithread. --- Core/System.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Core/System.cpp b/Core/System.cpp index 81de7a3db5..6c044bf5ce 100644 --- a/Core/System.cpp +++ b/Core/System.cpp @@ -345,6 +345,9 @@ void PSP_RunLoopUntil(u64 globalticks) { // TODO: Something smarter? Or force CPU to bail periodically? while (!CPU_IsReady()) { gpu->RunEventsUntil(CoreTiming::GetTicks() + msToCycles(1000)); + if (coreState != CORE_RUNNING) { + CPU_WaitStatus(cpuThreadReplyCond, &CPU_IsReady); + } } } else { ERROR_LOG(CPU, "Unable to execute CPU run loop, unexpected state: %d", cpuThreadState);