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:
parent
7de2bde3c4
commit
2d14a0ed4f
@ -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.
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user