OSDN Git Service

BQ: Add support for single buffer mode
authorPablo Ceballos <pceballos@google.com>
Wed, 7 Oct 2015 22:05:45 +0000 (15:05 -0700)
committerPablo Ceballos <pceballos@google.com>
Tue, 3 Nov 2015 20:03:51 +0000 (12:03 -0800)
- Adds a single buffer mode to BufferQueue. In this mode designate the
  first dequeued buffer as the shared buffer. All calls to dequeue()
  and acquire() will then return the shared buffer, allowing the
  producer and consumer to share it.
- Modify the buffer slot state tracking. Add a new SHARED state for
  the shared buffer in single buffer mode. Also track how many times
  a buffer has been dequeued/queued/acquired as it's possible for a
  shared buffer to be both dequeued and acquired at the same time, or
  dequeued/acquired multiple times. This tracking is needed to know
  when to drop the buffer out of the SHARED state after single buffer
  mode has been disabled.
- Add plumbing for enabling/disabling single buffer mode from Surface.

Bug 24940410

Change-Id: I3fc550c74bacb5523c049a227111356257386853

20 files changed:
include/gui/BufferQueueCore.h
include/gui/BufferQueueProducer.h
include/gui/BufferSlot.h
include/gui/IGraphicBufferProducer.h
include/gui/Surface.h
include/private/gui/LayerState.h
libs/gui/BufferItem.cpp
libs/gui/BufferQueueConsumer.cpp
libs/gui/BufferQueueCore.cpp
libs/gui/BufferQueueProducer.cpp
libs/gui/BufferSlot.cpp
libs/gui/GLConsumer.cpp
libs/gui/IGraphicBufferProducer.cpp
libs/gui/Surface.cpp
libs/gui/tests/BufferQueue_test.cpp
opengl/libs/EGL/eglApi.cpp
services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
services/surfaceflinger/MonitoredProducer.cpp
services/surfaceflinger/MonitoredProducer.h

index 36cd238..fbd5114 100644 (file)
@@ -107,10 +107,10 @@ private:
 
     // freeBufferLocked frees the GraphicBuffer and sync resources for the
     // given slot.
-    void freeBufferLocked(int slot);
+    void freeBufferLocked(int slot, bool validate = true);
 
     // freeAllBuffersLocked frees the GraphicBuffer and sync resources for
-    // all slots.
+    // all slots, even if they're currently dequeued, queued, or acquired.
     void freeAllBuffersLocked();
 
     // stillTracking returns true iff the buffer item is still being tracked
@@ -271,6 +271,32 @@ private:
     // enqueue buffers without blocking.
     bool mAsyncMode;
 
+    // mSingleBufferMode indicates whether or not single buffer mode is enabled.
+    // In single buffer mode, the last buffer that was dequeued is cached and
+    // returned to all calls to dequeueBuffer and acquireBuffer. This allows the
+    // consumer and producer to access the same buffer simultaneously.
+    bool mSingleBufferMode;
+
+    // When single buffer mode is enabled, this tracks which slot contains the
+    // shared buffer.
+    int mSingleBufferSlot;
+
+    // Cached data about the shared buffer in single buffer mode
+    struct SingleBufferCache {
+        SingleBufferCache(Rect _crop, uint32_t _transform, int _scalingMode,
+                android_dataspace _dataspace)
+        : crop(_crop),
+          transform(_transform),
+          scalingMode(_scalingMode),
+          dataspace(_dataspace) {
+        };
+
+        Rect crop;
+        uint32_t transform;
+        uint32_t scalingMode;
+        android_dataspace dataspace;
+    } mSingleBufferCache;
+
 }; // class BufferQueueCore
 
 } // namespace android
index 322f2ec..5fe5ce0 100644 (file)
@@ -173,6 +173,9 @@ public:
     // See IGraphicBufferProducer::getNextFrameNumber
     virtual uint64_t getNextFrameNumber() const override;
 
+    // See IGraphicBufferProducer::setSingleBufferMode
+    virtual status_t setSingleBufferMode(bool singleBufferMode);
+
 private:
     // This is required by the IBinder::DeathRecipient interface
     virtual void binderDied(const wp<IBinder>& who);
