status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) { status_t err; if (!nativeBuffer) return BAD_VALUE; if (mCurrentLockedBuffers == mMaxLockedBuffers) { return INVALID_OPERATION; } BufferQueue::BufferItem b; Mutex::Autolock _l(mMutex); err = acquireBufferLocked(&b); if (err != OK) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { return BAD_VALUE; } else { CC_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); return err; } } int buf = b.mBuf; if (b.mFence.get()) { err = b.mFence->waitForever(1000, "CpuConsumer::lockNextBuffer"); if (err != OK) { CC_LOGE("Failed to wait for fence of acquired buffer: %s (%d)", strerror(-err), err); return err; } } err = mSlots[buf].mGraphicBuffer->lock( GraphicBuffer::USAGE_SW_READ_OFTEN, b.mCrop, &mBufferPointers[buf]); if (mBufferPointers[buf] != NULL && err != OK) { CC_LOGE("Unable to lock buffer for CPU reading: %s (%d)", strerror(-err), err); return err; } nativeBuffer->data = reinterpret_cast<uint8_t*>(mBufferPointers[buf]); nativeBuffer->width = mSlots[buf].mGraphicBuffer->getWidth(); nativeBuffer->height = mSlots[buf].mGraphicBuffer->getHeight(); nativeBuffer->format = mSlots[buf].mGraphicBuffer->getPixelFormat(); nativeBuffer->stride = mSlots[buf].mGraphicBuffer->getStride(); nativeBuffer->crop = b.mCrop; nativeBuffer->transform = b.mTransform; nativeBuffer->scalingMode = b.mScalingMode; nativeBuffer->timestamp = b.mTimestamp; nativeBuffer->frameNumber = b.mFrameNumber; mCurrentLockedBuffers++; return OK; }
status_t GonkNativeWindow::acquireBuffer(BufferItem *item, bool waitForFence) { status_t err; if (!item) return BAD_VALUE; Mutex::Autolock _l(mMutex); err = acquireBufferLocked(item); if (err != OK) { if (err != NO_BUFFER_AVAILABLE) { BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); } return err; } if (waitForFence) { err = item->mFence->waitForever("GonkNativeWindow::acquireBuffer"); if (err != OK) { BI_LOGE("Failed to wait for fence of acquired buffer: %s (%d)", strerror(-err), err); return err; } } item->mGraphicBuffer = mSlots[item->mBuf].mGraphicBuffer; return OK; }
status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence) { Mutex::Autolock lock(mMutex); BufferQueue::BufferItem item; #if ANDROID_VERSION >= 19 status_t err = acquireBufferLocked(&item, 0); #else status_t err = acquireBufferLocked(&item); #endif if (err == BufferQueue::NO_BUFFER_AVAILABLE) { outBuffer = mCurrentBuffer; return NO_ERROR; } else if (err != NO_ERROR) { ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err); return err; } // If the BufferQueue has freed and reallocated a buffer in mCurrentSlot // then we may have acquired the slot we already own. If we had released // our current buffer before we call acquireBuffer then that release call // would have returned STALE_BUFFER_SLOT, and we would have called // freeBufferLocked on that slot. Because the buffer slot has already // been overwritten with the new buffer all we have to do is skip the // releaseBuffer call and we should be in the same state we'd be in if we // had released the old buffer first. if (mCurrentBufferSlot != BufferQueue::INVALID_BUFFER_SLOT && item.mBuf != mCurrentBufferSlot) { // Release the previous buffer. #if ANDROID_VERSION >= 19 err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); #else err = releaseBufferLocked(mCurrentBufferSlot, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR); #endif if (err != NO_ERROR && err != BufferQueue::STALE_BUFFER_SLOT) { ALOGE("error releasing buffer: %s (%d)", strerror(-err), err); return err; } } mCurrentBufferSlot = item.mBuf; mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer; outFence = item.mFence; outBuffer = mCurrentBuffer; return NO_ERROR; }
status_t VirtualDisplaySurface::queueBuffer(int pslot, const QueueBufferInput& input, QueueBufferOutput* output) { if (mDisplayId < 0) return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output); VDS_LOGW_IF(mDbgState != DBG_STATE_GLES, "Unexpected queueBuffer(pslot=%d) in %s state", pslot, dbgStateStr()); mDbgState = DBG_STATE_GLES_DONE; VDS_LOGV("queueBuffer pslot=%d", pslot); status_t result; if (mCompositionType == COMPOSITION_MIXED) { // Queue the buffer back into the scratch pool QueueBufferOutput scratchQBO; int sslot = mapProducer2SourceSlot(SOURCE_SCRATCH, pslot); result = mSource[SOURCE_SCRATCH]->queueBuffer(sslot, input, &scratchQBO); if (result != NO_ERROR) return result; // Now acquire the buffer from the scratch pool -- should be the same // slot and fence as we just queued. Mutex::Autolock lock(mMutex); BufferItem item; result = acquireBufferLocked(&item, 0); if (result != NO_ERROR) return result; VDS_LOGW_IF(item.mBuf != sslot, "queueBuffer: acquired sslot %d from SCRATCH after queueing sslot %d", item.mBuf, sslot); mFbProducerSlot = mapSource2ProducerSlot(SOURCE_SCRATCH, item.mBuf); mFbFence = mSlots[item.mBuf].mFence; } else { LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES, "Unexpected queueBuffer in state %s for compositionType %s", dbgStateStr(), dbgCompositionTypeStr(mCompositionType)); // Extract the GLES release fence for HWC to acquire int64_t timestamp; bool isAutoTimestamp; android_dataspace dataSpace; Rect crop; int scalingMode; uint32_t transform; bool async; input.deflate(×tamp, &isAutoTimestamp, &dataSpace, &crop, &scalingMode, &transform, &async, &mFbFence); mFbProducerSlot = pslot; mOutputFence = mFbFence; } *output = mQueueBufferOutput; return NO_ERROR; }
void RingBufferConsumer::onFrameAvailable(const BufferItem& item) { status_t err; { Mutex::Autolock _l(mMutex); /** * Release oldest frame */ if (mBufferItemList.size() >= (size_t)mBufferCount) { err = releaseOldestBufferLocked(/*pinnedFrames*/NULL); assert(err != NOT_ENOUGH_DATA); // TODO: implement the case for NO_BUFFER_AVAILABLE assert(err != NO_BUFFER_AVAILABLE); if (err != OK) { return; } // TODO: in unpinBuffer rerun this routine if we had buffers // we could've locked but didn't because there was no space } RingBufferItem& item = *mBufferItemList.insert(mBufferItemList.end(), RingBufferItem()); /** * Acquire new frame */ err = acquireBufferLocked(&item, 0); if (err != OK) { if (err != NO_BUFFER_AVAILABLE) { BI_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); } mBufferItemList.erase(--mBufferItemList.end()); return; } BI_LOGV("New buffer acquired (timestamp %" PRId64 "), " "buffer items %zu out of %d", item.mTimestamp, mBufferItemList.size(), mBufferCount); if (item.mTimestamp < mLatestTimestamp) { BI_LOGE("Timestamp decreases from %" PRId64 " to %" PRId64, mLatestTimestamp, item.mTimestamp); } mLatestTimestamp = item.mTimestamp; item.mGraphicBuffer = mSlots[item.mBuf].mGraphicBuffer; } // end of mMutex lock ConsumerBase::onFrameAvailable(item); }
already_AddRefed<GraphicBufferLocked> GonkNativeWindow::getCurrentBuffer() { Mutex::Autolock _l(mMutex); GonkBufferQueue::BufferItem item; // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. status_t err = acquireBufferLocked(&item); if (err != NO_ERROR) { return NULL; } nsRefPtr<GraphicBufferLocked> ret = new CameraGraphicBuffer(this, item.mBuf, mBufferQueue->getGeneration(), item.mSurfaceDescriptor); return ret.forget(); }
status_t GLConsumer::updateTexImage() { ATRACE_CALL(); ST_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("updateTexImage: GLConsumer is abandoned!"); return NO_INIT; } // Make sure the EGL state is the same as in previous calls. status_t err = checkAndUpdateEglStateLocked(); if (err != NO_ERROR) { return err; } BufferQueue::BufferItem item; // Acquire the next buffer. // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. err = acquireBufferLocked(&item, 0); if (err != NO_ERROR) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // We always bind the texture even if we don't update its contents. ST_LOGV("updateTexImage: no buffers were available"); glBindTexture(mTexTarget, mTexName); err = NO_ERROR; } else { ST_LOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); } return err; } // Release the previous buffer. err = updateAndReleaseLocked(item); if (err != NO_ERROR) { // We always bind the texture. glBindTexture(mTexTarget, mTexName); return err; } // Bind the new buffer to the GL texture, and wait until it's ready. return bindTextureImageLocked(); }
TemporaryRef<TextureClient> GonkNativeWindow::getCurrentBuffer() { Mutex::Autolock _l(mMutex); BufferItem item; // In asynchronous mode the list is guaranteed to be one buffer // deep, while in synchronous mode we use the oldest buffer. status_t err = acquireBufferLocked(&item, 0); //??? if (err != NO_ERROR) { return NULL; } RefPtr<TextureClient> textureClient = mConsumer->getTextureClientFromBuffer(item.mGraphicBuffer.get()); if (!textureClient) { return NULL; } textureClient->SetRecycleCallback(GonkNativeWindow::RecycleCallback, this); return textureClient; }
status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter) { ATRACE_CALL(); ALOGV("updateTexImage"); Mutex::Autolock lock(mMutex); if (mAbandoned) { ALOGE("updateTexImage: GLConsumer is abandoned!"); return NO_INIT; } // Make sure the EGL state is the same as in previous calls. status_t err = checkAndUpdateEglStateLocked(); if (err != NO_ERROR) { return err; } BufferQueue::BufferItem item; // Acquire the next buffer. // 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) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // This variant of updateTexImage does not guarantee that the // texture is bound, so no need to call glBindTexture. err = NO_ERROR; } else { ALOGE("updateTexImage: acquire failed: %s (%d)", strerror(-err), err); } return err; } // 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 int buf = item.mBuf; if (rejecter && rejecter->reject(mSlots[buf].mGraphicBuffer, item)) { releaseBufferLocked(buf, EGL_NO_SYNC_KHR); return NO_ERROR; } // Release the previous buffer. err = releaseAndUpdateLocked(item); if (err != NO_ERROR) { return err; } if (!SyncFeatures::getInstance().useNativeFenceSync()) { // Bind the new buffer to the GL texture. // // Older devices require the "implicit" synchronization provided // by glEGLImageTargetTexture2DOES, which this method calls. Newer // devices will either call this in Layer::onDraw, or (if it's not // a GL-composited layer) not at all. err = bindTextureImageLocked(); } return 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); } }
status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) { status_t err; if (!nativeBuffer) return BAD_VALUE; if (mCurrentLockedBuffers == mMaxLockedBuffers) { return INVALID_OPERATION; } BufferQueue::BufferItem b; Mutex::Autolock _l(mMutex); err = acquireBufferLocked(&b); if (err != OK) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { return BAD_VALUE; } else { CC_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); return err; } } int buf = b.mBuf; if (b.mFence.get()) { err = b.mFence->waitForever("CpuConsumer::lockNextBuffer"); if (err != OK) { CC_LOGE("Failed to wait for fence of acquired buffer: %s (%d)", strerror(-err), err); return err; } } void *bufferPointer = NULL; android_ycbcr ycbcr = android_ycbcr(); if (mSlots[buf].mGraphicBuffer->getPixelFormat() == HAL_PIXEL_FORMAT_YCbCr_420_888) { err = mSlots[buf].mGraphicBuffer->lockYCbCr( GraphicBuffer::USAGE_SW_READ_OFTEN, b.mCrop, &ycbcr); if (err != OK) { CC_LOGE("Unable to lock YCbCr buffer for CPU reading: %s (%d)", strerror(-err), err); return err; } bufferPointer = ycbcr.y; } else { err = mSlots[buf].mGraphicBuffer->lock( GraphicBuffer::USAGE_SW_READ_OFTEN, b.mCrop, &bufferPointer); if (err != OK) { CC_LOGE("Unable to lock buffer for CPU reading: %s (%d)", strerror(-err), err); return err; } } size_t lockedIdx = 0; for (; lockedIdx < mMaxLockedBuffers; lockedIdx++) { if (mAcquiredBuffers[lockedIdx].mSlot == BufferQueue::INVALID_BUFFER_SLOT) { break; } } assert(lockedIdx < mMaxLockedBuffers); AcquiredBuffer &ab = mAcquiredBuffers.editItemAt(lockedIdx); ab.mSlot = buf; ab.mBufferPointer = bufferPointer; ab.mGraphicBuffer = mSlots[buf].mGraphicBuffer; nativeBuffer->data = reinterpret_cast<uint8_t*>(bufferPointer); nativeBuffer->width = mSlots[buf].mGraphicBuffer->getWidth(); nativeBuffer->height = mSlots[buf].mGraphicBuffer->getHeight(); nativeBuffer->format = mSlots[buf].mGraphicBuffer->getPixelFormat(); nativeBuffer->stride = (ycbcr.y != NULL) ? ycbcr.ystride : mSlots[buf].mGraphicBuffer->getStride(); nativeBuffer->crop = b.mCrop; nativeBuffer->transform = b.mTransform; nativeBuffer->scalingMode = b.mScalingMode; nativeBuffer->timestamp = b.mTimestamp; nativeBuffer->frameNumber = b.mFrameNumber; nativeBuffer->dataCb = reinterpret_cast<uint8_t*>(ycbcr.cb); nativeBuffer->dataCr = reinterpret_cast<uint8_t*>(ycbcr.cr); nativeBuffer->chromaStride = ycbcr.cstride; nativeBuffer->chromaStep = ycbcr.chroma_step; mCurrentLockedBuffers++; return OK; }
status_t CpuConsumer::lockNextBuffer(LockedBuffer *nativeBuffer) { status_t err; if (!nativeBuffer) return BAD_VALUE; if (mCurrentLockedBuffers == mMaxLockedBuffers) { CC_LOGW("Max buffers have been locked (%zd), cannot lock anymore.", mMaxLockedBuffers); return NOT_ENOUGH_DATA; } BufferItem b; Mutex::Autolock _l(mMutex); err = acquireBufferLocked(&b, 0); if (err != OK) { if (err == BufferQueue::NO_BUFFER_AVAILABLE) { return BAD_VALUE; } else { CC_LOGE("Error acquiring buffer: %s (%d)", strerror(err), err); return err; } } int slot = b.mSlot; void *bufferPointer = NULL; android_ycbcr ycbcr = android_ycbcr(); PixelFormat format = mSlots[slot].mGraphicBuffer->getPixelFormat(); PixelFormat flexFormat = format; if (isPossiblyYUV(format)) { if (b.mFence.get()) { err = mSlots[slot].mGraphicBuffer->lockAsyncYCbCr( GraphicBuffer::USAGE_SW_READ_OFTEN, b.mCrop, &ycbcr, b.mFence->dup()); } else { err = mSlots[slot].mGraphicBuffer->lockYCbCr( GraphicBuffer::USAGE_SW_READ_OFTEN, b.mCrop, &ycbcr); } if (err == OK) { bufferPointer = ycbcr.y; flexFormat = HAL_PIXEL_FORMAT_YCbCr_420_888; if (format != HAL_PIXEL_FORMAT_YCbCr_420_888) { CC_LOGV("locking buffer of format %#x as flex YUV", format); } } else if (format == HAL_PIXEL_FORMAT_YCbCr_420_888) { CC_LOGE("Unable to lock YCbCr buffer for CPU reading: %s (%d)", strerror(-err), err); return err; } } if (bufferPointer == NULL) { // not flexible YUV if (b.mFence.get()) { err = mSlots[slot].mGraphicBuffer->lockAsync( GraphicBuffer::USAGE_SW_READ_OFTEN, b.mCrop, &bufferPointer, b.mFence->dup()); } else { err = mSlots[slot].mGraphicBuffer->lock( GraphicBuffer::USAGE_SW_READ_OFTEN, b.mCrop, &bufferPointer); } if (err != OK) { CC_LOGE("Unable to lock buffer for CPU reading: %s (%d)", strerror(-err), err); return err; } } size_t lockedIdx = 0; for (; lockedIdx < static_cast<size_t>(mMaxLockedBuffers); lockedIdx++) { if (mAcquiredBuffers[lockedIdx].mSlot == BufferQueue::INVALID_BUFFER_SLOT) { break; } } assert(lockedIdx < mMaxLockedBuffers); AcquiredBuffer &ab = mAcquiredBuffers.editItemAt(lockedIdx); ab.mSlot = slot; ab.mBufferPointer = bufferPointer; ab.mGraphicBuffer = mSlots[slot].mGraphicBuffer; nativeBuffer->data = reinterpret_cast<uint8_t*>(bufferPointer); nativeBuffer->width = mSlots[slot].mGraphicBuffer->getWidth(); nativeBuffer->height = mSlots[slot].mGraphicBuffer->getHeight(); nativeBuffer->format = format; nativeBuffer->flexFormat = flexFormat; nativeBuffer->stride = (ycbcr.y != NULL) ? static_cast<uint32_t>(ycbcr.ystride) : mSlots[slot].mGraphicBuffer->getStride(); nativeBuffer->crop = b.mCrop; nativeBuffer->transform = b.mTransform; nativeBuffer->scalingMode = b.mScalingMode; nativeBuffer->timestamp = b.mTimestamp; nativeBuffer->dataSpace = b.mDataSpace; nativeBuffer->frameNumber = b.mFrameNumber; nativeBuffer->dataCb = reinterpret_cast<uint8_t*>(ycbcr.cb); nativeBuffer->dataCr = reinterpret_cast<uint8_t*>(ycbcr.cr); nativeBuffer->chromaStride = static_cast<uint32_t>(ycbcr.cstride); nativeBuffer->chromaStep = static_cast<uint32_t>(ycbcr.chroma_step); mCurrentLockedBuffers++; return OK; }
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; }