status_t FramebufferSurface::setReleaseFenceFd(int fenceFd) { status_t err = NO_ERROR; if (fenceFd >= 0) { sp<Fence> fence(new Fence(fenceFd)); if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT) { #if ANDROID_VERSION >= 19 status_t err = addReleaseFence(mCurrentBufferSlot, mCurrentBuffer, fence); #else status_t err = addReleaseFence(mCurrentBufferSlot, fence); #endif ALOGE_IF(err, "setReleaseFenceFd: failed to add the fence: %s (%d)", strerror(-err), err); } } return err; }
void FramebufferSurface::onFrameCommitted() { sp<Fence> fence = mHwc.getAndResetReleaseFence(mDisplayType); if (fence->isValid() && mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT) { status_t err = addReleaseFence(mCurrentBufferSlot, mCurrentBuffer, fence); ALOGE_IF(err, "setReleaseFenceFd: failed to add the fence: %s (%d)", strerror(-err), 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); } }
void GLConsumer::setReleaseFence(const sp<Fence>& fence) { if (fence->isValid() && mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { 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, 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); } }