Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad678e18 authored by Mathias Agopian's avatar Mathias Agopian
Browse files

single buffer mode for BufferQueue

Bug: 9891035
Change-Id: Id1ab5f911a6dc4c1d8235e65775b3d3635231ad4
parent bf5b849e
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -340,6 +340,13 @@ public:
    // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
    status_t setDefaultMaxBufferCount(int bufferCount);

    // disableAsyncBuffer disables the extra buffer used in async mode
    // (when both producer and consumer have set their "isControlledByApp"
    // flag) and has dequeueBuffer() return WOULD_BLOCK instead.
    //
    // This can only be called before consumerConnect().
    status_t disableAsyncBuffer();

    // setMaxAcquiredBufferCount sets the maximum number of buffers that can
    // be acquired by the consumer at one time (default 1).  This call will
    // fail if a producer is connected to the BufferQueue.
@@ -364,6 +371,7 @@ public:
    // NATIVE_WINDOW_TRANSFORM_ROT_90.  The default is 0 (no transform).
    status_t setTransformHint(uint32_t hint);


private:
    // freeBufferLocked frees the GraphicBuffer and sync resources for the
    // given slot.
@@ -559,10 +567,14 @@ private:
    bool mConsumerControlledByApp;

    // mDequeueBufferCannotBlock whether dequeueBuffer() isn't allowed to block.
    // this flag is set durring connect() when both consumer and producer are controlled
    // this flag is set during connect() when both consumer and producer are controlled
    // by the application.
    bool mDequeueBufferCannotBlock;

    // mUseAsyncBuffer whether an extra buffer is used in async mode to prevent
    // dequeueBuffer() from ever blocking.
    bool mUseAsyncBuffer;

    // mConnectedApi indicates the producer API that is currently connected
    // to this BufferQueue.  It defaults to NO_CONNECTED_API (= 0), and gets
    // updated by the connect and disconnect methods.
+12 −1
Original line number Diff line number Diff line
@@ -98,6 +98,13 @@ public:
    // This calls doGLFenceWait to ensure proper synchronization.
    status_t updateTexImage();

    // releaseTexImage releases the texture acquired in updateTexImage().
    // This is intended to be used in single buffer mode.
    //
    // This call may only be made while the OpenGL ES context to which the
    // target texture belongs is bound to the calling thread.
    status_t releaseTexImage();

    // setReleaseFence stores a fence that will signal when the current buffer
    // is no longer being read. This fence will be returned to the producer
    // when the current buffer is released by updateTexImage(). Multiple
@@ -251,7 +258,7 @@ protected:
    // This releases the buffer in the slot referenced by mCurrentTexture,
    // then updates state to refer to the BufferItem, which must be a
    // newly-acquired buffer.
    status_t releaseAndUpdateLocked(const BufferQueue::BufferItem& item);
    status_t updateAndReleaseLocked(const BufferQueue::BufferItem& item);

    // Binds mTexName and the current buffer to mTexTarget.  Uses
    // mCurrentTexture if it's set, mCurrentTextureBuf if not.  If the
@@ -416,6 +423,10 @@ private:
    // It is set to false by detachFromContext, and then set to true again by
    // attachToContext.
    bool mAttached;

    // mReleasedTexImageBuffer is a dummy buffer used when in single buffer
    // mode and releaseTexImage() has been called
    sp<GraphicBuffer> mReleasedTexImageBuffer;
};

// ----------------------------------------------------------------------------
+25 −3
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ BufferQueue::BufferQueue(const sp<IGraphicBufferAlloc>& allocator) :
    mOverrideMaxBufferCount(0),
    mConsumerControlledByApp(false),
    mDequeueBufferCannotBlock(false),
    mUseAsyncBuffer(true),
    mConnectedApi(NO_CONNECTED_API),
    mAbandoned(false),
    mFrameCounter(0),
@@ -100,7 +101,8 @@ BufferQueue::~BufferQueue() {
}

