Help with GLSurfaceView and GL context

1,007 views
Skip to first unread message

damian

unread,
May 28, 2012, 5:53:35 PM5/28/12
to replica-island-...@googlegroups.com
Hi,
I'm porting an iPhone c++ game to Android and I'm having a real hard time with the OpenGL context being lost when the game goes off the screen. 
I should reload all the textures and shaders, but since my code is not prepared to do so, it's a lot of work to make the changes. Moreover, I render to textures, and even create bitmaps prodedurally and load them to textures, so it's an almost impossible job for me to do those changes now.

So I looked at Reploca Island's GLSurfaceView but have a number of questions:
1) Can I use it in my game? 
2) Is it stable? My game will work on Android 2.2 and up and it's using Opengl 2, so I don't mind about very old devices.
3) I've already tried to take it and put it in my game, but I'm not understanding the code, and I don't know what to do with the methods that takes a TextureLibrary or BufferLibrary (flushTextures, loadTextures, flushBuffers, loadBuffers)

Any help will be really apreciated.
Damian.

Chris Pruett

unread,
May 28, 2012, 6:00:31 PM5/28/12
to replica-island-...@googlegroups.com
Hi Damian,

Yes, it is stable. Actually, most of the changes I added to it have since been rolled into the Android mainline version of GLSurfaceView. If you are running on Honeycomb (3.0) or newer, there's now an API call to preserve contexts between pauses.

I also have since discovered a simpler way to do this, which does not require any modifications to GLSurfaceView. You can detach the surface holder from the view hierarchy at pause time and reconnect it when focus returns--this way the thread doesn't realize that the game has lost its context and doesn't throw anything away. I use this on my newer games and it's quite convenient.

However, neither method will really solve your problem. The OpenGL ES spec indicates that contexts can be lost, so you have to be able to deal with that case. This is evident on Android because Android is a multitasking OS, but it's not specific to Android (though GLSurfaceView is a little aggressive about dropping state, I think). You can make unpausing your app faster in the general case (using the methods described above), but it is *always* possible that your vram will be completely lost during an application pause. That's part of the GLES spec.

My suggestion is to work to improve your code so that all texture / shader / vbo allocations go through a single level of indirection that can reload (or regenerate) them all if necessary upon request (and hand out new handles or whatever). Also try to minimize the frequency of such an event by preserving the context across pauses. But you still have to solve the underlying problem.

Good luck,
Chris

Damian

unread,
May 28, 2012, 7:06:57 PM5/28/12
to replica-island-...@googlegroups.com
Thanks for the answer Chris,
About the seconf approach, how do you detach the surface holder from the view hierarchy?
I'm trying to simply remove the glview and adding it again with setContentView() like this:

    @Override
    protected void onPause() {
    FrameLayout vpar = (FrameLayout) mGLView.getParent();
    vpar.removeView(mGLView);
        super.onPause();
        mGLView.onPause();
    }

    @Override
    protected void onResume() {
    setContentView(mGLView);
        super.onResume();
        mGLView.onResume();
    }

But it's not working at all... 
Thanks!

Damian

unread,
Jun 6, 2012, 12:44:13 PM6/6/12
to replica-island-...@googlegroups.com
Hi Chris, 
I'm kind of lost here, I can't realize how to implement your second approach and can't find any info about it.
How do I detach the surface holder from the view hierarchy?

Thanks.

Chris Pruett

unread,
Jun 7, 2012, 2:14:37 PM6/7/12
to replica-island-...@googlegroups.com
The GLSurfaceView is a node in the view hierarchy.  It might be the root of your hierarchy, or maybe not.  If it is the root, you can put it under a FrameLayout for convenience.

Once you have a pointer the the view and its parent, you can disconnect the view from the parent (and reconnect it later) like any other view in the hierarchy.  Does that make sense?

You don't want to cause setContentView() on resume.  You want to keep a pointer to the parent returned from mGLView.getParent() and add the view back in as a child.  Also, you don't need to cause mGLView.onPause() anymore.  And you shouldn't reconnect it in onResume() because you can be resumed while the lock screen is still up; use onWindowFocusedChanged instead.  My code looks something like this:

@Override
    protected void onPause() {
     mGLView.setVisibility(View.GONE);
     mGLViewParent.removeView(mUnityView);
     super.onPause();
    }

    
@Override
public void onWindowFocusChanged(boolean hasFocus) {
if (hasFocus && mGLView != null && mGLView.getVisibility() == View.GONE){
mGLViewParent.addView(mUnityView);
mGLView.setVisibility(View.VISIBLE);
}
super.onWindowFocusChanged(hasFocus);
}

