Merge "GLConsumer: Fix eglTerminate/eglInit edge case." into lmp-dev
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 37530db..f91fe46 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -287,7 +287,9 @@
 
         // createIfNeeded creates an EGLImage if required (we haven't created
         // one yet, or the EGLDisplay or crop-rect has changed).
-        status_t createIfNeeded(EGLDisplay display, const Rect& cropRect);
+        status_t createIfNeeded(EGLDisplay display,
+                                const Rect& cropRect,
+                                bool forceCreate = false);
 
         // This calls glEGLImageTargetTexture2DOES to bind the image to the
         // texture in the specified texture target.
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 939c4a9..ccafe81 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -462,24 +462,37 @@
     }
 
     status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay,
-                                                      mCurrentCrop);
-
+                                                        mCurrentCrop);
     if (err != NO_ERROR) {
         ST_LOGW("bindTextureImage: can't create image on display=%p slot=%d",
                 mEglDisplay, mCurrentTexture);
         return UNKNOWN_ERROR;
     }
-
     mCurrentTextureImage->bindToTextureTarget(mTexTarget);
 
-    while ((error = glGetError()) != GL_NO_ERROR) {
-        ST_LOGE("bindTextureImage: error binding external image: %#04x", error);
-        return UNKNOWN_ERROR;
+    // In the rare case that the display is terminated and then initialized
+    // again, we can't detect that the display changed (it didn't), but the
+    // image is invalid. In this case, repeat the exact same steps while
+    // forcing the creation of a new image.
+    if ((error = glGetError()) != GL_NO_ERROR) {
+        glBindTexture(mTexTarget, mTexName);
+        status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay,
+                                                            mCurrentCrop,
+                                                            true);
+        if (err != NO_ERROR) {
+            ST_LOGW("bindTextureImage: can't create image on display=%p slot=%d",
+                    mEglDisplay, mCurrentTexture);
+            return UNKNOWN_ERROR;
+        }
+        mCurrentTextureImage->bindToTextureTarget(mTexTarget);
+        if ((error = glGetError()) != GL_NO_ERROR) {
+            ST_LOGE("bindTextureImage: error binding external image: %#04x", error);
+            return UNKNOWN_ERROR;
+        }
     }
 
     // Wait for the new buffer to be ready.
     return doGLFenceWaitLocked();
-
 }
 
 status_t GLConsumer::checkAndUpdateEglStateLocked(bool contextCheck) {
@@ -1056,12 +1069,13 @@
 }
 
 status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay,
-                                              const Rect& cropRect) {
+                                              const Rect& cropRect,
+                                              bool forceCreation) {
     // If there's an image and it's no longer valid, destroy it.
     bool haveImage = mEglImage != EGL_NO_IMAGE_KHR;
     bool displayInvalid = mEglDisplay != eglDisplay;
     bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect;
-    if (haveImage && (displayInvalid || cropInvalid)) {
+    if (haveImage && (displayInvalid || cropInvalid || forceCreation)) {
         if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
            ALOGE("createIfNeeded: eglDestroyImageKHR failed");
         }
diff --git a/libs/gui/tests/SurfaceTextureMultiContextGL_test.cpp b/libs/gui/tests/SurfaceTextureMultiContextGL_test.cpp
index 115a47d..1cd101e 100644
--- a/libs/gui/tests/SurfaceTextureMultiContextGL_test.cpp
+++ b/libs/gui/tests/SurfaceTextureMultiContextGL_test.cpp
@@ -386,4 +386,59 @@
     ASSERT_EQ(OK, mST->updateTexImage());
 }
 
+TEST_F(SurfaceTextureMultiContextGLTest,
+       AttachAfterDisplayTerminatedSucceeds) {
+    ASSERT_EQ(NO_ERROR, mST->setDefaultMaxBufferCount(2));
+
+    // produce two frames and consume them both on the primary context
+    ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
+    mFW->waitForFrame();
+    ASSERT_EQ(OK, mST->updateTexImage());
+
+    ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
+    mFW->waitForFrame();
+    ASSERT_EQ(OK, mST->updateTexImage());
+
+    // produce one more frame
+    ASSERT_NO_FATAL_FAILURE(produceOneRGBA8Frame(mANW));
+
+    // Detach from the primary context.
+    ASSERT_EQ(OK, mST->releaseTexImage());
+    ASSERT_EQ(OK, mST->detachFromContext());
+
+    // Terminate and then initialize the display. All contexts, surfaces
+    // and images are invalid at this point.
+    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay);
+    EGLint majorVersion = 0;
+    EGLint minorVersion = 0;
+    EXPECT_TRUE(eglTerminate(display));
+    EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    // The surface is invalid so create it again.
+    EGLint pbufferAttribs[] = {
+        EGL_WIDTH, 64,
+        EGL_HEIGHT, 64,
+        EGL_NONE };
+    mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig,
+            pbufferAttribs);
+
+    // The second context is invalid so create it again.
+    mSecondEglContext = eglCreateContext(mEglDisplay, mGlConfig,
+            EGL_NO_CONTEXT, getContextAttribs());
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    ASSERT_NE(EGL_NO_CONTEXT, mSecondEglContext);
+
+    ASSERT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+            mSecondEglContext));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    // Now attach to and consume final frame on secondary context.
+    ASSERT_EQ(OK, mST->attachToContext(SECOND_TEX_ID));
+    mFW->waitForFrame();
+    ASSERT_EQ(OK, mST->updateTexImage());
+}
+
+
 } // namespace android