GLConsumer: Fix eglTerminate/eglInit edge case.

If a display is terminated and then initialized, we can't detect
this using the display itself (it has the same value), but all
EglImages still become invalid for the display. This patch detects
this during image binding and forces creation of a new EglImage.

Bug: 10430249
Change-Id: I75101c50962f21263dca3ec6e241a2e5a3c23dad
This commit is contained in:
Eric Penner 2014-08-25 20:16:37 -07:00
parent 7de2bde3c4
commit 2d14a0ed4f
3 changed files with 81 additions and 10 deletions

View File

@ -287,7 +287,9 @@ private:
// createIfNeeded creates an EGLImage if required (we haven't created // createIfNeeded creates an EGLImage if required (we haven't created
// one yet, or the EGLDisplay or crop-rect has changed). // 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 // This calls glEGLImageTargetTexture2DOES to bind the image to the
// texture in the specified texture target. // texture in the specified texture target.

View File

@ -463,23 +463,36 @@ status_t GLConsumer::bindTextureImageLocked() {
status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay, status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay,
mCurrentCrop); mCurrentCrop);
if (err != NO_ERROR) { if (err != NO_ERROR) {
ST_LOGW("bindTextureImage: can't create image on display=%p slot=%d", ST_LOGW("bindTextureImage: can't create image on display=%p slot=%d",
mEglDisplay, mCurrentTexture); mEglDisplay, mCurrentTexture);
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
mCurrentTextureImage->bindToTextureTarget(mTexTarget); mCurrentTextureImage->bindToTextureTarget(mTexTarget);
while ((error = glGetError()) != GL_NO_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); ST_LOGE("bindTextureImage: error binding external image: %#04x", error);
return UNKNOWN_ERROR; return UNKNOWN_ERROR;
} }
}
// Wait for the new buffer to be ready. // Wait for the new buffer to be ready.
return doGLFenceWaitLocked(); return doGLFenceWaitLocked();
} }
status_t GLConsumer::checkAndUpdateEglStateLocked(bool contextCheck) { status_t GLConsumer::checkAndUpdateEglStateLocked(bool contextCheck) {
@ -1056,12 +1069,13 @@ GLConsumer::EglImage::~EglImage() {
} }
status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay, 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. // If there's an image and it's no longer valid, destroy it.
bool haveImage = mEglImage != EGL_NO_IMAGE_KHR; bool haveImage = mEglImage != EGL_NO_IMAGE_KHR;
bool displayInvalid = mEglDisplay != eglDisplay; bool displayInvalid = mEglDisplay != eglDisplay;
bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect; bool cropInvalid = hasEglAndroidImageCrop() && mCropRect != cropRect;
if (haveImage && (displayInvalid || cropInvalid)) { if (haveImage && (displayInvalid || cropInvalid || forceCreation)) {
if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) { if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
ALOGE("createIfNeeded: eglDestroyImageKHR failed"); ALOGE("createIfNeeded: eglDestroyImageKHR failed");
} }

View File

@ -386,4 +386,59 @@ TEST_F(SurfaceTextureMultiContextGLTest,
ASSERT_EQ(OK, mST->updateTexImage()); 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 } // namespace android