Cheers,
Chris

Keith Johnston

unread,
Jul 30, 2012, 4:40:34 PM7/30/12
to replica-island-...@googlegroups.com
Hey Chris - thank you so much for sharing Replica Island - it has been a great resource.

I have been trying to fix this OpenGL context destruction issue as well and I have a fix working. I ended up backporting the GLSurfaceView from Android 4.0. In that code, they basically allow the context to be retained as long as the phone feature "ro.opengles.version" is greater than 2.0. Since I cannot access SystemProperties, I just decided to make our app only support OpenGLES2.0 by adding that requirement in the manifest.

My question is - is it really valid that any phone that supports OpenGLES2.0 will support retaining the OpenGL context? Or is this assumption only valid if the Android version is 3.0 or greater?

Chris Pruett

unread,
Jul 30, 2012, 4:45:58 PM7/30/12
to replica-island-...@googlegroups.com
No, that's not valid--context loss is part of the spec, and it can happen on ANY phone, regardless of system or OpenGL version.  You can change GLSurfaceView to keep state around rather than explicitly throwing it away on pause (which is what I did in Replica Island, and it's what is supported in the stock GLSurfaceView on Android 3.0+), but even then the driver has the right (per the OpenGL ES spec--nothing here is Android specific) to pull the context out from under you if it decides that it needs the resources.

So, long story short, you MUST solve the context loss case.  However, by modding GLSurfaceView or using the code in Android 3.0+, you can avoid that case in most (but not all) circumstances.  What you are solving here is "time to return from pause," not "code that violates the GLES spec by being unable to handle a context loss."

Chris

Keith Johnston

unread,
Jul 30, 2012, 5:18:28 PM7/30/12
to replica-island-...@googlegroups.com
Oh I understand now (I think). Looking at the GLSurfaceView from 4.0 again, it looks like as long as I handle the context being lost by reloading textures and resources in the onSurfaceCreated, I should be OK.


On Monday, July 30, 2012 1:45:58 PM UTC-7, Chris Pruett wrote:
No, that's not valid--context loss is part of the spec, and it can happen on ANY phone, regardless of system or OpenGL version.  You can change GLSurfaceView to keep state around rather than explicitly throwing it away on pause (which is what I did in Replica Island, and it's what is supported in the stock GLSurfaceView on Android 3.0+), but even then the driver has the right (per the OpenGL ES spec--nothing here is Android specific) to pull the context out from under you if it decides that it needs the resources.

So, long story short, you MUST solve the context loss case.  However, by modding GLSurfaceView or using the code in Android 3.0+, you can avoid that case in most (but not all) circumstances.  What you are solving here is "time to return from pause," not "code that violates the GLES spec by being unable to handle a context loss."

Chris

Chris Pruett

unread,
Jul 30, 2012, 5:19:45 PM7/30/12
to replica-island-...@googlegroups.com
Yes, that's correct.

qee...@gmail.com

unread,
Aug 18, 2013, 9:53:03 AM8/18/13
to replica-island-...@googlegroups.com
same to issue。i did it like your code, but it still doesn't work!

在 2012年6月8日星期五UTC+8上午2时14分37秒,Chris Pruett写道:

Chris Pruett

unread,
Aug 18, 2013, 9:57:38 AM8/18/13
to replica-island-...@googlegroups.com
I no longer use this technique on devices that are Android 3 or higher.  On 2.2 - 2.3 devices, I still do this, and it works fine. In Android 3 GLSurfaceView has a setting to preserve the context across pauses, which is much safer.  I'd recommend trying that unless you need fast resume on old devices.  

Chris

--
You received this message because you are subscribed to the Google Groups "ReplicaIsland Coding Community" group.
To unsubscribe from this group and stop receiving emails from it, send an email to replica-island-coding...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

qee...@gmail.com

unread,
Aug 19, 2013, 1:38:27 PM8/19/13
to replica-island-...@googlegroups.com
thanks a lot. my app have to fit for lots of devices even on android 2.0, for renderering 3d's buildings in my navigation's app. normally, need switch ui between multiple activities, and had to persist the correct state, so i want to reuse one GLSurfaceView, however it's too difficult to implement it, it have been troubled me for a long time. i saw your answer and very excited, but i don't know what's wrong with my code, it doesn't work.My code is shown below. first run into the one activity, it's fine.when switch to the second activity, and return to the one activity from the second activity again, the GLSurfaceView doesn't work.


======MyApplication===========================================================================================


