Merge "GLProducer: Reference count images rather than buffers." into lmp-dev
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 1aacee9..37530db 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -231,7 +231,7 @@
 protected:
 
     // abandonLocked overrides the ConsumerBase method to clear
-    // mCurrentTextureBuf in addition to the ConsumerBase behavior.
+    // mCurrentTextureImage in addition to the ConsumerBase behavior.
     virtual void abandonLocked();
 
     // dumpLocked overrides the ConsumerBase method to dump GLConsumer-
@@ -262,7 +262,7 @@
     status_t updateAndReleaseLocked(const BufferQueue::BufferItem& item);
 
     // Binds mTexName and the current buffer to mTexTarget.  Uses
-    // mCurrentTexture if it's set, mCurrentTextureBuf if not.  If the
+    // mCurrentTexture if it's set, mCurrentTextureImage if not.  If the
     // bind succeeds, this calls doGLFenceWait.
     status_t bindTextureImageLocked();
 
@@ -275,11 +275,57 @@
     status_t checkAndUpdateEglStateLocked(bool contextCheck = false);
 
 private:
-    // createImage creates a new EGLImage from a GraphicBuffer.
-    EGLImageKHR createImage(EGLDisplay dpy,
-            const sp<GraphicBuffer>& graphicBuffer, const Rect& crop);
+    // EglImage is a utility class for tracking and creating EGLImageKHRs. There
+    // is primarily just one image per slot, but there is also special cases:
+    //  - For releaseTexImage, we use a debug image (mReleasedTexImage)
+    //  - After freeBuffer, we must still keep the current image/buffer
+    // Reference counting EGLImages lets us handle all these cases easily while
+    // also only creating new EGLImages from buffers when required.
+    class EglImage : public LightRefBase<EglImage>  {
+    public:
+        EglImage(sp<GraphicBuffer> graphicBuffer);
 
-    // freeBufferLocked frees up the given buffer slot.  If the slot has been
+        // 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);
+
+        // This calls glEGLImageTargetTexture2DOES to bind the image to the
+        // texture in the specified texture target.
+        void bindToTextureTarget(uint32_t texTarget);
+
+        const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; }
+        const native_handle* graphicBufferHandle() {
+            return mGraphicBuffer == NULL ? NULL : mGraphicBuffer->handle;
+        }
+
+    private:
+        // Only allow instantiation using ref counting.
+        friend class LightRefBase<EglImage>;
+        virtual ~EglImage();
+
+        // createImage creates a new EGLImage from a GraphicBuffer.
+        EGLImageKHR createImage(EGLDisplay dpy,
+                const sp<GraphicBuffer>& graphicBuffer, const Rect& crop);
+
+        // Disallow copying
+        EglImage(const EglImage& rhs);
+        void operator = (const EglImage& rhs);
+
+        // mGraphicBuffer is the buffer that was used to create this image.
+        sp<GraphicBuffer> mGraphicBuffer;
+
+        // mEglImage is the EGLImage created from mGraphicBuffer.
+        EGLImageKHR mEglImage;
+
+        // mEGLDisplay is the EGLDisplay that was used to create mEglImage.
+        EGLDisplay mEglDisplay;
+
+        // mCropRect is the crop rectangle passed to EGL when mEglImage
+        // was created.
+        Rect mCropRect;
+    };
+
+    // freeBufferLocked frees up the given buffer slot. If the slot has been
     // initialized this will release the reference to the GraphicBuffer in that
     // slot and destroy the EGLImage in that slot.  Otherwise it has no effect.
     //
@@ -289,7 +335,7 @@
     // computeCurrentTransformMatrixLocked computes the transform matrix for the
     // current texture.  It uses mCurrentTransform and the current GraphicBuffer
     // to compute this matrix and stores it in mCurrentTransformMatrix.
-    // mCurrentTextureBuf must not be NULL.
+    // mCurrentTextureImage must not be NULL.
     void computeCurrentTransformMatrixLocked();
 
     // doGLFenceWaitLocked inserts a wait command into the OpenGL ES command
@@ -303,13 +349,6 @@
     // before the outstanding accesses have completed.
     status_t syncForReleaseLocked(EGLDisplay dpy);
 
