single buffer mode for BufferQueue

Bug: 9891035
Change-Id: Id1ab5f911a6dc4c1d8235e65775b3d3635231ad4
diff --git a/include/gui/BufferQueue.h b/include/gui/BufferQueue.h
index 628678f..cfce40d 100644
--- a/include/gui/BufferQueue.h
+++ b/include/gui/BufferQueue.h
@@ -340,6 +340,13 @@
     // 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 @@
     // 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 @@
     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.
diff --git a/include/gui/GLConsumer.h b/include/gui/GLConsumer.h
index 1df5b42..ac4a832 100644
--- a/include/gui/GLConsumer.h
+++ b/include/gui/GLConsumer.h
@@ -98,6 +98,13 @@
     // 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 @@
     // 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 @@
     // 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;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp
index 95ba095..45488ff 100644
--- a/libs/gui/BufferQueue.cpp
+++ b/libs/gui/BufferQueue.cpp
@@ -72,6 +72,7 @@
     mOverrideMaxBufferCount(0),
     mConsumerControlledByApp(false),
     mDequeueBufferCannotBlock(false),
+    mUseAsyncBuffer(true),
     mConnectedApi(NO_CONNECTED_API),
     mAbandoned(false),
     mFrameCounter(0),
@@ -100,7 +101,8 @@
 }
 
 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 @@
     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 @@
 }
 
 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 {
diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp
index bd1671d..b8a3d28 100644
--- a/libs/gui/GLConsumer.cpp
+++ b/libs/gui/GLConsumer.cpp
@@ -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 @@
 #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 @@
     }
 
     // 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 @@
     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 @@
     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 @@
     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 @@
         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 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 [?]
diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
index bd2f5f3..419b81c 100644
--- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
@@ -108,7 +108,7 @@
         // 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;
         }
diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
index b181b60..e95e057 100644
--- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp
+++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp
@@ -76,7 +76,7 @@
     }
 
     // Release the previous buffer.
-    err = releaseAndUpdateLocked(item);
+    err = updateAndReleaseLocked(item);
     if (err != NO_ERROR) {
         return err;
     }