Fixes #1422, restores GL context automatically under Android
authorGabriel Jacobo <gabomdq@gmail.com>
Tue, 19 Jun 2012 13:57:42 -0300
changeset 6330 0fa55ca2efdd
parent 6329 77dcb8c486e9
child 6331 5732e1a80bde
Fixes #1422, restores GL context automatically under Android
README.android
android-project/src/org/libsdl/app/SDLActivity.java
src/core/android/SDL_android.cpp
src/video/android/SDL_androidevents.c
src/video/android/SDL_androidvideo.c
src/video/android/SDL_androidvideo.h
src/video/android/SDL_androidwindow.c
--- a/README.android	Tue Jun 19 12:29:53 2012 -0400
+++ b/README.android	Tue Jun 19 13:57:42 2012 -0300
@@ -74,6 +74,24 @@
 
 
 ================================================================================
+ Pause / Resume behaviour
+================================================================================
+
+If SDL is compiled with SDL_ANDROID_BLOCK_ON_PAUSE defined, the event loop will
+block itself when the app is paused (ie, when the user returns to the main
+Android dashboard). Blocking is better in terms of battery use, and it allows your
+app to spring back to life instantaneously after resume (versus polling for
+a resume message).
+Upon resume, SDL will attempt to restore the GL context automatically.
+In modern devices (Android 3.0 and up) this will most likely succeed and your
+app can continue to operate as it was.
+However, there's a chance (on older hardware, or on systems under heavy load),
+where the GL context can not be restored. In that case you have to listen for
+a specific message, (which is not yet implemented!) and restore your textures
+manually or quit the app (which is actually the kind of behaviour you'll see
+under iOS, if the OS can not restore your GL context it will just kill your app)
+
+================================================================================
  Additional documentation
 ================================================================================
 
--- a/android-project/src/org/libsdl/app/SDLActivity.java	Tue Jun 19 12:29:53 2012 -0400
+++ b/android-project/src/org/libsdl/app/SDLActivity.java	Tue Jun 19 13:57:42 2012 -0300
@@ -68,17 +68,17 @@
     }
 
     // Events
-    protected void onPause() {
+    /*protected void onPause() {
         Log.v("SDL", "onPause()");
         super.onPause();
-        SDLActivity.nativePause();
+        // Don't call SDLActivity.nativePause(); here, it will be called by SDLSurface::surfaceDestroyed
     }
 
     protected void onResume() {
         Log.v("SDL", "onResume()");
         super.onResume();
-        SDLActivity.nativeResume();
-    }
+        // Don't call SDLActivity.nativeResume(); here, it will be called via SDLSurface::surfaceChanged->SDLActivity::startApp
+    }*/
 
     protected void onDestroy() {
         super.onDestroy();
@@ -249,12 +249,15 @@
                 return false;
             }
 
-            if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) {
-                Log.e("SDL", "Old EGL Context doesnt work, trying with a new one");
-                createEGLContext();
+            if (egl.eglGetCurrentContext() != SDLActivity.mEGLContext) {
                 if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) {
-                    Log.e("SDL", "Failed making EGL Context current");
-                    return false;
+                    Log.e("SDL", "Old EGL Context doesnt work, trying with a new one");
+                    // TODO: Notify the user via a message that the old context could not be restored, and that textures need to be manually restored.
+                    createEGLContext();
+                    if (!egl.eglMakeCurrent(SDLActivity.mEGLDisplay, surface, surface, SDLActivity.mEGLContext)) {
+                        Log.e("SDL", "Failed making EGL Context current");
+                        return false;
+                    }
                 }
             }
             SDLActivity.mEGLSurface = surface;
@@ -426,7 +429,6 @@
     public void surfaceCreated(SurfaceHolder holder) {
         Log.v("SDL", "surfaceCreated()");
         holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
-        SDLActivity.createEGLSurface();
         enableSensor(Sensor.TYPE_ACCELEROMETER, true);
     }
 
--- a/src/core/android/SDL_android.cpp	Tue Jun 19 12:29:53 2012 -0400
+++ b/src/core/android/SDL_android.cpp	Tue Jun 19 13:57:42 2012 -0300
@@ -176,6 +176,8 @@
                                     JNIEnv* env, jclass cls)
 {
     if (Android_Window) {
+        /* Signal the pause semaphore so the event loop knows to pause and (optionally) block itself */
+        if (!SDL_SemValue(Android_PauseSem)) SDL_SemPost(Android_PauseSem);
         SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0);
         SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0);
     }
@@ -186,6 +188,11 @@
                                     JNIEnv* env, jclass cls)
 {
     if (Android_Window) {
+        /* Signal the resume semaphore so the event loop knows to resume and restore the GL Context
+         * We can't restore the GL Context here because it needs to be done on the SDL main thread
+         * and this function will be called from the Java thread instead.
+         */
+        if (!SDL_SemValue(Android_ResumeSem)) SDL_SemPost(Android_ResumeSem);
         SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0);
         SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0);
     }