-    // Normally, when we bind a buffer to a texture target, we bind a buffer
-    // that is referenced by an entry in mEglSlots.  In some situations we
-    // have a buffer in mCurrentTextureBuf, but no corresponding entry for
-    // it in our slot array.  bindUnslottedBuffer handles that situation by
-    // binding the buffer without touching the EglSlots.
-    status_t bindUnslottedBufferLocked(EGLDisplay dpy);
-
     // returns a graphic buffer used when the texture image has been released
     static sp<GraphicBuffer> getDebugTexImageBuffer();
 
@@ -319,10 +358,10 @@
     // consume buffers as hardware textures.
     static const uint32_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE;
 
-    // mCurrentTextureBuf is the graphic buffer of the current texture. It's
+    // mCurrentTextureImage is the EglImage/buffer of the current texture. It's
     // possible that this buffer is not associated with any buffer slot, so we
     // must track it separately in order to support the getCurrentBuffer method.
-    sp<GraphicBuffer> mCurrentTextureBuf;
+    sp<EglImage> mCurrentTextureImage;
 
     // mCurrentCrop is the crop rectangle that applies to the current texture.
     // It gets set each time updateTexImage is called.
@@ -382,17 +421,10 @@
     // EGLSlot contains the information and object references that
     // GLConsumer maintains about a BufferQueue buffer slot.
     struct EglSlot {
-        EglSlot()
-        : mEglImage(EGL_NO_IMAGE_KHR),
-          mEglFence(EGL_NO_SYNC_KHR) {
-        }
+        EglSlot() : mEglFence(EGL_NO_SYNC_KHR) {}
 
         // mEglImage is the EGLImage created from mGraphicBuffer.
-        EGLImageKHR mEglImage;
-
-        // mCropRect is the crop rectangle passed to EGL when mEglImage was
-        // created.
-        Rect mCropRect;
+        sp<EglImage> mEglImage;
 
         // mFence is the EGL sync object that must signal before the buffer
         // associated with this buffer slot may be dequeued. It is initialized
@@ -444,6 +476,7 @@
     // mReleasedTexImageBuffer is a dummy buffer used when in single buffer
     // mode and releaseTexImage() has been called
     static sp<GraphicBuffer> sReleasedTexImageBuffer;
+    sp<EglImage> mReleasedTexImage;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index 924fc0d..939c4a9 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -279,8 +279,12 @@
             return err;
         }
 
+        if (mReleasedTexImage == NULL) {
+            mReleasedTexImage = new EglImage(getDebugTexImageBuffer());
+        }
+
         mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
-        mCurrentTextureBuf = getDebugTexImageBuffer();
+        mCurrentTextureImage = mReleasedTexImage;
         mCurrentCrop.makeInvalid();
         mCurrentTransform = 0;
         mCurrentScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
@@ -288,9 +292,11 @@
         mCurrentFence = Fence::NO_FENCE;
 
         if (mAttached) {
-            // bind a dummy texture
-            glBindTexture(mTexTarget, mTexName);
-            bindUnslottedBufferLocked(mEglDisplay);
+            // This binds a dummy buffer (mReleasedTexImage).
+            status_t err =  bindTextureImageLocked();
+            if (err != NO_ERROR) {
+                return err;
+            }
         } else {
             // detached, don't touch the texture (and we may not even have an
             // EGLDisplay here.
@@ -332,29 +338,12 @@
         return err;
     }
 
-    int slot = item->mBuf;
-    bool destroyEglImage = false;
-
-    if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) {
-        if (item->mGraphicBuffer != NULL) {
-            // This buffer has not been acquired before, so we must assume
-            // that any EGLImage in mEglSlots is stale.
-            destroyEglImage = true;
-        } else if (mEglSlots[slot].mCropRect != item->mCrop) {
-            // We've already seen this buffer before, but it now has a
-            // different crop rect, so we'll need to recreate the EGLImage if
-            // we're using the EGL_ANDROID_image_crop extension.
-            destroyEglImage = hasEglAndroidImageCrop();
-        }
-    }
-
-    if (destroyEglImage) {
-        if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) {
-            ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d",
-                  slot);
-            // keep going
-        }
-        mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR;
+    // If item->mGraphicBuffer is not null, this buffer has not been acquired
+    // before, so any prior EglImage created is using a stale buffer. This
+    // replaces any old EglImage with a new one (using the new buffer).
+    if (item->mGraphicBuffer != NULL) {
+        int slot = item->mBuf;
+        mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer);
     }
 
     return NO_ERROR;
