Problem when resizing and resuming/pausing GLSurfaceView
Asked Answered
O

0

9

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?

Overlap answered 9/9, 2020 at 12:57 Comment(2)
May try invoke glView.onPause() in the onPause() method and invoke glView.onResume() in the onResume() method.Ease
@Ease Thank you for your assumption. Unfortunately, I had already tried that and got the same result. I have also tried to resume(pause) glView both before and after the potentially time-consuming calls, which has not effect.Overlap

© 2022 - 2024 — McMap. All rights reserved.