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;
}
예제 #2
0
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;
}