From 16206d18ab5688ac0b88e1e31c8855e97fa1101d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Wed, 5 Aug 2026 09:48:51 +0200 Subject: [PATCH] Simplify the EmuThread API again --- Core/EmuThread.cpp | 10 +++++++++- Qt/QtMain.cpp | 4 ---- SDL/SDLMain.cpp | 32 +++++++++++++++++++++----------- android/jni/app-android.cpp | 6 ------ ios/ViewController.mm | 3 --- 5 files changed, 30 insertions(+), 25 deletions(-) diff --git a/Core/EmuThread.cpp b/Core/EmuThread.cpp index b176d93f74..01c82f80ca 100644 --- a/Core/EmuThread.cpp +++ b/Core/EmuThread.cpp @@ -84,11 +84,19 @@ std::thread EmuThread_Start(GraphicsContext *graphicsContext, Application *appli // This is useful when the render thread is in control. void EmuThread_RequestExit() { INFO_LOG(Log::System, "EmuTread_RequestExit"); - g_emuThreadState = EmuThreadState::QUIT_REQUESTED; + if (g_emuThreadState == EmuThreadState::RUNNING) { + g_emuThreadState = EmuThreadState::QUIT_REQUESTED; + } else { + INFO_LOG(Log::System, "EmuTread_RequestExit: g_emuThreadState was not RUNNING, so not requesting exit."); + } } void EmuThread_Join(GraphicsContext *graphicsContext, std::thread &emuThread) { INFO_LOG(Log::System, "EmuTread_Join"); + if (graphicsContext->NeedsSeparateEmuThread()) { + EmuThread_RequestExit(); + while (graphicsContext->ThreadFrame()) {} + } emuThread.join(); emuThread = std::thread(); graphicsContext->ThreadEnd(); diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 4371413141..2717fe5992 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -681,10 +681,6 @@ MainUI::MainUI(QWidget *parent) MainUI::~MainUI() { INFO_LOG(Log::System, "MainUI::Destructor"); if (graphicsContext->NeedsSeparateEmuThread()) { - EmuThread_RequestExit(); - while (graphicsContext->ThreadFrame()) { - // Wait for the emu thread to finish. - } EmuThread_Join(graphicsContext, emuThread_); } #if defined(MOBILE_DEVICE) diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 904d809c53..4fc1998704 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -2215,16 +2215,23 @@ int main(int argc, char *argv[]) { } } - const bool mainThreadIsRender = graphicsContext->NeedsSeparateEmuThread(); - if (!mainThreadIsRender) { - // TODO: This shouldn't be the EmuThread, but rather RunMainLoop? - std::thread emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), [](GraphicsContext *graphicsContext){ - NativeFrame(graphicsContext); - return true; - }); + const bool needsSeparateEmuThread = graphicsContext->NeedsSeparateEmuThread(); + if (!needsSeparateEmuThread) { // Vulkan mode uses this. - // We should only be a message pump. This allows for lower latency - // input events, and so on. The spawned EmuThread runs emulation and rendering. + + std::thread emuThread = std::thread([&] { + RunMainLoop(graphicsContext, new NativeApplication(), [&](GraphicsContext *graphicsContext) { + NativeFrame(graphicsContext); + bool keepRunning = !(g_QuitRequested || g_RestartRequested); + if (!keepRunning) { + INFO_LOG(Log::System, "EmuThread was requested to exit normally."); + } + return keepRunning; + }); + }); + + // The SDL main thread only becomes a plain message pump. This allows for lower latency + // input events, and so on. The spawned main thread runs emulation and rendering. while (true) { SDL_Event event; if (SDL_WaitEventTimeout(&event, 100)) { @@ -2251,9 +2258,11 @@ int main(int argc, char *argv[]) { } } } - EmuThread_Join(graphicsContext, emuThread); + INFO_LOG(Log::System, "Joining main thread..."); + emuThread.join(); } else { - std::thread emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), [](GraphicsContext *graphicsContext){ + // OpenGL mode uses this path. + std::thread emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), [&](GraphicsContext *graphicsContext){ NativeFrame(graphicsContext); return true; }); @@ -2291,6 +2300,7 @@ int main(int argc, char *argv[]) { } } } + INFO_LOG(Log::System, "Requesting render thread exit..."); EmuThread_Join(graphicsContext, emuThread); } diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 6464b5da77..d622134f9c 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -903,9 +903,6 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { if (renderer_inited && graphicsContext && graphicsContext->NeedsSeparateEmuThread()) { // Only used in Java EGL path. INFO_LOG(Log::System, "Joining emuthread."); - EmuThread_RequestExit(); - // Eat up any remaining frames. - while (graphicsContext->ThreadFrame()) {} EmuThread_Join(graphicsContext, g_emuThread); INFO_LOG(Log::System, "EmuThread joined."); @@ -973,9 +970,6 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * e // but the only mechanism for handling lost devices seems to be that onSurfaceCreated is called again, // which ends up calling displayInit. INFO_LOG(Log::G3D, "NativeApp.displayInit(): Second time, joining the emuthread and starting it up again."); - EmuThread_RequestExit(); - // Eat up any remaining frames. - while (graphicsContext->ThreadFrame()) {} EmuThread_Join(graphicsContext, g_emuThread); graphicsContext->ShutdownSurface(); diff --git a/ios/ViewController.mm b/ios/ViewController.mm index ea03d548bf..6383d9c08d 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -92,9 +92,6 @@ PPSSPPBaseViewController *sharedViewController; - (void)requestExitGLRenderLoop { _assert_(g_emuThread.joinable()); - EmuThread_RequestExit(); - // Eat up any remaining frames. - while (graphicsContext->ThreadFrame()) {} EmuThread_Join(graphicsContext, g_emuThread); _assert_(!g_emuThread.joinable()); }