index 6085e11..17a654a 100644 (file)
@@ -29,11 +29,153 @@ namespace android {
 
 class Fence;
 
+// BufferState tracks the states in which a buffer slot can be.
+struct BufferState {
+
+    // All slots are initially FREE (not dequeued, queued, acquired, or shared).
+    BufferState()
+    : mDequeueCount(0),
+      mQueueCount(0),
+      mAcquireCount(0),
+      mShared(false) {
+    }
+
+    uint32_t mDequeueCount;
+    uint32_t mQueueCount;
+    uint32_t mAcquireCount;
+    bool mShared;
+
+    // A buffer can be in one of five states, represented as below:
+    //
+    //         | mShared | mDequeueCount | mQueueCount | mAcquireCount |
+    // --------|---------|---------------|-------------|---------------|
+    // FREE    |  false  |       0       |      0      |       0       |
+    // DEQUEUED|  false  |       1       |      0      |       0       |
+    // QUEUED  |  false  |       0       |      1      |       0       |
+    // ACQUIRED|  false  |       0       |      0      |       1       |
+    // SHARED  |  true   |      any      |     any     |      any      |
+    //
+    // FREE indicates that the buffer is available to be dequeued by the
+    // producer. The slot is "owned" by BufferQueue. It transitions to DEQUEUED
+    // when dequeueBuffer is called.
+    //
+    // DEQUEUED indicates that the buffer has been dequeued by the producer, but
+    // has not yet been queued or canceled. The producer may modify the
+    // buffer's contents as soon as the associated release fence is signaled.
+    // The slot is "owned" by the producer. It can transition to QUEUED (via
+    // queueBuffer or attachBuffer) or back to FREE (via cancelBuffer or
+    // detachBuffer).
+    //
+    // QUEUED indicates that the buffer has been filled by the producer and
+    // queued for use by the consumer. The buffer contents may continue to be
+    // modified for a finite time, so the contents must not be accessed until
+    // the associated fence is signaled. The slot is "owned" by BufferQueue. It
+    // can transition to ACQUIRED (via acquireBuffer) or to FREE (if another
+    // buffer is queued in asynchronous mode).
+    //
+    // ACQUIRED indicates that the buffer has been acquired by the consumer. As
+    // with QUEUED, the contents must not be accessed by the consumer until the
+    // acquire fence is signaled. The slot is "owned" by the consumer. It
+    // transitions to FREE when releaseBuffer (or detachBuffer) is called. A
+    // detached buffer can also enter the ACQUIRED state via attachBuffer.
+    //
+    // SHARED indicates that this buffer is being used in single-buffer
+    // mode. It can be in any combination of the other states at the same time,
+    // except for FREE (since that excludes being in any other state). It can
+    // also be dequeued, queued, or acquired multiple times.
+
+    inline bool isFree() const {
+        return !isAcquired() && !isDequeued() && !isQueued();
+    }
+
+    inline bool isDequeued() const {
+        return mDequeueCount > 0;
+    }
+
+    inline bool isQueued() const {
+        return mQueueCount > 0;
+    }
+
+    inline bool isAcquired() const {
+        return mAcquireCount > 0;
+    }
+
+    inline bool isShared() const {
+        return mShared;
+    }
+
+    inline void reset() {
+        *this = BufferState();
+    }
+
+    const char* string() const;
+
+    inline void dequeue() {
+        mDequeueCount++;
+    }
+
+    inline void detachProducer() {
+        if (mDequeueCount > 0) {
+            mDequeueCount--;
+        }
+    }
+
+    inline void attachProducer() {
+        mDequeueCount++;
+    }
+
+    inline void queue() {
+        if (mDequeueCount > 0) {
+            mDequeueCount--;
+        }
+        mQueueCount++;
+    }
+
+    inline void cancel() {
+        if (mDequeueCount > 0) {
+            mDequeueCount--;
+        }
+    }
+
+    inline void freeQueued() {
+        if (mQueueCount > 0) {
+            mQueueCount--;
+        }
+    }
+
+    inline void acquire() {
+        if (mQueueCount > 0) {
+            mQueueCount--;
+        }
+        mAcquireCount++;
+    }
+
+    inline void acquireNotInQueue() {
+        mAcquireCount++;
+    }
+
+    inline void release() {
+        if (mAcquireCount > 0) {
+            mAcquireCount--;
+        }
+    }
+
+    inline void detachConsumer() {
+        if (mAcquireCount > 0) {
+            mAcquireCount--;
+        }
+    }
+
+    inline void attachConsumer() {
+        mAcquireCount++;
+    }
+};
+
 struct BufferSlot {
 
     BufferSlot()
     : mEglDisplay(EGL_NO_DISPLAY),
-      mBufferState(BufferSlot::FREE),
+      mBufferState(),
       mRequestBufferCalled(false),
       mFrameNumber(0),
       mEglFence(EGL_NO_SYNC_KHR),
@@ -49,47 +191,6 @@ struct BufferSlot {
     // mEglDisplay is the EGLDisplay used to create EGLSyncKHR objects.
     EGLDisplay mEglDisplay;
 
-    // BufferState represents the different states in which a buffer slot
-    // can be.  All slots are initially FREE.
-    enum BufferState {
-        // FREE indicates that the buffer is available to be dequeued
-        // by the producer.  The buffer may be in use by the consumer for
-        // a finite time, so the buffer must not be modified until the
-        // associated fence is signaled.
-        //
-        // The slot is "owned" by BufferQueue.  It transitions to DEQUEUED
-        // when dequeueBuffer is called.
-        FREE = 0,
-
-        // DEQUEUED indicates that the buffer has been dequeued by the
-        // producer, but has not yet been queued or canceled.  The
-        // producer may modify the buffer's contents as soon as the
-        // associated ready fence is signaled.
-        //
-        // The slot is "owned" by the producer.  It can transition to
-        // QUEUED (via queueBuffer) or back to FREE (via cancelBuffer).
-        DEQUEUED = 1,
-
-        // QUEUED indicates that the buffer has been filled by the
-        // producer and queued for use by the consumer.  The buffer
-        // contents may continue to be modified for a finite time, so
-        // the contents must not be accessed until the associated fence
-        // is signaled.
-        //
-        // The slot is "owned" by BufferQueue.  It can transition to
-        // ACQUIRED (via acquireBuffer) or to FREE (if another buffer is
-        // queued in asynchronous mode).
-        QUEUED = 2,
-
-        // ACQUIRED indicates that the buffer has been acquired by the
-        // consumer.  As with QUEUED, the contents must not be accessed
-        // by the consumer until the fence is signaled.
-        //
-        // The slot is "owned" by the consumer.  It transitions to FREE
-        // when releaseBuffer is called.
-        ACQUIRED = 3
-    };
-
     static const char* bufferStateName(BufferState state);
 
     // mBufferState is the current state of this buffer slot.
index 4586839..d6daca7 100644 (file)
@@ -511,6 +511,13 @@ public:
 
     // Returns the number of the next frame which will be dequeued.
     virtual uint64_t getNextFrameNumber() const = 0;
+
+    // Used to enable/disable single buffer mode.
+    //
+    // In single buffer mode the last buffer that was dequeued will be cached
+    // and returned to all calls to dequeueBuffer and acquireBuffer. This allows
+    // the producer and consumer to simultaneously access the same buffer.
+    virtual status_t setSingleBufferMode(bool singleBufferMode) = 0;
 };
 
 // ----------------------------------------------------------------------------
index 3f46460..f9fc6df 100644 (file)
@@ -159,6 +159,7 @@ private:
     int dispatchSetSidebandStream(va_list args);
     int dispatchSetBuffersDataSpace(va_list args);
     int dispatchSetSurfaceDamage(va_list args);
+    int dispatchSetSingleBufferMode(va_list args);
 
 protected:
     virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
@@ -188,6 +189,7 @@ protected:
 public:
     virtual int setMaxDequeuedBufferCount(int maxDequeuedBuffers);
     virtual int setAsyncMode(bool async);
+    virtual int setSingleBufferMode(bool singleBufferMode);
     virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
     virtual int unlockAndPost();
 
index 1711071..5cf316f 100644 (file)
@@ -59,7 +59,7 @@ struct layer_state_t {
         :   what(0),
             x(0), y(0), z(0), w(0), h(0), layerStack(0),
             alpha(0), flags(0), mask(0),
-            reserved(0), crop(Rect::INVALID_RECT)
+            reserved(0), crop(Rect::INVALID_RECT), frameNumber(0)
     {
         matrix.dsdx = matrix.dtdy = 1.0f;
         matrix.dsdy = matrix.dtdx = 0.0f;
index 6a883cf..7305861 100644 (file)
@@ -24,6 +24,8 @@
 namespace android {
 
 BufferItem::BufferItem() :
+    mGraphicBuffer(NULL),
+    mFence(NULL),
     mCrop(Rect::INVALID_RECT),
     mTransform(0),
     mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
@@ -34,7 +36,8 @@ BufferItem::BufferItem() :
     mSlot(INVALID_BUFFER_SLOT),
     mIsDroppable(false),
     mAcquireCalled(false),
-    mTransformToDisplayInverse(false) {
+    mTransformToDisplayInverse(false),
+    mSurfaceDamage() {
 }
 
 BufferItem::~BufferItem() {}
index d52b47f..8e2afd0 100644 (file)
@@ -50,7 +50,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
         // buffer before releasing the old one.
         int numAcquiredBuffers = 0;
         for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-            if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
+            if (mSlots[s].mBufferState.isAcquired()) {
                 ++numAcquiredBuffers;
             }
         }
@@ -60,10 +60,13 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
             return INVALID_OPERATION;
         }
 
-        // Check if the queue is empty.
+        bool sharedBufferAvailable = mCore->mSingleBufferMode &&
+                mCore->mSingleBufferSlot !=
+                BufferQueueCore::INVALID_BUFFER_SLOT;
+
         // In asynchronous mode the list is guaranteed to be one buffer deep,
         // while in synchronous mode we use the oldest buffer.
-        if (mCore->mQueue.empty()) {
+        if (mCore->mQueue.empty() && !sharedBufferAvailable) {
             return NO_BUFFER_AVAILABLE;
         }
 
@@ -72,7 +75,9 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
         // If expectedPresent is specified, we may not want to return a buffer yet.
         // If it's specified and there's more than one buffer queued, we may want
         // to drop a buffer.
-        if (expectedPresent != 0) {
+        // Skip this if we're in single buffer mode and the queue is empty,
+        // since in that case we'll just return the shared buffer.
+        if (expectedPresent != 0 && !mCore->mQueue.empty()) {
             const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second
 
             // The 'expectedPresent' argument indicates when the buffer is expected
@@ -130,8 +135,19 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
                         desiredPresent, expectedPresent, mCore->mQueue.size());
                 if (mCore->stillTracking(front)) {
                     // Front buffer is still in mSlots, so mark the slot as free
-                    mSlots[front->mSlot].mBufferState = BufferSlot::FREE;
-                    mCore->mFreeBuffers.push_back(front->mSlot);
+                    mSlots[front->mSlot].mBufferState.freeQueued();
+
+                    // After leaving single buffer mode, the shared buffer will
+                    // still be around. Mark it as no longer shared if this
+                    // operation causes it to be free.
+                    if (!mCore->mSingleBufferMode &&
+                            mSlots[front->mSlot].mBufferState.isFree()) {
+                        mSlots[front->mSlot].mBufferState.mShared = false;
+                    }
+                    // Don't put the shared buffer on the free list.
+                    if (!mSlots[front->mSlot].mBufferState.isShared()) {
+                        mCore->mFreeBuffers.push_back(front->mSlot);
+                    }
                     listener = mCore->mConnectedProducerListener;
                     ++numDroppedBuffers;
                 }
@@ -162,17 +178,52 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
                     systemTime(CLOCK_MONOTONIC));
         }
 
-        int slot = front->mSlot;
-        *outBuffer = *front;
+        int slot = BufferQueueCore::INVALID_BUFFER_SLOT;
+
+        if (sharedBufferAvailable && mCore->mQueue.empty()) {
+            // make sure the buffer has finished allocating before acquiring it
+            mCore->waitWhileAllocatingLocked();
+
+            slot = mCore->mSingleBufferSlot;
+
+            // Recreate the BufferItem for the shared buffer from the data that
+            // was cached when it was last queued.
+            outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer;
+            outBuffer->mFence = Fence::NO_FENCE;
+            outBuffer->mCrop = mCore->mSingleBufferCache.crop;
+            outBuffer->mTransform = mCore->mSingleBufferCache.transform &
+                    ~static_cast<uint32_t>(
+                    NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
+            outBuffer->mScalingMode = mCore->mSingleBufferCache.scalingMode;
+            outBuffer->mDataSpace = mCore->mSingleBufferCache.dataspace;
+            outBuffer->mFrameNumber = mCore->mFrameCounter;
+            outBuffer->mSlot = slot;
+            outBuffer->mAcquireCalled = mSlots[slot].mAcquireCalled;
+            outBuffer->mTransformToDisplayInverse =
+                    (mCore->mSingleBufferCache.transform &
+                    NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
+            outBuffer->mSurfaceDamage = Region::INVALID_REGION;
+        } else {
+            slot = front->mSlot;
+            *outBuffer = *front;
+        }
+
         ATRACE_BUFFER_INDEX(slot);
 
         BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
-                slot, front->mFrameNumber, front->mGraphicBuffer->handle);
+                slot, outBuffer->mFrameNumber, outBuffer->mGraphicBuffer->handle);
         // If the front buffer is still being tracked, update its slot state
-        if (mCore->stillTracking(front)) {
+        if (mCore->stillTracking(outBuffer)) {
             mSlots[slot].mAcquireCalled = true;
             mSlots[slot].mNeedsCleanupOnRelease = false;
-            mSlots[slot].mBufferState = BufferSlot::ACQUIRED;
+            // Don't decrease the queue count if the BufferItem wasn't
+            // previously in the queue. This happens in single buffer mode when
+            // the queue is empty and the BufferItem is created above.
+            if (mCore->mQueue.empty()) {
+                mSlots[slot].mBufferState.acquireNotInQueue();
+            } else {
+                mSlots[slot].mBufferState.acquire();
+            }
             mSlots[slot].mFence = Fence::NO_FENCE;
         }
 
@@ -207,24 +258,31 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
 status_t BufferQueueConsumer::detachBuffer(int slot) {
     ATRACE_CALL();
     ATRACE_BUFFER_INDEX(slot);
-    BQ_LOGV("detachBuffer(C): slot %d", slot);
+    BQ_LOGV("detachBuffer: slot %d", slot);
     Mutex::Autolock lock(mCore->mMutex);
 
     if (mCore->mIsAbandoned) {
-        BQ_LOGE("detachBuffer(C): BufferQueue has been abandoned");
+        BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
         return NO_INIT;
     }
 
+    if (mCore->mSingleBufferMode) {
+        BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer"
+                "mode");
+        return BAD_VALUE;
+    }
+
     if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
-        BQ_LOGE("detachBuffer(C): slot index %d out of range [0, %d)",
+        BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
                 slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
         return BAD_VALUE;
-    } else if (mSlots[slot].mBufferState != BufferSlot::ACQUIRED) {
-        BQ_LOGE("detachBuffer(C): slot %d is not owned by the consumer "
-                "(state = %d)", slot, mSlots[slot].mBufferState);
+    } else if (!mSlots[slot].mBufferState.isAcquired()) {
+        BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
+                "(state = %s)", slot, mSlots[slot].mBufferState.string());
         return BAD_VALUE;
     }
 
+    mSlots[slot].mBufferState.detachConsumer();
     mCore->freeBufferLocked(slot);
     mCore->mDequeueCondition.broadcast();
     mCore->validateConsistencyLocked();
@@ -237,25 +295,31 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot,
     ATRACE_CALL();
 
     if (outSlot == NULL) {
-        BQ_LOGE("attachBuffer(P): outSlot must not be NULL");
+        BQ_LOGE("attachBuffer: outSlot must not be NULL");
         return BAD_VALUE;
     } else if (buffer == NULL) {
-        BQ_LOGE("attachBuffer(P): cannot attach NULL buffer");
+        BQ_LOGE("attachBuffer: cannot attach NULL buffer");
         return BAD_VALUE;
     }
 
     Mutex::Autolock lock(mCore->mMutex);
 
+    if (mCore->mSingleBufferMode) {
+        BQ_LOGE("attachBuffer: cannot attach a buffer in single buffer"
+                "mode");
+        return BAD_VALUE;
+    }
+
     // Make sure we don't have too many acquired buffers
     int numAcquiredBuffers = 0;
     for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-        if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
+        if (mSlots[s].mBufferState.isAcquired()) {
             ++numAcquiredBuffers;
         }
     }
 
     if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
-        BQ_LOGE("attachBuffer(P): max acquired buffer count reached: %d "
+        BQ_LOGE("attachBuffer: max acquired buffer count reached: %d "
                 "(max %d)", numAcquiredBuffers,
                 mCore->mMaxAcquiredBufferCount);
         return INVALID_OPERATION;
@@ -279,16 +343,16 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot,
         mCore->mFreeBuffers.remove(found);
     }
     if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
-        BQ_LOGE("attachBuffer(P): could not find free buffer slot");
+        BQ_LOGE("attachBuffer: could not find free buffer slot");
         return NO_MEMORY;
     }
 
     *outSlot = found;
     ATRACE_BUFFER_INDEX(*outSlot);
-    BQ_LOGV("attachBuffer(C): returning slot %d", *outSlot);
+    BQ_LOGV("attachBuffer: returning slot %d", *outSlot);
 
     mSlots[*outSlot].mGraphicBuffer = buffer;
-    mSlots[*outSlot].mBufferState = BufferSlot::ACQUIRED;
+    mSlots[*outSlot].mBufferState.attachConsumer();
     mSlots[*outSlot].mAttachedByConsumer = true;
     mSlots[*outSlot].mNeedsCleanupOnRelease = false;
     mSlots[*outSlot].mFence = Fence::NO_FENCE;
@@ -334,38 +398,45 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
         Mutex::Autolock lock(mCore->mMutex);
 
         // If the frame number has changed because the buffer has been reallocated,
-        // we can ignore this releaseBuffer for the old buffer
-        if (frameNumber != mSlots[slot].mFrameNumber) {
+        // we can ignore this releaseBuffer for the old buffer.
+        // Ignore this for the shared buffer where the frame number can easily
+        // get out of sync due to the buffer being queued and acquired at the
+        // same time.
+        if (frameNumber != mSlots[slot].mFrameNumber &&
+                !mSlots[slot].mBufferState.isShared()) {
             return STALE_BUFFER_SLOT;
         }
 
-        // Make sure this buffer hasn't been queued while acquired by the consumer
-        BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
-        while (current != mCore->mQueue.end()) {
-            if (current->mSlot == slot) {
-                BQ_LOGE("releaseBuffer: buffer slot %d pending release is "
-                        "currently queued", slot);
-                return BAD_VALUE;
-            }
-            ++current;
-        }
 
-        if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
+        if (mSlots[slot].mBufferState.isAcquired()) {
             mSlots[slot].mEglDisplay = eglDisplay;
             mSlots[slot].mEglFence = eglFence;
             mSlots[slot].mFence = releaseFence;
-            mSlots[slot].mBufferState = BufferSlot::FREE;
-            mCore->mFreeBuffers.push_back(slot);
+            mSlots[slot].mBufferState.release();
+
+            // After leaving single buffer mode, the shared buffer will
+            // still be around. Mark it as no longer shared if this
+            // operation causes it to be free.
+            if (!mCore->mSingleBufferMode &&
+                    mSlots[slot].mBufferState.isFree()) {
+                mSlots[slot].mBufferState.mShared = false;
+            }
+            // Don't put the shared buffer on the free list.
+            if (!mSlots[slot].mBufferState.isShared()) {
+                mCore->mFreeBuffers.push_back(slot);
+            }
+
             listener = mCore->mConnectedProducerListener;
             BQ_LOGV("releaseBuffer: releasing slot %d", slot);
         } else if (mSlots[slot].mNeedsCleanupOnRelease) {
             BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d "
-                    "(state = %d)", slot, mSlots[slot].mBufferState);
+                    "(state = %s)", slot, mSlots[slot].mBufferState.string());
             mSlots[slot].mNeedsCleanupOnRelease = false;
             return STALE_BUFFER_SLOT;
         } else {
             BQ_LOGE("releaseBuffer: attempted to release buffer slot %d "
-                    "but its state was %d", slot, mSlots[slot].mBufferState);
+                    "but its state was %s", slot,
+                    mSlots[slot].mBufferState.string());
             return BAD_VALUE;
         }
 
