Skip to content

Commit db6c78b

Browse files
committed
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 <jackpal@google.com>
1 parent db20260 commit db6c78b

File tree

1 file changed

+31
-34
lines changed

1 file changed

+31
-34
lines changed

opengl/java/android/opengl/GLSurfaceView.java

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1130,36 +1130,13 @@ GL createGL() {
11301130

11311131
/**
11321132
* Display the current render surface.
1133-
* @return false if the context has been lost.
1133+
* @return the EGL error code from eglSwapBuffers.
11341134
*/
1135-
public boolean swap() {
1135+
public int swap() {
11361136
if (! mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
1137-
1138-
/*
1139-
* Check for EGL_CONTEXT_LOST, which means the context
1140-
* and all associated data were lost (For instance because
1141-
* the device went to sleep). We need to sleep until we
1142-
* get a new surface.
1143-
*/
1144-
int error = mEgl.eglGetError();
1145-
switch(error) {
1146-
case EGL11.EGL_CONTEXT_LOST:
1147-
return false;
1148-
case EGL10.EGL_BAD_CURRENT_SURFACE:
1149-
// The current surface is bad, probably because the window manager has closed
1150-
// the associated window. Ignore this error, on the assumption that the
1151-
// application will be closed soon.
1152-
break;
1153-
case EGL10.EGL_BAD_NATIVE_WINDOW:
1154-
// The native window is bad, probably because the window manager has closed it.
1155-
// Ignore this error, on the assumption that the application will be closed
1156-
// soon.
1157-
break;
1158-
default:
1159-
throwEglException("eglSwapBuffers", error);
1160-
}
1137+
return mEgl.eglGetError();
11611138
}
1162-
return true;
1139+
return EGL10.EGL_SUCCESS;
11631140
}
11641141

11651142
public void destroySurface() {
@@ -1366,6 +1343,7 @@ private void guardedRun() throws InterruptedException {
13661343
stopEglSurfaceLocked();
13671344
}
13681345
mWaitingForSurface = true;
1346+
mSurfaceIsBad = false;
13691347
sGLThreadManager.notifyAll();
13701348
}
13711349

@@ -1423,7 +1401,9 @@ private void guardedRun() throws InterruptedException {
14231401
h = mHeight;
14241402
wantRenderNotification = true;
14251403
if (LOG_SURFACE) {
1426-
Log.i("GLThread", "noticing that we want render notification tid=" + getId());
1404+
Log.i("GLThread",
1405+
"noticing that we want render notification tid="
1406+
+ getId());
14271407
}
14281408

14291409
// Destroy and recreate the EGL surface.
@@ -1444,6 +1424,7 @@ private void guardedRun() throws InterruptedException {
14441424
+ " mHaveEglSurface: " + mHaveEglSurface
14451425
+ " mPaused: " + mPaused
14461426
+ " mHasSurface: " + mHasSurface
1427+
+ " mSurfaceIsBad: " + mSurfaceIsBad
14471428
+ " mWaitingForSurface: " + mWaitingForSurface
14481429
+ " mWidth: " + mWidth
14491430
+ " mHeight: " + mHeight
@@ -1509,11 +1490,26 @@ private void guardedRun() throws InterruptedException {
15091490
view.mRenderer.onDrawFrame(gl);
15101491
}
15111492
}
1512-
if (!mEglHelper.swap()) {
1513-
if (LOG_SURFACE) {
1514-
Log.i("GLThread", "egl context lost tid=" + getId());
1515-
}
1516-
lostEglContext = true;
1493+
int swapError = mEglHelper.swap();
1494+
switch (swapError) {
1495+
case EGL10.EGL_SUCCESS:
1496+
break;
1497+
case EGL11.EGL_CONTEXT_LOST:
1498+
if (LOG_SURFACE) {
1499+
Log.i("GLThread", "egl context lost tid=" + getId());
1500+
}
1501+
lostEglContext = true;
1502+
break;
1503+
default:
1504+
// Other errors typically mean that the current surface is bad,
1505+
// probably because the surfaceview surface has been destroyed,
1506+
// but we haven't been notified yet.
1507+
// Log the error to help developers understand why rendering stopped.
1508+
Log.w("GLThread", "eglSwapBuffers error: " + swapError +
1509+
". Assume surfaceview surface is being destroyed. tid="
1510+
+ getId());
1511+
mSurfaceIsBad = true;
1512+
break;
15171513
}
15181514

15191515
if (wantRenderNotification) {
@@ -1537,7 +1533,7 @@ public boolean ableToDraw() {
15371533
}
15381534

15391535
private boolean readyToDraw() {
1540-
return (!mPaused) && mHasSurface
1536+
return (!mPaused) && mHasSurface && (!mSurfaceIsBad)
15411537
&& (mWidth > 0) && (mHeight > 0)
15421538
&& (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY));
15431539
}
@@ -1707,6 +1703,7 @@ public void queueEvent(Runnable r) {
17071703
private boolean mRequestPaused;
17081704
private boolean mPaused;
17091705
private boolean mHasSurface;
1706+
private boolean mSurfaceIsBad;
17101707
private boolean mWaitingForSurface;
17111708
private boolean mHaveEglContext;
17121709
private boolean mHaveEglSurface;

0 commit comments

Comments
 (0)