Use the common EmuThread in the Android backend

This commit is contained in:
Henrik Rydgård committed 2026-07-26 11:54:50 +02:00
1 parent 2e837e88b4
commit ea3a2a2e84
6 files changed
+15 -95

No files matched your search

+2
View File
@@ -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");
}
+2
View File
@@ -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<void()> 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<void()> postFrame);
void EmuThread_Join(GraphicsContext *graphicsContext, std::thread &emuThread);
+1 -3
View File
@@ -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();
-6
View File
@@ -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()) {
-3
View File
@@ -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;
+10 -83
View File
@@ -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<int> 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<std::mutex> 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();