@@ -395,29 +384,18 @@
         return err;
     }
 
-    // If the mEglSlot entry is empty, create an EGLImage for the gralloc
-    // buffer currently in the slot in ConsumerBase.
-    //
+    // Ensure we have a valid EglImageKHR for the slot, creating an EglImage
+    // if nessessary, for the gralloc buffer currently in the slot in
+    // ConsumerBase.
     // We may have to do this even when item.mGraphicBuffer == NULL (which
-    // means the buffer was previously acquired), if we destroyed the
-    // EGLImage when detaching from a context but the buffer has not been
-    // re-allocated.
-    if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) {
-        EGLImageKHR image = createImage(mEglDisplay,
-                mSlots[buf].mGraphicBuffer, item.mCrop);
-        if (image == EGL_NO_IMAGE_KHR) {
-            ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
-                  mEglDisplay, buf);
-            const sp<GraphicBuffer>& gb = mSlots[buf].mGraphicBuffer;
-            ST_LOGW("buffer size=%ux%u st=%u usage=0x%x fmt=%d",
-                gb->getWidth(), gb->getHeight(), gb->getStride(),
-                gb->getUsage(), gb->getPixelFormat());
-            releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
-                    mEglDisplay, EGL_NO_SYNC_KHR);
-            return UNKNOWN_ERROR;
-        }
-        mEglSlots[buf].mEglImage = image;
-        mEglSlots[buf].mCropRect = item.mCrop;
+    // means the buffer was previously acquired).
+    err = mEglSlots[buf].mEglImage->createIfNeeded(mEglDisplay, item.mCrop);
+    if (err != NO_ERROR) {
+        ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
+                mEglDisplay, buf);
+        releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
+                mEglDisplay, EGL_NO_SYNC_KHR);
+        return UNKNOWN_ERROR;
     }
 
     // Do whatever sync ops we need to do before releasing the old slot.
@@ -433,15 +411,15 @@
     }
 
     ST_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)",
-            mCurrentTexture,
-            mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
+            mCurrentTexture, mCurrentTextureImage != NULL ?
+                    mCurrentTextureImage->graphicBufferHandle() : 0,
             buf, mSlots[buf].mGraphicBuffer->handle);
 
     // release old buffer
     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
         status_t status = releaseBufferLocked(
-                mCurrentTexture, mCurrentTextureBuf, mEglDisplay,
-                mEglSlots[mCurrentTexture].mEglFence);
+                mCurrentTexture, mCurrentTextureImage->graphicBuffer(),
+                mEglDisplay, mEglSlots[mCurrentTexture].mEglFence);
         if (status < NO_ERROR) {
             ST_LOGE("updateAndRelease: failed to release buffer: %s (%d)",
                    strerror(-status), status);
@@ -452,7 +430,7 @@
 
     // Update the GLConsumer state.
     mCurrentTexture = buf;
-    mCurrentTextureBuf = mSlots[buf].mGraphicBuffer;
+    mCurrentTextureImage = mEglSlots[buf].mEglImage;
     mCurrentCrop = item.mCrop;
     mCurrentTransform = item.mTransform;
     mCurrentScalingMode = item.mScalingMode;
@@ -477,25 +455,26 @@
     }
 
     glBindTexture(mTexTarget, mTexName);
-    if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
-        if (mCurrentTextureBuf == NULL) {
-            ST_LOGE("bindTextureImage: no currently-bound texture");
-            return NO_INIT;
-        }
-        status_t err = bindUnslottedBufferLocked(mEglDisplay);
-        if (err != NO_ERROR) {
-            return err;
-        }
-    } else {
-        EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage;
+    if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT &&
+            mCurrentTextureImage == NULL) {
+        ST_LOGE("bindTextureImage: no currently-bound texture");
+        return NO_INIT;
+    }
 
