diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 1d667d72ebcb..ffe7e411b61b 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -688,8 +688,9 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init EARLY_LOG("NativeApp.init() -- begin"); PROFILE_INIT(); - std::lock_guard guard(renderLock); // Note: This is held for the rest of this function - intended? + std::lock_guard guard(renderLock); renderer_inited = false; + exitRenderLoop = false; androidVersion = jAndroidVersion; deviceType = jdeviceType; @@ -872,8 +873,14 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_pause(JNIEnv *, jclass) { } extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { + INFO_LOG(SYSTEM, "NativeApp.shutdown() -- begin"); + if (renderer_inited && useCPUThread && graphicsContext) { // Only used in Java EGL path. + + // We can't lock renderLock here because the emu thread will be in NativeFrame + // which locks renderLock already, and only gets out once we call ThreadFrame() + // in a loop before, to empty the queue. EmuThreadStop("shutdown"); INFO_LOG(SYSTEM, "BeginAndroidShutdown"); graphicsContext->BeginAndroidShutdown(); @@ -891,19 +898,19 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_shutdown(JNIEnv *, jclass) { EmuThreadJoin(); } - INFO_LOG(SYSTEM, "NativeApp.shutdown() -- begin"); - if (renderer_inited) { - INFO_LOG(G3D, "Shutting down renderer"); - graphicsContext->Shutdown(); - delete graphicsContext; - graphicsContext = nullptr; - renderer_inited = false; - } else { - INFO_LOG(G3D, "Not shutting down renderer - not initialized"); - } - { std::lock_guard guard(renderLock); + + if (graphicsContext) { + INFO_LOG(G3D, "Shutting down renderer"); + graphicsContext->Shutdown(); + delete graphicsContext; + graphicsContext = nullptr; + renderer_inited = false; + } else { + INFO_LOG(G3D, "Not shutting down renderer - not initialized"); + } + NativeShutdown(); g_VFS.Clear(); } @@ -1135,6 +1142,9 @@ void UpdateRunLoopAndroid(JNIEnv *env) { } extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, jobject obj) { + // This doesn't get called on the Vulkan path. + _assert_(useCPUThread); + static bool hasSetThreadName = false; if (!hasSetThreadName) { hasSetThreadName = true; @@ -1144,13 +1154,9 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, if (IsVREnabled() && !StartVRRender()) return; - if (useCPUThread) { - // This is the "GPU thread". Call ThreadFrame. - if (!graphicsContext || !graphicsContext->ThreadFrame()) { - return; - } - } else { - UpdateRunLoopAndroid(env); + // This is the "GPU thread". Call ThreadFrame. + if (!graphicsContext || !graphicsContext->ThreadFrame()) { + return; } if (IsVREnabled()) { @@ -1457,15 +1463,24 @@ static void ProcessFrameCommands(JNIEnv *env) { } // This runs in Vulkan mode only. +// This handles the entire lifecycle of the Vulkan context, init and exit. extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runVulkanRenderLoop(JNIEnv *env, jobject obj, jobject _surf) { _assert_(!useCPUThread); if (!graphicsContext) { ERROR_LOG(G3D, "runVulkanRenderLoop: Tried to enter without a created graphics context."); + renderLoopRunning = false; + exitRenderLoop = false; return false; } - exitRenderLoop = false; + if (exitRenderLoop) { + WARN_LOG(G3D, "runVulkanRenderLoop: ExitRenderLoop requested at start, skipping the whole thing."); + renderLoopRunning = false; + exitRenderLoop = false; + return true; + } + // This is up here to prevent race conditions, in case we pause during init. renderLoopRunning = true; @@ -1507,11 +1522,11 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runVulkanRenderLoo hasSetThreadName = true; SetCurrentThreadName("AndroidRender"); } - } - while (!exitRenderLoop) { - LockedNativeUpdateRender(); - ProcessFrameCommands(env); + while (!exitRenderLoop) { + LockedNativeUpdateRender(); + ProcessFrameCommands(env); + } } INFO_LOG(G3D, "Leaving EGL/Vulkan render loop."); @@ -1525,6 +1540,7 @@ extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runVulkanRenderLoo INFO_LOG(G3D, "Shutting down graphics context from render thread..."); graphicsContext->ShutdownFromRenderThread(); renderLoopRunning = false; + exitRenderLoop = false; WARN_LOG(G3D, "Render loop function exited."); return true; diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java index db4a4c1c931d..4f6cc7835359 100644 --- a/android/src/org/ppsspp/ppsspp/NativeActivity.java +++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java @@ -662,7 +662,7 @@ public void onCreate(Bundle savedInstanceState) { Log.i(TAG, "setcontentview before"); setContentView(mSurfaceView); Log.i(TAG, "setcontentview after"); - ensureRenderLoop(); + startRenderLoopThread(); } } @@ -677,12 +677,18 @@ public void onWindowFocusChanged(boolean hasFocus) { public void notifySurface(Surface surface) { mSurface = surface; + + if (!initialized) { + Log.e(TAG, "Can't deal with surfaces while not initialized"); + return; + } + if (!javaGL) { // If we got a surface, this starts the thread. If not, it doesn't. if (mSurface == null) { joinRenderLoopThread(); } else { - ensureRenderLoop(); + startRenderLoopThread(); } } updateSustainedPerformanceMode(); @@ -690,7 +696,7 @@ public void notifySurface(Surface surface) { // Invariants: After this, mRenderLoopThread will be set, and the thread will be running, // if in Vulkan mode. - protected synchronized void ensureRenderLoop() { + protected synchronized void startRenderLoopThread() { if (javaGL) { Log.e(TAG, "JavaGL mode - should not get into ensureRenderLoop."); return; @@ -740,27 +746,26 @@ void setupSystemUiCallback() { navigationCallbackView = decorView; } - @Override - protected void onStop() { - super.onStop(); - Log.i(TAG, "onStop - do nothing special"); - } - @Override protected void onDestroy() { super.onDestroy(); Log.i(TAG, "onDestroy"); if (javaGL) { - if (nativeRenderer != null && nativeRenderer.isRenderingFrame()) { - Log.i(TAG, "Waiting for renderer to finish."); - int tries = 200; - do { - try { - Thread.sleep(10); - } catch (InterruptedException e) { - } - tries--; - } while (nativeRenderer.isRenderingFrame() && tries > 0); + if (nativeRenderer != null) { + if (nativeRenderer.isRenderingFrame()) { + Log.i(TAG, "Waiting for renderer to finish."); + int tries = 200; + do { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + } + tries--; + } while (nativeRenderer.isRenderingFrame() && tries > 0); + } else { + Log.i(TAG, "nativerenderer done."); + nativeRenderer = null; + } } mGLSurfaceView.onDestroy(); mGLSurfaceView = null; @@ -785,7 +790,7 @@ protected void onDestroy() { // TODO: Can we ensure that the GL thread has stopped rendering here? // I've seen crashes that seem to indicate that sometimes it hasn't... NativeApp.audioShutdown(); - if (shuttingDown || isFinishing()) { + if (shuttingDown) { NativeApp.shutdown(); unregisterCallbacks(); initialized = false; @@ -803,6 +808,7 @@ protected void onPause() { super.onPause(); Log.i(TAG, "onPause"); loseAudioFocus(this.audioManager, this.audioFocusChangeListener); + sizeManager.setPaused(true); NativeApp.pause(); if (!javaGL) { mSurfaceView.onPause(); @@ -838,6 +844,7 @@ private boolean detectOpenGLES30() { protected void onResume() { super.onResume(); updateSustainedPerformanceMode(); + sizeManager.setPaused(false); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { updateSystemUiVisibility(); } @@ -866,7 +873,7 @@ protected void onResume() { if (!javaGL) { // Restart the render loop. - ensureRenderLoop(); + startRenderLoopThread(); } } diff --git a/android/src/org/ppsspp/ppsspp/SizeManager.java b/android/src/org/ppsspp/ppsspp/SizeManager.java index 25509769eb4d..af81f640ddc3 100644 --- a/android/src/org/ppsspp/ppsspp/SizeManager.java +++ b/android/src/org/ppsspp/ppsspp/SizeManager.java @@ -39,10 +39,18 @@ public class SizeManager implements SurfaceHolder.Callback { private Point desiredSize = new Point(); private int badOrientationCount = 0; + + private boolean paused = false; + public SizeManager(final NativeActivity a) { activity = a; } + + public void setPaused(boolean p) { + paused = p; + } + @TargetApi(Build.VERSION_CODES.P) public void setSurfaceView(SurfaceView view) { surfaceView = view; @@ -107,7 +115,11 @@ public void surfaceChanged(SurfaceHolder holder, int format, int width, int heig NativeApp.backbufferResize(width, height, format); updateDisplayMeasurements(); - activity.notifySurface(holder.getSurface()); + if (!paused) { + activity.notifySurface(holder.getSurface()); + } else { + Log.i(TAG, "Skipping notifySurface while paused"); + } } @Override