diff --git a/Common/System/Application.h b/Common/System/Application.h index e5097af80a..511b45fd6e 100644 --- a/Common/System/Application.h +++ b/Common/System/Application.h @@ -14,5 +14,4 @@ public: virtual ~Application() = default; virtual bool InitGraphics(GraphicsContext *graphicsContext) = 0; virtual void ShutdownGraphics(GraphicsContext *graphicsContext) = 0; - virtual void Frame(GraphicsContext *graphicsContext) = 0; }; diff --git a/Common/System/NativeApp.h b/Common/System/NativeApp.h index a87e6d8913..dd4327b1c8 100644 --- a/Common/System/NativeApp.h +++ b/Common/System/NativeApp.h @@ -117,7 +117,4 @@ public: void ShutdownGraphics(GraphicsContext *graphicsContext) override { NativeShutdownGraphics(graphicsContext); } - void Frame(GraphicsContext *graphicsContext) override { - NativeFrame(graphicsContext); - } }; diff --git a/Core/EmuThread.cpp b/Core/EmuThread.cpp index 4d46198d83..5b51dae28c 100644 --- a/Core/EmuThread.cpp +++ b/Core/EmuThread.cpp @@ -36,7 +36,7 @@ bool MainThread_Ready() { return g_inLoop; } -static void EmuThreadFunc(GraphicsContext *graphicsContext, Application *application, std::function postFrame) { +static void EmuThreadFunc(GraphicsContext *graphicsContext, Application *application, std::function frame) { INFO_LOG(Log::G3D, "Entering separate emu thread"); SetCurrentThreadName("EmuThread"); @@ -57,11 +57,7 @@ static void EmuThreadFunc(GraphicsContext *graphicsContext, Application *applica // We're here again, so the game quit. Restart Run() which controls the UI. // This way they can load a new game. // This normally calls NativeFrame() - application->Frame(graphicsContext); - if (postFrame) { - postFrame(); - } - if (GetUIState() == UISTATE_EXIT) { + if (!frame(graphicsContext)) { g_emuThreadState = EmuThreadState::QUIT_REQUESTED; } } @@ -77,10 +73,10 @@ static void EmuThreadFunc(GraphicsContext *graphicsContext, Application *applica g_emuThreadState = EmuThreadState::STOPPED; } -std::thread EmuThread_Start(GraphicsContext *graphicsContext, Application *application, std::function postFrame) { +std::thread EmuThread_Start(GraphicsContext *graphicsContext, Application *application, std::function frame) { INFO_LOG(Log::System, "EmuTread_Start"); _dbg_assert_(g_emuThreadState == EmuThreadState::STOPPED); - std::thread emuThread = std::thread(&EmuThreadFunc, graphicsContext, application, postFrame); + std::thread emuThread = std::thread(&EmuThreadFunc, graphicsContext, application, frame); graphicsContext->ThreadStart(); return emuThread; } @@ -98,7 +94,7 @@ void EmuThread_Join(GraphicsContext *graphicsContext, std::thread &emuThread) { graphicsContext->ThreadEnd(); } -bool RunMainLoop(GraphicsContext *graphicsContext, Application *application, std::function runCondition, std::function postFrame) { +bool RunMainLoop(GraphicsContext *graphicsContext, Application *application, std::function frame) { // This is the main thread. the graphics contexts will spawn and handle its own threads if needed. // InitFromRenderThread/ShutdownFromRenderThread are not used. @@ -109,19 +105,17 @@ bool RunMainLoop(GraphicsContext *graphicsContext, Application *application, std g_inLoop = true; - while (runCondition()) { + while (frame(graphicsContext)) { // We're here again, so the game quit. Restart Run() which controls the UI. // This way they can load a new game. - application->Frame(graphicsContext); - postFrame(); } // NOTE: Don't call stuff like Core_Stop here. On Android, we fully shut down graphics when you switch away from the app, // then boot it up again when returning. That means stopping this thread and restarting it. - // Process the shutdown. Without this, non-GL delays 800ms on shutdown. + // Process the shutdown. Without this, non-GL delays 800ms on shutdown. TODO: is this still an issue? Core_StateProcessed(); - application->Frame(graphicsContext); + frame(graphicsContext); g_inLoop = false; @@ -131,7 +125,7 @@ bool RunMainLoop(GraphicsContext *graphicsContext, Application *application, std } // Call InitAPI and ShutdownAPI outside this! -bool MainThreadFunc(GraphicsContext *graphicsContext, Application *application, WindowSystem windowSystem, void *windowData1, void *windowData2, std::function postFrame) { +bool MainThreadFunc(GraphicsContext *graphicsContext, Application *application, WindowSystem windowSystem, void *windowData1, void *windowData2, std::function frame) { // This is now the render thread, and will spawn the emu thread below. std::string error_string; bool success = graphicsContext->InitSurface(windowSystem, windowData1, windowData2, &error_string); @@ -146,7 +140,7 @@ bool MainThreadFunc(GraphicsContext *graphicsContext, Application *application, DEBUG_LOG(Log::Boot, "Done."); g_inLoop = true; - std::thread emuThread = EmuThread_Start(graphicsContext, application, postFrame); + std::thread emuThread = EmuThread_Start(graphicsContext, application, frame); graphicsContext->ThreadStart(); // This thread becomes the render thread. EmuThread will tell it when to quit by sending a message. while (graphicsContext->ThreadFrame()) {} @@ -160,9 +154,8 @@ bool MainThreadFunc(GraphicsContext *graphicsContext, Application *application, } else { SetCurrentThreadName("MainThread"); - RunMainLoop(graphicsContext, application, []() { return GetUIState() != UISTATE_EXIT; }, postFrame); + RunMainLoop(graphicsContext, application, frame); } - graphicsContext->ShutdownSurface(); return true; } diff --git a/Core/EmuThread.h b/Core/EmuThread.h index f6117c5534..51cbb14fe2 100644 --- a/Core/EmuThread.h +++ b/Core/EmuThread.h @@ -31,16 +31,16 @@ class GraphicsContext; // 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); +bool MainThreadFunc(GraphicsContext * graphicsContext, Application *application, WindowSystem windowSystem, void *windowData1, void *windowData2, std::function frame); // 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. Then use RunMainLoop to implement your main loop for // the case where a separate EmuThread is not needed. // 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); +std::thread EmuThread_Start(GraphicsContext *graphicsContext, Application *application, std::function frame); void EmuThread_RequestExit(); // Useful when the render thread is in control like on Android. void EmuThread_Join(GraphicsContext *graphicsContext, std::thread &emuThread); // Call from the main thread. // NOTE: Does take ownership over Application (which is just a wrapper for NativeInitGraphics/NativeShutdownGraphics/NativeFrame). -bool RunMainLoop(GraphicsContext *graphicsContext, Application *application, std::function runCondition, std::function postFrame); +bool RunMainLoop(GraphicsContext *graphicsContext, Application *application, std::function frame); diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index 00e48f025a..4371413141 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -879,8 +879,10 @@ void MainUI::initializeGL() { graphicsContext->InitAPI(nullptr, nullptr, &errorMessage); graphicsContext->InitSurface(WINDOWSYSTEM_NONE, nullptr, nullptr, &errorMessage); INFO_LOG(Log::System, "Using thread, starting emu thread"); - emuThread_ = EmuThread_Start(graphicsContext, new NativeApplication(), [this](){ + emuThread_ = EmuThread_Start(graphicsContext, new NativeApplication(), [this](GraphicsContext *graphicsContext){ + NativeFrame(graphicsContext); updateAccelerometer(); + return true; }); } else { INFO_LOG(Log::System, "Not using thread, backend=%d", (int)g_Config.iGPUBackend); diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 4da83c376c..caf4ec582a 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -2211,7 +2211,10 @@ 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(), nullptr); + std::thread emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), [](GraphicsContext *graphicsContext){ + NativeFrame(graphicsContext); + return true; + }); // 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. @@ -2243,7 +2246,10 @@ int main(int argc, char *argv[]) { } EmuThread_Join(graphicsContext, emuThread); } else { - std::thread emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), nullptr); + std::thread emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), [](GraphicsContext *graphicsContext){ + NativeFrame(graphicsContext); + return true; + }); while (true) { // OpenGL mode uses this. { diff --git a/Windows/main.cpp b/Windows/main.cpp index f59273cbe7..c8347891bc 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -1233,6 +1233,7 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin System_Notify(SystemNotification::UI); std::thread mainThread = std::thread([]() { + // TODO: We can really merge all of this into MainThreadFunc std::string errorMessage; std::string *deviceNameSetting; std::unique_ptr graphicsContext(CreateGraphicsContext((GPUBackend)g_Config.iGPUBackend, &deviceNameSetting)); @@ -1240,7 +1241,11 @@ int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLin HandleGraphicsFailure(errorMessage); return; } - if (!MainThreadFunc(graphicsContext.get(), new NativeApplication(), WINDOWSYSTEM_WIN32, MainWindow::GetHInstance(), MainWindow::GetHWND(), []() {})) { + if (!MainThreadFunc(graphicsContext.get(), new NativeApplication(), WINDOWSYSTEM_WIN32, MainWindow::GetHInstance(), MainWindow::GetHWND(), + [](GraphicsContext *graphicsContext) { + NativeFrame(graphicsContext); + return GetUIState() != UISTATE_EXIT; + })) { HandleGraphicsFailure("Failed to initialize main thread function."); return; } diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 74eeb6f35b..81ce40b6c7 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -958,8 +958,10 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * e }, nullptr); // This is where we start the emuthread now - after InitFromRenderThread. This eliminates a race condition. - g_emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), []() { + g_emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), [](GraphicsContext *graphicsContext) { + NativeFrame(graphicsContext); ProcessFrameCommands(); + return true; }); renderer_inited = true; } else { @@ -985,8 +987,10 @@ extern "C" jboolean Java_org_ppsspp_ppsspp_NativeRenderer_displayInit(JNIEnv * e g_OSD.Show(OSDType::MESSAGE_ERROR, details, 5.0); }, nullptr); - g_emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), []() { + g_emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), [](GraphicsContext *graphicsContext) { + NativeFrame(graphicsContext); ProcessFrameCommands(); + return true; }); INFO_LOG(Log::G3D, "Restored."); diff --git a/ios/ViewController.mm b/ios/ViewController.mm index d42f873af4..ea03d548bf 100644 --- a/ios/ViewController.mm +++ b/ios/ViewController.mm @@ -83,7 +83,10 @@ PPSSPPBaseViewController *sharedViewController; return false; } - g_emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), [](){}); + g_emuThread = EmuThread_Start(graphicsContext, new NativeApplication(), [](GraphicsContext *graphicsContext){ + NativeFrame(graphicsContext); + return true; + }); return true; }