-        glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
+    status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay,
+                                                      mCurrentCrop);
 
-        while ((error = glGetError()) != GL_NO_ERROR) {
-            ST_LOGE("bindTextureImage: error binding external texture image %p"
-                    ": %#04x", image, error);
-            return UNKNOWN_ERROR;
-        }
+    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;
     }
 
     // Wait for the new buffer to be ready.
@@ -537,7 +516,7 @@
     if (fence->isValid() &&
             mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
         status_t err = addReleaseFence(mCurrentTexture,
-                mCurrentTextureBuf, fence);
+                mCurrentTextureImage->graphicBuffer(), fence);
         if (err != OK) {
             ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)",
                     strerror(-err), err);
@@ -583,18 +562,6 @@
         glDeleteTextures(1, &mTexName);
     }
 
-    // Because we're giving up the EGLDisplay we need to free all the EGLImages
-    // that are associated with it.  They'll be recreated when the
-    // GLConsumer gets attached to a new OpenGL ES context (and thus gets a
-    // new EGLDisplay).
-    for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
-        EGLImageKHR img = mEglSlots[i].mEglImage;
-        if (img != EGL_NO_IMAGE_KHR) {
-            eglDestroyImageKHR(mEglDisplay, img);
-            mEglSlots[i].mEglImage = EGL_NO_IMAGE_KHR;
-        }
-    }
-
     mEglDisplay = EGL_NO_DISPLAY;
     mEglContext = EGL_NO_CONTEXT;
     mAttached = false;
@@ -635,56 +602,25 @@
     // buffer.
     glBindTexture(mTexTarget, GLuint(tex));
 
-    if (mCurrentTextureBuf != NULL) {
-        // The EGLImageKHR that was associated with the slot was destroyed when
-        // the GLConsumer was detached from the old context, so we need to
-        // recreate it here.
-        status_t err = bindUnslottedBufferLocked(dpy);
-        if (err != NO_ERROR) {
-            return err;
-        }
-    }
-
     mEglDisplay = dpy;
     mEglContext = ctx;
     mTexName = tex;
     mAttached = true;
 
+    if (mCurrentTextureImage != NULL) {
+        // This may wait for a buffer a second time. This is likely required if
+        // this is a different context, since otherwise the wait could be skipped
+        // by bouncing through another context. For the same context the extra
+        // wait is redundant.
+        status_t err =  bindTextureImageLocked();
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
     return OK;
 }
 
-status_t GLConsumer::bindUnslottedBufferLocked(EGLDisplay dpy) {
-    ST_LOGV("bindUnslottedBuffer ct=%d ctb=%p",
-            mCurrentTexture, mCurrentTextureBuf.get());
-
-    // Create a temporary EGLImageKHR.
-    Rect crop;
-    EGLImageKHR image = createImage(dpy, mCurrentTextureBuf, mCurrentCrop);
-    if (image == EGL_NO_IMAGE_KHR) {
-        return UNKNOWN_ERROR;
-    }
-
-    // Attach the current buffer to the GL texture.
-    glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
-
-    GLint error;
-    status_t err = OK;
-    while ((error = glGetError()) != GL_NO_ERROR) {
-        ST_LOGE("bindUnslottedBuffer: error binding external texture image %p "
-                "(slot %d): %#04x", image, mCurrentTexture, error);
-        err = UNKNOWN_ERROR;
-    }
-
-    // We destroy the EGLImageKHR here because the current buffer may no
-    // longer be associated with one of the buffer slots, so we have
-    // nowhere to to store it.  If the buffer is still associated with a
-    // slot then another EGLImageKHR will be created next time that buffer
-    // gets acquired in updateTexImage.
-    eglDestroyImageKHR(dpy, image);
-
-    return err;
-}
-
 
 status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) {
     ST_LOGV("syncForReleaseLocked");
@@ -708,7 +644,7 @@
             }
             sp<Fence> fence(new Fence(fenceFd));
             status_t err = addReleaseFenceLocked(mCurrentTexture,
-                    mCurrentTextureBuf, fence);
+                    mCurrentTextureImage->graphicBuffer(), fence);
             if (err != OK) {
                 ST_LOGE("syncForReleaseLocked: error adding release fence: "
                         "%s (%d)", strerror(-err), err);
@@ -787,11 +723,11 @@
     bool needsRecompute = mFilteringEnabled != enabled;
     mFilteringEnabled = enabled;
 
-    if (needsRecompute && mCurrentTextureBuf==NULL) {
-        ST_LOGD("setFilteringEnabled called with mCurrentTextureBuf == NULL");
+    if (needsRecompute && mCurrentTextureImage==NULL) {
+        ST_LOGD("setFilteringEnabled called with mCurrentTextureImage == NULL");
     }
 
-    if (needsRecompute && mCurrentTextureBuf != NULL) {
+    if (needsRecompute && mCurrentTextureImage != NULL) {
         computeCurrentTransformMatrixLocked();
     }
 }
@@ -825,10 +761,11 @@
         }
     }
 
