I use GLSurfaceView
in my app and recently discovered a strange behavior when resizing it. Finally, I figured out what causes the problem in my app and created a simple example to reproduce it.
Let say we have a FrameLayout
containing simple GLSurfaceView
. On each click on GLSurfaceView
we change its height using the following code
CyclicIterator<Integer> iterator = CyclicIterator.from(200, 300, 500);
glView.setOnClickListener(v -> {
glView.getLayoutParams().height = sizeIterator.next();
glView.requestLayout();
});
All goes well until we quickly move the app in background and then back to the foreground using "Overview" button (something like "monkey test"). If we switch between the background/foreground fast enough and resize the view again, we could end up with the following situation: there appears a 'dead' region within the surface, which it is not updating until we stop/start the activity again.
I had checked glGetError
after every gl call and did not receive any errors. At the same time I received the correct width
and height
in onSurfaceChanged
callback.
I tried to reproduce this behavior and obtained the same result on both Nexus 5X and Xiaomi Redmi 6A.
More details
GLSurfaceView
is configured as:
glView.setEGLContextClientVersion(2);
glView.setRenderer(new Renderer() {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GLES20.GL_COLOR_BUFFER_BIT);
}
});
glView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
As the official documentation says we should notify GLSurfaceView
about lifecycle events:
@Override
protected void onStart() {
super.onStart();
glView.onResume();
}
@Override
protected void onStop() {
glView.onPause();
super.onStop();
}
Hovewer, in real-world application we also need to save the preferences and release some resources and services (see e.g. camera example from Google) when our app goes to background. As I figured out, it is these calls who cause the glitch of GLSurfaceView
in my app, so I simulated them through:
@Override
protected void onResume() {
super.onResume();
// Simulate such actions as e.g.:
// restorePreferences();
// startCameraThread();
// openCamera();
synchronized (this){
try{
this.wait(400);
}catch (InterruptedException ie){
ie.printStackTrace();
}
}
}
@Override
protected void onPause() {
// Simulate such actions as e.g.:
// releaseCamera();
// stopCameraThread();
// savePreferences();
synchronized (this){
try{
this.wait(400);
}catch (InterruptedException ie){
ie.printStackTrace();
}
}
super.onPause();
}
Dirty solution
I found that this glitch can be fixed by switching GLSurfaceView
's visibility to GONE
and back to VISIBLE
in onResume
.
@Override
protected void onResume() {
super.onResume();
//...
glView.setVisibility(View.GONE);
glView.setVisibility(View.VISIBLE);
//...
}
However, it is clearly not the solution I am looking for, so
Summary
Did I miss something important working with GLSurfaceView
, or it is simply a bug inside its implementation?
glView
both before and after the potentially time-consuming calls, which has not effect. – Overlap