From db6c78b5bdf264abe8f6de97f67ca5e90fb6a05a Mon Sep 17 00:00:00 2001 From: Jack Palevich Date: Thu, 29 Mar 2012 12:23:57 -0700 Subject: Make GLSurfaceView handle eglSwapBuffers errors more robustly. A careful reading of the EGL spec, as well as experience with many different EGL drivers, has shown that it is error prone to attempt to discriminate between different error conditions. We now treat any error besides EGL_CONTEXT_LOST as an indication that the EGL context is in a bad state, most likely due to the window manager having removed the underlying surface flinger surface. In addition, we changed the way we deal with this kind of error: Previously we would ignore the error and keep rendering. But if the EGL context and surface has become invalid, it would be better to stop drawing. We now stop drawing until the surface view surface is recreated. See b/6032663 for an example of this problem affecting the GMM app, but note that GMM is using their own version of GLSurfaceView, so this change won't help them directly. They'll have to make a similar change to their version of GLSurfaceView. Change-Id: Iffe3e1e3a3c7a91d03140fd34391eadeaecf777e Signed-off-by: Jack Palevich --- opengl/java/android/opengl/GLSurfaceView.java | 65 +++++++++++++-------------- 1 file changed, 31 insertions(+), 34 deletions(-) (limited to 'opengl/java') diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index f69fc53..b868049 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -1130,36 +1130,13 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback /** * Display the current render surface. - * @return false if the context has been lost. + * @return the EGL error code from eglSwapBuffers. */ - public boolean swap() { + public int swap() { if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) { - - /* - * Check for EGL_CONTEXT_LOST, which means the context - * and all associated data were lost (For instance because - * the device went to sleep). We need to sleep until we - * get a new surface. - */ - int error = mEgl.eglGetError(); - switch(error) { - case EGL11.EGL_CONTEXT_LOST: - return false; - case EGL10.EGL_BAD_CURRENT_SURFACE: - // The current surface is bad, probably because the window manager has closed - // the associated window. Ignore this error, on the assumption that the - // application will be closed soon. - break; - case EGL10.EGL_BAD_NATIVE_WINDOW: - // The native window is bad, probably because the window manager has closed it. - // Ignore this error, on the assumption that the application will be closed - // soon. - break; - default: - throwEglException("eglSwapBuffers", error); - } + return mEgl.eglGetError(); } - return true; + return EGL10.EGL_SUCCESS; } public void destroySurface() { @@ -1366,6 +1343,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback stopEglSurfaceLocked(); } mWaitingForSurface = true; + mSurfaceIsBad = false; sGLThreadManager.notifyAll(); } @@ -1423,7 +1401,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback h = mHeight; wantRenderNotification = true; if (LOG_SURFACE) { - Log.i("GLThread", "noticing that we want render notification tid=" + getId()); + Log.i("GLThread", + "noticing that we want render notification tid=" + + getId()); } // Destroy and recreate the EGL surface. @@ -1444,6 +1424,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback + " mHaveEglSurface: " + mHaveEglSurface + " mPaused: " + mPaused + " mHasSurface: " + mHasSurface + + " mSurfaceIsBad: " + mSurfaceIsBad + " mWaitingForSurface: " + mWaitingForSurface + " mWidth: " + mWidth + " mHeight: " + mHeight @@ -1509,11 +1490,26 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback view.mRenderer.onDrawFrame(gl); } } - if (!mEglHelper.swap()) { - if (LOG_SURFACE) { - Log.i("GLThread", "egl context lost tid=" + getId()); - } - lostEglContext = true; + int swapError = mEglHelper.swap(); + switch (swapError) { + case EGL10.EGL_SUCCESS: + break; + case EGL11.EGL_CONTEXT_LOST: + if (LOG_SURFACE) { + Log.i("GLThread", "egl context lost tid=" + getId()); + } + lostEglContext = true; + break; + default: + // Other errors typically mean that the current surface is bad, + // probably because the surfaceview surface has been destroyed, + // but we haven't been notified yet. + // Log the error to help developers understand why rendering stopped. + Log.w("GLThread", "eglSwapBuffers error: " + swapError + + ". Assume surfaceview surface is being destroyed. tid=" + + getId()); + mSurfaceIsBad = true; + break; } if (wantRenderNotification) { @@ -1537,7 +1533,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } private boolean readyToDraw() { - return (!mPaused) && mHasSurface + return (!mPaused) && mHasSurface && (!mSurfaceIsBad) && (mWidth > 0) && (mHeight > 0) && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY)); } @@ -1707,6 +1703,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private boolean mRequestPaused; private boolean mPaused; private boolean mHasSurface; + private boolean mSurfaceIsBad; private boolean mWaitingForSurface; private boolean mHaveEglContext; private boolean mHaveEglSurface; -- cgit v1.1