-    sp<GraphicBuffer>& buf(mCurrentTextureBuf);
+    sp<GraphicBuffer> buf = (mCurrentTextureImage == NULL) ?
+            NULL : mCurrentTextureImage->graphicBuffer();
 
     if (buf == NULL) {
-        ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureBuf is NULL");
+        ST_LOGD("computeCurrentTransformMatrixLocked: mCurrentTextureImage is NULL");
     }
 
     float mtxBeforeFlipV[16];
@@ -911,39 +848,10 @@
     return mCurrentFrameNumber;
 }
 
-EGLImageKHR GLConsumer::createImage(EGLDisplay dpy,
-        const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) {
-    EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
-    EGLint attrs[] = {
-        EGL_IMAGE_PRESERVED_KHR,        EGL_TRUE,
-        EGL_IMAGE_CROP_LEFT_ANDROID,    crop.left,
-        EGL_IMAGE_CROP_TOP_ANDROID,     crop.top,
-        EGL_IMAGE_CROP_RIGHT_ANDROID,   crop.right,
-        EGL_IMAGE_CROP_BOTTOM_ANDROID,  crop.bottom,
-        EGL_NONE,
-    };
-    if (!crop.isValid()) {
-        // No crop rect to set, so terminate the attrib array before the crop.
-        attrs[2] = EGL_NONE;
-    } else if (!isEglImageCroppable(crop)) {
-        // The crop rect is not at the origin, so we can't set the crop on the
-        // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
-        // extension.  In the future we can add a layered extension that
-        // removes this restriction if there is hardware that can support it.
-        attrs[2] = EGL_NONE;
-    }
-    EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
-            EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
-    if (image == EGL_NO_IMAGE_KHR) {
-        EGLint error = eglGetError();
-        ST_LOGE("error creating EGLImage: %#x", error);
-    }
-    return image;
-}
-
 sp<GraphicBuffer> GLConsumer::getCurrentBuffer() const {
     Mutex::Autolock lock(mMutex);
-    return mCurrentTextureBuf;
+    return (mCurrentTextureImage == NULL) ?
+            NULL : mCurrentTextureImage->graphicBuffer();
 }
 
 Rect GLConsumer::getCurrentCrop() const {
@@ -1067,18 +975,13 @@
     if (slotIndex == mCurrentTexture) {
         mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
     }
-    EGLImageKHR img = mEglSlots[slotIndex].mEglImage;
-    if (img != EGL_NO_IMAGE_KHR) {
-        ST_LOGV("destroying EGLImage dpy=%p img=%p", mEglDisplay, img);
-        eglDestroyImageKHR(mEglDisplay, img);
-    }
-    mEglSlots[slotIndex].mEglImage = EGL_NO_IMAGE_KHR;
+    mEglSlots[slotIndex].mEglImage.clear();
     ConsumerBase::freeBufferLocked(slotIndex);
 }
 
 void GLConsumer::abandonLocked() {
     ST_LOGV("abandonLocked");
-    mCurrentTextureBuf.clear();
+    mCurrentTextureImage.clear();
     ConsumerBase::abandonLocked();
 }
 
