40
\$\begingroup\$

The Android documentation says:

There are situations where the EGL rendering context will be lost. This typically happens when device wakes up after going to sleep. When the EGL context is lost, all OpenGL resources (such as textures) that are associated with that context will be automatically deleted. In order to keep rendering correctly, a renderer must recreate any lost resources that it still needs. The onSurfaceCreated(GL10, EGLConfig) method is a convenient place to do this.

But having to reload all the textures in the OpenGL context is both a pain and hurts the game experience for the user when reentering the app after a pause. I know that "Angry Birds" somehow avoids this, I'm looking for suggestions on how to accomplish the same?

I'm working with the Android NDK r5 (CrystaX version.) I did find this possible hack to the problem but I'm trying to avoid building an entire custom SDK version.

\$\endgroup\$
1
  • \$\begingroup\$ sleeping and waking up is refered to device so while user just pause your game or switch processs it should loose any of the EGL context. \$\endgroup\$
    – Ali1S232
    May 23, 2011 at 16:00

5 Answers 5

22
\$\begingroup\$

Replica Island has a modified version of GLSurfaceView that deals with this issue (and works with earlier Android versions). According to Chris Pruett:

Basically, I hacked up the original GLSurfaceView to solve a very specific problem: I wanted to go to different Activities within my app without throwing all of my OpenGL state away. The major change was to separate the EGLSurface from the EGLContext, and to throw the former away onPause(), but preserve the latter until the context is explicitly lost. The default implementation of GLSurfaceView (which I didn't write, by the way), throws all GL state away when the activity is paused, and calls onSurfaceCreated() when it is resumed. That meant that, when a dialog box popped up in my game, closing it incurred a delay because all the textures had to be reloaded.

You should use the default GLSurfaceView. If you must have the same functionality that mine has, you can look at mine. But doing what I did exposed all sorts of awful driver bugs in some handsets (see the very long comments near the end of that file), and you can avoid all that mess by just using the default one.

Edit: I just realized you already posted the link to a similar hack. I don't think there is any built-in solution prior to honeycomb. Replica Island is a popular game working on many devices and you might find Chris's implementation and comments helpful.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ After trying various approaches to circumventing this, I've decided the best solution is just to recode the app to recreate the context. This is such a wasteful approach but it doesn't look like there's any good solution code.google.com/p/android/issues/detail?id=12774 \$\endgroup\$
    – Nick Gotch
    May 27, 2011 at 18:14
8
\$\begingroup\$

I'd like to add another answer to this that was passed on to me a year or two back by Chris Pruett (Replica Island, Wind-Up Knight, etc). It's especially useful here in 2013 since setPreserveEglContextOnPause(true) doesn't seem to work on 4.3. (I could be wrong about that but that's how it looks to me right now as I update game code last touched in 2011).

Basically the trick is to detach your GLSurfaceView from the view hierarchy from your Activity's onPause(). Since it's not in the view hierarchy at the point onPause() runs, the context never gets destroyed.

So your Activity's onPause() should look like this:

@Override
public void onPause() {
    view.setVisibility(View.GONE);
    super.onPause();
    ...
}

And you restore your GLSurfaceView to the hierarchy not from onResume() but from onWindowFocusChanged() :

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus && view.getVisibility() == View.GONE) {
         view.setVisibility(View.VISIBLE);
    }
    ...
}

Note that you never call the GLSurfaceView's onPause() and onResume() and that this is the official SDK GLSurfaceView, no hacked-up alternative version is required.

\$\endgroup\$
2
  • \$\begingroup\$ In fact this approach doesn't seem to work. Chris Pruett used it in combination with Unity, is it possible that this workaround is not working in native projects? But only not calling GLView.onPause() seems to work!? Are there others who can confirm this? \$\endgroup\$
    – sjkm
    Aug 15, 2014 at 18:22
  • \$\begingroup\$ It worked for me targeted at Android 2.2 OpenGl ES 2. Not sure how it performs on other devices. I have an LG G2 D802 phone. Does anyone know if this is a general solution? \$\endgroup\$
    – user24493
    Jul 22, 2016 at 11:58
7
\$\begingroup\$

< rant > I spent a huge amount of time on this problem, I tried many different solutions and none worked until today, I think this is one of the most awful design decision I even seen, but coming from the Android team I'm not really surprised. < /rant >

So, the solution is to move the eglContext member upwards and make it static (global) so it won't be destroyed, then you simply have to check if it's null or not before creating it again.

So far this solution seems to works for us, and we don't care if it breaks on 2005 devices.

\$\endgroup\$
4
\$\begingroup\$

Use honeycomb API. There is an option do preserve your OGL context. Otherwise, you need to reload your context. It is not difficult nor painful.

You need to understand that there is a two cases (Android 2.1):

  • Screen Pause: your application is always the front one => hack available
  • Application Pause: there is another front application => No Solution

Note: old android gpus don't support multi context. So the opengl context is lost when you switch to another application => no solution available (You can hack to preserve your context on screen pause).

Note 2: HoneyComb function is setPreserveEGLContextOnPause

\$\endgroup\$
3
  • 1
    \$\begingroup\$ I'm porting an C++ game to the Android NDK which isn't designed to reload the context easily, so at least for me it is somewhat difficult. Difficulties aside, reloading textures will introduce a delay which isn't present on our other devices (iPhone, PSP, DS, etc..) Glad to hear honeycomb fixes this but unfortunately we need to support 2.1+ \$\endgroup\$
    – Nick Gotch
    May 23, 2011 at 21:57
  • 1
    \$\begingroup\$ This does not answer his question at all. \$\endgroup\$
    – notlesh
    May 23, 2011 at 22:48
  • 1
    \$\begingroup\$ If you don't understand, you can't do it. \$\endgroup\$
    – Ellis
    May 24, 2011 at 5:54
2
\$\begingroup\$

Some of the answers here are reasonable for early uses of OpenGL ES on Android. The first GLES devices only supported a single context, so GLSurfaceView was designed to aggressively discard state. Convincing GLSurfaceView to do otherwise isn't easy.

For more recent versions of Android (probably anything using GLES 2.x), the best answer is to use a plain SurfaceView and do your own EGL and thread management. You can find multiple examples of GLES used with a plain SurfaceView in Grafika, including a library of simple classes for creating and destroying EGL contexts.

It's still good practice to unload state when an app goes into the background, but for the example from Chris Pruett -- where the app was still in the foreground, but the Activity hosting the GLSurfaceView was switched away from -- there's no value in tearing down the context.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .