void GonkConsumerBase::abandonLocked() { ALOGV("abandonLocked"); for (int i =0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) { freeBufferLocked(i); } // disconnect from the BufferQueue mConsumer->consumerDisconnect(); mConsumer.clear(); }
void ConsumerBase::abandonLocked() { CB_LOGV("abandonLocked"); for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { freeBufferLocked(i); } // disconnect from the BufferQueue mBufferQueue->consumerDisconnect(); mBufferQueue.clear(); }
void BufferQueue::freeAllBuffersLocked() { ALOGW_IF(!mQueue.isEmpty(), "freeAllBuffersLocked called but mQueue is not empty"); mQueue.clear(); mBufferHasBeenQueued = false; for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { freeBufferLocked(i); } }
void SurfaceTexture::freeAllBuffersLocked() { LOGW_IF(!mQueue.isEmpty(), "freeAllBuffersLocked called but mQueue is not empty"); mCurrentTexture = INVALID_BUFFER_SLOT; for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { freeBufferLocked(i); } #ifdef QCOM_HARDWARE mGraphicBufferAlloc->freeAllGraphicBuffersExcept(-1); #endif }
void BufferQueue::freeAllBuffersExceptHeadLocked() { int head = -1; if (!mQueue.empty()) { Fifo::iterator front(mQueue.begin()); head = *front; } mBufferHasBeenQueued = false; for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { if (i != head) { freeBufferLocked(i); } } }
status_t ConsumerBase::releaseBufferLocked(int slot, EGLDisplay display, EGLSyncKHR eglFence) { CB_LOGV("releaseBufferLocked: slot=%d", slot); status_t err = mBufferQueue->releaseBuffer(slot, display, eglFence, mSlots[slot].mFence); if (err == BufferQueue::STALE_BUFFER_SLOT) { freeBufferLocked(slot); } mSlots[slot].mFence = Fence::NO_FENCE; return err; }
status_t ConsumerBase::detachBuffer(int slot) { CB_LOGV("detachBuffer"); Mutex::Autolock lock(mMutex); status_t result = mConsumer->detachBuffer(slot); if (result != NO_ERROR) { CB_LOGE("Failed to detach buffer: %d", result); return result; } freeBufferLocked(slot); return result; }
void SurfaceTexture::onBuffersReleased() { ST_LOGV("onBuffersReleased"); Mutex::Autolock lock(mMutex); if (mAbandoned) { // Nothing to do if we're already abandoned. return; } uint32_t mask = 0; mBufferQueue->getReleasedBuffers(&mask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { if (mask & (1 << i)) { freeBufferLocked(i); } } }
void SurfaceTexture::abandon() { ST_LOGV("abandon"); Mutex::Autolock lock(mMutex); if (!mAbandoned) { mAbandoned = true; mCurrentTextureBuf.clear(); // destroy all egl buffers for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { freeBufferLocked(i); } // disconnect from the BufferQueue mBufferQueue->consumerDisconnect(); mBufferQueue.clear(); } }
void SurfaceTexture::freeAllBuffersExceptHeadLocked() { LOGW_IF(!mQueue.isEmpty(), "freeAllBuffersExceptCurrentLocked called but mQueue is not empty"); int head = -1; if (!mQueue.empty()) { Fifo::iterator front(mQueue.begin()); head = *front; } mCurrentTexture = INVALID_BUFFER_SLOT; for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { if (i != head) { freeBufferLocked(i); } } #ifdef QCOM_HARDWARE mGraphicBufferAlloc->freeAllGraphicBuffersExcept(head); #endif }
void GonkConsumerBase::onBuffersReleased() { Mutex::Autolock lock(mMutex); ALOGV("onBuffersReleased"); if (mAbandoned) { // Nothing to do if we're already abandoned. return; } uint32_t mask = 0; mConsumer->getReleasedBuffers(&mask); for (int i = 0; i < GonkBufferQueue::NUM_BUFFER_SLOTS; i++) { if (mask & (1 << i)) { freeBufferLocked(i); } } }
status_t GonkConsumerBase::releaseBufferLocked(int slot, const sp<GraphicBuffer> graphicBuffer) { // If consumer no longer tracks this graphicBuffer (we received a new // buffer on the same slot), the buffer producer is definitely no longer // tracking it. if (!stillTracking(slot, graphicBuffer)) { return OK; } ALOGV("releaseBufferLocked: slot=%d/%llu", slot, mSlots[slot].mFrameNumber); status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, mSlots[slot].mFence); if (err == GonkBufferQueue::STALE_BUFFER_SLOT) { freeBufferLocked(slot); } mSlots[slot].mFence = Fence::NO_FENCE; return err; }
status_t ConsumerBase::releaseBufferLocked( int slot, const sp<GraphicBuffer> graphicBuffer, EGLDisplay display, EGLSyncKHR eglFence) { // If consumer no longer tracks this graphicBuffer (we received a new // buffer on the same slot), the buffer producer is definitely no longer // tracking it. if (!stillTracking(slot, graphicBuffer)) { return OK; } CB_LOGV("releaseBufferLocked: slot=%d/%" PRIu64, slot, mSlots[slot].mFrameNumber); status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber, display, eglFence, mSlots[slot].mFence); if (err == IGraphicBufferConsumer::STALE_BUFFER_SLOT) { freeBufferLocked(slot); } mSlots[slot].mFence = Fence::NO_FENCE; return err; }
void ConsumerBase::onBuffersReleased() { Mutex::Autolock lock(mMutex); CB_LOGV("onBuffersReleased"); if (mAbandoned) { // Nothing to do if we're already abandoned. return; } #ifndef MTK_DEFAULT_AOSP // force conversion here for last buffer forceAuxConversionLocked(); #endif uint32_t mask = 0; mConsumer->getReleasedBuffers(&mask); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { if (mask & (1 << i)) { freeBufferLocked(i); } } }
void BufferQueueCoreBF::freeAllBuffersLocked() { mBufferHasBeenQueued = false; for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { freeBufferLocked(s); } }
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 BufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { ATRACE_CALL(); ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage); if ((w && !h) || (!w && h)) { ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h); return BAD_VALUE; } status_t returnFlags(OK); EGLDisplay dpy = EGL_NO_DISPLAY; EGLSyncKHR eglFence = EGL_NO_SYNC_KHR; { // Scope for the lock Mutex::Autolock lock(mMutex); if (format == 0) { format = mDefaultBufferFormat; } // turn on usage bits the consumer requested usage |= mConsumerUsageBits; int found = -1; int dequeuedCount = 0; bool tryAgain = true; while (tryAgain) { if (mAbandoned) { ST_LOGE("dequeueBuffer: BufferQueue has been abandoned!"); return NO_INIT; } const int maxBufferCount = getMaxBufferCountLocked(); // Free up any buffers that are in slots beyond the max buffer // count. for (int i = maxBufferCount; i < NUM_BUFFER_SLOTS; i++) { assert(mSlots[i].mBufferState == BufferSlot::FREE); if (mSlots[i].mGraphicBuffer != NULL) { freeBufferLocked(i); returnFlags |= IGraphicBufferProducer::RELEASE_ALL_BUFFERS; } } // look for a free buffer to give to the client found = INVALID_BUFFER_SLOT; dequeuedCount = 0; for (int i = 0; i < maxBufferCount; i++) { const int state = mSlots[i].mBufferState; if (state == BufferSlot::DEQUEUED) { dequeuedCount++; } if (state == BufferSlot::FREE) { /* We return the oldest of the free buffers to avoid * stalling the producer if possible. This is because * the consumer may still have pending reads of the * buffers in flight. */ if ((found < 0) || mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) { found = i; } } } // clients are not allowed to dequeue more than one buffer // if they didn't set a buffer count. if (!mOverrideMaxBufferCount && dequeuedCount) { ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " "setting the buffer count"); return -EINVAL; } // See whether a buffer has been queued since the last // setBufferCount so we know whether to perform the min undequeued // buffers check below. if (mBufferHasBeenQueued) { // make sure the client is not trying to dequeue more buffers // than allowed. const int newUndequeuedCount = maxBufferCount - (dequeuedCount+1); const int minUndequeuedCount = getMinUndequeuedBufferCountLocked(); if (newUndequeuedCount < minUndequeuedCount) { ST_LOGE("dequeueBuffer: min undequeued buffer count (%d) " "exceeded (dequeued=%d undequeudCount=%d)", minUndequeuedCount, dequeuedCount, newUndequeuedCount); return -EBUSY; } } // If no buffer is found, wait for a buffer to be released or for // the max buffer count to change. tryAgain = found == INVALID_BUFFER_SLOT; if (tryAgain) { mDequeueCondition.wait(mMutex); } } if (found == INVALID_BUFFER_SLOT) { // This should not happen. ST_LOGE("dequeueBuffer: no available buffer slots"); return -EBUSY; } const int buf = found; *outBuf = found; ATRACE_BUFFER_INDEX(buf); const bool useDefaultSize = !w && !h; if (useDefaultSize) { // use the default size w = mDefaultWidth; h = mDefaultHeight; } mSlots[buf].mBufferState = BufferSlot::DEQUEUED; const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); if ((buffer == NULL) || (uint32_t(buffer->width) != w) || (uint32_t(buffer->height) != h) || (uint32_t(buffer->format) != format) || ((uint32_t(buffer->usage) & usage) != usage)) { mSlots[buf].mAcquireCalled = false; mSlots[buf].mGraphicBuffer = NULL; mSlots[buf].mRequestBufferCalled = false; mSlots[buf].mEglFence = EGL_NO_SYNC_KHR; mSlots[buf].mFence = Fence::NO_FENCE; mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; returnFlags |= IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION; } dpy = mSlots[buf].mEglDisplay; eglFence = mSlots[buf].mEglFence; *outFence = mSlots[buf].mFence; mSlots[buf].mEglFence = EGL_NO_SYNC_KHR; mSlots[buf].mFence = Fence::NO_FENCE; } // end lock scope if (returnFlags & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { status_t error; sp<GraphicBuffer> graphicBuffer( mGraphicBufferAlloc->createGraphicBuffer( w, h, format, usage, &error)); if (graphicBuffer == 0) { ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " "failed"); return error; } { // Scope for the lock Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("dequeueBuffer: BufferQueue has been abandoned!"); return NO_INIT; } mSlots[*outBuf].mGraphicBuffer = graphicBuffer; } } if (eglFence != EGL_NO_SYNC_KHR) { EGLint result = eglClientWaitSyncKHR(dpy, eglFence, 0, 1000000000); // If something goes wrong, log the error, but return the buffer without // synchronizing access to it. It's too late at this point to abort the // dequeue operation. if (result == EGL_FALSE) { ST_LOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError()); } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { ST_LOGE("dequeueBuffer: timeout waiting for fence"); } eglDestroySyncKHR(dpy, eglFence); } ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); return returnFlags; }