Loading include/gui/GLConsumer.h +58 −25 Original line number Diff line number Diff line Loading @@ -231,7 +231,7 @@ public: 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- Loading Loading @@ -262,7 +262,7 @@ protected: 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(); Loading @@ -275,10 +275,56 @@ protected: status_t checkAndUpdateEglStateLocked(bool contextCheck = false); private: // 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); // 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. Loading @@ -289,7 +335,7 @@ private: // 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 Loading @@ -303,13 +349,6 @@ private: // 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(); Loading @@ -319,10 +358,10 @@ private: // 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. Loading Loading @@ -382,17 +421,10 @@ private: // 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 Loading Loading @@ -444,6 +476,7 @@ private: // mReleasedTexImageBuffer is a dummy buffer used when in single buffer // mode and releaseTexImage() has been called static sp<GraphicBuffer> sReleasedTexImageBuffer; sp<EglImage> mReleasedTexImage; }; // ---------------------------------------------------------------------------- Loading libs/gui/GLConsumer.cpp +155 −169 Original line number Diff line number Diff line Loading @@ -279,8 +279,12 @@ status_t GLConsumer::releaseTexImage() { 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; Loading @@ -288,9 +292,11 @@ status_t GLConsumer::releaseTexImage() { 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. Loading Loading @@ -332,29 +338,12 @@ status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item, return err; } int slot = item->mBuf; bool destroyEglImage = false; if (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) { // 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; int slot = item->mBuf; mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer); } return NO_ERROR; Loading Loading @@ -395,30 +384,19 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item) 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) { // 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); 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; } // Do whatever sync ops we need to do before releasing the old slot. err = syncForReleaseLocked(mEglDisplay); Loading @@ -433,15 +411,15 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item) } 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); Loading @@ -452,7 +430,7 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item) // Update the GLConsumer state. mCurrentTexture = buf; mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; mCurrentTextureImage = mEglSlots[buf].mEglImage; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; Loading @@ -477,26 +455,27 @@ status_t GLConsumer::bindTextureImageLocked() { } glBindTexture(mTexTarget, mTexName); if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { if (mCurrentTextureBuf == NULL) { if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == NULL) { ST_LOGE("bindTextureImage: no currently-bound texture"); return NO_INIT; } status_t err = bindUnslottedBufferLocked(mEglDisplay); status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay, mCurrentCrop); if (err != NO_ERROR) { return err; ST_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, mCurrentTexture); return UNKNOWN_ERROR; } } else { EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage; glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); mCurrentTextureImage->bindToTextureTarget(mTexTarget); while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("bindTextureImage: error binding external texture image %p" ": %#04x", image, error); ST_LOGE("bindTextureImage: error binding external image: %#04x", error); return UNKNOWN_ERROR; } } // Wait for the new buffer to be ready. return doGLFenceWaitLocked(); Loading Loading @@ -537,7 +516,7 @@ void GLConsumer::setReleaseFence(const sp<Fence>& fence) { 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); Loading Loading @@ -583,18 +562,6 @@ status_t GLConsumer::detachFromContext() { 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; Loading Loading @@ -635,54 +602,23 @@ status_t GLConsumer::attachToContext(uint32_t tex) { // 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; 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; 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; } // 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; return OK; } Loading @@ -708,7 +644,7 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { } 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); Loading Loading @@ -787,11 +723,11 @@ void GLConsumer::setFilteringEnabled(bool enabled) { 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(); } } Loading Loading @@ -825,10 +761,11 @@ void GLConsumer::computeCurrentTransformMatrixLocked() { } } 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]; Loading Loading @@ -911,39 +848,10 @@ nsecs_t GLConsumer::getFrameNumber() { 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 { Loading Loading @@ -1067,18 +975,13 @@ void GLConsumer::freeBufferLocked(int slotIndex) { 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(); } Loading Loading @@ -1138,4 +1041,87 @@ static void mtxMul(float out[16], const float a[16], const float b[16]) { 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 Loading
include/gui/GLConsumer.h +58 −25 Original line number Diff line number Diff line Loading @@ -231,7 +231,7 @@ public: 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- Loading Loading @@ -262,7 +262,7 @@ protected: 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(); Loading @@ -275,10 +275,56 @@ protected: status_t checkAndUpdateEglStateLocked(bool contextCheck = false); private: // 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); // 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. Loading @@ -289,7 +335,7 @@ private: // 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 Loading @@ -303,13 +349,6 @@ private: // 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(); Loading @@ -319,10 +358,10 @@ private: // 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. Loading Loading @@ -382,17 +421,10 @@ private: // 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 Loading Loading @@ -444,6 +476,7 @@ private: // mReleasedTexImageBuffer is a dummy buffer used when in single buffer // mode and releaseTexImage() has been called static sp<GraphicBuffer> sReleasedTexImageBuffer; sp<EglImage> mReleasedTexImage; }; // ---------------------------------------------------------------------------- Loading
libs/gui/GLConsumer.cpp +155 −169 Original line number Diff line number Diff line Loading @@ -279,8 +279,12 @@ status_t GLConsumer::releaseTexImage() { 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; Loading @@ -288,9 +292,11 @@ status_t GLConsumer::releaseTexImage() { 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. Loading Loading @@ -332,29 +338,12 @@ status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item, return err; } int slot = item->mBuf; bool destroyEglImage = false; if (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) { // 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; int slot = item->mBuf; mEglSlots[slot].mEglImage = new EglImage(item->mGraphicBuffer); } return NO_ERROR; Loading Loading @@ -395,30 +384,19 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item) 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) { // 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); 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; } // Do whatever sync ops we need to do before releasing the old slot. err = syncForReleaseLocked(mEglDisplay); Loading @@ -433,15 +411,15 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item) } 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); Loading @@ -452,7 +430,7 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item) // Update the GLConsumer state. mCurrentTexture = buf; mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; mCurrentTextureImage = mEglSlots[buf].mEglImage; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; Loading @@ -477,26 +455,27 @@ status_t GLConsumer::bindTextureImageLocked() { } glBindTexture(mTexTarget, mTexName); if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) { if (mCurrentTextureBuf == NULL) { if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT && mCurrentTextureImage == NULL) { ST_LOGE("bindTextureImage: no currently-bound texture"); return NO_INIT; } status_t err = bindUnslottedBufferLocked(mEglDisplay); status_t err = mCurrentTextureImage->createIfNeeded(mEglDisplay, mCurrentCrop); if (err != NO_ERROR) { return err; ST_LOGW("bindTextureImage: can't create image on display=%p slot=%d", mEglDisplay, mCurrentTexture); return UNKNOWN_ERROR; } } else { EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage; glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); mCurrentTextureImage->bindToTextureTarget(mTexTarget); while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("bindTextureImage: error binding external texture image %p" ": %#04x", image, error); ST_LOGE("bindTextureImage: error binding external image: %#04x", error); return UNKNOWN_ERROR; } } // Wait for the new buffer to be ready. return doGLFenceWaitLocked(); Loading Loading @@ -537,7 +516,7 @@ void GLConsumer::setReleaseFence(const sp<Fence>& fence) { 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); Loading Loading @@ -583,18 +562,6 @@ status_t GLConsumer::detachFromContext() { 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; Loading Loading @@ -635,54 +602,23 @@ status_t GLConsumer::attachToContext(uint32_t tex) { // 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; 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; 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; } // 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; return OK; } Loading @@ -708,7 +644,7 @@ status_t GLConsumer::syncForReleaseLocked(EGLDisplay dpy) { } 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); Loading Loading @@ -787,11 +723,11 @@ void GLConsumer::setFilteringEnabled(bool enabled) { 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(); } } Loading Loading @@ -825,10 +761,11 @@ void GLConsumer::computeCurrentTransformMatrixLocked() { } } 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]; Loading Loading @@ -911,39 +848,10 @@ nsecs_t GLConsumer::getFrameNumber() { 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 { Loading Loading @@ -1067,18 +975,13 @@ void GLConsumer::freeBufferLocked(int slotIndex) { 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(); } Loading Loading @@ -1138,4 +1041,87 @@ static void mtxMul(float out[16], const float a[16], const float b[16]) { 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