Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Android: Minor activity lifecycle stuff #18230

Merged
merged 6 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 40 additions & 24 deletions android/jni/app-android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,8 +688,9 @@ extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init
EARLY_LOG("NativeApp.init() -- begin");
PROFILE_INIT();

std::lock_guard<std::mutex> guard(renderLock); // Note: This is held for the rest of this function - intended?
std::lock_guard<std::mutex> guard(renderLock);
renderer_inited = false;
exitRenderLoop = false;
androidVersion = jAndroidVersion;
deviceType = jdeviceType;

Expand Down Expand Up @@ -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();
Expand All @@ -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<std::mutex> 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();
}
Expand Down Expand Up @@ -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;
Expand All @@ -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()) {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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.");
Expand All @@ -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;
Expand Down
49 changes: 28 additions & 21 deletions android/src/org/ppsspp/ppsspp/NativeActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "setcontentview before");
setContentView(mSurfaceView);
Log.i(TAG, "setcontentview after");
ensureRenderLoop();
startRenderLoopThread();
}
}

Expand All @@ -677,20 +677,26 @@ 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();
}

// 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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -866,7 +873,7 @@ protected void onResume() {

if (!javaGL) {
// Restart the render loop.
ensureRenderLoop();
startRenderLoopThread();
}
}

Expand Down
14 changes: 13 additions & 1 deletion android/src/org/ppsspp/ppsspp/SizeManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can't cause it to have an outdated surface, can it?

-[Unknown]

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does appear to be OK because we always get a surface notification after the next resume, which is when we want it.

I believe spurious surfaceChanged events after pause are a bug that seems to have been fixed around Android 12.

}
}

@Override
Expand Down