status_t GonkBufferQueueProducer::dequeueBuffer(int *outSlot, sp<android::Fence> *outFence, bool async, uint32_t width, uint32_t height, uint32_t format, uint32_t usage) { ATRACE_CALL(); { // Autolock scope Mutex::Autolock lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; } // Autolock scope ALOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x", async ? "true" : "false", width, height, format, usage); if ((width && !height) || (!width && height)) { ALOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height); return BAD_VALUE; } status_t returnFlags = NO_ERROR; // Reset slot *outSlot = GonkBufferQueueCore::INVALID_BUFFER_SLOT; bool attachedByConsumer = false; { // Autolock scope Mutex::Autolock lock(mCore->mMutex); mCore->waitWhileAllocatingLocked(); if (format == 0) { format = mCore->mDefaultBufferFormat; } // Enable the usage bits the consumer requested usage |= mCore->mConsumerUsageBits; int found; status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async, &found, &returnFlags); if (status != NO_ERROR) { return status; } // This should not happen if (found == GonkBufferQueueCore::INVALID_BUFFER_SLOT) { ALOGE("dequeueBuffer: no available buffer slots"); return -EBUSY; } *outSlot = found; attachedByConsumer = mSlots[found].mAttachedByConsumer; const bool useDefaultSize = !width && !height; if (useDefaultSize) { width = mCore->mDefaultWidth; height = mCore->mDefaultHeight; } mSlots[found].mBufferState = GonkBufferSlot::DEQUEUED; const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); if ((buffer == NULL) || (static_cast<uint32_t>(buffer->width) != width) || (static_cast<uint32_t>(buffer->height) != height) || (static_cast<uint32_t>(buffer->format) != format) || ((static_cast<uint32_t>(buffer->usage) & usage) != usage)) { mSlots[found].mAcquireCalled = false; mSlots[found].mGraphicBuffer = NULL; mSlots[found].mRequestBufferCalled = false; mSlots[found].mFence = Fence::NO_FENCE; if (mSlots[found].mTextureClient) { mSlots[found].mTextureClient->ClearRecycleCallback(); // release TextureClient in ImageBridge thread TextureClientReleaseTask* task = new TextureClientReleaseTask(mSlots[found].mTextureClient); mSlots[found].mTextureClient = NULL; ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(FROM_HERE, task); } returnFlags |= BUFFER_NEEDS_REALLOCATION; } if (CC_UNLIKELY(mSlots[found].mFence == NULL)) { ALOGE("dequeueBuffer: about to return a NULL fence - " "slot=%d w=%d h=%d format=%u", found, buffer->width, buffer->height, buffer->format); } *outFence = mSlots[found].mFence; mSlots[found].mFence = Fence::NO_FENCE; } // Autolock scope if (returnFlags & BUFFER_NEEDS_REALLOCATION) { RefPtr<GrallocTextureClientOGL> textureClient = new GrallocTextureClientOGL(ImageBridgeChild::GetSingleton(), gfx::SurfaceFormat::UNKNOWN, gfx::BackendType::NONE, TextureFlags::DEALLOCATE_CLIENT); textureClient->SetIsOpaque(true); usage |= GraphicBuffer::USAGE_HW_TEXTURE; bool result = textureClient->AllocateGralloc(IntSize(width, height), format, usage); sp<GraphicBuffer> graphicBuffer = textureClient->GetGraphicBuffer(); if (!result || !graphicBuffer.get()) { ALOGE("dequeueBuffer: failed to alloc gralloc buffer"); return -ENOMEM; } { // Autolock scope Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { ALOGE("dequeueBuffer: GonkBufferQueue has been abandoned"); return NO_INIT; } mSlots[*outSlot].mFrameNumber = UINT32_MAX; mSlots[*outSlot].mGraphicBuffer = graphicBuffer; mSlots[*outSlot].mTextureClient = textureClient; } // Autolock scope } if (attachedByConsumer) { returnFlags |= BUFFER_NEEDS_REALLOCATION; } ALOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x", *outSlot, mSlots[*outSlot].mFrameNumber, mSlots[*outSlot].mGraphicBuffer->handle, returnFlags); return returnFlags; }
status_t GonkBufferQueue::dequeueBuffer(int *outBuf, sp<Fence>* outFence, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { 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); int buf = INVALID_BUFFER_SLOT; { // 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: GonkBufferQueue 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; } buf = found; *outBuf = found; 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].mFence = Fence::NO_FENCE; if (mSlots[buf].mTextureClient) { mSlots[buf].mTextureClient->ClearRecycleCallback(); // release TextureClient in ImageBridge thread TextureClientReleaseTask* task = new TextureClientReleaseTask(mSlots[buf].mTextureClient); mSlots[buf].mTextureClient = NULL; ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(FROM_HERE, task); } returnFlags |= IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION; } *outFence = mSlots[buf].mFence; mSlots[buf].mFence = Fence::NO_FENCE; } // end lock scope sp<GraphicBuffer> graphicBuffer; if (returnFlags & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) { RefPtr<GrallocTextureClientOGL> textureClient = new GrallocTextureClientOGL(ImageBridgeChild::GetSingleton(), gfx::SurfaceFormat::UNKNOWN, gfx::BackendType::NONE, TextureFlags::DEALLOCATE_CLIENT); textureClient->SetIsOpaque(true); usage |= GraphicBuffer::USAGE_HW_TEXTURE; bool result = textureClient->AllocateGralloc(IntSize(w, h), format, usage); sp<GraphicBuffer> graphicBuffer = textureClient->GetGraphicBuffer(); if (!result || !graphicBuffer.get()) { ST_LOGE("dequeueBuffer: failed to alloc gralloc buffer"); return -ENOMEM; } { // Scope for the lock Mutex::Autolock lock(mMutex); if (mAbandoned) { ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); return NO_INIT; } mSlots[buf].mGraphicBuffer = graphicBuffer; mSlots[buf].mTextureClient = textureClient; ST_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf, mSlots[buf].mGraphicBuffer->handle); //mSlots[*outBuf].mGraphicBuffer = graphicBuffer; } } ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", *outBuf, mSlots[*outBuf].mGraphicBuffer->handle, returnFlags); return returnFlags; }