status_t BufferQueue::setDefaultMaxBufferCountLocked(int count) {
    if (count < 2 || count > NUM_BUFFER_SLOTS)
    const int minBufferCount = mUseAsyncBuffer ? 2 : 1;
    if (count < minBufferCount || count > NUM_BUFFER_SLOTS)
        return BAD_VALUE;

    mDefaultMaxBufferCount = count;
@@ -1033,6 +1035,17 @@ status_t BufferQueue::setDefaultMaxBufferCount(int bufferCount) {
    return setDefaultMaxBufferCountLocked(bufferCount);
}

status_t BufferQueue::disableAsyncBuffer() {
    ATRACE_CALL();
    Mutex::Autolock lock(mMutex);
    if (mConsumerListener != NULL) {
        ST_LOGE("disableAsyncBuffer: consumer already connected!");
        return INVALID_OPERATION;
    }
    mUseAsyncBuffer = false;
    return NO_ERROR;
}

status_t BufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
    ATRACE_CALL();
    Mutex::Autolock lock(mMutex);
@@ -1049,8 +1062,17 @@ status_t BufferQueue::setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
}

int BufferQueue::getMinUndequeuedBufferCount(bool async) const {
    return (mDequeueBufferCannotBlock || async) ?
                mMaxAcquiredBufferCount+1 : mMaxAcquiredBufferCount;
    // if dequeueBuffer is allowed to error out, we don't have to
    // add an extra buffer.
    if (!mUseAsyncBuffer)
        return mMaxAcquiredBufferCount;

    // we're in async mode, or we want to prevent the app to
    // deadlock itself, we throw-in an extra buffer to guarantee it.
    if (mDequeueBufferCannotBlock || async)
        return mMaxAcquiredBufferCount+1;

    return mMaxAcquiredBufferCount;
}

