diff --git a/Windows/EmuThread.cpp b/Windows/EmuThread.cpp index c48094bf79..2f839695eb 100644 --- a/Windows/EmuThread.cpp +++ b/Windows/EmuThread.cpp @@ -54,6 +54,9 @@ enum EmuThreadStatus : int { void EmuThreadFunc(); void RenderThreadFunc(); +// On most other platforms, we let the main thread become the render thread and +// start a separate emu thread from that, if needed. Should probably switch to that +// to make it the same on all platforms. void EmuThread_Start(bool separateRenderThread) { std::lock_guard guard(emuThreadLock); emuThread = std::thread(&EmuThreadFunc); @@ -218,7 +221,7 @@ void EmuThreadFunc() { if (g_Config.bBrowse) PostMessage(MainWindow::GetHWND(), WM_COMMAND, ID_FILE_LOAD, 0); - Core_EnableStepping(FALSE); + Core_EnableStepping(false); while (GetUIState() != UISTATE_EXIT) { // We're here again, so the game quit. Restart Core_Run() which controls the UI. diff --git a/android/jni/AndroidEGLContext.h b/android/jni/AndroidEGLContext.h index fd86bc6cfe..2bc9d5341c 100644 --- a/android/jni/AndroidEGLContext.h +++ b/android/jni/AndroidEGLContext.h @@ -14,6 +14,9 @@ public: Draw::DrawContext *GetDrawContext() override { return draw_; } + bool Initialized() override { + return draw_ != nullptr; + } private: Draw::DrawContext *draw_; diff --git a/android/jni/AndroidGraphicsContext.h b/android/jni/AndroidGraphicsContext.h index 589cddc97d..50a213e68b 100644 --- a/android/jni/AndroidGraphicsContext.h +++ b/android/jni/AndroidGraphicsContext.h @@ -22,4 +22,5 @@ public: // This is different than the base class function since on // Android (EGL, Vulkan) we do have all this info on the render thread. virtual bool InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) = 0; + virtual bool Initialized() = 0; }; diff --git a/android/jni/AndroidJavaGLContext.cpp b/android/jni/AndroidJavaGLContext.cpp index c201c9fb10..4671cb7943 100644 --- a/android/jni/AndroidJavaGLContext.cpp +++ b/android/jni/AndroidJavaGLContext.cpp @@ -20,6 +20,7 @@ bool AndroidJavaEGLGraphicsContext::InitFromRenderThread(ANativeWindow *wnd, int void AndroidJavaEGLGraphicsContext::ShutdownFromRenderThread() { ILOG("AndroidJavaEGLGraphicsContext::Shutdown"); + renderManager_->WaitUntilQueueIdle(); renderManager_ = nullptr; // owned by draw_. delete draw_; draw_ = nullptr; diff --git a/android/jni/AndroidJavaGLContext.h b/android/jni/AndroidJavaGLContext.h index c6e11910e7..10b63c61bc 100644 --- a/android/jni/AndroidJavaGLContext.h +++ b/android/jni/AndroidJavaGLContext.h @@ -15,6 +15,10 @@ public: delete draw_; } + bool Initialized() override { + return draw_ != nullptr; + } + // This performs the actual initialization, bool InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override; diff --git a/android/jni/AndroidVulkanContext.h b/android/jni/AndroidVulkanContext.h index e43041d3dc..2c65de11c3 100644 --- a/android/jni/AndroidVulkanContext.h +++ b/android/jni/AndroidVulkanContext.h @@ -22,6 +22,10 @@ public: Draw::DrawContext *GetDrawContext() override { return draw_; } + bool Initialized() override { + return draw_ != nullptr; + } + private: VulkanContext *g_Vulkan = nullptr; Draw::DrawContext *draw_ = nullptr; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 2d0731d711..d58b68dd92 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -171,6 +171,20 @@ static void EmuThreadFunc() { gJvm->AttachCurrentThread(&env, nullptr); setCurrentThreadName("Emu"); + ILOG("Entering emu thread"); + + // Wait for render loop to get started. + if (!graphicsContext || !graphicsContext->Initialized()) { + ILOG("Runloop: Waiting for displayInit..."); + while (!graphicsContext || !graphicsContext->Initialized()) { + sleep_ms(20); + } + } else { + ILOG("Runloop: Graphics context available! %p", graphicsContext); + } + NativeInitGraphics(graphicsContext); + + ILOG("Graphics initialized. Entering loop."); // There's no real requirement that NativeInit happen on this thread. // We just call the update/render loop here. @@ -179,18 +193,31 @@ static void EmuThreadFunc() { UpdateRunLoopAndroid(env); } emuThreadState = (int)EmuThreadState::STOPPED; + + NativeShutdownGraphics(); + gJvm->DetachCurrentThread(); + ILOG("Leaving emu thread"); } -static void EmuThreadStart(JNIEnv *env) { +static void EmuThreadStart() { + ILOG("EmuThreadStart"); emuThreadState = (int)EmuThreadState::START_REQUESTED; emuThread = std::thread(&EmuThreadFunc); } +// Call EmuThreadStop first, then keep running the GPU (or eat commands) +// as long as emuThreadState isn't STOPPED and/or there are still things queued up. +// Only after that, call EmuThreadJoin. static void EmuThreadStop() { + ILOG("EmuThreadStop - stopping..."); emuThreadState = (int)EmuThreadState::QUIT_REQUESTED; +} + +static void EmuThreadJoin() { emuThread.join(); emuThread = std::thread(); + ILOG("EmuThreadStop - joined."); } static void ProcessFrameCommands(JNIEnv *env); @@ -429,9 +456,8 @@ retry: if (useCPUThread) { ILOG("NativeApp.init() - launching emu thread"); - EmuThreadStart(env); + EmuThreadStart(); } - ILOG("NativeApp.init() -- end"); } extern "C" void Java_org_ppsspp_ppsspp_NativeApp_audioInit(JNIEnv *, jclass) { @@ -480,8 +506,13 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_pause(JNIEnv *, jclass) { } extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { - if (useCPUThread) + if (useCPUThread && graphicsContext) { EmuThreadStop(); + while (emuThreadState != (int)EmuThreadState::STOPPED) { + graphicsContext->ThreadFrame(); + } + EmuThreadJoin(); + } ILOG("NativeApp.shutdown() -- begin"); if (renderer_inited) { @@ -507,11 +538,28 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * env, // We should be running on the render thread here. std::string errorMessage; if (renderer_inited) { + // Would be really nice if we could get something on the GL thread immediately when shutting down... ILOG("NativeApp.displayInit() restoring"); graphicsContext->ThreadEnd(); + if (useCPUThread) { + EmuThreadStop(); + while (emuThreadState != (int)EmuThreadState::STOPPED) { + graphicsContext->ThreadFrame(); + } + EmuThreadJoin(); + } else { + NativeShutdownGraphics(); + } graphicsContext->ShutdownFromRenderThread(); + ILOG("Shut down both threads. Now let's bring it up again!"); + graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0); + if (useCPUThread) { + EmuThreadStart(); + } else { + NativeInitGraphics(graphicsContext); + } graphicsContext->ThreadStart(); ILOG("Restored."); } else { @@ -564,15 +612,6 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv } void UpdateRunLoopAndroid(JNIEnv *env) { - // Wait for render loop to get started. - if (!renderer_inited) { - ILOG("Runloop: Waiting for displayInit"); - while (!renderer_inited) { - sleep_ms(20); - } - NativeInitGraphics(graphicsContext); - } - NativeUpdate(); NativeRender(graphicsContext); diff --git a/ext/native/thread/prioritizedworkqueue.cpp b/ext/native/thread/prioritizedworkqueue.cpp index 1583738f40..35a1e94a62 100644 --- a/ext/native/thread/prioritizedworkqueue.cpp +++ b/ext/native/thread/prioritizedworkqueue.cpp @@ -112,7 +112,7 @@ PrioritizedWorkQueueItem *PrioritizedWorkQueue::Pop() { static std::thread *workThread; static void threadfunc(PrioritizedWorkQueue *wq) { - setCurrentThreadName("PrioritizedWorkQueue"); + setCurrentThreadName("PrioQueue"); while (true) { PrioritizedWorkQueueItem *item = wq->Pop(); if (!item) { @@ -126,7 +126,7 @@ static void threadfunc(PrioritizedWorkQueue *wq) { } void ProcessWorkQueueOnThreadWhile(PrioritizedWorkQueue *wq) { - workThread = new std::thread(std::bind(&threadfunc, wq)); + workThread = new std::thread([=](){threadfunc(wq);}); } void StopProcessingWorkQueue(PrioritizedWorkQueue *wq) { @@ -135,5 +135,5 @@ void StopProcessingWorkQueue(PrioritizedWorkQueue *wq) { workThread->join(); delete workThread; } - workThread = 0; + workThread = nullptr; }