@@ -386,17 +457,17 @@ status_t BufferQueueConsumer::connect(
     ATRACE_CALL();
 
     if (consumerListener == NULL) {
-        BQ_LOGE("connect(C): consumerListener may not be NULL");
+        BQ_LOGE("connect: consumerListener may not be NULL");
         return BAD_VALUE;
     }
 
-    BQ_LOGV("connect(C): controlledByApp=%s",
+    BQ_LOGV("connect: controlledByApp=%s",
             controlledByApp ? "true" : "false");
 
     Mutex::Autolock lock(mCore->mMutex);
 
     if (mCore->mIsAbandoned) {
-        BQ_LOGE("connect(C): BufferQueue has been abandoned");
+        BQ_LOGE("connect: BufferQueue has been abandoned");
         return NO_INIT;
     }
 
@@ -409,12 +480,12 @@ status_t BufferQueueConsumer::connect(
 status_t BufferQueueConsumer::disconnect() {
     ATRACE_CALL();
 
-    BQ_LOGV("disconnect(C)");
+    BQ_LOGV("disconnect");
 
     Mutex::Autolock lock(mCore->mMutex);
 
     if (mCore->mConsumerListener == NULL) {
-        BQ_LOGE("disconnect(C): no consumer is connected");
+        BQ_LOGE("disconnect: no consumer is connected");
         return BAD_VALUE;
     }
 
index b1cbc86..c24ad19 100644 (file)
@@ -69,7 +69,11 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
     mAllowAllocation(true),
     mBufferAge(0),
     mGenerationNumber(0),
-    mAsyncMode(false)
+    mAsyncMode(false),
+    mSingleBufferMode(false),
+    mSingleBufferSlot(INVALID_BUFFER_SLOT),
+    mSingleBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
+            HAL_DATASPACE_UNKNOWN)
 {
     if (allocator == NULL) {
         sp<ISurfaceComposer> composer(ComposerService::getComposerService());
@@ -113,7 +117,7 @@ void BufferQueueCore::dump(String8& result, const char* prefix) const {
     int maxBufferCount = 0;
     for (int s = BufferQueueDefs::NUM_BUFFER_SLOTS - 1; s >= 0; --s) {
         const BufferSlot& slot(mSlots[s]);
-        if (slot.mBufferState != BufferSlot::FREE ||
+        if (!slot.mBufferState.isFree() ||
                 slot.mGraphicBuffer != NULL) {
             maxBufferCount = s + 1;
             break;
@@ -124,9 +128,9 @@ void BufferQueueCore::dump(String8& result, const char* prefix) const {
         const BufferSlot& slot(mSlots[s]);
         const sp<GraphicBuffer>& buffer(slot.mGraphicBuffer);
         result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix,
-                (slot.mBufferState == BufferSlot::ACQUIRED) ? ">" : " ",
+                (slot.mBufferState.isAcquired()) ? ">" : " ",
                 s, buffer.get(),
-                BufferSlot::bufferStateName(slot.mBufferState));
+                slot.mBufferState.string());
 
         if (buffer != NULL) {
             result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle,
@@ -164,8 +168,8 @@ int BufferQueueCore::getMaxBufferCountLocked() const {
     // will temporarily keep the max buffer count up until the slots no longer
     // need to be preserved.
     for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-        BufferSlot::BufferState state = mSlots[s].mBufferState;
-        if (state == BufferSlot::QUEUED || state == BufferSlot::DEQUEUED) {
+        BufferState state = mSlots[s].mBufferState;
+        if (state.isQueued() || state.isDequeued()) {
             maxBufferCount = s + 1;
         }
     }
@@ -173,14 +177,14 @@ int BufferQueueCore::getMaxBufferCountLocked() const {
     return maxBufferCount;
 }
 
-void BufferQueueCore::freeBufferLocked(int slot) {
+void BufferQueueCore::freeBufferLocked(int slot, bool validate) {
     BQ_LOGV("freeBufferLocked: slot %d", slot);
     bool hadBuffer = mSlots[slot].mGraphicBuffer != NULL;
     mSlots[slot].mGraphicBuffer.clear();
-    if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
+    if (mSlots[slot].mBufferState.isAcquired()) {
         mSlots[slot].mNeedsCleanupOnRelease = true;
     }
-    if (mSlots[slot].mBufferState != BufferSlot::FREE) {
+    if (!mSlots[slot].mBufferState.isFree()) {
         mFreeSlots.insert(slot);
     } else if (hadBuffer) {
         // If the slot was FREE, but we had a buffer, we need to move this slot
@@ -188,7 +192,6 @@ void BufferQueueCore::freeBufferLocked(int slot) {
         mFreeBuffers.remove(slot);
         mFreeSlots.insert(slot);
     }
-    mSlots[slot].mBufferState = BufferSlot::FREE;
     mSlots[slot].mAcquireCalled = false;
     mSlots[slot].mFrameNumber = 0;
 
@@ -198,14 +201,19 @@ void BufferQueueCore::freeBufferLocked(int slot) {
         mSlots[slot].mEglFence = EGL_NO_SYNC_KHR;
     }
     mSlots[slot].mFence = Fence::NO_FENCE;
-    validateConsistencyLocked();
+    if (validate) {
+        validateConsistencyLocked();
+    }
 }
 
 void BufferQueueCore::freeAllBuffersLocked() {
     mBufferHasBeenQueued = false;
     for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-        freeBufferLocked(s);
+        freeBufferLocked(s, false);
+        mSlots[s].mBufferState.reset();
     }
+    mSingleBufferSlot = INVALID_BUFFER_SLOT;
+    validateConsistencyLocked();
 }
 
 bool BufferQueueCore::stillTracking(const BufferItem* item) const {
@@ -238,7 +246,8 @@ void BufferQueueCore::validateConsistencyLocked() const {
         bool isInFreeBuffers =
                 std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) !=
                 mFreeBuffers.cend();
-        if (mSlots[slot].mBufferState == BufferSlot::FREE) {
+        if (mSlots[slot].mBufferState.isFree() &&
+                !mSlots[slot].mBufferState.isShared()) {
             if (mSlots[slot].mGraphicBuffer == NULL) {
                 if (!isInFreeSlots) {
                     BQ_LOGE("Slot %d is FREE but is not in mFreeSlots", slot);
@@ -262,13 +271,13 @@ void BufferQueueCore::validateConsistencyLocked() const {
             }
         } else {
             if (isInFreeSlots) {
-                BQ_LOGE("Slot %d is in mFreeSlots but is not FREE (%d)",
-                        slot, mSlots[slot].mBufferState);
+                BQ_LOGE("Slot %d is in mFreeSlots but is not FREE (%s)",
+                        slot, mSlots[slot].mBufferState.string());
                 usleep(PAUSE_TIME);
             }
             if (isInFreeBuffers) {
-                BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE (%d)",
-                        slot, mSlots[slot].mBufferState);
+                BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE (%s)",
+                        slot, mSlots[slot].mBufferState.string());
                 usleep(PAUSE_TIME);
             }
         }
index 9ef8ff7..268c9da 100644 (file)
@@ -66,9 +66,9 @@ status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
         BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
                 slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
         return BAD_VALUE;
-    } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
+    } else if (!mSlots[slot].mBufferState.isDequeued()) {
         BQ_LOGE("requestBuffer: slot %d is not owned by the producer "
-                "(state = %d)", slot, mSlots[slot].mBufferState);
+                "(state = %s)", slot, mSlots[slot].mBufferState.string());
         return BAD_VALUE;
     }
 
@@ -96,7 +96,7 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount(
 
         // There must be no dequeued buffers when changing the buffer count.
         for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-            if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) {
+            if (mSlots[s].mBufferState.isDequeued()) {
                 BQ_LOGE("setMaxDequeuedBufferCount: buffer owned by producer");
                 return BAD_VALUE;
             }
@@ -196,7 +196,7 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
 
         // Free up any buffers that are in slots beyond the max buffer count
         for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
-            assert(mSlots[s].mBufferState == BufferSlot::FREE);
+            assert(mSlots[s].mBufferState.isFree());
             if (mSlots[s].mGraphicBuffer != NULL) {
                 mCore->freeBufferLocked(s);
                 *returnFlags |= RELEASE_ALL_BUFFERS;
@@ -206,15 +206,11 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
         int dequeuedCount = 0;
         int acquiredCount = 0;
         for (int s = 0; s < maxBufferCount; ++s) {
-            switch (mSlots[s].mBufferState) {
-                case BufferSlot::DEQUEUED:
-                    ++dequeuedCount;
-                    break;
-                case BufferSlot::ACQUIRED:
-                    ++acquiredCount;
-                    break;
-                default:
-                    break;
+            if (mSlots[s].mBufferState.isDequeued()) {
+                ++dequeuedCount;
+            }
+            if (mSlots[s].mBufferState.isAcquired()) {
+                ++acquiredCount;
             }
         }
 
@@ -240,7 +236,12 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller,
             BQ_LOGV("%s: queue size is %zu, waiting", caller,
                     mCore->mQueue.size());
         } else {
-            if (!mCore->mFreeBuffers.empty()) {
+            // If in single buffer mode and a shared buffer exists, always
+            // return it.
+            if (mCore->mSingleBufferMode && mCore->mSingleBufferSlot !=
+                    BufferQueueCore::INVALID_BUFFER_SLOT) {
+                *found = mCore->mSingleBufferSlot;
+            } else if (!mCore->mFreeBuffers.empty()) {
                 auto slot = mCore->mFreeBuffers.begin();
                 *found = *slot;
                 mCore->mFreeBuffers.erase(slot);
@@ -348,6 +349,13 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
             // requested attributes, we free it and attempt to get another one.
             if (!mCore->mAllowAllocation) {
                 if (buffer->needsReallocation(width, height, format, usage)) {
+                    if (mCore->mSingleBufferMode &&
+                            mCore->mSingleBufferSlot == found) {
+                        BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
+                                "buffer");
+                        return BAD_VALUE;
+                    }
+
                     mCore->freeBufferLocked(found);
                     found = BufferItem::INVALID_BUFFER_SLOT;
                     continue;
@@ -360,7 +368,15 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
 
         attachedByConsumer = mSlots[found].mAttachedByConsumer;
 
-        mSlots[found].mBufferState = BufferSlot::DEQUEUED;
+        mSlots[found].mBufferState.dequeue();
+
+        // If single buffer mode has just been enabled, cache the slot of the
+        // first buffer that is dequeued and mark it as the shared buffer.
+        if (mCore->mSingleBufferMode && mCore->mSingleBufferSlot ==
+                BufferQueueCore::INVALID_BUFFER_SLOT) {
+            mCore->mSingleBufferSlot = found;
+            mSlots[found].mBufferState.mShared = true;
+        }
 
         const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
         if ((buffer == NULL) ||
@@ -373,6 +389,7 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
             mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
             mSlots[found].mFence = Fence::NO_FENCE;
             mCore->mBufferAge = 0;
+            mCore->mIsAllocating = true;
 
             returnFlags |= BUFFER_NEEDS_REALLOCATION;
         } else {
@@ -405,21 +422,26 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
         BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
         sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
                 width, height, format, usage, &error));
-        if (graphicBuffer == NULL) {
-            BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
-            return error;
-        }
-
         { // Autolock scope
             Mutex::Autolock lock(mCore->mMutex);
 
+            if (graphicBuffer != NULL && !mCore->mIsAbandoned) {
+                graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
+                mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
+            }
+
+            mCore->mIsAllocating = false;
+            mCore->mIsAllocatingCondition.broadcast();
+
+            if (graphicBuffer == NULL) {
+                BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
+                return error;
+            }
+
             if (mCore->mIsAbandoned) {
                 BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
                 return NO_INIT;
             }
-
-            graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
-            mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
         } // Autolock scope
     }
 
@@ -453,33 +475,40 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
 status_t BufferQueueProducer::detachBuffer(int slot) {
     ATRACE_CALL();
     ATRACE_BUFFER_INDEX(slot);
-    BQ_LOGV("detachBuffer(P): slot %d", slot);
+    BQ_LOGV("detachBuffer: slot %d", slot);
     Mutex::Autolock lock(mCore->mMutex);
 
     if (mCore->mIsAbandoned) {
-        BQ_LOGE("detachBuffer(P): BufferQueue has been abandoned");
+        BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
         return NO_INIT;
     }
 
     if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
-        BQ_LOGE("detachBuffer(P): BufferQueue has no connected producer");
+        BQ_LOGE("detachBuffer: BufferQueue has no connected producer");
         return NO_INIT;
     }
 
+    if (mCore->mSingleBufferMode) {
+        BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer"
+                "mode");
+        return BAD_VALUE;
+    }
+
     if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
-        BQ_LOGE("detachBuffer(P): slot index %d out of range [0, %d)",
+        BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
                 slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
         return BAD_VALUE;
-    } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
-        BQ_LOGE("detachBuffer(P): slot %d is not owned by the producer "
-                "(state = %d)", slot, mSlots[slot].mBufferState);
+    } else if (!mSlots[slot].mBufferState.isDequeued()) {
+        BQ_LOGE("detachBuffer: slot %d is not owned by the producer "
+                "(state = %s)", slot, mSlots[slot].mBufferState.string());
         return BAD_VALUE;
     } else if (!mSlots[slot].mRequestBufferCalled) {
-        BQ_LOGE("detachBuffer(P): buffer in slot %d has not been requested",
+        BQ_LOGE("detachBuffer: buffer in slot %d has not been requested",
                 slot);
         return BAD_VALUE;
     }
 
+    mSlots[slot].mBufferState.detachProducer();
     mCore->freeBufferLocked(slot);
     mCore->mDequeueCondition.broadcast();
     mCore->validateConsistencyLocked();
@@ -511,6 +540,12 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
         return NO_INIT;
     }
 
+    if (mCore->mSingleBufferMode) {
+        BQ_LOGE("detachNextBuffer: cannot detach a buffer in single buffer"
+                "mode");
+        return BAD_VALUE;
+    }
+
     mCore->waitWhileAllocatingLocked();
 
     if (mCore->mFreeBuffers.empty()) {
@@ -535,25 +570,30 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot,
     ATRACE_CALL();
 
     if (outSlot == NULL) {
-        BQ_LOGE("attachBuffer(P): outSlot must not be NULL");
+        BQ_LOGE("attachBuffer: outSlot must not be NULL");
         return BAD_VALUE;
     } else if (buffer == NULL) {
-        BQ_LOGE("attachBuffer(P): cannot attach NULL buffer");
+        BQ_LOGE("attachBuffer: cannot attach NULL buffer");
         return BAD_VALUE;
     }
 
     Mutex::Autolock lock(mCore->mMutex);
 
     if (mCore->mIsAbandoned) {
-        BQ_LOGE("attachBuffer(P): BufferQueue has been abandoned");
+        BQ_LOGE("attachBuffer: BufferQueue has been abandoned");
         return NO_INIT;
     }
 
     if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
-        BQ_LOGE("attachBuffer(P): BufferQueue has no connected producer");
+        BQ_LOGE("attachBuffer: BufferQueue has no connected producer");
         return NO_INIT;
     }
 
+    if (mCore->mSingleBufferMode) {
+        BQ_LOGE("attachBuffer: cannot atach a buffer in single buffer mode");
+        return BAD_VALUE;
+    }
+
     if (buffer->getGenerationNumber() != mCore->mGenerationNumber) {
         BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] "
                 "[queue %u]", buffer->getGenerationNumber(),
@@ -565,7 +605,7 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot,
 
     status_t returnFlags = NO_ERROR;
     int found;
-    status_t status = waitForFreeSlotThenRelock("attachBuffer(P)", &found,
+    status_t status = waitForFreeSlotThenRelock("attachBuffer", &found,
             &returnFlags);
     if (status != NO_ERROR) {
         return status;
@@ -573,17 +613,17 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot,
 
     // This should not happen
     if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
-        BQ_LOGE("attachBuffer(P): no available buffer slots");
+        BQ_LOGE("attachBuffer: no available buffer slots");
         return -EBUSY;
     }
 
     *outSlot = found;
     ATRACE_BUFFER_INDEX(*outSlot);
-    BQ_LOGV("attachBuffer(P): returning slot %d flags=%#x",
+    BQ_LOGV("attachBuffer: returning slot %d flags=%#x",
             *outSlot, returnFlags);
 
     mSlots[*outSlot].mGraphicBuffer = buffer;
-    mSlots[*outSlot].mBufferState = BufferSlot::DEQUEUED;
+    mSlots[*outSlot].mBufferState.attachProducer();
     mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR;
     mSlots[*outSlot].mFence = Fence::NO_FENCE;
     mSlots[*outSlot].mRequestBufferCalled = true;
@@ -649,9 +689,9 @@ status_t BufferQueueProducer::queueBuffer(int slot,
             BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
                     slot, maxBufferCount);
             return BAD_VALUE;
-        } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
+        } else if (!mSlots[slot].mBufferState.isDequeued()) {
             BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
-                    "(state = %d)", slot, mSlots[slot].mBufferState);
+                    "(state = %s)", slot, mSlots[slot].mBufferState.string());
             return BAD_VALUE;
         } else if (!mSlots[slot].mRequestBufferCalled) {
             BQ_LOGE("queueBuffer: slot %d was queued without requesting "
@@ -681,7 +721,8 @@ status_t BufferQueueProducer::queueBuffer(int slot,
         }
 
         mSlots[slot].mFence = fence;
-        mSlots[slot].mBufferState = BufferSlot::QUEUED;
+        mSlots[slot].mBufferState.queue();
+
         ++mCore->mFrameCounter;
         mSlots[slot].mFrameNumber = mCore->mFrameCounter;
 
@@ -700,11 +741,21 @@ status_t BufferQueueProducer::queueBuffer(int slot,
         item.mSlot = slot;
         item.mFence = fence;
         item.mIsDroppable = mCore->mAsyncMode ||
-                mCore->mDequeueBufferCannotBlock;
+                mCore->mDequeueBufferCannotBlock ||
+                (mCore->mSingleBufferMode && mCore->mSingleBufferSlot == slot);
         item.mSurfaceDamage = surfaceDamage;
 
         mStickyTransform = stickyTransform;
 
+        // Cache the shared buffer data so that the BufferItem can be recreated.
+        if (mCore->mSingleBufferMode) {
+            mCore->mSingleBufferCache.crop = crop;
+            mCore->mSingleBufferCache.transform = transform;
+            mCore->mSingleBufferCache.scalingMode = static_cast<uint32_t>(
+                    scalingMode);
+            mCore->mSingleBufferCache.dataspace = dataSpace;
+        }
+
         if (mCore->mQueue.empty()) {
             // When the queue is empty, we can ignore mDequeueBufferCannotBlock
             // and simply queue this buffer
@@ -718,8 +769,19 @@ status_t BufferQueueProducer::queueBuffer(int slot,
                 // If the front queued buffer is still being tracked, we first
                 // mark it as freed
                 if (mCore->stillTracking(front)) {
-                    mSlots[front->mSlot].mBufferState = BufferSlot::FREE;
-                    mCore->mFreeBuffers.push_front(front->mSlot);
+                    mSlots[front->mSlot].mBufferState.freeQueued();
+
+                    // After leaving single buffer mode, the shared buffer will
+                    // still be around. Mark it as no longer shared if this
+                    // operation causes it to be free.
+                    if (!mCore->mSingleBufferMode &&
+                            mSlots[front->mSlot].mBufferState.isFree()) {
+                        mSlots[front->mSlot].mBufferState.mShared = false;
+                    }
+                    // Don't put the shared buffer on the free list.
+                    if (!mSlots[front->mSlot].mBufferState.isShared()) {
+                        mCore->mFreeBuffers.push_front(front->mSlot);
+                    }
                 }
                 // Overwrite the droppable buffer with the incoming one
                 *front = item;
@@ -795,21 +857,36 @@ status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
         return NO_INIT;
     }
 
+    if (mCore->mSingleBufferMode) {
+        BQ_LOGE("cancelBuffer: cannot cancel a buffer in single buffer mode");
+        return BAD_VALUE;
+    }
+
     if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
         BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)",
                 slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
         return BAD_VALUE;
-    } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
+    } else if (!mSlots[slot].mBufferState.isDequeued()) {
         BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
-                "(state = %d)", slot, mSlots[slot].mBufferState);
+                "(state = %s)", slot, mSlots[slot].mBufferState.string());
         return BAD_VALUE;
     } else if (fence == NULL) {
         BQ_LOGE("cancelBuffer: fence is NULL");
         return BAD_VALUE;
     }
 
-    mCore->mFreeBuffers.push_front(slot);
-    mSlots[slot].mBufferState = BufferSlot::FREE;
+    mSlots[slot].mBufferState.cancel();
+
+    // After leaving single buffer mode, the shared buffer will still be around.
+    // Mark it as no longer shared if this operation causes it to be free.
+    if (!mCore->mSingleBufferMode && mSlots[slot].mBufferState.isFree()) {
+        mSlots[slot].mBufferState.mShared = false;
+    }
+
+    // Don't put the shared buffer on the free list.
+    if (!mSlots[slot].mBufferState.isShared()) {
+        mCore->mFreeBuffers.push_front(slot);
+    }
     mSlots[slot].mFence = fence;
     mCore->mDequeueCondition.broadcast();
     mCore->validateConsistencyLocked();
@@ -878,26 +955,26 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
     ATRACE_CALL();
     Mutex::Autolock lock(mCore->mMutex);
     mConsumerName = mCore->mConsumerName;
-    BQ_LOGV("connect(P): api=%d producerControlledByApp=%s", api,
+    BQ_LOGV("connect: api=%d producerControlledByApp=%s", api,
             producerControlledByApp ? "true" : "false");
 
     if (mCore->mIsAbandoned) {
-        BQ_LOGE("connect(P): BufferQueue has been abandoned");
+        BQ_LOGE("connect: BufferQueue has been abandoned");
         return NO_INIT;
     }
 
     if (mCore->mConsumerListener == NULL) {
-        BQ_LOGE("connect(P): BufferQueue has no consumer");
+        BQ_LOGE("connect: BufferQueue has no consumer");
         return NO_INIT;
     }
 
     if (output == NULL) {
-        BQ_LOGE("connect(P): output was NULL");
+        BQ_LOGE("connect: output was NULL");
         return BAD_VALUE;
     }
 
     if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
-        BQ_LOGE("connect(P): already connected (cur=%d req=%d)",
+        BQ_LOGE("connect: already connected (cur=%d req=%d)",
                 mCore->mConnectedApi, api);
         return BAD_VALUE;
     }
@@ -920,14 +997,14 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
                 status = IInterface::asBinder(listener)->linkToDeath(
                         static_cast<IBinder::DeathRecipient*>(this));
                 if (status != NO_ERROR) {
-                    BQ_LOGE("connect(P): linkToDeath failed: %s (%d)",
+                    BQ_LOGE("connect: linkToDeath failed: %s (%d)",
                             strerror(-status), status);
                 }
             }
             mCore->mConnectedProducerListener = listener;
             break;
         default:
-            BQ_LOGE("connect(P): unknown API %d", api);
+            BQ_LOGE("connect: unknown API %d", api);
             status = BAD_VALUE;
             break;
     }
@@ -942,7 +1019,7 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
 
 status_t BufferQueueProducer::disconnect(int api) {
     ATRACE_CALL();
-    BQ_LOGV("disconnect(P): api %d", api);
+    BQ_LOGV("disconnect: api %d", api);
 
     int status = NO_ERROR;
     sp<IConsumerListener> listener;
@@ -979,13 +1056,13 @@ status_t BufferQueueProducer::disconnect(int api) {
                     mCore->mDequeueCondition.broadcast();
                     listener = mCore->mConsumerListener;
                 } else if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
-                    BQ_LOGE("disconnect(P): still connected to another API "
+                    BQ_LOGE("disconnect: still connected to another API "
                             "(cur=%d req=%d)", mCore->mConnectedApi, api);
                     status = BAD_VALUE;
                 }
                 break;
             default:
-                BQ_LOGE("disconnect(P): unknown API %d", api);
+                BQ_LOGE("disconnect: unknown API %d", api);
                 status = BAD_VALUE;
                 break;
         }
@@ -1038,7 +1115,7 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height,
                 if (mSlots[slot].mGraphicBuffer != NULL) {
                     ++currentBufferCount;
                 } else {
-                    if (mSlots[slot].mBufferState != BufferSlot::FREE) {
+                    if (!mSlots[slot].mBufferState.isFree()) {
                         BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE",
                                 slot);
                         continue;
@@ -1101,7 +1178,7 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height,
 
             for (size_t i = 0; i < newBufferCount; ++i) {
                 int slot = freeSlots[i];
-                if (mSlots[slot].mBufferState != BufferSlot::FREE) {
+                if (!mSlots[slot].mBufferState.isFree()) {
                     // A consumer allocated the FREE slot with attachBuffer. Discard the buffer we
                     // allocated.
                     BQ_LOGV("allocateBuffers: slot %d was acquired while allocating. "
@@ -1159,6 +1236,19 @@ uint64_t BufferQueueProducer::getNextFrameNumber() const {
     return nextFrameNumber;
 }
 
+status_t BufferQueueProducer::setSingleBufferMode(bool singleBufferMode) {
+    ATRACE_CALL();
+    BQ_LOGV("setSingleBufferMode: %d", singleBufferMode);
+
+    Mutex::Autolock lock(mCore->mMutex);
+    if (!singleBufferMode) {
+        mCore->mSingleBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
+    }
+    mCore->mSingleBufferMode = singleBufferMode;
+
+    return NO_ERROR;
+}
+
 void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
     // If we're here, it means that a producer we were connected to died.
     // We're guaranteed that we are still connected to it because we remove
index 01595de..b1cdc5d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 The Android Open Source Project
+ * Copyright 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 namespace android {
 
-const char* BufferSlot::bufferStateName(BufferState state) {
-    switch (state) {
-        case BufferSlot::DEQUEUED: return "DEQUEUED";
-        case BufferSlot::QUEUED: return "QUEUED";
-        case BufferSlot::FREE: return "FREE";
-        case BufferSlot::ACQUIRED: return "ACQUIRED";
+
+const char* BufferState::string() const {
+
+    if (isShared()) {
+        return "SHARED";
+    }
+
+    if (isFree()) {
+        return "FREE";
+    }
+
+    if (isAcquired()) {
+        return "ACQUIRED";
+    }
+
+    if (isDequeued()) {
+        return "DEQUEUED";
+    }
+
+    if (isQueued()) {
+        return "QUEUED";
     }
-    return "Unknown";
+    return "UNKNOWN";
 }
 
 } // namespace android
index 1572a89..83e4d66 100644 (file)
@@ -424,6 +424,11 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item)
                     mCurrentTextureImage->graphicBufferHandle() : 0,
             slot, mSlots[slot].mGraphicBuffer->handle);
 
+    // Hang onto the pointer so that it isn't freed in the call to
+    // releaseBufferLocked() if we're in single buffer mode and both buffers are
+    // the same.
+    sp<EglImage> nextTextureImage = mEglSlots[slot].mEglImage;
+
     // release old buffer
     if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
         status_t status = releaseBufferLocked(
@@ -439,7 +444,7 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item)
 
     // Update the GLConsumer state.
     mCurrentTexture = slot;
-    mCurrentTextureImage = mEglSlots[slot].mEglImage;
+    mCurrentTextureImage = nextTextureImage;
     mCurrentCrop = item.mCrop;
     mCurrentTransform = item.mTransform;
     mCurrentScalingMode = item.mScalingMode;
index 640a9f2..d5310bd 100644 (file)
@@ -50,7 +50,8 @@ enum {
     GET_CONSUMER_NAME,
     SET_MAX_DEQUEUED_BUFFER_COUNT,
     SET_ASYNC_MODE,
-    GET_NEXT_FRAME_NUMBER
+    GET_NEXT_FRAME_NUMBER,
+    SET_SINGLE_BUFFER_MODE
 };
 
 class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
@@ -339,6 +340,19 @@ public:
         uint64_t frameNumber = reply.readUint64();
         return frameNumber;
     }
+
+    virtual status_t setSingleBufferMode(bool singleBufferMode) {
+        Parcel data, reply;
+        data.writeInterfaceToken(
+                IGraphicBufferProducer::getInterfaceDescriptor());
+        data.writeInt32(singleBufferMode);
+        status_t result = remote()->transact(SET_SINGLE_BUFFER_MODE, data,
+                &reply);
+        if (result == NO_ERROR) {
+            result = reply.readInt32();
+        }
+        return result;
+    }
 };
 
 // Out-of-line virtual method definition to trigger vtable emission in this
@@ -527,6 +541,13 @@ status_t BnGraphicBufferProducer::onTransact(
             reply->writeUint64(frameNumber);
             return NO_ERROR;
         }
+        case SET_SINGLE_BUFFER_MODE: {
+            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+            bool singleBufferMode = data.readInt32();
+            status_t result = setSingleBufferMode(singleBufferMode);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        }
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
index 9191b2a..7578a3d 100644 (file)
@@ -550,6 +550,9 @@ int Surface::perform(int operation, va_list args)
     case NATIVE_WINDOW_SET_SURFACE_DAMAGE:
         res = dispatchSetSurfaceDamage(args);
         break;
+    case NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE:
+        res = dispatchSetSingleBufferMode(args);
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -660,6 +663,12 @@ int Surface::dispatchSetSurfaceDamage(va_list args) {
     return NO_ERROR;
 }
 
+int Surface::dispatchSetSingleBufferMode(va_list args) {
+    bool singleBufferMode = va_arg(args, int);
+    setSingleBufferMode(singleBufferMode);
+    return NO_ERROR;
+}
+
 int Surface::connect(int api) {
     static sp<IProducerListener> listener = new DummyProducerListener();
     return connect(api, listener);
@@ -861,6 +870,19 @@ int Surface::setAsyncMode(bool async) {
     return err;
 }
 
+int Surface::setSingleBufferMode(bool singleBufferMode) {
+    ATRACE_CALL();
+    ALOGV("Surface::setSingleBufferMode (%d)", singleBufferMode);
+    Mutex::Autolock lock(mMutex);
+
+    status_t err = mGraphicBufferProducer->setSingleBufferMode(
+            singleBufferMode);
+    ALOGE_IF(err, "IGraphicsBufferProducer::setSingleBufferMode(%d) returned"
+            "%s", singleBufferMode, strerror(-err));
+
+    return err;
+}
+
 int Surface::setBuffersDimensions(uint32_t width, uint32_t height)
 {
     ATRACE_CALL();
index 5244d82..a32a90c 100644 (file)
@@ -465,4 +465,74 @@ TEST_F(BufferQueueTest, TestGenerationNumbers) {
     ASSERT_EQ(OK, mConsumer->attachBuffer(&outSlot, buffer));
 }
 
+TEST_F(BufferQueueTest, TestSingleBufferMode) {
+    createBufferQueue();
+    sp<DummyConsumer> dc(new DummyConsumer);
+    ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+    IGraphicBufferProducer::QueueBufferOutput output;
+    ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+            NATIVE_WINDOW_API_CPU, true, &output));
+
+    ASSERT_EQ(OK, mProducer->setSingleBufferMode(true));
+
+    // Get a buffer
+    int singleSlot;
+    sp<Fence> fence;
+    sp<GraphicBuffer> buffer;
+    ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+            mProducer->dequeueBuffer(&singleSlot, &fence, 0, 0, 0, 0));
+    ASSERT_EQ(OK, mProducer->requestBuffer(singleSlot, &buffer));
+
+    // Queue the buffer
+    IGraphicBufferProducer::QueueBufferInput input(0, false,
+            HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
+            NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+    ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+
+    // Repeatedly acquire and release a buffer from the consumer side, it should
+    // always return the same one.
+    BufferItem item;
+    for (int i = 0; i < 5; i++) {
+        ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+        ASSERT_EQ(singleSlot, item.mSlot);
+        ASSERT_EQ(0, item.mTimestamp);
+        ASSERT_EQ(false, item.mIsAutoTimestamp);
+        ASSERT_EQ(HAL_DATASPACE_UNKNOWN, item.mDataSpace);
+        ASSERT_EQ(Rect(0, 0, 1, 1), item.mCrop);
+        ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode);
+        ASSERT_EQ(0, item.mTransform);
+        ASSERT_EQ(Fence::NO_FENCE, item.mFence);
+
+        ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+    }
+
+    // Repeatedly queue and dequeue a buffer from the producer side, it should
+    // always return the same one.
+    int slot;
+    for (int i = 0; i < 5; i++) {
+        ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+        ASSERT_EQ(singleSlot, slot);
+        ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+    }
+
+    // Repeatedly acquire and release a buffer from the consumer side, it should
+    // always return the same one. First grabbing them from the queue and then
+    // when the queue is empty, returning the single buffer.
+    for (int i = 0; i < 10; i++) {
+        ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+        ASSERT_EQ(singleSlot, item.mSlot);
+        ASSERT_EQ(0, item.mTimestamp);
+        ASSERT_EQ(false, item.mIsAutoTimestamp);
+        ASSERT_EQ(HAL_DATASPACE_UNKNOWN, item.mDataSpace);
+        ASSERT_EQ(Rect(0, 0, 1, 1), item.mCrop);
+        ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode);
+        ASSERT_EQ(0, item.mTransform);
+        ASSERT_EQ(Fence::NO_FENCE, item.mFence);
+
+        ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+                EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+    }
+}
+
 } // namespace android
index fb29276..5bd7464 100644 (file)
@@ -1186,6 +1186,14 @@ EGLBoolean eglSurfaceAttrib(
         return setError(EGL_BAD_SURFACE, EGL_FALSE);
 
     egl_surface_t const * const s = get_surface(surface);
+
+    //XXX: temporary hack for the EGL hook-up for single buffer mode
+    if (attribute == EGL_RENDER_BUFFER && (value == EGL_BACK_BUFFER ||
+            value == EGL_SINGLE_BUFFER)) {
+        return (native_window_set_single_buffer_mode(s->win.get(),
+                value == EGL_SINGLE_BUFFER)) ? EGL_TRUE : EGL_FALSE;
+    }
+
     if (s->cnx->egl.eglSurfaceAttrib) {
         return s->cnx->egl.eglSurfaceAttrib(
                 dp->disp.dpy, s->surface, attribute, value);
index 5b85481..f8fe8e1 100644 (file)
@@ -547,6 +547,10 @@ uint64_t VirtualDisplaySurface::getNextFrameNumber() const {
     return 0;
 }
 
+status_t VirtualDisplaySurface::setSingleBufferMode(bool singleBufferMode) {
+    return mSource[SOURCE_SINK]->setSingleBufferMode(singleBufferMode);
+}
+
 void VirtualDisplaySurface::updateQueueBufferOutput(
         const QueueBufferOutput& qbo) {
     uint32_t w, h, transformHint, numPendingBuffers;
index fb37373..bde7fcf 100644 (file)
@@ -120,6 +120,7 @@ private:
     virtual status_t setGenerationNumber(uint32_t generationNumber);
     virtual String8 getConsumerName() const override;
     virtual uint64_t getNextFrameNumber() const override;
+    virtual status_t setSingleBufferMode(bool singleBufferMode);
 
     //
     // Utility methods
index 68b6b86..16879ca 100644 (file)
@@ -131,6 +131,10 @@ uint64_t MonitoredProducer::getNextFrameNumber() const {
     return mProducer->getNextFrameNumber();
 }
 
+status_t MonitoredProducer::setSingleBufferMode(bool singleBufferMode) {
+    return mProducer->setSingleBufferMode(singleBufferMode);
+}
+
 IBinder* MonitoredProducer::onAsBinder() {
     return IInterface::asBinder(mProducer).get();
 }
index 4d2ed6e..c2b2e24 100644 (file)
@@ -59,6 +59,7 @@ public:
     virtual String8 getConsumerName() const override;
     virtual uint64_t getNextFrameNumber() const override;
     virtual IBinder* onAsBinder();
+    virtual status_t setSingleBufferMode(bool singleBufferMode);
 
 private:
     sp<IGraphicBufferProducer> mProducer;