I am trying to improve openframeworks so that the GL context is preserved when pausing and resuming my game. So that I don't have to reload all textures after each pause. I don't expect you to know openframeworks code, I'll explain my problem independent of it.
I have java activity which loads a main_layout.xml file with a RelativeLayout inside another RelativeLayout. Then I instantiate a GLSurfaceView and add it to the inner RelativeLayout using glContainer.addView(glView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
. In onPause function, I call glView.onPause();
on GLSurfaceView and in onResume, I call glView.onResume();
on GLSurfaceView.
This code works but it destroys the gl surface in onPause
(GLSurfaceView.surfaceDestroyed
gets called on my GLSurfaceView
, which is extended to OFGLSurfaceView
, so that I can intercept this call).
To fix this, I added call to GLSurfaceView.setPreserveEGLContextOnPause
with param 'true' to initialization of my GLSurfaceView
. Now I see only black screen after pausing and resuming the game. I found out, that I can fix this by removing the GLSurfaceView
from parent RelativeLayout
and adding it back right away in onResume
function: glContainer.removeView( glView ); glContainer.addView( glView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) );
. This however destroys the gl surface again and forces me to reload textures.
There are no reinstantiations of either one of the RelativeLayout
s or the GLSurfaceView
(I checked it by looking at the code and then checking identities of those instances with System.identityHashCode
).
main_layout.xml:
xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/relativeLayout1" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout android:id="@+id/of_gl_surface_container" android:layout_width="fill_parent" android:layout_height="fill_parent"/>
<!-- add here other views' layouts -->
</RelativeLayout>
Code in OFAndroidLifeCycle.java which handles events received from Activity:
java
public static void glCreateSurface()
{
if(mGLView == null)
{
mGLView = new OFGLSurfaceView(m_activity);
OFGLSurfaceView glView = getGLView();
glView.setPreserveEGLContextOnPause( true );
ViewGroup parent = (ViewGroup)glView.getParent();
if( parent == null )
{
ViewGroup glContainer = getActivity().getSurfaceContainer();
glContainer.addView(glView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
}
}
}
public static void glPause()
{
OFGLSurfaceView glView = getGLView();
if( glView != null )
glView.onPause();
}
public static void glResume(ViewGroup glContainer)
{
OFGLSurfaceView glView = getGLView();
if( glView != null )
{
glView.onResume();
glContainer.removeView( glView );
glContainer.addView( glView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT) );
}
}
After working on it a bit, I improved it so that the GLSurfaceView
is being paused and resumed from Activity.onStop
and Activity.onStart
respectively.
Problem remains and I was able to better locate the source of the problem. The surface is destroyed between onPause
and onStop
events, or better to say right before onStop
event. Here is the stack trace of the call that reports that the surface was destroyed.
java.lang.Exception
at cc.openframeworks.OFGLSurfaceView.surfaceDestroyed(OFAndroidWindow.java:251)
at android.view.SurfaceView.updateSurface(SurfaceView.java:639)
at android.view.SurfaceView.windowStopped(SurfaceView.java:222)
at android.view.ViewRootImpl.setWindowStopped(ViewRootImpl.java:1348)
at android.view.WindowManagerGlobal.setStoppedState(WindowManagerGlobal.java:623)
at android.app.Activity.performStop(Activity.java:7151)
at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:4158)
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:4210)
at android.app.ActivityThread.-wrap24(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1646)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6710)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:770)
This corresponds to android-26 sdk sources.
I am still not sure where the problem is. I consider three alternatives:
- problem with android platform version (I could use some advises here)
- the way
openframeworks
interfaces with theGLSurfaceView
might be somehow invalid (overriding some functionality) - other problem in propagation of that
preserveEGLContextOnPause
flag, I will need to read through the android code to better understand how that works
I found it! Summary:
- The point is that GL objects do not get destroyed when
GLSurfaceView.surfaceDestroyed
is called. I kept thesetPreserveEGLContextOnPause
on true and disconnected any signals to native code fromGLSurfaceView.surfaceDestroyed
and now the textures are good after minimizing and restoring the window.
So I guess that the surface and probably also EGL context can be destroyed and textures will still remain in GL context or something.
So now I just need to do some cleanup work and find the real indicator that GL objects were destroyed. And maybe try to verify that this approach really is the correct one, but I am confident I am on the right track. I might post more info once I do some more work on it.