Skip to content
This repository has been archived by the owner on Feb 11, 2021. It is now read-only.

Commit

Permalink
Fixes #1422, restores GL context automatically under Android
Browse files Browse the repository at this point in the history
  • Loading branch information
gabomdq committed Jun 19, 2012
1 parent ae07322 commit 20419c5
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 10 deletions.
18 changes: 18 additions & 0 deletions README.android
Expand Up @@ -73,6 +73,24 @@ android-project/
src/org/libsdl/app/SDLActivity.java - the Java class handling the initialization and binding to SDL. Be very careful changing this, as the SDL library relies on this implementation.


================================================================================
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
================================================================================
Expand Down
22 changes: 12 additions & 10 deletions android-project/src/org/libsdl/app/SDLActivity.java
Expand Up @@ -68,17 +68,17 @@ protected void onCreate(Bundle savedInstanceState) {
}

// 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();
Expand Down Expand Up @@ -249,12 +249,15 @@ public static boolean createEGLSurface() {
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;
Expand Down Expand Up @@ -426,7 +429,6 @@ public SDLSurface(Context context) {
public void surfaceCreated(SurfaceHolder holder) {
Log.v("SDL", "surfaceCreated()");
holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
SDLActivity.createEGLSurface();
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
}

Expand Down
7 changes: 7 additions & 0 deletions src/core/android/SDL_android.cpp
Expand Up @@ -176,6 +176,8 @@ extern "C" void Java_org_libsdl_app_SDLActivity_nativePause(
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);
}
Expand All @@ -186,6 +188,11 @@ extern "C" void Java_org_libsdl_app_SDLActivity_nativeResume(
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);
}
Expand Down
30 changes: 30 additions & 0 deletions src/video/android/SDL_androidevents.c
Expand Up @@ -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 */
Expand Down
1 change: 1 addition & 0 deletions src/video/android/SDL_androidvideo.c
Expand Up @@ -64,6 +64,7 @@ extern void Android_GL_DeleteContext(_THIS, SDL_GLContext context);
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;
Expand Down
3 changes: 3 additions & 0 deletions src/video/android/SDL_androidvideo.h
Expand Up @@ -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 */
Expand All @@ -33,8 +34,10 @@ extern void Android_SetScreenResolution(int width, int height, Uint32 format);
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: */
6 changes: 6 additions & 0 deletions src/video/android/SDL_androidwindow.c
Expand Up @@ -35,6 +35,8 @@ Android_CreateWindow(_THIS, SDL_Window * window)
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;
Expand Down Expand Up @@ -62,6 +64,10 @@ Android_DestroyWindow(_THIS, SDL_Window * window)
{
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;
}
}

Expand Down

0 comments on commit 20419c5

Please sign in to comment.