int BufferQueue::getMinMaxBufferCountLocked(bool async) const {
+88 −7
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <EGL/eglext.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#include <cutils/compiler.h>

#include <hardware/hardware.h>

@@ -49,6 +50,12 @@ namespace android {
#define ST_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
#define ST_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)

static const struct {
    size_t width, height;
    char const* bits;
} kDebugData = { 11, 8,
    "__X_____X_____X___X_____XXXXXXX___XX_XXX_XX_XXXXXXXXXXXX_XXXXXXX_XX_X_____X_X___XX_XX___" };

// Transform matrices
static float mtxIdentity[16] = {
    1, 0, 0, 0,
@@ -154,7 +161,7 @@ status_t GLConsumer::updateTexImage() {
    }

    // Release the previous buffer.
    err = releaseAndUpdateLocked(item);
    err = updateAndReleaseLocked(item);
    if (err != NO_ERROR) {
        // We always bind the texture.
        glBindTexture(mTexTarget, mTexName);
@@ -165,6 +172,80 @@ status_t GLConsumer::updateTexImage() {
    return bindTextureImageLocked();
}


status_t GLConsumer::releaseTexImage() {
    ATRACE_CALL();
    ST_LOGV("releaseTexImage");
    Mutex::Autolock lock(mMutex);

    if (mAbandoned) {
        ST_LOGE("releaseTexImage: 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;
    }

    // Update the GLConsumer state.
    int buf = mCurrentTexture;
    if (buf != BufferQueue::INVALID_BUFFER_SLOT) {

        ST_LOGV("releaseTexImage:(slot=%d", buf);

        // Do whatever sync ops we need to do before releasing the slot.
        err = syncForReleaseLocked(mEglDisplay);
        if (err != NO_ERROR) {
            ST_LOGE("syncForReleaseLocked failed (slot=%d), err=%d", buf, err);
            return err;
        }

        err = releaseBufferLocked(buf, mSlots[buf].mGraphicBuffer,
                mEglDisplay, EGL_NO_SYNC_KHR);
        if (err < NO_ERROR) {
            ST_LOGE("releaseTexImage: failed to release buffer: %s (%d)",
                    strerror(-err), err);
            return err;
        }

        if (CC_UNLIKELY(mReleasedTexImageBuffer == NULL)) {
            // The first time, create the debug texture in case the application
            // continues to use it.
            sp<GraphicBuffer> buffer = new GraphicBuffer(11, 8, PIXEL_FORMAT_RGBA_8888,
                    GraphicBuffer::USAGE_SW_WRITE_RARELY);
            uint32_t* bits;
            buffer->lock(GraphicBuffer::USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&bits));
            size_t w = buffer->getStride();
            size_t h = buffer->getHeight();
            memset(bits, 0, w*h*4);
            for (size_t y=0 ; y<kDebugData.height ; y++) {
                for (size_t x=0 ; x<kDebugData.width ; x++) {
                    bits[x] = (kDebugData.bits[y*11+x] == 'X') ? 0xFF000000 : 0xFFFFFFFF;
                }
                bits += w;
            }
            buffer->unlock();
            mReleasedTexImageBuffer = buffer;
        }

        mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT;
        mCurrentTextureBuf = mReleasedTexImageBuffer;
        mCurrentCrop.makeInvalid();
        mCurrentTransform = 0;
        mCurrentScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE;
        mCurrentTimestamp = 0;
        mCurrentFence = Fence::NO_FENCE;

        // bind a dummy texture
        glBindTexture(mTexTarget, mTexName);
        bindUnslottedBufferLocked(mEglDisplay);
    }

    return NO_ERROR;
}

status_t GLConsumer::acquireBufferLocked(BufferQueue::BufferItem *item,
        nsecs_t presentWhen) {
    status_t err = ConsumerBase::acquireBufferLocked(item, presentWhen);
@@ -202,12 +283,12 @@ status_t GLConsumer::releaseBufferLocked(int buf,
    return err;
}

status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item)
status_t GLConsumer::updateAndReleaseLocked(const BufferQueue::BufferItem& item)
{
    status_t err = NO_ERROR;

    if (!mAttached) {
        ST_LOGE("releaseAndUpdate: GLConsumer is not attached to an OpenGL "
        ST_LOGE("updateAndRelease: GLConsumer is not attached to an OpenGL "
                "ES context");
        return INVALID_OPERATION;
    }
@@ -230,7 +311,7 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item)
    if (mEglSlots[buf].mEglImage == EGL_NO_IMAGE_KHR) {
        EGLImageKHR image = createImage(mEglDisplay, mSlots[buf].mGraphicBuffer);
        if (image == EGL_NO_IMAGE_KHR) {
            ST_LOGW("releaseAndUpdate: unable to createImage on display=%p slot=%d",
            ST_LOGW("updateAndRelease: unable to createImage on display=%p slot=%d",
                  mEglDisplay, buf);
            return UNKNOWN_ERROR;
        }
@@ -249,7 +330,7 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item)
        return err;
    }

    ST_LOGV("releaseAndUpdate: (slot=%d buf=%p) -> (slot=%d buf=%p)",
    ST_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)",
            mCurrentTexture,
            mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0,
            buf, mSlots[buf].mGraphicBuffer->handle);
@@ -259,8 +340,8 @@ status_t GLConsumer::releaseAndUpdateLocked(const BufferQueue::BufferItem& item)
        status_t status = releaseBufferLocked(
                mCurrentTexture, mCurrentTextureBuf, mEglDisplay,
                mEglSlots[mCurrentTexture].mEglFence);
        if (status != NO_ERROR && status != BufferQueue::STALE_BUFFER_SLOT) {
            ST_LOGE("releaseAndUpdate: failed to release buffer: %s (%d)",
        if (status < NO_ERROR) {
            ST_LOGE("updateAndRelease: failed to release buffer: %s (%d)",
                   strerror(-status), status);
            err = status;
            // keep going, with error raised [?]
+1 −1
Original line number Diff line number Diff line
@@ -108,7 +108,7 @@ status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>&
        // Release the previous buffer.
        err = releaseBufferLocked(mCurrentBufferSlot, mCurrentBuffer,
                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR);
        if (err != NO_ERROR && err != BufferQueue::STALE_BUFFER_SLOT) {
        if (err < NO_ERROR) {
            ALOGE("error releasing buffer: %s (%d)", strerror(-err), err);
            return err;
        }
Loading