--- a/src/video/android/SDL_androidevents.c	Tue Jun 19 12:29:53 2012 -0400
+++ b/src/video/android/SDL_androidevents.c	Tue Jun 19 13:57:42 2012 -0300
@@ -27,7 +27,37 @@
 void
 Android_PumpEvents(_THIS)
 {
+    static int isPaused = 0;
     /* No polling necessary */
+
+    /*
+     * Android_ResumeSem and Android_PauseSem are signaled from Java_org_libsdl_app_SDLActivity_nativePause and Java_org_libsdl_app_SDLActivity_nativeResume
+     * When the pause semaphoe is signaled, if SDL_ANDROID_BLOCK_ON_PAUSE is defined the event loop will block until the resume signal is emitted.
+     * When the resume semaphore is signaled, SDL_GL_CreateContext is called which in turn calls Java code
+     * SDLActivity::createGLContext -> SDLActivity:: initEGL -> SDLActivity::createEGLSurface -> SDLActivity::createEGLContext
+     */
+    if (isPaused) {
+#if SDL_ANDROID_BLOCK_ON_PAUSE
+        if(SDL_SemWait(Android_ResumeSem) == 0) {
+#else
+        if(SDL_SemTryWait(Android_ResumeSem) == 0) {
+#endif
+            isPaused = 0;
+            /* TODO: Should we double check if we are on the same thread as the one that made the original GL context?
+             * This call will go through the following chain of calls in Java:
+             * SDLActivity::createGLContext -> SDLActivity:: initEGL -> SDLActivity::createEGLSurface -> SDLActivity::createEGLContext
+             * SDLActivity::createEGLContext will attempt to restore the GL context first, and if that fails it will create a new one
+             * If a new GL context is created, the user needs to restore the textures manually (TODO: notify the user that this happened with a message)
+             */
+            SDL_GL_CreateContext(Android_Window);
+        }
+    }
+    else {
+        if(SDL_SemTryWait(Android_PauseSem) == 0) {
+            /* If we fall in here, the system is/was paused */
+            isPaused = 1;
+        }
+    }
 }
 
 #endif /* SDL_VIDEO_DRIVER_ANDROID */
--- a/src/video/android/SDL_androidvideo.c	Tue Jun 19 12:29:53 2012 -0400
+++ b/src/video/android/SDL_androidvideo.c	Tue Jun 19 13:57:42 2012 -0300
@@ -64,6 +64,7 @@
 int Android_ScreenWidth = 0;
 int Android_ScreenHeight = 0;
 Uint32 Android_ScreenFormat = SDL_PIXELFORMAT_UNKNOWN;
+SDL_sem *Android_PauseSem = NULL, *Android_ResumeSem = NULL;
 
 /* Currently only one window */
 SDL_Window *Android_Window = NULL;
--- a/src/video/android/SDL_androidvideo.h	Tue Jun 19 12:29:53 2012 -0400
+++ b/src/video/android/SDL_androidvideo.h	Tue Jun 19 13:57:42 2012 -0300
@@ -23,6 +23,7 @@
 #ifndef _SDL_androidvideo_h
 #define _SDL_androidvideo_h
 
+#include "SDL_mutex.h"
 #include "../SDL_sysvideo.h"
 
 /* Called by the JNI layer when the screen changes size or format */
@@ -33,8 +34,10 @@
 extern int Android_ScreenWidth;
 extern int Android_ScreenHeight;
 extern Uint32 Android_ScreenFormat;
+extern SDL_sem *Android_PauseSem, *Android_ResumeSem;
 extern SDL_Window *Android_Window;
 
+
 #endif /* _SDL_androidvideo_h */
 
 /* vi: set ts=4 sw=4 expandtab: */
--- a/src/video/android/SDL_androidwindow.c	Tue Jun 19 12:29:53 2012 -0400
+++ b/src/video/android/SDL_androidwindow.c	Tue Jun 19 13:57:42 2012 -0300
@@ -35,6 +35,8 @@
         return -1;
     }
     Android_Window = window;
+    Android_PauseSem = SDL_CreateSemaphore(0);
+    Android_ResumeSem = SDL_CreateSemaphore(0);
 
     /* Adjust the window data to match the screen */
     window->x = 0;
@@ -62,6 +64,10 @@
 {
     if (window == Android_Window) {
         Android_Window = NULL;
+        if (Android_PauseSem) SDL_DestroySemaphore(Android_PauseSem);
+        if (Android_ResumeSem) SDL_DestroySemaphore(Android_ResumeSem);
+        Android_PauseSem = NULL;
+        Android_ResumeSem = NULL;
     }
 }