mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Android: Fix emuthread management to exit cleanly without hanging. Helps with task switching on Android.
This commit is contained in:
parent
b3a09791b1
commit
7f30037e45
8 changed files with 72 additions and 17 deletions
|
@ -54,6 +54,9 @@ enum EmuThreadStatus : int {
|
||||||
void EmuThreadFunc();
|
void EmuThreadFunc();
|
||||||
void RenderThreadFunc();
|
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) {
|
void EmuThread_Start(bool separateRenderThread) {
|
||||||
std::lock_guard<std::mutex> guard(emuThreadLock);
|
std::lock_guard<std::mutex> guard(emuThreadLock);
|
||||||
emuThread = std::thread(&EmuThreadFunc);
|
emuThread = std::thread(&EmuThreadFunc);
|
||||||
|
@ -218,7 +221,7 @@ void EmuThreadFunc() {
|
||||||
if (g_Config.bBrowse)
|
if (g_Config.bBrowse)
|
||||||
PostMessage(MainWindow::GetHWND(), WM_COMMAND, ID_FILE_LOAD, 0);
|
PostMessage(MainWindow::GetHWND(), WM_COMMAND, ID_FILE_LOAD, 0);
|
||||||
|
|
||||||
Core_EnableStepping(FALSE);
|
Core_EnableStepping(false);
|
||||||
|
|
||||||
while (GetUIState() != UISTATE_EXIT) {
|
while (GetUIState() != UISTATE_EXIT) {
|
||||||
// We're here again, so the game quit. Restart Core_Run() which controls the UI.
|
// We're here again, so the game quit. Restart Core_Run() which controls the UI.
|
||||||
|
|
|
@ -14,6 +14,9 @@ public:
|
||||||
Draw::DrawContext *GetDrawContext() override {
|
Draw::DrawContext *GetDrawContext() override {
|
||||||
return draw_;
|
return draw_;
|
||||||
}
|
}
|
||||||
|
bool Initialized() override {
|
||||||
|
return draw_ != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Draw::DrawContext *draw_;
|
Draw::DrawContext *draw_;
|
||||||
|
|
|
@ -22,4 +22,5 @@ public:
|
||||||
// This is different than the base class function since on
|
// This is different than the base class function since on
|
||||||
// Android (EGL, Vulkan) we do have all this info on the render thread.
|
// 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 InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) = 0;
|
||||||
|
virtual bool Initialized() = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,6 +20,7 @@ bool AndroidJavaEGLGraphicsContext::InitFromRenderThread(ANativeWindow *wnd, int
|
||||||
|
|
||||||
void AndroidJavaEGLGraphicsContext::ShutdownFromRenderThread() {
|
void AndroidJavaEGLGraphicsContext::ShutdownFromRenderThread() {
|
||||||
ILOG("AndroidJavaEGLGraphicsContext::Shutdown");
|
ILOG("AndroidJavaEGLGraphicsContext::Shutdown");
|
||||||
|
renderManager_->WaitUntilQueueIdle();
|
||||||
renderManager_ = nullptr; // owned by draw_.
|
renderManager_ = nullptr; // owned by draw_.
|
||||||
delete draw_;
|
delete draw_;
|
||||||
draw_ = nullptr;
|
draw_ = nullptr;
|
||||||
|
|
|
@ -15,6 +15,10 @@ public:
|
||||||
delete draw_;
|
delete draw_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Initialized() override {
|
||||||
|
return draw_ != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// This performs the actual initialization,
|
// This performs the actual initialization,
|
||||||
bool InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override;
|
bool InitFromRenderThread(ANativeWindow *wnd, int desiredBackbufferSizeX, int desiredBackbufferSizeY, int backbufferFormat, int androidVersion) override;
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,10 @@ public:
|
||||||
Draw::DrawContext *GetDrawContext() override {
|
Draw::DrawContext *GetDrawContext() override {
|
||||||
return draw_;
|
return draw_;
|
||||||
}
|
}
|
||||||
|
bool Initialized() override {
|
||||||
|
return draw_ != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VulkanContext *g_Vulkan = nullptr;
|
VulkanContext *g_Vulkan = nullptr;
|
||||||
Draw::DrawContext *draw_ = nullptr;
|
Draw::DrawContext *draw_ = nullptr;
|
||||||
|
|
|
@ -171,6 +171,20 @@ static void EmuThreadFunc() {
|
||||||
gJvm->AttachCurrentThread(&env, nullptr);
|
gJvm->AttachCurrentThread(&env, nullptr);
|
||||||
|
|
||||||
setCurrentThreadName("Emu");
|
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.
|
// There's no real requirement that NativeInit happen on this thread.
|
||||||
// We just call the update/render loop here.
|
// We just call the update/render loop here.
|
||||||
|
@ -179,18 +193,31 @@ static void EmuThreadFunc() {
|
||||||
UpdateRunLoopAndroid(env);
|
UpdateRunLoopAndroid(env);
|
||||||
}
|
}
|
||||||
emuThreadState = (int)EmuThreadState::STOPPED;
|
emuThreadState = (int)EmuThreadState::STOPPED;
|
||||||
|
|
||||||
|
NativeShutdownGraphics();
|
||||||
|
|
||||||
gJvm->DetachCurrentThread();
|
gJvm->DetachCurrentThread();
|
||||||
|
ILOG("Leaving emu thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void EmuThreadStart(JNIEnv *env) {
|
static void EmuThreadStart() {
|
||||||
|
ILOG("EmuThreadStart");
|
||||||
emuThreadState = (int)EmuThreadState::START_REQUESTED;
|
emuThreadState = (int)EmuThreadState::START_REQUESTED;
|
||||||
emuThread = std::thread(&EmuThreadFunc);
|
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() {
|
static void EmuThreadStop() {
|
||||||
|
ILOG("EmuThreadStop - stopping...");
|
||||||
emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;
|
emuThreadState = (int)EmuThreadState::QUIT_REQUESTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void EmuThreadJoin() {
|
||||||
emuThread.join();
|
emuThread.join();
|
||||||
emuThread = std::thread();
|
emuThread = std::thread();
|
||||||
|
ILOG("EmuThreadStop - joined.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ProcessFrameCommands(JNIEnv *env);
|
static void ProcessFrameCommands(JNIEnv *env);
|
||||||
|
@ -429,9 +456,8 @@ retry:
|
||||||
|
|
||||||
if (useCPUThread) {
|
if (useCPUThread) {
|
||||||
ILOG("NativeApp.init() - launching emu thread");
|
ILOG("NativeApp.init() - launching emu thread");
|
||||||
EmuThreadStart(env);
|
EmuThreadStart();
|
||||||
}
|
}
|
||||||
ILOG("NativeApp.init() -- end");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void Java_org_ppsspp_ppsspp_NativeApp_audioInit(JNIEnv *, jclass) {
|
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) {
|
extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) {
|
||||||
if (useCPUThread)
|
if (useCPUThread && graphicsContext) {
|
||||||
EmuThreadStop();
|
EmuThreadStop();
|
||||||
|
while (emuThreadState != (int)EmuThreadState::STOPPED) {
|
||||||
|
graphicsContext->ThreadFrame();
|
||||||
|
}
|
||||||
|
EmuThreadJoin();
|
||||||
|
}
|
||||||
|
|
||||||
ILOG("NativeApp.shutdown() -- begin");
|
ILOG("NativeApp.shutdown() -- begin");
|
||||||
if (renderer_inited) {
|
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.
|
// We should be running on the render thread here.
|
||||||
std::string errorMessage;
|
std::string errorMessage;
|
||||||
if (renderer_inited) {
|
if (renderer_inited) {
|
||||||
|
// Would be really nice if we could get something on the GL thread immediately when shutting down...
|
||||||
ILOG("NativeApp.displayInit() restoring");
|
ILOG("NativeApp.displayInit() restoring");
|
||||||
graphicsContext->ThreadEnd();
|
graphicsContext->ThreadEnd();
|
||||||
|
if (useCPUThread) {
|
||||||
|
EmuThreadStop();
|
||||||
|
while (emuThreadState != (int)EmuThreadState::STOPPED) {
|
||||||
|
graphicsContext->ThreadFrame();
|
||||||
|
}
|
||||||
|
EmuThreadJoin();
|
||||||
|
} else {
|
||||||
|
NativeShutdownGraphics();
|
||||||
|
}
|
||||||
graphicsContext->ShutdownFromRenderThread();
|
graphicsContext->ShutdownFromRenderThread();
|
||||||
|
|
||||||
|
ILOG("Shut down both threads. Now let's bring it up again!");
|
||||||
|
|
||||||
graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0);
|
graphicsContext->InitFromRenderThread(nullptr, 0, 0, 0, 0);
|
||||||
|
if (useCPUThread) {
|
||||||
|
EmuThreadStart();
|
||||||
|
} else {
|
||||||
|
NativeInitGraphics(graphicsContext);
|
||||||
|
}
|
||||||
graphicsContext->ThreadStart();
|
graphicsContext->ThreadStart();
|
||||||
ILOG("Restored.");
|
ILOG("Restored.");
|
||||||
} else {
|
} else {
|
||||||
|
@ -564,15 +612,6 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_backbufferResize(JNIEnv
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateRunLoopAndroid(JNIEnv *env) {
|
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();
|
NativeUpdate();
|
||||||
|
|
||||||
NativeRender(graphicsContext);
|
NativeRender(graphicsContext);
|
||||||
|
|
|
@ -112,7 +112,7 @@ PrioritizedWorkQueueItem *PrioritizedWorkQueue::Pop() {
|
||||||
static std::thread *workThread;
|
static std::thread *workThread;
|
||||||
|
|
||||||
static void threadfunc(PrioritizedWorkQueue *wq) {
|
static void threadfunc(PrioritizedWorkQueue *wq) {
|
||||||
setCurrentThreadName("PrioritizedWorkQueue");
|
setCurrentThreadName("PrioQueue");
|
||||||
while (true) {
|
while (true) {
|
||||||
PrioritizedWorkQueueItem *item = wq->Pop();
|
PrioritizedWorkQueueItem *item = wq->Pop();
|
||||||
if (!item) {
|
if (!item) {
|
||||||
|
@ -126,7 +126,7 @@ static void threadfunc(PrioritizedWorkQueue *wq) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessWorkQueueOnThreadWhile(PrioritizedWorkQueue *wq) {
|
void ProcessWorkQueueOnThreadWhile(PrioritizedWorkQueue *wq) {
|
||||||
workThread = new std::thread(std::bind(&threadfunc, wq));
|
workThread = new std::thread([=](){threadfunc(wq);});
|
||||||
}
|
}
|
||||||
|
|
||||||
void StopProcessingWorkQueue(PrioritizedWorkQueue *wq) {
|
void StopProcessingWorkQueue(PrioritizedWorkQueue *wq) {
|
||||||
|
@ -135,5 +135,5 @@ void StopProcessingWorkQueue(PrioritizedWorkQueue *wq) {
|
||||||
workThread->join();
|
workThread->join();
|
||||||
delete workThread;
|
delete workThread;
|
||||||
}
|
}
|
||||||
workThread = 0;
|
workThread = nullptr;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue