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
              RefPtr<TextureClientReleaseTask> task =
                MakeAndAddRef<TextureClientReleaseTask>(mSlots[buf].mTextureClient);
              mSlots[buf].mTextureClient = NULL;
              ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(task.forget());
            }
            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) {

        usage |= GraphicBuffer::USAGE_HW_TEXTURE;
        RefPtr<LayersIPCChannel> allocator = ImageBridgeChild::GetSingleton();
        GrallocTextureData* texData = GrallocTextureData::Create(IntSize(w,h), format,
                                                                 gfx::BackendType::NONE, usage,
                                                                 allocator);
        if (!texData) {
            ST_LOGE("dequeueBuffer: failed to alloc gralloc buffer");
            return -ENOMEM;
        }
        RefPtr<TextureClient> textureClient = new TextureClient(texData, TextureFlags::RECYCLE | TextureFlags::DEALLOCATE_CLIENT, allocator);
        sp<GraphicBuffer> graphicBuffer = texData->GetGraphicBuffer();

        { // 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;
}
Example #2
0
status_t GonkNativeWindow::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
        uint32_t format, uint32_t usage)
{
    if ((w && !h) || (!w && h)) {
        CNW_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
        return BAD_VALUE;
    }

    status_t returnFlags(OK);
    bool updateFormat = false;
    bool alloc = false;
    int buf = INVALID_BUFFER_SLOT;

    {
        Mutex::Autolock lock(mMutex);

        int found = -1;
        int dequeuedCount = 0;
        int renderingCount = 0;
        bool tryAgain = true;

        CNW_LOGD("dequeueBuffer: E");
        while (tryAgain) {
            if (mAbandoned) {
                CNW_LOGE("dequeueBuffer: GonkNativeWindow has been abandoned!");
                return NO_INIT;
            }
            // look for a free buffer to give to the client
            found = INVALID_BUFFER_SLOT;
            dequeuedCount = 0;
            renderingCount = 0;
            for (int i = 0; i < mBufferCount; i++) {
                const int state = mSlots[i].mBufferState;
                switch (state) {
                    case BufferSlot::DEQUEUED:
                        CNW_LOGD("dequeueBuffer: slot %d is DEQUEUED\n", i);
                        dequeuedCount++;
                        break;

                    case BufferSlot::RENDERING:
                        CNW_LOGD("dequeueBuffer: slot %d is RENDERING\n", i);
                        renderingCount++;
                        break;

                    case BufferSlot::FREE:
                        CNW_LOGD("dequeueBuffer: slot %d is FREE\n", i);
                        /* 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;
                        }
                        break;

                    default:
                        CNW_LOGD("dequeueBuffer: slot %d is %d\n", i, state);
                        break;
                }
            }

            // See whether a buffer has been in RENDERING state since the last
            // setBufferCount so we know whether to perform the
            // MIN_UNDEQUEUED_BUFFERS check below.
            if (renderingCount > 0) {
                // make sure the client is not trying to dequeue more buffers
                // than allowed.
                const int avail = mBufferCount - (dequeuedCount + 1);
                if (avail < MIN_UNDEQUEUED_BUFFERS) {
                    CNW_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded "
                            "(dequeued=%d)",
                            MIN_UNDEQUEUED_BUFFERS,
                            dequeuedCount);
                    return -EBUSY;
                }
            }

            // we're in synchronous mode and didn't find a buffer, we need to
            // wait for some buffers to be consumed
            tryAgain = (found == INVALID_BUFFER_SLOT);
            if (tryAgain) {
                CNW_LOGD("dequeueBuffer: Try again");
                mDequeueCondition.wait(mMutex);
                CNW_LOGD("dequeueBuffer: Now");
            }
        }

        if (found == INVALID_BUFFER_SLOT) {
            // This should not happen.
            CNW_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;
        }

        updateFormat = (format != 0);
        if (!updateFormat) {
            // keep the current (or default) format
            format = mPixelFormat;
        }

        mSlots[buf].mBufferState = BufferSlot::DEQUEUED;

        const sp<GraphicBuffer>& gbuf(mSlots[buf].mGraphicBuffer);
        if ((gbuf == NULL) ||
           ((uint32_t(gbuf->width)  != w) ||
            (uint32_t(gbuf->height) != h) ||
            (uint32_t(gbuf->format) != format) ||
            ((uint32_t(gbuf->usage) & usage) != usage))) {
            mSlots[buf].mGraphicBuffer = NULL;
            mSlots[buf].mRequestBufferCalled = false;
            if (mSlots[buf].mTextureClient) {
                mSlots[buf].mTextureClient->ClearRecycleCallback();
                // release TextureClient in ImageBridge thread
                RefPtr<TextureClientReleaseTask> task =
                  MakeAndAddRef<TextureClientReleaseTask>(mSlots[buf].mTextureClient);
                mSlots[buf].mTextureClient = NULL;
                ImageBridgeChild::GetSingleton()->GetMessageLoop()->PostTask(task.forget());
            }
            alloc = true;
        }
    }  // end lock scope

    if (alloc) {
        ClientIPCAllocator* allocator = ImageBridgeChild::GetSingleton();
        usage |= GraphicBuffer::USAGE_HW_TEXTURE;
        GrallocTextureData* texData = GrallocTextureData::Create(IntSize(w, h), format,
                                                                 gfx::BackendType::NONE, usage,
                                                                 allocator);
        if (!texData) {
            return -ENOMEM;
        }

        RefPtr<TextureClient> textureClient = new TextureClient(texData, TextureFlags::DEALLOCATE_CLIENT, allocator);

        { // Scope for the lock
            Mutex::Autolock lock(mMutex);

            if (mAbandoned) {
                CNW_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!");
                return NO_INIT;
            }

            if (updateFormat) {
                mPixelFormat = format;
            }
            mSlots[buf].mGraphicBuffer = texData->GetGraphicBuffer();
            mSlots[buf].mTextureClient = textureClient;

            returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;

            CNW_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf,
                    mSlots[buf].mGraphicBuffer->handle);
        }
    }

    CNW_LOGD("dequeueBuffer: returning slot=%d buf=%p ", buf,
            mSlots[buf].mGraphicBuffer->handle );

    CNW_LOGD("dequeueBuffer: X");
    return returnFlags;
}
/*static*/ UniquePtr<SharedSurface_Gralloc>
SharedSurface_Gralloc::Create(GLContext* prodGL,
                              const GLFormats& formats,
                              const gfx::IntSize& size,
                              bool hasAlpha,
                              layers::TextureFlags flags,
                              LayersIPCChannel* allocator)
{
    GLLibraryEGL* egl = &sEGLLibrary;
    MOZ_ASSERT(egl);

    UniquePtr<SharedSurface_Gralloc> ret;

    DEBUG_PRINT("SharedSurface_Gralloc::Create -------\n");

    if (!HasExtensions(egl, prodGL))
        return Move(ret);

    gfxContentType type = hasAlpha ? gfxContentType::COLOR_ALPHA
                                   : gfxContentType::COLOR;

    GrallocTextureData* texData = GrallocTextureData::CreateForGLRendering(
        size, gfxPlatform::GetPlatform()->Optimal2DFormatForContent(type), allocator
    );

    if (!texData) {
        return Move(ret);
    }

    RefPtr<TextureClient> grallocTC = new TextureClient(texData, flags, allocator);

    sp<GraphicBuffer> buffer = texData->GetGraphicBuffer();

    EGLDisplay display = egl->Display();
    EGLClientBuffer clientBuffer = buffer->getNativeBuffer();
    EGLint attrs[] = {
        LOCAL_EGL_NONE, LOCAL_EGL_NONE
    };
    EGLImage image = egl->fCreateImage(display,
                                       EGL_NO_CONTEXT,
                                       LOCAL_EGL_NATIVE_BUFFER_ANDROID,
                                       clientBuffer, attrs);
    if (!image) {
        return Move(ret);
    }

    prodGL->MakeCurrent();
    GLuint prodTex = 0;
    prodGL->fGenTextures(1, &prodTex);
    ScopedBindTexture autoTex(prodGL, prodTex);

    prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
    prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
    prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
    prodGL->fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);

    prodGL->fEGLImageTargetTexture2D(LOCAL_GL_TEXTURE_2D, image);

    egl->fDestroyImage(display, image);

    ret.reset( new SharedSurface_Gralloc(prodGL, size, hasAlpha, egl,
                                         allocator, grallocTC,
                                         prodTex) );

    DEBUG_PRINT("SharedSurface_Gralloc::Create: success -- surface %p,"
                " GraphicBuffer %p.\n",
                ret.get(), buffer.get());

    return Move(ret);
}
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) {
        ISurfaceAllocator* allocator = ImageBridgeChild::GetSingleton();
        usage |= GraphicBuffer::USAGE_HW_TEXTURE;
        GrallocTextureData* texData = GrallocTextureData::Create(IntSize(width,height), format,
                                                                 gfx::BackendType::NONE,
                                                                 usage, allocator);
        if (!texData) {
            ALOGE("dequeueBuffer: failed to alloc gralloc buffer");
            return -ENOMEM;
        }
        RefPtr<TextureClient> textureClient = TextureClient::CreateWithData(
            texData, TextureFlags::DEALLOCATE_CLIENT, allocator);

        sp<GraphicBuffer> graphicBuffer = texData->GetGraphicBuffer();

        { // 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;
}