void BufferQueue::cancelBuffer(int buf, const sp<Fence>& fence) { ATRACE_CALL(); ST_LOGV("cancelBuffer: slot=%d", buf); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGW("cancelBuffer: BufferQueue has been abandoned!"); return; } int maxBufferCount = getMaxBufferCountLocked(); if (buf < 0 || buf >= maxBufferCount) { ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", maxBufferCount, buf); return; } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", buf, mSlots[buf].mBufferState); return; } else if (fence == NULL) { ST_LOGE("cancelBuffer: fence is NULL"); return; } mSlots[buf].mBufferState = BufferSlot::FREE; mSlots[buf].mFrameNumber = 0; mSlots[buf].mFence = fence; mDequeueCondition.broadcast(); }
void BufferQueueDump::onAcquireBuffer(const int slot, const sp<GraphicBuffer>& buffer, const sp<Fence>& fence) { if (buffer == NULL) { ST_LOGW("onAcquireBuffer: The GraphicBuffer of slot%d is NULL, ignore it", slot); return; } // check this slot in vector for (uint32_t i = mAcquiredBufs.size(); i > 0; i--) { uint32_t index = i - 1; if ((mAcquiredBufs[index]->mSlot == slot) || (mAcquiredBufs[index]->mSlot == BufferQueue::INVALID_BUFFER_SLOT)) { // remove the items which are same with slot id or freed mAcquiredBufs.removeAt(index); ST_LOGD("onAcquireBuffer: find slot%d has been in acquired buffer[%u], remove it", slot, index); } } // push new buffer into vector sp<AcquiredBuffer> acquiredBuffer = new AcquiredBuffer(slot, buffer, fence, systemTime()); if (acquiredBuffer == NULL) { XLOGE("[%s] alloc AcquiredBuffer failed", __func__); return; } mAcquiredBufs.push_back(acquiredBuffer); }
status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item, nsecs_t presentWhen) { status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen); if (err != NO_ERROR) { 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; } return NO_ERROR; }
void SurfaceTexture::cancelBuffer(int buf) { ST_LOGV("cancelBuffer: slot=%d", buf); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGW("cancelBuffer: SurfaceTexture has been abandoned!"); return; } if (buf < 0 || buf >= mBufferCount) { ST_LOGE("cancelBuffer: slot index out of range [0, %d]: %d", mBufferCount, buf); return; } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { ST_LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", buf, mSlots[buf].mBufferState); return; } mSlots[buf].mBufferState = BufferSlot::FREE; mSlots[buf].mFrameNumber = 0; mDequeueCondition.signal(); }
status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item) { status_t err = ConsumerBase::acquireBufferLocked(item); if (err != NO_ERROR) { return err; } int slot = item->mBuf; if (item->mGraphicBuffer != NULL) { // This buffer has not been acquired before, so we must assume // that any EGLImage in mEglSlots is stale. if (mEglSlots[slot].mEglImage != EGL_NO_IMAGE_KHR) { if (!eglDestroyImageKHR(mEglDisplay, mEglSlots[slot].mEglImage)) { ST_LOGW("acquireBufferLocked: eglDestroyImageKHR failed for slot=%d", slot); // keep going } mEglSlots[slot].mEglImage = EGL_NO_IMAGE_KHR; } } return NO_ERROR; }
status_t GLConsumer::bindTextureImageLocked() { if (mEglDisplay == EGL_NO_DISPLAY) { ALOGE("bindTextureImage: invalid display"); return INVALID_OPERATION; } GLint error; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGW("bindTextureImage: clearing GL error: %#04x", error); } 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; glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("bindTextureImage: error binding external texture image %p" ": %#04x", image, error); return UNKNOWN_ERROR; } } // Wait for the new buffer to be ready. return doGLFenceWaitLocked(); }
status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) { #else status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync, bool deferConversion) { #endif ATRACE_CALL(); ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); status_t err = NO_ERROR; if (mAbandoned) { ST_LOGE("updateTexImage: SurfaceTexture is abandoned!"); return NO_INIT; } if (!mAttached) { ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL " "ES context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || dpy == EGL_NO_DISPLAY) { ST_LOGE("updateTexImage: invalid current EGLDisplay"); return INVALID_OPERATION; } if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || ctx == EGL_NO_CONTEXT) { ST_LOGE("updateTexImage: invalid current EGLContext"); return INVALID_OPERATION; } mEglDisplay = dpy; mEglContext = ctx; BufferQueue::BufferItem item; // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. err = acquireBufferLocked(&item); if (err == NO_ERROR) { int buf = item.mBuf; #ifdef STE_HARDWARE EGLImageKHR image; if (conversionIsNeeded(mSlots[buf].mGraphicBuffer)) { mNeedsConversion = deferConversion; // If color conversion is needed we can't use the graphic buffers // located in mSlots for the textures (wrong color format). Instead // color convert it into a buffer in mBlitSlots and use that instead. image = mBlitSlots[mNextBlitSlot].mEglImage; // If there exists an image already, make sure that // the dimensions match the current source buffer. // Otherwise, destroy the buffer and let a new one be allocated. if (image != EGL_NO_IMAGE_KHR && mSlots[buf].mGraphicBuffer != NULL && mBlitSlots[mNextBlitSlot].mGraphicBuffer != NULL) { sp<GraphicBuffer> &srcBuf = mSlots[buf].mGraphicBuffer; sp<GraphicBuffer> &bltBuf = mBlitSlots[mNextBlitSlot].mGraphicBuffer; if (srcBuf->getWidth() != bltBuf->getWidth() || srcBuf->getHeight() != bltBuf->getHeight()) { eglDestroyImageKHR(mBlitSlots[mNextBlitSlot].mEglDisplay, image); mBlitSlots[mNextBlitSlot].mEglImage = EGL_NO_IMAGE_KHR; mBlitSlots[mNextBlitSlot].mGraphicBuffer = NULL; image = EGL_NO_IMAGE_KHR; } } if (image == EGL_NO_IMAGE_KHR) { sp<GraphicBuffer> &srcBuf = mSlots[buf].mGraphicBuffer; status_t res = 0; sp<GraphicBuffer> blitBuffer( mGraphicBufferAlloc->createGraphicBuffer( srcBuf->getWidth(), srcBuf->getHeight(), PIXEL_FORMAT_RGBA_8888, srcBuf->getUsage(), &res)); if (blitBuffer == 0) { ST_LOGE("updateTexImage: SurfaceComposer::createGraphicBuffer failed"); return NO_MEMORY; } if (res != NO_ERROR) { ST_LOGW("updateTexImage: SurfaceComposer::createGraphicBuffer error=%#04x", res); } mBlitSlots[mNextBlitSlot].mGraphicBuffer = blitBuffer; EGLDisplay dpy = eglGetCurrentDisplay(); image = createImage(dpy, blitBuffer); mBlitSlots[mNextBlitSlot].mEglImage = image; mBlitSlots[mNextBlitSlot].mEglDisplay = dpy; } if (deferConversion) { item.mGraphicBuffer = mSlots[buf].mGraphicBuffer; mConversionSrcSlot = buf; mConversionBltSlot = mNextBlitSlot; // At this point item.mGraphicBuffer and image do not point // at matching buffers. This is intentional as this // surface might end up being taken care of by HWComposer, // which needs access to the original buffer. // GL however, is fed an EGLImage that is created from // a conversion buffer. It will have its // content updated once the surface is actually drawn // in Layer::onDraw() } else { if (convert(mSlots[buf].mGraphicBuffer, mBlitSlots[mNextBlitSlot].mGraphicBuffer) != OK) { ALOGE("updateTexImage: convert failed"); return UNKNOWN_ERROR; } item.mGraphicBuffer = mBlitSlots[mNextBlitSlot].mGraphicBuffer; } // mBlitSlots contains several buffers (NUM_BLIT_BUFFER_SLOTS), // advance (potentially wrap) the index mNextBlitSlot = (mNextBlitSlot + 1) % BufferQueue::NUM_BLIT_BUFFER_SLOTS; } else { mNeedsConversion = false; image = mEglSlots[buf].mEglImage; item.mGraphicBuffer = mSlots[buf].mGraphicBuffer; if (image == EGL_NO_IMAGE_KHR) { EGLDisplay dpy = eglGetCurrentDisplay(); if (item.mGraphicBuffer == 0) { ST_LOGE("buffer at slot %d is null", buf); return BAD_VALUE; } image = createImage(dpy, item.mGraphicBuffer); mEglSlots[buf].mEglImage = image; mEglDisplay = dpy; if (image == EGL_NO_IMAGE_KHR) { // NOTE: if dpy was invalid, createImage() is guaranteed to // fail. so we'd end up here. return -EINVAL; } } } #endif // we call the rejecter here, in case the caller has a reason to // not accept this buffer. this is used by SurfaceFlinger to // reject buffers which have the wrong size #ifdef STE_HARDWARE if (rejecter && rejecter->reject(item.mGraphicBuffer, item)) { #else if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) { #endif releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR); glBindTexture(mTexTarget, mTexName); return NO_ERROR; } GLint error; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGW("updateTexImage: clearing GL error: %#04x", error); } #ifndef STE_HARDWARE EGLImageKHR image = mEglSlots[buf].mEglImage; #endif glBindTexture(mTexTarget, mTexName); glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("updateTexImage: error binding external texture image %p " "(slot %d): %#04x", image, buf, error); err = UNKNOWN_ERROR; } if (err == NO_ERROR) { err = syncForReleaseLocked(dpy); } if (err != NO_ERROR) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR); return err; } ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf, mSlots[buf].mGraphicBuffer->handle); // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { status_t status = releaseBufferLocked(mCurrentTexture, dpy, mEglSlots[mCurrentTexture].mEglFence); if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) { ST_LOGE("updateTexImage: failed to release buffer: %s (%d)", strerror(-status), status); err = status; } } // Update the SurfaceTexture state. mCurrentTexture = buf; #ifndef STE_HARDWARE mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; #else mCurrentTextureBuf = item.mGraphicBuffer; #endif mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; mCurrentFence = item.mFence; if (!skipSync) { // SurfaceFlinger needs to lazily perform GLES synchronization // only when it's actually going to use GLES for compositing. // Eventually SurfaceFlinger should have its own consumer class, // but for now we'll just hack it in to SurfaceTexture. // SurfaceFlinger is responsible for calling doGLFenceWait before // texturing from this SurfaceTexture. doGLFenceWaitLocked(); } computeCurrentTransformMatrixLocked(); } else { if (err < 0) { ST_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); return err; } // We always bind the texture even if we don't update its contents. glBindTexture(mTexTarget, mTexName); return OK; } return err; } void SurfaceTexture::setReleaseFence(int fenceFd) { sp<Fence> fence(new Fence(fenceFd)); if (fenceFd == -1 || mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) return; status_t err = addReleaseFence(mCurrentTexture, fence); if (err != OK) { ST_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err); } }
status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) { ATRACE_CALL(); ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); status_t err = NO_ERROR; if (mAbandoned) { ST_LOGE("updateTexImage: SurfaceTexture is abandoned!"); return NO_INIT; } if (!mAttached) { ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL " "ES context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || dpy == EGL_NO_DISPLAY) { ST_LOGE("updateTexImage: invalid current EGLDisplay"); return INVALID_OPERATION; } if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || ctx == EGL_NO_CONTEXT) { ST_LOGE("updateTexImage: invalid current EGLContext"); return INVALID_OPERATION; } mEglDisplay = dpy; mEglContext = ctx; BufferQueue::BufferItem item; // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. err = mBufferQueue->acquireBuffer(&item); if (err == NO_ERROR) { int buf = item.mBuf; // This buffer was newly allocated, so we need to clean up on our side if (item.mGraphicBuffer != NULL) { mEGLSlots[buf].mGraphicBuffer = 0; if (mEGLSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(dpy, mEGLSlots[buf].mEglImage); mEGLSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; } mEGLSlots[buf].mGraphicBuffer = item.mGraphicBuffer; } // we call the rejecter here, in case the caller has a reason to // not accept this buffer. this is used by SurfaceFlinger to // reject buffers which have the wrong size if (rejecter && rejecter->reject(mEGLSlots[buf].mGraphicBuffer, item)) { mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence); mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR; glBindTexture(mTexTarget, mTexName); return NO_ERROR; } // Update the GL texture object. We may have to do this even when // item.mGraphicBuffer == NULL, if we destroyed the EGLImage when // detaching from a context but the buffer has not been re-allocated. EGLImageKHR image = mEGLSlots[buf].mEglImage; if (image == EGL_NO_IMAGE_KHR) { if (mEGLSlots[buf].mGraphicBuffer == NULL) { ST_LOGE("updateTexImage: buffer at slot %d is null", buf); err = BAD_VALUE; } else { image = createImage(dpy, mEGLSlots[buf].mGraphicBuffer); mEGLSlots[buf].mEglImage = image; if (image == EGL_NO_IMAGE_KHR) { // NOTE: if dpy was invalid, createImage() is guaranteed to // fail. so we'd end up here. err = UNKNOWN_ERROR; } } } if (err == NO_ERROR) { GLint error; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGW("updateTexImage: clearing GL error: %#04x", error); } glBindTexture(mTexTarget, mTexName); glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("updateTexImage: error binding external texture image %p " "(slot %d): %#04x", image, buf, error); err = UNKNOWN_ERROR; } if (err == NO_ERROR) { err = syncForReleaseLocked(dpy); } } if (err != NO_ERROR) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. mBufferQueue->releaseBuffer(buf, dpy, mEGLSlots[buf].mFence); mEGLSlots[buf].mFence = EGL_NO_SYNC_KHR; return err; } ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf, item.mGraphicBuffer != NULL ? item.mGraphicBuffer->handle : 0); // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { status_t status = mBufferQueue->releaseBuffer(mCurrentTexture, dpy, mEGLSlots[mCurrentTexture].mFence); mEGLSlots[mCurrentTexture].mFence = EGL_NO_SYNC_KHR; if (status == BufferQueue::STALE_BUFFER_SLOT) { freeBufferLocked(mCurrentTexture); } else if (status != NO_ERROR) { ST_LOGE("updateTexImage: released invalid buffer"); err = status; } } // Update the SurfaceTexture state. mCurrentTexture = buf; mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; computeCurrentTransformMatrix(); } else { if (err < 0) { ALOGE("updateTexImage failed on acquire %d", err); } // We always bind the texture even if we don't update its contents. glBindTexture(mTexTarget, mTexName); return OK; } return err; }
status_t SurfaceTexture::updateTexImage() { ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("calling updateTexImage() on an abandoned SurfaceTexture"); return NO_INIT; } // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. if (!mQueue.empty()) { Fifo::iterator front(mQueue.begin()); int buf = *front; // Update the GL texture object. EGLImageKHR image = mSlots[buf].mEglImage; EGLDisplay dpy = eglGetCurrentDisplay(); #ifdef QCOM_HARDWARE if (isGPUSupportedFormat(mSlots[buf].mGraphicBuffer->format)) { // Update the GL texture object. EGLImageKHR image = mSlots[buf].mEglImage; #else if (image == EGL_NO_IMAGE_KHR) { if (mSlots[buf].mGraphicBuffer == 0) { ST_LOGE("buffer at slot %d is null", buf); return BAD_VALUE; } image = createImage(dpy, mSlots[buf].mGraphicBuffer); mSlots[buf].mEglImage = image; mSlots[buf].mEglDisplay = dpy; #endif if (image == EGL_NO_IMAGE_KHR) { #ifdef QCOM_HARDWARE EGLDisplay dpy = eglGetCurrentDisplay(); if (mSlots[buf].mGraphicBuffer == 0) { ST_LOGE("buffer at slot %d is null", buf); return BAD_VALUE; } image = createImage(dpy, mSlots[buf].mGraphicBuffer); mSlots[buf].mEglImage = image; mSlots[buf].mEglDisplay = dpy; if (image == EGL_NO_IMAGE_KHR) { #endif // NOTE: if dpy was invalid, createImage() is guaranteed to // fail. so we'd end up here. return -EINVAL; } } GLint error; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGW("updateTexImage: clearing GL error: %#04x", error); } glBindTexture(mTexTarget, mTexName); glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); bool failed = false; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("error binding external texture image %p (slot %d): %#04x", image, buf, error); failed = true; } if (failed) { return -EINVAL; } #ifdef QCOM_HARDWARE } #endif if (mCurrentTexture != INVALID_BUFFER_SLOT) { if (mUseFenceSync) { EGLSyncKHR fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL); if (fence == EGL_NO_SYNC_KHR) { LOGE("updateTexImage: error creating fence: %#x", eglGetError()); return -EINVAL; } glFlush(); mSlots[mCurrentTexture].mFence = fence; } } ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf, mSlots[buf].mGraphicBuffer->handle); if (mCurrentTexture != INVALID_BUFFER_SLOT) { // The current buffer becomes FREE if it was still in the queued // state. If it has already been given to the client // (synchronous mode), then it stays in DEQUEUED state. if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) { mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE; } } // Update the SurfaceTexture state. mCurrentTexture = buf; mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; mCurrentCrop = mSlots[buf].mCrop; mCurrentTransform = mSlots[buf].mTransform; mCurrentScalingMode = mSlots[buf].mScalingMode; mCurrentTimestamp = mSlots[buf].mTimestamp; computeCurrentTransformMatrix(); // Now that we've passed the point at which failures can happen, // it's safe to remove the buffer from the front of the queue. mQueue.erase(front); mDequeueCondition.signal(); } else { // We always bind the texture even if we don't update its contents. glBindTexture(mTexTarget, mTexName); } return OK; } bool SurfaceTexture::isExternalFormat(uint32_t format) { switch (format) { // supported YUV formats case HAL_PIXEL_FORMAT_YV12: // Legacy/deprecated YUV formats case HAL_PIXEL_FORMAT_YCbCr_422_SP: case HAL_PIXEL_FORMAT_YCrCb_420_SP: case HAL_PIXEL_FORMAT_YCbCr_422_I: return true; } // Any OEM format needs to be considered if (format>=0x100 && format<=0x1FF) return true; return false; } GLenum SurfaceTexture::getCurrentTextureTarget() const { return mTexTarget; } void SurfaceTexture::getTransformMatrix(float mtx[16]) { Mutex::Autolock lock(mMutex); memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); }
status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item) { status_t err = NO_ERROR; if (!mAttached) { ST_LOGE("updateAndRelease: GLConsumer is not attached to an OpenGL " "ES context"); return INVALID_OPERATION; } // Confirm state. err = checkAndUpdateEglStateLocked(); if (err != NO_ERROR) { return err; } int buf = item.mBuf; // If the mEglSlot entry is empty, create an EGLImage 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); 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); if (err != NO_ERROR) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. // As we are still under lock since acquireBuffer, it is safe to // release by slot. releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer, mEglDisplay, EGL_NO_SYNC_KHR); return err; } ST_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 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); if (status < NO_ERROR) { ST_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), status); err = status; // keep going, with error raised [?] } } // Update the GLConsumer state. mCurrentTexture = buf; mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; mCurrentFence = item.mFence; mCurrentFrameNumber = item.mFrameNumber; computeCurrentTransformMatrixLocked(); return err; }
status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync, bool isComposition) { #else status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter, bool skipSync) { #endif ATRACE_CALL(); ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); status_t err = NO_ERROR; if (mAbandoned) { ST_LOGE("updateTexImage: SurfaceTexture is abandoned!"); return NO_INIT; } if (!mAttached) { ST_LOGE("updateTexImage: SurfaceTexture is not attached to an OpenGL " "ES context"); return INVALID_OPERATION; } EGLDisplay dpy = eglGetCurrentDisplay(); EGLContext ctx = eglGetCurrentContext(); if ((mEglDisplay != dpy && mEglDisplay != EGL_NO_DISPLAY) || dpy == EGL_NO_DISPLAY) { ST_LOGE("updateTexImage: invalid current EGLDisplay"); return INVALID_OPERATION; } if ((mEglContext != ctx && mEglContext != EGL_NO_CONTEXT) || ctx == EGL_NO_CONTEXT) { ST_LOGE("updateTexImage: invalid current EGLContext"); return INVALID_OPERATION; } mEglDisplay = dpy; mEglContext = ctx; BufferQueue::BufferItem item; // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. err = acquireBufferLocked(&item); if (err == NO_ERROR) { int buf = item.mBuf; // we call the rejecter here, in case the caller has a reason to // not accept this buffer. this is used by SurfaceFlinger to // reject buffers which have the wrong size if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) { releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR); glBindTexture(mTexTarget, mTexName); return NO_ERROR; } #ifdef DECIDE_TEXTURE_TARGET // GPU is not efficient in handling GL_TEXTURE_EXTERNAL_OES // texture target. Depending on the image format, decide, // the texture target to be used if(isComposition){ switch (mSlots[buf].mGraphicBuffer->format) { case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGBX_8888: case HAL_PIXEL_FORMAT_RGB_888: case HAL_PIXEL_FORMAT_RGB_565: case HAL_PIXEL_FORMAT_BGRA_8888: case HAL_PIXEL_FORMAT_RGBA_5551: case HAL_PIXEL_FORMAT_RGBA_4444: mTexTarget = GL_TEXTURE_2D; break; default: mTexTarget = GL_TEXTURE_EXTERNAL_OES; break; } } #endif GLint error; while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGW("updateTexImage: clearing GL error: %#04x", error); } EGLImageKHR image = mEglSlots[buf].mEglImage; glBindTexture(mTexTarget, mTexName); glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image); while ((error = glGetError()) != GL_NO_ERROR) { ST_LOGE("updateTexImage: error binding external texture image %p " "(slot %d): %#04x", image, buf, error); err = UNKNOWN_ERROR; } if (err == NO_ERROR) { err = syncForReleaseLocked(dpy); } if (err != NO_ERROR) { // Release the buffer we just acquired. It's not safe to // release the old buffer, so instead we just drop the new frame. releaseBufferLocked(buf, dpy, EGL_NO_SYNC_KHR); return err; } ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf, mSlots[buf].mGraphicBuffer->handle); // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { status_t status = releaseBufferLocked(mCurrentTexture, dpy, mEglSlots[mCurrentTexture].mEglFence); if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) { ST_LOGE("updateTexImage: failed to release buffer: %s (%d)", strerror(-status), status); err = status; } } // Update the SurfaceTexture state. mCurrentTexture = buf; mCurrentTextureBuf = mSlots[buf].mGraphicBuffer; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; mCurrentTimestamp = item.mTimestamp; mCurrentFence = item.mFence; if (!skipSync) { // SurfaceFlinger needs to lazily perform GLES synchronization // only when it's actually going to use GLES for compositing. // Eventually SurfaceFlinger should have its own consumer class, // but for now we'll just hack it in to SurfaceTexture. // SurfaceFlinger is responsible for calling doGLFenceWait before // texturing from this SurfaceTexture. doGLFenceWaitLocked(); } computeCurrentTransformMatrixLocked(); } else { if (err < 0) { ST_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); return err; } // We always bind the texture even if we don't update its contents. glBindTexture(mTexTarget, mTexName); return OK; } return err; }