thanks a lot. my app have to fit for lots of devices even on android 2.0, for renderering 3d's buildings in my navigation's app. normally, need switch ui between multiple activities, and had to persist the correct state, so i want to reuse one GLSurfaceView, however it's too difficult to implement it, it have been troubled me for a long time. i saw your answer and very excited, but i don't know what's wrong with my code, it doesn't work.My code is shown below. the first run into the one activity, it's fine.when switch to the second activity, and return to the one activity from the second activity again, the GLSurfaceView doesn't work.

======MyApplication===========================================================================================

//in myApplication, i create a GLSurfaceView and  a Renderer.

     private ClearGLSurfaceView mGLView;
     private ClearRenderer mRenderer;
     private LinearLayout mUnityView;

     @Override
     public void onCreate() {
        mGLView = new ClearGLSurfaceView(this.getApplicationContext());
        mRenderer = new ClearRenderer();
        mGLView.setRenderer(mRenderer);
        mGLView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
      
        mUnityView = new LinearLayout(this.getApplicationContext());
        mUnityView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
        mUnityView.setOrientation(LinearLayout.VERTICAL);
        mUnityView.addView(mGLView);
     }

======OneActivity===========================================================================================

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mGLViewParent = new LinearLayout(this);
        mGLViewParent.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        MyApplication myApp = (MyApplication) getApplication();
        mGLViewParent.setOrientation(LinearLayout.VERTICAL);
      
        mGLView = myApp.getGLView();
        mUnityView = myApp.getUnityView();
        mGLViewParent.addView(mUnityView);
        setContentView(mGLViewParent);
    }
    @Override
    protected void onPause() {

        super.onPause();
        mGLView.setVisibility(View.INVISIBLE);
        mGLViewParent.removeView(mUnityView);   
    }

    @Override
     public void onWindowFocusChanged(boolean hasFocus) {
      
    if (hasFocus && mGLView != null && mGLView.getVisibility() == View.GONE)){
        mGLViewParent.addView(mUnityView);
        mGLView.setVisibility(View.VISIBLE);
    }  
    super.onWindowFocusChanged(hasFocus);
     }
    //here jump to the second activity, however return to this activity from the second activity again, the GLSurfaceView doesn't work.
    public boolean onTouchEvent(final MotionEvent event) {
      
    Intent it = new Intent();
    it.setClass(MainActivity.this, SecondActivity.class);
    MainActivity.this.startActivity(it);
        return false;
    }
=========================================================================================================================


Any help will be really apreciated.
Qeeshan.


在 2013年8月18日星期日UTC+8下午9时57分38秒,Chris Pruett写道:
To unsubscribe from this group and stop receiving emails from it, send an email to replica-island-coding-community+unsubscribe@googlegroups.com.

Chris Pruett

unread,
Aug 20, 2013, 1:06:29 AM8/20/13
to replica-island-...@googlegroups.com
Hi there,

Just off the top of my head, there are a number of problems with this code.

You want to disconnect the view before you call super.onPause().  Also, you are setting the view visibility to INVISIBLE but then later checking to see if it is GONE.  You probably want GONE in both cases.

Good luck, this looks like it's pretty close.  Like I said before, this code will cause problems on Android 4 devices, so you'll want to add some checks for that system version and disable it in that case.

Chris

To unsubscribe from this group and stop receiving emails from it, send an email to replica-island-coding...@googlegroups.com.

qee...@gmail.com

unread,
Aug 20, 2013, 11:25:46 AM8/20/13
to replica-island-...@googlegroups.com
I modified the problem that you had mentioned, it's shown below. unfortunately, my app still doesn't work and i had to temporarily give up this technique, tried to put a GLSurfaceView each activity  and it's no problem to renderer the 3D's building. but i will be have a hard work to adjust my app's framework。

======OneActivity===========================================================================================

       @Override
       protected void onPause() {
           mGLView.setVisibility(View.GONE);
           mGLViewParent.removeView(mUnityView);   
           super.onPause();
      }
      @Override
      public void onWindowFocusChanged(boolean hasFocus) {
      
         if (hasFocus && mGLView != null && mGLView.getVisibility() == View.GONE){
              mGLViewParent.addView(mUnityView);
              mGLView.setVisibility(View.VISIBLE);
         }  
         super.onWindowFocusChanged(hasFocus);
      }
=========================================================================================================================
    best regards.
    qeeshan.

在 2013年8月20日星期二UTC+8下午1时06分29秒,Chris Pruett写道:
Reply all
Reply to author
Forward
0 new messages