@@ -1138,4 +1041,87 @@
     out[15] = a[3]*b[12] + a[7]*b[13] + a[11]*b[14] + a[15]*b[15];
 }
 
+GLConsumer::EglImage::EglImage(sp<GraphicBuffer> graphicBuffer) :
+    mGraphicBuffer(graphicBuffer),
+    mEglImage(EGL_NO_IMAGE_KHR),
+    mEglDisplay(EGL_NO_DISPLAY) {
+}
+
+GLConsumer::EglImage::~EglImage() {
+    if (mEglImage != EGL_NO_IMAGE_KHR) {
+        if (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+           ALOGE("~EglImage: eglDestroyImageKHR failed");
+        }
+    }
+}
+
+status_t GLConsumer::EglImage::createIfNeeded(EGLDisplay eglDisplay,
+                                              const Rect& cropRect) {
+    // 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 (!eglDestroyImageKHR(mEglDisplay, mEglImage)) {
+           ALOGE("createIfNeeded: eglDestroyImageKHR failed");
+        }
+        mEglImage = EGL_NO_IMAGE_KHR;
+        mEglDisplay = EGL_NO_DISPLAY;
+    }
+
+    // If there's no image, create one.
+    if (mEglImage == EGL_NO_IMAGE_KHR) {
+        mEglDisplay = eglDisplay;
+        mCropRect = cropRect;
+        mEglImage = createImage(mEglDisplay, mGraphicBuffer, mCropRect);
+    }
+
+    // Fail if we can't create a valid image.
+    if (mEglImage == EGL_NO_IMAGE_KHR) {
+        mEglDisplay = EGL_NO_DISPLAY;
+        mCropRect.makeInvalid();
+        const sp<GraphicBuffer>& buffer = mGraphicBuffer;
+        ALOGE("Failed to create image. size=%ux%u st=%u usage=0x%x fmt=%d",
+            buffer->getWidth(), buffer->getHeight(), buffer->getStride(),
+            buffer->getUsage(), buffer->getPixelFormat());
+        return UNKNOWN_ERROR;
+    }
+
+    return OK;
+}
+
+void GLConsumer::EglImage::bindToTextureTarget(uint32_t texTarget) {
+    glEGLImageTargetTexture2DOES(texTarget, (GLeglImageOES)mEglImage);
+}
+
+EGLImageKHR GLConsumer::EglImage::createImage(EGLDisplay dpy,
+        const sp<GraphicBuffer>& graphicBuffer, const Rect& crop) {
+    EGLClientBuffer cbuf = (EGLClientBuffer)graphicBuffer->getNativeBuffer();
+    EGLint attrs[] = {
+        EGL_IMAGE_PRESERVED_KHR,        EGL_TRUE,
+        EGL_IMAGE_CROP_LEFT_ANDROID,    crop.left,
+        EGL_IMAGE_CROP_TOP_ANDROID,     crop.top,
+        EGL_IMAGE_CROP_RIGHT_ANDROID,   crop.right,
+        EGL_IMAGE_CROP_BOTTOM_ANDROID,  crop.bottom,
+        EGL_NONE,
+    };
+    if (!crop.isValid()) {
+        // No crop rect to set, so terminate the attrib array before the crop.
+        attrs[2] = EGL_NONE;
+    } else if (!isEglImageCroppable(crop)) {
+        // The crop rect is not at the origin, so we can't set the crop on the
+        // EGLImage because that's not allowed by the EGL_ANDROID_image_crop
+        // extension.  In the future we can add a layered extension that
+        // removes this restriction if there is hardware that can support it.
+        attrs[2] = EGL_NONE;
+    }
+    EGLImageKHR image = eglCreateImageKHR(dpy, EGL_NO_CONTEXT,
+            EGL_NATIVE_BUFFER_ANDROID, cbuf, attrs);
+    if (image == EGL_NO_IMAGE_KHR) {
+        EGLint error = eglGetError();
+        ALOGE("error creating EGLImage: %#x", error);
+    }
+    return image;
+}
+
 }; // namespace android