diff --git a/Core/EmuThread.cpp b/Core/EmuThread.cpp index 7efe887366..c406fcd8a3 100644 --- a/Core/EmuThread.cpp +++ b/Core/EmuThread.cpp @@ -71,6 +71,8 @@ static void EmuThreadFunc(GraphicsContext *graphicsContext, Application *applica // This normally calls NativeShutdownGraphics() application->ShutdownGraphics(graphicsContext); + delete application; + INFO_LOG(Log::System, "Leaving separate emu thread"); } diff --git a/Core/EmuThread.h b/Core/EmuThread.h index b887ffd2e0..f1a89115f8 100644 --- a/Core/EmuThread.h +++ b/Core/EmuThread.h @@ -30,9 +30,11 @@ class GraphicsContext; // Doesn't take ownership of the graphicsContext, you have to delete it. // This should be used by platforms that launch a separate thread and doesn't // need to run a polling loop in it. +// NOTE: Does take ownership over Application (which is just a wrapper for NativeInitGraphics/NativeShutdownGraphics/NativeFrame). bool MainThreadFunc(GraphicsContext * graphicsContext, Application *application, WindowSystem windowSystem, void *windowData1, void *windowData2, std::function postFrame); // If you're not using MainThreadFunc, you can at least use these to manage a spinning EmuThread (that calls NativeFrame), // whether your graphics context requires multithreading or not. +// NOTE: Does take ownership over Application (which is just a wrapper for NativeInitGraphics/NativeShutdownGraphics/NativeFrame). std::thread EmuThread_Start(GraphicsContext *graphicsContext, Application *application, std::function postFrame); void EmuThread_Join(GraphicsContext *graphicsContext, std::thread &emuThread); diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index b82be099b6..a184e1ee95 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -2104,10 +2104,8 @@ int main(int argc, char *argv[]) { } EnableFZ(); - NativeApplication application; - // We use the emuthread both for OpenGL and Vulkan, but in OpenGL mode we also render from the main thread. - std::thread emuThread = EmuThread_Start(graphicsContext, &application, nullptr); + std::thread emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), nullptr); graphicsContext->ThreadStart(); diff --git a/android/jni/AndroidJavaGLContext.cpp b/android/jni/AndroidJavaGLContext.cpp index 2f5209be29..25a4110bf1 100644 --- a/android/jni/AndroidJavaGLContext.cpp +++ b/android/jni/AndroidJavaGLContext.cpp @@ -8,17 +8,11 @@ AndroidJavaEGLGraphicsContext::AndroidJavaEGLGraphicsContext() { SetGPUBackend(GPUBackend::OPENGL); -} - -bool AndroidJavaEGLGraphicsContext::InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) { // OpenGL handles rotated rendering in the driver. g_display.rotation = DisplayRotation::ROTATE_0; g_display.rot_matrix.setIdentity(); - return true; } -void AndroidJavaEGLGraphicsContext::ShutdownAPI() {} - bool AndroidJavaEGLGraphicsContext::InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *errorMessage) { INFO_LOG(Log::G3D, "AndroidJavaEGLGraphicsContext::InitFromRenderThread"); if (!CheckGLExtensions()) { diff --git a/android/jni/AndroidJavaGLContext.h b/android/jni/AndroidJavaGLContext.h index 394d2e8362..3d554a8f81 100644 --- a/android/jni/AndroidJavaGLContext.h +++ b/android/jni/AndroidJavaGLContext.h @@ -11,9 +11,6 @@ public: bool NeedsSeparateEmuThread() const override { return true; } - bool InitAPI(void *wnd, std::string *deviceName, std::string *errorMessage) override; - void ShutdownAPI() override; - bool InitSurface(WindowSystem winsys, void *data1, void *data2, std::string *error_message) override; void ShutdownSurface() override; diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index da5a81988b..a825902aec 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -100,6 +100,7 @@ struct JNIEnv {}; #include "Core/Loaders.h" #include "Core/KeyMap.h" #include "Core/System.h" +#include "Core/EmuThread.h" #include "Core/HLE/sceUsbCam.h" #include "Core/HLE/sceUsbGps.h" #include "Common/CPUDetect.h" @@ -116,8 +117,7 @@ enum class EmuThreadState { }; // OpenGL emu thread -static std::thread emuThread; -static std::atomic emuThreadState((int)EmuThreadState::DISABLED); +static std::thread g_emuThread; AndroidAudioState *g_audioState; @@ -262,59 +262,6 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) { return JNI_VERSION_1_6; } -// Only used in OpenGL mode. -static void EmuThreadFunc() { - SetCurrentThreadName("Entering EmuThread"); - - AndroidJNIThreadContext jniContext; - - INFO_LOG(Log::System, "Entering emu thread"); - - _assert_(graphicsContext); - if (!NativeInitGraphics(graphicsContext)) { - _assert_msg_(false, "NativeInitGraphics failed, might as well bail"); - emuThreadState = (int)EmuThreadState::QUIT_REQUESTED; - return; - } - - INFO_LOG(Log::System, "Graphics initialized. Entering loop."); - - // There's no real requirement that NativeInit happen on this thread. - // We just call the update/render loop here. - emuThreadState = (int)EmuThreadState::RUNNING; - while (emuThreadState != (int)EmuThreadState::QUIT_REQUESTED) { - NativeFrame(graphicsContext); - ProcessFrameCommands(); - } - - INFO_LOG(Log::System, "emuThreadState was set to QUIT_REQUESTED, left EmuThreadFunc loop. Setting state to STOPPED."); - emuThreadState = (int)EmuThreadState::STOPPED; - - NativeShutdownGraphics(graphicsContext); - - INFO_LOG(Log::System, "Leaving EmuThread"); -} - -static void EmuThreadStart() { - INFO_LOG(Log::System, "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(const char *caller) { - INFO_LOG(Log::System, "EmuThreadStop - stopping (%s)...", caller); - emuThreadState = (int)EmuThreadState::QUIT_REQUESTED; -} - -static void EmuThreadJoin() { - emuThread.join(); - emuThread = std::thread(); - INFO_LOG(Log::System, "EmuThreadJoin - joined"); -} - static void PushCommand(std::string_view cmd, std::string_view param) { std::lock_guard guard(frameCommandLock); g_frameCommands.emplace_back(std::string(cmd), std::string(param)); @@ -956,23 +903,7 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { if (renderer_inited && graphicsContext && graphicsContext->NeedsSeparateEmuThread()) { // Only used in Java EGL path. - EmuThreadStop("shutdown"); - // NOTE: We know that the GLSurfaceView render thread is stopped here, since we now - // correctly call GLSurfaceView.onPause/onResume. However, there may still be queued frames. - // We can't join until we've cleared the queue by calling ThreadFrame. - - // Now we know that more frames won't be coming in. - - INFO_LOG(Log::System, "BeginShutdown"); - - // Now, it could be that we had some frames queued up. Get through them. - // We're on the render thread, so this is synchronous. - graphicsContext->ThreadFrameUntilCondition([]() -> bool { - return emuThreadState == (int)EmuThreadState::STOPPED; - }); - graphicsContext->ThreadEnd(); - - EmuThreadJoin(); + EmuThread_Join(graphicsContext, g_emuThread); INFO_LOG(Log::System, "ThreadEnd called."); graphicsContext->ShutdownSurface(); @@ -1023,7 +954,9 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * e return false; } // This is where we start the emuthread now - after InitFromRenderThread. This eliminates a race condition. - EmuThreadStart(); + g_emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), []() { + ProcessFrameCommands(); + }); graphicsContext->GetDrawContext()->SetErrorCallback([](const char *shortDesc, const char *details, void *userdata) { g_OSD.Show(OSDType::MESSAGE_ERROR, details, 5.0); @@ -1037,15 +970,7 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * e // which ends up calling displayInit. INFO_LOG(Log::G3D, "NativeApp.displayInit() restoring"); - EmuThreadStop("displayInit"); - // Skipping GL calls here because the old context is lost. - INFO_LOG(Log::G3D, "Looping until emu thread done..."); - // TODO: Why can't we just join the EmuThread first? - graphicsContext->ThreadFrameUntilCondition([]() -> bool { - return emuThreadState == (int)EmuThreadState::STOPPED; - }); - INFO_LOG(Log::G3D, "Joining emu thread"); - EmuThreadJoin(); + EmuThread_Join(graphicsContext, g_emuThread); graphicsContext->ThreadEnd(); graphicsContext->ShutdownSurface(); @@ -1061,7 +986,9 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * e g_OSD.Show(OSDType::MESSAGE_ERROR, details, 5.0); }, nullptr); - EmuThreadStart(); + g_emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), []() { + ProcessFrameCommands(); + }); graphicsContext->ThreadStart();