OSDN Git Service

Split BufferQueue into core + producer + consumer
authorDan Stoza <stoza@google.com>
Fri, 28 Feb 2014 19:17:17 +0000 (11:17 -0800)
committerDan Stoza <stoza@google.com>
Tue, 4 Mar 2014 17:33:14 +0000 (09:33 -0800)
Change-Id: Idc39f1e511d68ce4f02202d35425a419bc0bcd92

include/gui/BufferItem.h [new file with mode: 0644]
include/gui/BufferQueueConsumer.h [new file with mode: 0644]
include/gui/BufferQueueCore.h [new file with mode: 0644]
include/gui/BufferQueueProducer.h [new file with mode: 0644]
include/gui/BufferSlot.h [new file with mode: 0644]
libs/gui/Android.mk
libs/gui/BufferItem.cpp [new file with mode: 0644]
libs/gui/BufferQueueConsumer.cpp [new file with mode: 0644]
libs/gui/BufferQueueCore.cpp [new file with mode: 0644]
libs/gui/BufferQueueProducer.cpp [new file with mode: 0644]
libs/gui/BufferSlot.cpp [new file with mode: 0644]

diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h
new file mode 100644 (file)
index 0000000..5effd10
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_BUFFERITEM_H
+#define ANDROID_GUI_BUFFERITEM_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <gui/IGraphicBufferConsumer.h>
+
+#include <ui/Rect.h>
+
+#include <utils/Flattenable.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class Fence;
+class GraphicBuffer;
+
+class BufferItem : public Flattenable<BufferItem> {
+    friend class Flattenable<BufferItem>;
+    size_t getPodSize() const;
+    size_t getFlattenedSize() const;
+    size_t getFdCount() const;
+    status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
+    status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
+
+    public:
+    // The default value of mBuf, used to indicate this doesn't correspond to a slot.
+    enum { INVALID_BUFFER_SLOT = -1 };
+    BufferItem();
+    operator IGraphicBufferConsumer::BufferItem() const;
+
+    static const char* scalingModeName(uint32_t scalingMode);
+
+    // mGraphicBuffer points to the buffer allocated for this slot, or is NULL
+    // if the buffer in this slot has been acquired in the past (see
+    // BufferSlot.mAcquireCalled).
+    sp<GraphicBuffer> mGraphicBuffer;
+
+    // mFence is a fence that will signal when the buffer is idle.
+    sp<Fence> mFence;
+
+    // mCrop is the current crop rectangle for this buffer slot.
+    Rect mCrop;
+
+    // mTransform is the current transform flags for this buffer slot.
+    // refer to NATIVE_WINDOW_TRANSFORM_* in <window.h>
+    uint32_t mTransform;
+
+    // mScalingMode is the current scaling mode for this buffer slot.
+    // refer to NATIVE_WINDOW_SCALING_* in <window.h>
+    uint32_t mScalingMode;
+
+    // mTimestamp is the current timestamp for this buffer slot. This gets
+    // to set by queueBuffer each time this slot is queued. This value
+    // is guaranteed to be monotonically increasing for each newly
+    // acquired buffer.
+    int64_t mTimestamp;
+
+    // mIsAutoTimestamp indicates whether mTimestamp was generated
+    // automatically when the buffer was queued.
+    bool mIsAutoTimestamp;
+
+    // mFrameNumber is the number of the queued frame for this slot.
+    uint64_t mFrameNumber;
+
+    // mSlot is the slot index of this buffer (default INVALID_BUFFER_SLOT).
+    int mSlot;
+
+    // mIsDroppable whether this buffer was queued with the
+    // property that it can be replaced by a new buffer for the purpose of
+    // making sure dequeueBuffer() won't block.
+    // i.e.: was the BufferQueue in "mDequeueBufferCannotBlock" when this buffer
+    // was queued.
+    bool mIsDroppable;
+
+    // Indicates whether this buffer has been seen by a consumer yet
+    bool mAcquireCalled;
+
+    // Indicates this buffer must be transformed by the inverse transform of the screen
+    // it is displayed onto. This is applied after mTransform.
+    bool mTransformToDisplayInverse;
+};
+
+} // namespace android
+
+#endif
diff --git a/include/gui/BufferQueueConsumer.h b/include/gui/BufferQueueConsumer.h
new file mode 100644 (file)
index 0000000..0980ebb
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_BUFFERQUEUECONSUMER_H
+#define ANDROID_GUI_BUFFERQUEUECONSUMER_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <gui/BufferQueueCore.h>
+#include <gui/IGraphicBufferConsumer.h>
+
+namespace android {
+
+class BufferQueueCore;
+
+class BufferQueueConsumer : public BnGraphicBufferConsumer {
+
+public:
+    BufferQueueConsumer(const sp<BufferQueueCore>& core);
+    virtual ~BufferQueueConsumer();
+
+    // acquireBuffer attempts to acquire ownership of the next pending buffer in
+    // the BufferQueue. If no buffer is pending then it returns
+    // NO_BUFFER_AVAILABLE. If a buffer is successfully acquired, the
+    // information about the buffer is returned in BufferItem.  If the buffer
+    // returned had previously been acquired then the BufferItem::mGraphicBuffer
+    // field of buffer is set to NULL and it is assumed that the consumer still
+    // holds a reference to the buffer.
+    //
+    // If expectedPresent is nonzero, it indicates the time when the buffer
+    // will be displayed on screen. If the buffer's timestamp is farther in the
+    // future, the buffer won't be acquired, and PRESENT_LATER will be
+    // returned.  The presentation time is in nanoseconds, and the time base
+    // is CLOCK_MONOTONIC.
+    virtual status_t acquireBuffer(BufferItem* outBuffer,
+            nsecs_t expectedPresent);
+
+    // releaseBuffer releases a buffer slot from the consumer back to the
+    // BufferQueue.  This may be done while the buffer's contents are still
+    // being accessed.  The fence will signal when the buffer is no longer
+    // in use. frameNumber is used to indentify the exact buffer returned.
+    //
+    // If releaseBuffer returns STALE_BUFFER_SLOT, then the consumer must free
+    // any references to the just-released buffer that it might have, as if it
+    // had received a onBuffersReleased() call with a mask set for the released
+    // buffer.
+    //
+    // Note that the dependencies on EGL will be removed once we switch to using
+    // the Android HW Sync HAL.
+    virtual status_t releaseBuffer(int slot, uint64_t frameNumber,
+            const sp<Fence>& releaseFence, EGLDisplay display,
+            EGLSyncKHR fence);
+
+    // connect connects a consumer to the BufferQueue.  Only one
+    // consumer may be connected, and when that consumer disconnects the
+    // BufferQueue is placed into the "abandoned" state, causing most
+    // interactions with the BufferQueue by the producer to fail.
+    // controlledByApp indicates whether the consumer is controlled by
+    // the application.
+    //
+    // consumerListener may not be NULL.
+    virtual status_t connect(const sp<IConsumerListener>& consumerListener,
+            bool controlledByApp);
+
+    // disconnect disconnects a consumer from the BufferQueue. All
+    // buffers will be freed and the BufferQueue is placed in the "abandoned"
+    // state, causing most interactions with the BufferQueue by the producer to
+    // fail.
+    virtual status_t disconnect();
+
+    // getReleasedBuffers sets the value pointed to by outSlotMask to a bit mask
+    // indicating which buffer slots have been released by the BufferQueue
+    // but have not yet been released by the consumer.
+    //
+    // This should be called from the onBuffersReleased() callback.
+    virtual status_t getReleasedBuffers(uint32_t* outSlotMask);
+
+    // setDefaultBufferSize is used to set the size of buffers returned by
+    // dequeueBuffer when a width and height of zero is requested.  Default
+    // is 1x1.
+    virtual status_t setDefaultBufferSize(uint32_t width, uint32_t height);
+
+    // setDefaultMaxBufferCount sets the default value for the maximum buffer
+    // count (the initial default is 2). If the producer has requested a
+    // buffer count using setBufferCount, the default buffer count will only
+    // take effect if the producer sets the count back to zero.
+    //
+    // The count must be between 2 and NUM_BUFFER_SLOTS, inclusive.
+    virtual 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 connect().
+    virtual 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.
+    virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers);
+
+    // setConsumerName sets the name used in logging
+    virtual void setConsumerName(const String8& name);
+
+    // setDefaultBufferFormat allows the BufferQueue to create
+    // GraphicBuffers of a defaultFormat if no format is specified
+    // in dequeueBuffer.  Formats are enumerated in graphics.h; the
+    // initial default is HAL_PIXEL_FORMAT_RGBA_8888.
+    virtual status_t setDefaultBufferFormat(uint32_t defaultFormat);
+
+    // setConsumerUsageBits will turn on additional usage bits for dequeueBuffer.
+    // These are merged with the bits passed to dequeueBuffer.  The values are
+    // enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER; the default is 0.
+    virtual status_t setConsumerUsageBits(uint32_t usage);
+
+    // setTransformHint bakes in rotation to buffers so overlays can be used.
+    // The values are enumerated in window.h, e.g.
+    // NATIVE_WINDOW_TRANSFORM_ROT_90.  The default is 0 (no transform).
+    virtual status_t setTransformHint(uint32_t hint);
+
+    // dump our state in a String
+    virtual void dump(String8& result, const char* prefix) const;
+
+private:
+    sp<BufferQueueCore> mCore;
+    BufferQueueCore::SlotsType& mSlots;
+    String8 mConsumerName; // Cached from mCore. Updated on setConsumerName.
+
+}; // class BufferQueueConsumer
+
+} // namespace android
+
+#endif
diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h
new file mode 100644 (file)
index 0000000..ef11c61
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_BUFFERQUEUECORE_H
+#define ANDROID_GUI_BUFFERQUEUECORE_H
+
+#include <gui/BufferSlot.h>
+
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <utils/Trace.h>
+#include <utils/Vector.h>
+
+#define BQ_LOGV(x, ...) ALOGV("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
+#define BQ_LOGD(x, ...) ALOGD("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
+#define BQ_LOGI(x, ...) ALOGI("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
+#define BQ_LOGW(x, ...) ALOGW("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
+#define BQ_LOGE(x, ...) ALOGE("[%s] "x, mConsumerName.string(), ##__VA_ARGS__)
+
+#define ATRACE_BUFFER_INDEX(index)                                   \
+    if (ATRACE_ENABLED()) {                                          \
+        char ___traceBuf[1024];                                      \
+        snprintf(___traceBuf, 1024, "%s: %d",                        \
+                mCore->mConsumerName.string(), (index));             \
+        android::ScopedTrace ___bufTracer(ATRACE_TAG, ___traceBuf);  \
+    }
+
+namespace android {
+
+class BufferItem;
+class IBinder;
+class IConsumerListener;
+class IGraphicBufferAlloc;
+
+class BufferQueueCore : public virtual RefBase {
+
+    friend class BufferQueueProducer;
+    friend class BufferQueueConsumer;
+
+public:
+    // BufferQueue will keep track of at most this value of buffers. Attempts
+    // at runtime to increase the number of buffers past this will fail.
+    enum { NUM_BUFFER_SLOTS = 32 };
+
+    // Used as a placeholder slot number when the value isn't pointing to an
+    // existing buffer.
+    enum { INVALID_BUFFER_SLOT = -1 }; // TODO: Extract from IGBC::BufferItem
+
+    // We reserve two slots in order to guarantee that the producer and
+    // consumer can run asynchronously.
+    enum { MAX_MAX_ACQUIRED_BUFFERS = NUM_BUFFER_SLOTS - 2 };
+
+    // The default API number used to indicate that no producer is connected
+    enum { NO_CONNECTED_API = 0 };
+
+    typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
+    typedef Vector<BufferItem> Fifo;
+
+    // BufferQueueCore manages a pool of gralloc memory slots to be used by
+    // producers and consumers. allocator is used to allocate all the needed
+    // gralloc buffers.
+    BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator = NULL);
+    virtual ~BufferQueueCore();
+
+private:
+    void dump(String8& result, const char* prefix) const;
+
+    int getMinUndequeuedBufferCountLocked(bool async) const;
+    int getMinMaxBufferCountLocked(bool async) const;
+    int getMaxBufferCountLocked(bool async) const;
+    status_t setDefaultMaxBufferCountLocked(int count);
+    void freeBufferLocked(int slot);
+    void freeAllBuffersLocked();
+    bool stillTracking(const BufferItem* item) const;
+
+    const sp<IGraphicBufferAlloc>& mAllocator;
+    mutable Mutex mMutex;
+    bool mIsAbandoned;
+    bool mConsumerControlledByApp;
+    String8 mConsumerName;
+    sp<IConsumerListener> mConsumerListener;
+    uint32_t mConsumerUsageBits;
+    int mConnectedApi;
+    sp<IBinder> mConnectedProducerToken;
+    BufferSlot mSlots[NUM_BUFFER_SLOTS];
+    Fifo mQueue;
+    int mOverrideMaxBufferCount;
+    mutable Condition mDequeueCondition;
+    bool mUseAsyncBuffer;
+    bool mDequeueBufferCannotBlock;
+    uint32_t mDefaultBufferFormat;
+    int mDefaultWidth;
+    int mDefaultHeight;
+    int mDefaultMaxBufferCount;
+    int mMaxAcquiredBufferCount;
+    bool mBufferHasBeenQueued;
+    uint64_t mFrameCounter;
+    uint32_t mTransformHint;
+
+}; // class BufferQueueCore
+
+} // namespace android
+
+#endif
diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h
new file mode 100644 (file)
index 0000000..7f063aa
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_BUFFERQUEUEPRODUCER_H
+#define ANDROID_GUI_BUFFERQUEUEPRODUCER_H
+
+#include <gui/BufferQueueCore.h>
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+class BufferSlot;
+
+class BufferQueueProducer : public BnGraphicBufferProducer,
+                            private IBinder::DeathRecipient {
+
+public:
+    BufferQueueProducer(const sp<BufferQueueCore>& core);
+    virtual ~BufferQueueProducer();
+
+    // requestBuffer returns the GraphicBuffer for slot N.
+    //
+    // In normal operation, this is called the first time slot N is returned
+    // by dequeueBuffer.  It must be called again if dequeueBuffer returns
+    // flags indicating that previously-returned buffers are no longer valid.
+    virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf);
+
+    // setBufferCount updates the number of available buffer slots.  If this
+    // method succeeds, buffer slots will be both unallocated and owned by
+    // the BufferQueue object (i.e. they are not owned by the producer or
+    // consumer).
+    //
+    // This will fail if the producer has dequeued any buffers, or if
+    // bufferCount is invalid.  bufferCount must generally be a value
+    // between the minimum undequeued buffer count (exclusive) and NUM_BUFFER_SLOTS
+    // (inclusive).  It may also be set to zero (the default) to indicate
+    // that the producer does not wish to set a value.  The minimum value
+    // can be obtained by calling query(NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+    // ...).
+    //
+    // This may only be called by the producer.  The consumer will be told
+    // to discard buffers through the onBuffersReleased callback.
+    virtual status_t setBufferCount(int bufferCount);
+
+    // dequeueBuffer gets the next buffer slot index for the producer to use.
+    // If a buffer slot is available then that slot index is written to the
+    // location pointed to by the buf argument and a status of OK is returned.
+    // If no slot is available then a status of -EBUSY is returned and buf is
+    // unmodified.
+    //
+    // The outFence parameter will be updated to hold the fence associated with
+    // the buffer. The contents of the buffer must not be overwritten until the
+    // fence signals. If the fence is Fence::NO_FENCE, the buffer may be
+    // written immediately.
+    //
+    // The width and height parameters must be no greater than the minimum of
+    // GL_MAX_VIEWPORT_DIMS and GL_MAX_TEXTURE_SIZE (see: glGetIntegerv).
+    // An error due to invalid dimensions might not be reported until
+    // updateTexImage() is called.  If width and height are both zero, the
+    // default values specified by setDefaultBufferSize() are used instead.
+    //
+    // The pixel formats are enumerated in graphics.h, e.g.
+    // HAL_PIXEL_FORMAT_RGBA_8888.  If the format is 0, the default format
+    // will be used.
+    //
+    // The usage argument specifies gralloc buffer usage flags.  The values
+    // are enumerated in gralloc.h, e.g. GRALLOC_USAGE_HW_RENDER.  These
+    // will be merged with the usage flags specified by setConsumerUsageBits.
+    //
+    // The return value may be a negative error value or a non-negative
+    // collection of flags.  If the flags are set, the return values are
+    // valid, but additional actions must be performed.
+    //
+    // If IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION is set, the
+    // producer must discard cached GraphicBuffer references for the slot
+    // returned in buf.
+    // If IGraphicBufferProducer::RELEASE_ALL_BUFFERS is set, the producer
+    // must discard cached GraphicBuffer references for all slots.
+    //
+    // In both cases, the producer will need to call requestBuffer to get a
+    // GraphicBuffer handle for the returned slot.
+    virtual status_t dequeueBuffer(int *outSlot, sp<Fence>* outFence, bool async,
+            uint32_t width, uint32_t height, uint32_t format, uint32_t usage);
+
+    // queueBuffer returns a filled buffer to the BufferQueue.
+    //
+    // Additional data is provided in the QueueBufferInput struct.  Notably,
+    // a timestamp must be provided for the buffer. The timestamp is in
+    // nanoseconds, and must be monotonically increasing. Its other semantics
+    // (zero point, etc) are producer-specific and should be documented by the
+    // producer.
+    //
+    // The caller may provide a fence that signals when all rendering
+    // operations have completed.  Alternatively, NO_FENCE may be used,
+    // indicating that the buffer is ready immediately.
+    //
+    // Some values are returned in the output struct: the current settings
+    // for default width and height, the current transform hint, and the
+    // number of queued buffers.
+    virtual status_t queueBuffer(int slot,
+            const QueueBufferInput& input, QueueBufferOutput* output);
+
+    // cancelBuffer returns a dequeued buffer to the BufferQueue, but doesn't
+    // queue it for use by the consumer.
+    //
+    // The buffer will not be overwritten until the fence signals.  The fence
+    // will usually be the one obtained from dequeueBuffer.
+    virtual void cancelBuffer(int slot, const sp<Fence>& fence);
+
+    // Query native window attributes.  The "what" values are enumerated in
+    // window.h (e.g. NATIVE_WINDOW_FORMAT).
+    virtual int query(int what, int* outValue);
+
+    // connect attempts to connect a producer API to the BufferQueue.  This
+    // must be called before any other IGraphicBufferProducer methods are
+    // called except for getAllocator.  A consumer must already be connected.
+    //
+    // This method will fail if connect was previously called on the
+    // BufferQueue and no corresponding disconnect call was made (i.e. if
+    // it's still connected to a producer).
+    //
+    // APIs are enumerated in window.h (e.g. NATIVE_WINDOW_API_CPU).
+    virtual status_t connect(const sp<IBinder>& token,
+            int api, bool producerControlledByApp, QueueBufferOutput* output);
+
+    // disconnect attempts to disconnect a producer API from the BufferQueue.
+    // Calling this method will cause any subsequent calls to other
+    // IGraphicBufferProducer methods to fail except for getAllocator and connect.
+    // Successfully calling connect after this will allow the other methods to
+    // succeed again.
+    //
+    // This method will fail if the the BufferQueue is not currently
+    // connected to the specified producer API.
+    virtual status_t disconnect(int api);
+
+private:
+    // This is required by the IBinder::DeathRecipient interface
+    virtual void binderDied(const wp<IBinder>& who);
+
+    sp<BufferQueueCore> mCore;
+    BufferQueueCore::SlotsType& mSlots;
+
+    // This is a cached copy of the name stored in the BufferQueueCore.
+    // It's updated during connect and dequeueBuffer (which should catch
+    // most updates).
+    String8 mConsumerName;
+
+}; // class BufferQueueProducer
+
+} // namespace android
+
+#endif
diff --git a/include/gui/BufferSlot.h b/include/gui/BufferSlot.h
new file mode 100644 (file)
index 0000000..2c4b43f
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_GUI_BUFFERSLOT_H
+#define ANDROID_GUI_BUFFERSLOT_H
+
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <utils/StrongPointer.h>
+
+namespace android {
+
+class Fence;
+
+struct BufferSlot {
+
+    BufferSlot()
+    : mEglDisplay(EGL_NO_DISPLAY),
+      mBufferState(BufferSlot::FREE),
+      mRequestBufferCalled(false),
+      mFrameNumber(0),
+      mEglFence(EGL_NO_SYNC_KHR),
+      mAcquireCalled(false),
+      mNeedsCleanupOnRelease(false) {
+    }
+
+    // mGraphicBuffer points to the buffer allocated for this slot or is NULL
+    // if no buffer has been allocated.
+    sp<GraphicBuffer> mGraphicBuffer;
+
+    // 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.
+    BufferState mBufferState;
+
+    // mRequestBufferCalled is used for validating that the producer did
+    // call requestBuffer() when told to do so. Technically this is not
+    // needed but useful for debugging and catching producer bugs.
+    bool mRequestBufferCalled;
+
+    // mFrameNumber is the number of the queued frame for this slot.  This
+    // is used to dequeue buffers in LRU order (useful because buffers
+    // may be released before their release fence is signaled).
+    uint64_t mFrameNumber;
+
+    // mEglFence is the EGL sync object that must signal before the buffer
+    // associated with this buffer slot may be dequeued. It is initialized
+    // to EGL_NO_SYNC_KHR when the buffer is created and may be set to a
+    // new sync object in releaseBuffer.  (This is deprecated in favor of
+    // mFence, below.)
+    EGLSyncKHR mEglFence;
+
+    // mFence is a fence which will signal when work initiated by the
+    // previous owner of the buffer is finished. When the buffer is FREE,
+    // the fence indicates when the consumer has finished reading
+    // from the buffer, or when the producer has finished writing if it
+    // called cancelBuffer after queueing some writes. When the buffer is
+    // QUEUED, it indicates when the producer has finished filling the
+    // buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been
+    // passed to the consumer or producer along with ownership of the
+    // buffer, and mFence is set to NO_FENCE.
+    sp<Fence> mFence;
+
+    // Indicates whether this buffer has been seen by a consumer yet
+    bool mAcquireCalled;
+
+    // Indicates whether this buffer needs to be cleaned up by the
+    // consumer.  This is set when a buffer in ACQUIRED state is freed.
+    // It causes releaseBuffer to return STALE_BUFFER_SLOT.
+    bool mNeedsCleanupOnRelease;
+};
+
+} // namespace android
+
+#endif
index c14c950..0a77317 100644 (file)
@@ -5,8 +5,13 @@ LOCAL_SRC_FILES:= \
        IGraphicBufferConsumer.cpp \
        IConsumerListener.cpp \
        BitTube.cpp \
+       BufferItem.cpp \
        BufferItemConsumer.cpp \
        BufferQueue.cpp \
+       BufferQueueConsumer.cpp \
+       BufferQueueCore.cpp \
+       BufferQueueProducer.cpp \
+       BufferSlot.cpp \
        ConsumerBase.cpp \
        CpuConsumer.cpp \
        DisplayEventReceiver.cpp \
diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp
new file mode 100644 (file)
index 0000000..cccb38e
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/BufferItem.h>
+
+#include <ui/Fence.h>
+#include <ui/GraphicBuffer.h>
+
+#include <system/window.h>
+
+namespace android {
+
+BufferItem::BufferItem() :
+    mTransform(0),
+    mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
+    mTimestamp(0),
+    mIsAutoTimestamp(false),
+    mFrameNumber(0),
+    mSlot(INVALID_BUFFER_SLOT),
+    mIsDroppable(false),
+    mAcquireCalled(false),
+    mTransformToDisplayInverse(false) {
+    mCrop.makeInvalid();
+}
+
+BufferItem::operator IGraphicBufferConsumer::BufferItem() const {
+    IGraphicBufferConsumer::BufferItem bufferItem;
+    bufferItem.mTransform = mTransform;
+    bufferItem.mScalingMode = mScalingMode;
+    bufferItem.mTimestamp = mTimestamp;
+    bufferItem.mIsAutoTimestamp = mIsAutoTimestamp;
+    bufferItem.mFrameNumber = mFrameNumber;
+    bufferItem.mBuf = mSlot;
+    bufferItem.mIsDroppable = mIsDroppable;
+    bufferItem.mAcquireCalled = mAcquireCalled;
+    bufferItem.mTransformToDisplayInverse = mTransformToDisplayInverse;
+    bufferItem.mCrop = mCrop;
+    return bufferItem;
+}
+
+size_t BufferItem::getPodSize() const {
+    size_t c =  sizeof(mCrop) +
+            sizeof(mTransform) +
+            sizeof(mScalingMode) +
+            sizeof(mTimestamp) +
+            sizeof(mIsAutoTimestamp) +
+            sizeof(mFrameNumber) +
+            sizeof(mSlot) +
+            sizeof(mIsDroppable) +
+            sizeof(mAcquireCalled) +
+            sizeof(mTransformToDisplayInverse);
+    return c;
+}
+
+size_t BufferItem::getFlattenedSize() const {
+    size_t c = 0;
+    if (mGraphicBuffer != 0) {
+        c += mGraphicBuffer->getFlattenedSize();
+        FlattenableUtils::align<4>(c);
+    }
+    if (mFence != 0) {
+        c += mFence->getFlattenedSize();
+        FlattenableUtils::align<4>(c);
+    }
+    return sizeof(int32_t) + c + getPodSize();
+}
+
+size_t BufferItem::getFdCount() const {
+    size_t c = 0;
+    if (mGraphicBuffer != 0) {
+        c += mGraphicBuffer->getFdCount();
+    }
+    if (mFence != 0) {
+        c += mFence->getFdCount();
+    }
+    return c;
+}
+
+status_t BufferItem::flatten(
+        void*& buffer, size_t& size, int*& fds, size_t& count) const {
+
+    // make sure we have enough space
+    if (count < BufferItem::getFlattenedSize()) {
+        return NO_MEMORY;
+    }
+
+    // content flags are stored first
+    uint32_t& flags = *static_cast<uint32_t*>(buffer);
+
+    // advance the pointer
+    FlattenableUtils::advance(buffer, size, sizeof(uint32_t));
+
+    flags = 0;
+    if (mGraphicBuffer != 0) {
+        status_t err = mGraphicBuffer->flatten(buffer, size, fds, count);
+        if (err) return err;
+        size -= FlattenableUtils::align<4>(buffer);
+        flags |= 1;
+    }
+    if (mFence != 0) {
+        status_t err = mFence->flatten(buffer, size, fds, count);
+        if (err) return err;
+        size -= FlattenableUtils::align<4>(buffer);
+        flags |= 2;
+    }
+
+    // check we have enough space (in case flattening the fence/graphicbuffer lied to us)
+    if (size < getPodSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::write(buffer, size, mCrop);
+    FlattenableUtils::write(buffer, size, mTransform);
+    FlattenableUtils::write(buffer, size, mScalingMode);
+    FlattenableUtils::write(buffer, size, mTimestamp);
+    FlattenableUtils::write(buffer, size, mIsAutoTimestamp);
+    FlattenableUtils::write(buffer, size, mFrameNumber);
+    FlattenableUtils::write(buffer, size, mSlot);
+    FlattenableUtils::write(buffer, size, mIsDroppable);
+    FlattenableUtils::write(buffer, size, mAcquireCalled);
+    FlattenableUtils::write(buffer, size, mTransformToDisplayInverse);
+
+    return NO_ERROR;
+}
+
+status_t BufferItem::unflatten(
+        void const*& buffer, size_t& size, int const*& fds, size_t& count) {
+
+    if (size < sizeof(uint32_t))
+        return NO_MEMORY;
+
+    uint32_t flags = 0;
+    FlattenableUtils::read(buffer, size, flags);
+
+    if (flags & 1) {
+        mGraphicBuffer = new GraphicBuffer();
+        status_t err = mGraphicBuffer->unflatten(buffer, size, fds, count);
+        if (err) return err;
+        size -= FlattenableUtils::align<4>(buffer);
+    }
+
+    if (flags & 2) {
+        mFence = new Fence();
+        status_t err = mFence->unflatten(buffer, size, fds, count);
+        if (err) return err;
+        size -= FlattenableUtils::align<4>(buffer);
+    }
+
+    // check we have enough space
+    if (size < getPodSize()) {
+        return NO_MEMORY;
+    }
+
+    FlattenableUtils::read(buffer, size, mCrop);
+    FlattenableUtils::read(buffer, size, mTransform);
+    FlattenableUtils::read(buffer, size, mScalingMode);
+    FlattenableUtils::read(buffer, size, mTimestamp);
+    FlattenableUtils::read(buffer, size, mIsAutoTimestamp);
+    FlattenableUtils::read(buffer, size, mFrameNumber);
+    FlattenableUtils::read(buffer, size, mSlot);
+    FlattenableUtils::read(buffer, size, mIsDroppable);
+    FlattenableUtils::read(buffer, size, mAcquireCalled);
+    FlattenableUtils::read(buffer, size, mTransformToDisplayInverse);
+
+    return NO_ERROR;
+}
+
+const char* BufferItem::scalingModeName(uint32_t scalingMode) {
+    switch (scalingMode) {
+        case NATIVE_WINDOW_SCALING_MODE_FREEZE: return "FREEZE";
+        case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW: return "SCALE_TO_WINDOW";
+        case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP: return "SCALE_CROP";
+        default: return "Unknown";
+    }
+}
+
+} // namespace android
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
new file mode 100644 (file)
index 0000000..7b70df1
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gui/BufferItem.h>
+#include <gui/BufferQueueConsumer.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/IConsumerListener.h>
+
+namespace android {
+
+BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
+    mCore(core),
+    mSlots(core->mSlots),
+    mConsumerName() {}
+
+BufferQueueConsumer::~BufferQueueConsumer() {}
+
+status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
+        nsecs_t expectedPresent) {
+    ATRACE_CALL();
+    Mutex::Autolock lock(mCore->mMutex);
+
+    // Check that the consumer doesn't currently have the maximum number of
+    // buffers acquired. We allow the max buffer count to be exceeded by one
+    // buffer so that the consumer can successfully set up the newly acquired
+    // buffer before releasing the old one.
+    int numAcquiredBuffers = 0;
+    for (int s = 0; s < BufferQueueCore::NUM_BUFFER_SLOTS; ++s) {
+        if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
+            ++numAcquiredBuffers;
+        }
+    }
+    if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
+        BQ_LOGE("acquireBuffer: max acquired buffer count reached: %d (max %d)",
+                numAcquiredBuffers, mCore->mMaxAcquiredBufferCount);
+        return INVALID_OPERATION;
+    }
+
+    // Check if the queue is empty.
+    // 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()) {
+        return NO_BUFFER_AVAILABLE;
+    }
+
+    BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
+
+    // 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) {
+        const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second
+
+        // The 'expectedPresent' argument indicates when the buffer is expected
+        // to be presented on-screen. If the buffer's desired present time is
+        // earlier (less) than expectedPresent -- meaning it will be displayed
+        // on time or possibly late if we show it as soon as possible -- we
+        // acquire and return it. If we don't want to display it until after the
+        // expectedPresent time, we return PRESENT_LATER without acquiring it.
+        //
+        // To be safe, we don't defer acquisition if expectedPresent is more
+        // than one second in the future beyond the desired present time
+        // (i.e., we'd be holding the buffer for a long time).
+        //
+        // NOTE: Code assumes monotonic time values from the system clock
+        // are positive.
+
+        // Start by checking to see if we can drop frames. We skip this check if
+        // the timestamps are being auto-generated by Surface. If the app isn't
+        // generating timestamps explicitly, it probably doesn't want frames to
+        // be discarded based on them.
+        while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) {
+            // If entry[1] is timely, drop entry[0] (and repeat). We apply an
+            // additional criterion here: we only drop the earlier buffer if our
+            // desiredPresent falls within +/- 1 second of the expected present.
+            // Otherwise, bogus desiredPresent times (e.g., 0 or a small
+            // relative timestamp), which normally mean "ignore the timestamp
+            // and acquire immediately", would cause us to drop frames.
+            //
+            // We may want to add an additional criterion: don't drop the
+            // earlier buffer if entry[1]'s fence hasn't signaled yet.
+            const BufferItem& bufferItem(mCore->mQueue[1]);
+            nsecs_t desiredPresent = bufferItem.mTimestamp;
+            if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC ||
+                    desiredPresent > expectedPresent) {
+                // This buffer is set to display in the near future, or
+                // desiredPresent is garbage. Either way we don't want to drop
+                // the previous buffer just to get this on the screen sooner.
+                BQ_LOGV("acquireBuffer: nodrop desire=%lld expect=%lld "
+                        "(%lld) now=%lld", desiredPresent, expectedPresent,
+                        desiredPresent - expectedPresent,
+                        systemTime(CLOCK_MONOTONIC));
+                break;
+            }
+
+            BQ_LOGV("acquireBuffer: drop desire=%lld expect=%lld size=%d",
+                    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->mQueue.erase(front);
+            front = mCore->mQueue.begin();
+        }
+
+        // See if the front buffer is due
+        nsecs_t desiredPresent = front->mTimestamp;
+        if (desiredPresent > expectedPresent &&
+                desiredPresent < expectedPresent + MAX_REASONABLE_NSEC) {
+            BQ_LOGV("acquireBuffer: defer desire=%lld expect=%lld "
+                    "(%lld) now=%lld", desiredPresent, expectedPresent,
+                    desiredPresent - expectedPresent,
+                    systemTime(CLOCK_MONOTONIC));
+            return PRESENT_LATER;
+        }
+
+        BQ_LOGV("acquireBuffer: accept desire=%lld expect=%lld "
+                "(%lld) now=%lld", desiredPresent, expectedPresent,
+                desiredPresent - expectedPresent,
+                systemTime(CLOCK_MONOTONIC));
+    }
+
+    int slot = front->mSlot;
+    *outBuffer = *front;
+    ATRACE_BUFFER_INDEX(slot);
+
+    BQ_LOGV("acquireBuffer: acquiring { slot=%d/%llu buffer=%p }",
+            slot, front->mFrameNumber, front->mGraphicBuffer->handle);
+    // If the front buffer is still being tracked, update its slot state
+    if (mCore->stillTracking(front)) {
+        mSlots[slot].mAcquireCalled = true;
+        mSlots[slot].mNeedsCleanupOnRelease = false;
+        mSlots[slot].mBufferState = BufferSlot::ACQUIRED;
+        mSlots[slot].mFence = Fence::NO_FENCE;
+    }
+
+    // If the buffer has previously been acquired by the consumer, set
+    // mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer
+    // on the consumer side
+    if (outBuffer->mAcquireCalled) {
+        outBuffer->mGraphicBuffer = NULL;
+    }
+
+    mCore->mQueue.erase(front);
+    // TODO: Should this call be after we free a slot while dropping buffers?
+    // Simply acquiring the next buffer doesn't enable a producer to dequeue.
+    mCore->mDequeueCondition.broadcast();
+
+    ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
+
+    return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
+        const sp<Fence>& releaseFence, EGLDisplay eglDisplay,
+        EGLSyncKHR eglFence) {
+    ATRACE_CALL();
+    ATRACE_BUFFER_INDEX(slot);
+
+    if (slot == BufferQueueCore::INVALID_BUFFER_SLOT || releaseFence == NULL) {
+        return BAD_VALUE;
+    }
+
+    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) {
+        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 -EINVAL;
+        }
+        ++current;
+    }
+
+    if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
+        mSlots[slot].mEglDisplay = eglDisplay;
+        mSlots[slot].mEglFence = eglFence;
+        mSlots[slot].mFence = releaseFence;
+        mSlots[slot].mBufferState = BufferSlot::FREE;
+    } else if (mSlots[slot].mNeedsCleanupOnRelease) {
+        BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d "
+                "(state = %d)", slot, mSlots[slot].mBufferState);
+        mSlots[slot].mNeedsCleanupOnRelease = false;
+        return STALE_BUFFER_SLOT;
+    } else {
+        BQ_LOGV("releaseBuffer: attempted to release buffer slot %d "
+                "but its state was %d", slot, mSlots[slot].mBufferState);
+        return -EINVAL;
+    }
+
+    mCore->mDequeueCondition.broadcast();
+
+    return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::connect(
+        const sp<IConsumerListener>& consumerListener, bool controlledByApp) {
+    ATRACE_CALL();
+
+    if (consumerListener == NULL) {
+        BQ_LOGE("connect(C): consumerListener may not be NULL");
+        return BAD_VALUE;
+    }
+
+    BQ_LOGV("connect(C): controlledByApp=%s",
+            controlledByApp ? "true" : "false");
+
+    Mutex::Autolock lock(mCore->mMutex);
+
+    if (mCore->mIsAbandoned) {
+        BQ_LOGE("connect(C): BufferQueue has been abandoned");
+        return NO_INIT;
+    }
+
+    mCore->mConsumerListener = consumerListener;
+    mCore->mConsumerControlledByApp = controlledByApp;
+
+    return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::disconnect() {
+    ATRACE_CALL();
+
+    BQ_LOGV("disconnect(C)");
+
+    Mutex::Autolock lock(mCore->mMutex);
+
+    if (mCore->mConsumerListener == NULL) {
+        BQ_LOGE("disconnect(C): no consumer is connected");
+        return -EINVAL;
+    }
+
+    mCore->mIsAbandoned = true;
+    mCore->mConsumerListener = NULL;
+    mCore->mQueue.clear();
+    mCore->freeAllBuffersLocked();
+    mCore->mDequeueCondition.broadcast();
+    return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::getReleasedBuffers(uint32_t *outSlotMask) {
+    ATRACE_CALL();
+
+    if (outSlotMask == NULL) {
+        BQ_LOGE("getReleasedBuffers: outSlotMask may not be NULL");
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mCore->mMutex);
+
+    if (mCore->mIsAbandoned) {
+        BQ_LOGE("getReleasedBuffers: BufferQueue has been abandoned");
+        return NO_INIT;
+    }
+
+    uint32_t mask = 0;
+    for (int s = 0; s < BufferQueueCore::NUM_BUFFER_SLOTS; ++s) {
+        if (!mSlots[s].mAcquireCalled) {
+            mask |= (1u << s);
+        }
+    }
+
+    // Remove from the mask queued buffers for which acquire has been called,
+    // since the consumer will not receive their buffer addresses and so must
+    // retain their cached information
+    BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
+    while (current != mCore->mQueue.end()) {
+        if (current->mAcquireCalled) {
+            mask &= ~(1u << current->mSlot);
+        }
+        ++current;
+    }
+
+    BQ_LOGV("getReleasedBuffers: returning mask %#x", mask);
+    *outSlotMask = mask;
+    return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::setDefaultBufferSize(uint32_t width,
+        uint32_t height) {
+    ATRACE_CALL();
+
+    if (width == 0 || height == 0) {
+        BQ_LOGV("setDefaultBufferSize: dimensions cannot be 0 (width=%u "
+                "height=%u)", width, height);
+        return BAD_VALUE;
+    }
+
+    BQ_LOGV("setDefaultBufferSize: width=%u height=%u", width, height);
+
+    Mutex::Autolock lock(mCore->mMutex);
+    mCore->mDefaultWidth = width;
+    mCore->mDefaultHeight = height;
+    return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::setDefaultMaxBufferCount(int bufferCount) {
+    ATRACE_CALL();
+    Mutex::Autolock lock(mCore->mMutex);
+    return mCore->setDefaultMaxBufferCountLocked(bufferCount);
+}
+
+status_t BufferQueueConsumer::disableAsyncBuffer() {
+    ATRACE_CALL();
+
+    Mutex::Autolock lock(mCore->mMutex);
+
+    if (mCore->mConsumerListener != NULL) {
+        BQ_LOGE("disableAsyncBuffer: consumer already connected");
+        return INVALID_OPERATION;
+    }
+
+    BQ_LOGV("disableAsyncBuffer");
+    mCore->mUseAsyncBuffer = false;
+    return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::setMaxAcquiredBufferCount(
+        int maxAcquiredBuffers) {
+    ATRACE_CALL();
+
+    if (maxAcquiredBuffers < 1 ||
+            maxAcquiredBuffers > BufferQueueCore::MAX_MAX_ACQUIRED_BUFFERS) {
+        BQ_LOGE("setMaxAcquiredBufferCount: invalid count %d",
+                maxAcquiredBuffers);
+        return BAD_VALUE;
+    }
+
+    Mutex::Autolock lock(mCore->mMutex);
+
+    if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+        BQ_LOGE("setMaxAcquiredBufferCount: producer is already connected");
+        return INVALID_OPERATION;
+    }
+
+    BQ_LOGV("setMaxAcquiredBufferCount: %d", maxAcquiredBuffers);
+    mCore->mMaxAcquiredBufferCount = maxAcquiredBuffers;
+    return NO_ERROR;
+}
+
+void BufferQueueConsumer::setConsumerName(const String8& name) {
+    ATRACE_CALL();
+    BQ_LOGV("setConsumerName: '%s'", name.string());
+    Mutex::Autolock lock(mCore->mMutex);
+    mCore->mConsumerName = name;
+    mConsumerName = name;
+}
+
+status_t BufferQueueConsumer::setDefaultBufferFormat(uint32_t defaultFormat) {
+    ATRACE_CALL();
+    BQ_LOGV("setDefaultBufferFormat: %u", defaultFormat);
+    Mutex::Autolock lock(mCore->mMutex);
+    mCore->mDefaultBufferFormat = defaultFormat;
+    return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::setConsumerUsageBits(uint32_t usage) {
+    ATRACE_CALL();
+    BQ_LOGV("setConsumerUsageBits: %#x", usage);
+    Mutex::Autolock lock(mCore->mMutex);
+    mCore->mConsumerUsageBits = usage;
+    return NO_ERROR;
+}
+
+status_t BufferQueueConsumer::setTransformHint(uint32_t hint) {
+    ATRACE_CALL();
+    BQ_LOGV("setTransformHint: %#x", hint);
+    Mutex::Autolock lock(mCore->mMutex);
+    mCore->mTransformHint = hint;
+    return NO_ERROR;
+}
+
+void BufferQueueConsumer::dump(String8& result, const char* prefix) const {
+    mCore->dump(result, prefix);
+}
+
+} // namespace android
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
new file mode 100644 (file)
index 0000000..edce3e0
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <gui/BufferItem.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/IConsumerListener.h>
+
+template <typename T>
+static inline T max(T a, T b) { return a > b ? a : b; }
+
+namespace android {
+
+static String8 getUniqueName() {
+    static volatile int32_t counter = 0;
+    return String8::format("unnamed-%d-%d", getpid(),
+            android_atomic_inc(&counter));
+}
+
+BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) :
+    mAllocator(allocator),
+    mMutex(),
+    mIsAbandoned(false),
+    mConsumerControlledByApp(false),
+    mConsumerName(getUniqueName()),
+    mConsumerListener(),
+    mConsumerUsageBits(0),
+    mConnectedApi(NO_CONNECTED_API),
+    mConnectedProducerToken(),
+    mSlots(),
+    mQueue(),
+    mOverrideMaxBufferCount(0),
+    mDequeueCondition(),
+    mUseAsyncBuffer(true),
+    mDequeueBufferCannotBlock(false),
+    mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
+    mDefaultWidth(1),
+    mDefaultHeight(1),
+    mDefaultMaxBufferCount(2),
+    mMaxAcquiredBufferCount(1),
+    mBufferHasBeenQueued(false),
+    mFrameCounter(0),
+    mTransformHint(0) {}
+
+BufferQueueCore::~BufferQueueCore() {}
+
+void BufferQueueCore::dump(String8& result, const char* prefix) const {
+    Mutex::Autolock lock(mMutex);
+
+    String8 fifo;
+    Fifo::const_iterator current(mQueue.begin());
+    while (current != mQueue.end()) {
+        fifo.appendFormat("%02d:%p crop=[%d,%d,%d,%d], "
+                "xform=0x%02x, time=%#llx, scale=%s\n",
+                current->mSlot, current->mGraphicBuffer.get(),
+                current->mCrop.left, current->mCrop.top, current->mCrop.right,
+                current->mCrop.bottom, current->mTransform, current->mTimestamp,
+                BufferItem::scalingModeName(current->mScalingMode));
+        ++current;
+    }
+
+    result.appendFormat("%s-BufferQueue mMaxAcquiredBufferCount=%d, "
+            "mDequeueBufferCannotBlock=%d, default-size=[%dx%d], "
+            "default-format=%d, transform-hint=%02x, FIFO(%d)={%s}\n",
+            prefix, mMaxAcquiredBufferCount, mDequeueBufferCannotBlock,
+            mDefaultWidth, mDefaultHeight, mDefaultBufferFormat, mTransformHint,
+            mQueue.size(), fifo.string());
+
+    // Trim the free buffers so as to not spam the dump
+    int maxBufferCount = 0;
+    for (int s = NUM_BUFFER_SLOTS - 1; s >= 0; --s) {
+        const BufferSlot& slot(mSlots[s]);
+        if (slot.mBufferState != BufferSlot::FREE ||
+                slot.mGraphicBuffer != NULL) {
+            maxBufferCount = s + 1;
+            break;
+        }
+    }
+
+    for (int s = 0; s < maxBufferCount; ++s) {
+        const BufferSlot& slot(mSlots[s]);
+        const sp<GraphicBuffer>& buffer(slot.mGraphicBuffer);
+        result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix,
+                (slot.mBufferState == BufferSlot::ACQUIRED) ? ">" : " ",
+                s, buffer.get(),
+                BufferSlot::bufferStateName(slot.mBufferState));
+
+        if (buffer != NULL) {
+            result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle,
+                    buffer->width, buffer->height, buffer->stride,
+                    buffer->format);
+        }
+
+        result.append("\n");
+    }
+}
+
+int BufferQueueCore::getMinUndequeuedBufferCountLocked(bool async) const {
+    // If dequeueBuffer is allowed to error out, we don't have to add an
+    // extra buffer.
+    if (!mUseAsyncBuffer) {
+        return mMaxAcquiredBufferCount;
+    }
+
+    if (mDequeueBufferCannotBlock || async) {
+        return mMaxAcquiredBufferCount + 1;
+    }
+
+    return mMaxAcquiredBufferCount;
+}
+
+int BufferQueueCore::getMinMaxBufferCountLocked(bool async) const {
+    return getMinUndequeuedBufferCountLocked(async) + 1;
+}
+
+int BufferQueueCore::getMaxBufferCountLocked(bool async) const {
+    int minMaxBufferCount = getMinMaxBufferCountLocked(async);
+
+    int maxBufferCount = max(mDefaultMaxBufferCount, minMaxBufferCount);
+    if (mOverrideMaxBufferCount != 0) {
+        assert(mOverrideMaxBufferCount >= minMaxBufferCount);
+        maxBufferCount = mOverrideMaxBufferCount;
+    }
+
+    // Any buffers that are dequeued by the producer or sitting in the queue
+    // waiting to be consumed need to have their slots preserved. Such buffers
+    // will temporarily keep the max buffer count up until the slots no longer
+    // need to be preserved.
+    for (int s = maxBufferCount; s < NUM_BUFFER_SLOTS; ++s) {
+        BufferSlot::BufferState state = mSlots[s].mBufferState;
+        if (state == BufferSlot::QUEUED || state == BufferSlot::DEQUEUED) {
+            maxBufferCount = s + 1;
+        }
+    }
+
+    return maxBufferCount;
+}
+
+status_t BufferQueueCore::setDefaultMaxBufferCountLocked(int count) {
+    const int minBufferCount = mUseAsyncBuffer ? 2 : 1;
+    if (count < minBufferCount || count > NUM_BUFFER_SLOTS) {
+        BQ_LOGV("setDefaultMaxBufferCount: invalid count %d, should be in "
+                "[%d, %d]", minBufferCount, NUM_BUFFER_SLOTS);
+        return BAD_VALUE;
+    }
+
+    BQ_LOGV("setDefaultMaxBufferCount: setting count to %d", count);
+    mDefaultMaxBufferCount = count;
+    mDequeueCondition.broadcast();
+
+    return NO_ERROR;
+}
+
+void BufferQueueCore::freeBufferLocked(int slot) {
+    BQ_LOGV("freeBufferLocked: slot %d", slot);
+    mSlots[slot].mGraphicBuffer.clear();
+    if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
+        mSlots[slot].mNeedsCleanupOnRelease = true;
+    }
+    mSlots[slot].mBufferState = BufferSlot::FREE;
+    mSlots[slot].mFrameNumber = 0;
+    mSlots[slot].mAcquireCalled = false;
+
+    // Destroy fence as BufferQueue now takes ownership
+    if (mSlots[slot].mEglFence != EGL_NO_SYNC_KHR) {
+        eglDestroySyncKHR(mSlots[slot].mEglDisplay, mSlots[slot].mEglFence);
+        mSlots[slot].mEglFence = EGL_NO_SYNC_KHR;
+    }
+    mSlots[slot].mFence = Fence::NO_FENCE;
+}
+
+void BufferQueueCore::freeAllBuffersLocked() {
+    mBufferHasBeenQueued = false;
+    for (int s = 0; s < NUM_BUFFER_SLOTS; ++s) {
+        freeBufferLocked(s);
+    }
+}
+
+bool BufferQueueCore::stillTracking(const BufferItem* item) const {
+    const BufferSlot& slot = mSlots[item->mSlot];
+
+    BQ_LOGV("stillTracking: item { slot=%d/%llu buffer=%p } "
+            "slot { slot=%d/%llu buffer=%p }",
+            item->mSlot, item->mFrameNumber,
+            (item->mGraphicBuffer.get() ? item->mGraphicBuffer->handle : 0),
+            item->mSlot, slot.mFrameNumber,
+            (slot.mGraphicBuffer.get() ? slot.mGraphicBuffer->handle : 0));
+
+    // Compare item with its original buffer slot. We can check the slot as
+    // the buffer would not be moved to a different slot by the producer.
+    return (slot.mGraphicBuffer != NULL) &&
+           (item->mGraphicBuffer->handle == slot.mGraphicBuffer->handle);
+}
+
+} // namespace android
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
new file mode 100644 (file)
index 0000000..9d569ad
--- /dev/null
@@ -0,0 +1,705 @@
+/*
+ * Copyright 2014 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.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define EGL_EGLEXT_PROTOTYPES
+
+#include <gui/BufferItem.h>
+#include <gui/BufferQueueCore.h>
+#include <gui/BufferQueueProducer.h>
+#include <gui/IConsumerListener.h>
+#include <gui/IGraphicBufferAlloc.h>
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+namespace android {
+
+BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) :
+    mCore(core),
+    mSlots(core->mSlots),
+    mConsumerName() {}
+
+BufferQueueProducer::~BufferQueueProducer() {}
+
+status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
+    ATRACE_CALL();
+    BQ_LOGV("requestBuffer: slot %d", slot);
+    Mutex::Autolock lock(mCore->mMutex);
+
+    if (mCore->mIsAbandoned) {
+        BQ_LOGE("requestBuffer: BufferQueue has been abandoned");
+        return NO_INIT;
+    }
+
+    if (slot < 0 || slot >= BufferQueueCore::NUM_BUFFER_SLOTS) {
+        BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
+                slot, BufferQueueCore::NUM_BUFFER_SLOTS);
+        return BAD_VALUE;
+    } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
+        BQ_LOGE("requestBuffer: slot %d is not owned by the producer "
+                "(state = %d)", slot, mSlots[slot].mBufferState);
+        return BAD_VALUE;
+    }
+
+    mSlots[slot].mRequestBufferCalled = true;
+    *buf = mSlots[slot].mGraphicBuffer;
+    return NO_ERROR;
+}
+
+status_t BufferQueueProducer::setBufferCount(int bufferCount) {
+    ATRACE_CALL();
+    BQ_LOGV("setBufferCount: count = %d", bufferCount);
+
+    sp<IConsumerListener> listener;
+    { // Autolock scope
+        Mutex::Autolock lock(mCore->mMutex);
+
+        if (mCore->mIsAbandoned) {
+            BQ_LOGE("setBufferCount: BufferQueue has been abandoned");
+            return NO_INIT;
+        }
+
+        if (bufferCount > BufferQueueCore::NUM_BUFFER_SLOTS) {
+            BQ_LOGE("setBufferCount: bufferCount %d too large (max %d)",
+                    bufferCount, BufferQueueCore::NUM_BUFFER_SLOTS);
+            return BAD_VALUE;
+        }
+
+        // There must be no dequeued buffers when changing the buffer count.
+        for (int s = 0; s < BufferQueueCore::NUM_BUFFER_SLOTS; ++s) {
+            if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) {
+                BQ_LOGE("setBufferCount: buffer owned by producer");
+                return -EINVAL;
+            }
+        }
+
+        if (bufferCount == 0) {
+            mCore->mOverrideMaxBufferCount = 0;
+            mCore->mDequeueCondition.broadcast();
+            return NO_ERROR;
+        }
+
+        const int minBufferSlots = mCore->getMinMaxBufferCountLocked(false);
+        if (bufferCount < minBufferSlots) {
+            BQ_LOGE("setBufferCount: requested buffer count %d is less than "
+                    "minimum %d", bufferCount, minBufferSlots);
+            return BAD_VALUE;
+        }
+
+        // Here we are guaranteed that the producer doesn't have any dequeued
+        // buffers and will release all of its buffer references. We don't
+        // clear the queue, however, so that currently queued buffers still
+        // get displayed.
+        mCore->freeAllBuffersLocked();
+        mCore->mOverrideMaxBufferCount = bufferCount;
+        mCore->mDequeueCondition.broadcast();
+        listener = mCore->mConsumerListener;
+    } // Autolock scope
+
+    // Call back without lock held
+    if (listener != NULL) {
+        listener->onBuffersReleased();
+    }
+
+    return NO_ERROR;
+}
+
+status_t BufferQueueProducer::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
+
+    BQ_LOGV("dequeueBuffer: async=%s w=%u h=%u format=%#x, usage=%#x",
+            async ? "true" : "false", width, height, format, usage);
+
+    if ((width && !height) || (!width && height)) {
+        BQ_LOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height);
+        return BAD_VALUE;
+    }
+
+    status_t returnFlags = NO_ERROR;
+    EGLDisplay eglDisplay = EGL_NO_DISPLAY;
+    EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
+
+    { // Autolock scope
+        Mutex::Autolock lock(mCore->mMutex);
+
+        if (format == 0) {
+            format = mCore->mDefaultBufferFormat;
+        }
+
+        // Enable the usage bits the consumer requested
+        usage |= mCore->mConsumerUsageBits;
+
+        int found = -1;
+        bool tryAgain = true;
+        while (tryAgain) {
+            if (mCore->mIsAbandoned) {
+                BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
+                return NO_INIT;
+            }
+
+            const int maxBufferCount = mCore->getMaxBufferCountLocked(async);
+            if (async && mCore->mOverrideMaxBufferCount) {
+                // FIXME: Some drivers are manually setting the buffer count
+                // (which they shouldn't), so we do this extra test here to
+                // handle that case. This is TEMPORARY until we get this fixed.
+                if (mCore->mOverrideMaxBufferCount < maxBufferCount) {
+                    BQ_LOGE("dequeueBuffer: async mode is invalid with "
+                            "buffer count override");
+                    return BAD_VALUE;
+                }
+            }
+
+            // Free up any buffers that are in slots beyond the max buffer count
+            for (int s = maxBufferCount; s < BufferQueueCore::NUM_BUFFER_SLOTS; ++s) {
+                assert(mSlots[s].mBufferState == BufferSlot::FREE);
+                if (mSlots[s].mGraphicBuffer != NULL) {
+                    mCore->freeBufferLocked(s);
+                    returnFlags |= RELEASE_ALL_BUFFERS;
+                }
+            }
+
+            // Look for a free buffer to give to the client
+            found = BufferQueueCore::INVALID_BUFFER_SLOT;
+            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;
+                    case BufferSlot::FREE:
+                        // We return the oldest of the free buffers to avoid
+                        // stalling the producer if possible, since the consumer
+                        // may still have pending reads of in-flight buffers
+                        if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
+                                mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
+                            found = s;
+                        }
+                        break;
+                    default:
+                        break;
+                }
+            }
+
+            // Producers are not allowed to dequeue more than one buffer if they
+            // did not set a buffer count
+            if (!mCore->mOverrideMaxBufferCount && dequeuedCount) {
+                BQ_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 (mCore->mBufferHasBeenQueued) {
+                // Make sure the producer is not trying to dequeue more buffers
+                // than allowed
+                const int newUndequeuedCount =
+                        maxBufferCount - (dequeuedCount + 1);
+                const int minUndequeuedCount =
+                        mCore->getMinUndequeuedBufferCountLocked(async);
+                if (newUndequeuedCount < minUndequeuedCount) {
+                    BQ_LOGE("dequeueBuffer: min undequeued buffer count (%d) "
+                            "exceeded (dequeued=%d undequeued=%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 == BufferQueueCore::INVALID_BUFFER_SLOT);
+            if (tryAgain) {
+                // Return an error if we're in non-blocking mode (producer and
+                // consumer are controlled by the application).
+                // However, the consumer is allowed to briefly acquire an extra
+                // buffer (which could cause us to have to wait here), which is
+                // okay, since it is only used to implement an atomic acquire +
+                // release (e.g., in GLConsumer::updateTexImage())
+                if (mCore->mDequeueBufferCannotBlock &&
+                        (acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
+                    return WOULD_BLOCK;
+                }
+                mCore->mDequeueCondition.wait(mCore->mMutex);
+            }
+        } // while (tryAgain)
+
+        // This should not happen
+        if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
+            BQ_LOGE("dequeueBuffer: no available buffer slots");
+            return -EBUSY;
+        }
+
+        *outSlot = found;
+        ATRACE_BUFFER_INDEX(found);
+
+        const bool useDefaultSize = !width && !height;
+        if (useDefaultSize) {
+            width = mCore->mDefaultWidth;
+            height = mCore->mDefaultHeight;
+        }
+
+        mSlots[found].mBufferState = BufferSlot::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].mEglDisplay = EGL_NO_DISPLAY;
+            mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
+            mSlots[found].mFence = Fence::NO_FENCE;
+
+            returnFlags |= BUFFER_NEEDS_REALLOCATION;
+        }
+
+        if (CC_UNLIKELY(mSlots[found].mFence == NULL)) {
+            BQ_LOGE("dequeueBuffer: about to return a NULL fence - "
+                    "slot=%d w=%d h=%d format=%u",
+                    found, buffer->width, buffer->height, buffer->format);
+        }
+
+        eglDisplay = mSlots[found].mEglDisplay;
+        eglFence = mSlots[found].mEglFence;
+        *outFence = mSlots[found].mFence;
+        mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
+        mSlots[found].mFence = Fence::NO_FENCE;
+    } // Autolock scope
+
+    if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
+        status_t error;
+        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 (mCore->mIsAbandoned) {
+                BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
+                return NO_INIT;
+            }
+
+            mSlots[*outSlot].mFrameNumber = ~0;
+            mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
+        } // Autolock scope
+    }
+
+    if (eglFence != EGL_NO_SYNC_KHR) {
+        EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0,
+                1000000000);
+        // If something goes wrong, log the error, but return the buffer without
+        // synchronizing access to it. It's too late at this point to abort the
+        // dequeue operation.
+        if (result == EGL_FALSE) {
+            BQ_LOGE("dequeueBuffer: error %#x waiting for fence",
+                    eglGetError());
+        } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+            BQ_LOGE("dequeueBuffer: timeout waiting for fence");
+        }
+        eglDestroySyncKHR(eglDisplay, eglFence);
+    }
+
+    BQ_LOGV("dequeueBuffer: returning slot=%d/%llu buf=%p flags=%#x", *outSlot,
+            mSlots[*outSlot].mFrameNumber,
+            mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
+
+    return returnFlags;
+}
+
+status_t BufferQueueProducer::queueBuffer(int slot,
+        const QueueBufferInput &input, QueueBufferOutput *output) {
+    ATRACE_CALL();
+    ATRACE_BUFFER_INDEX(slot);
+
+    int64_t timestamp;
+    bool isAutoTimestamp;
+    Rect crop;
+    int scalingMode;
+    uint32_t transform;
+    bool async;
+    sp<Fence> fence;
+    input.deflate(&timestamp, &isAutoTimestamp, &crop, &scalingMode, &transform,
+            &async, &fence);
+
+    if (fence == NULL) {
+        BQ_LOGE("queueBuffer: fence is NULL");
+        return BAD_VALUE;
+    }
+
+    switch (scalingMode) {
+        case NATIVE_WINDOW_SCALING_MODE_FREEZE:
+        case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
+        case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
+        case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
+            break;
+        default:
+            BQ_LOGE("queueBuffer: unknown scaling mode %d", scalingMode);
+            return -EINVAL;
+    }
+
+    sp<IConsumerListener> listener;
+    { // Autolock scope
+        Mutex::Autolock lock(mCore->mMutex);
+
+        if (mCore->mIsAbandoned) {
+            BQ_LOGE("queueBuffer: BufferQueue has been abandoned");
+            return NO_INIT;
+        }
+
+        const int maxBufferCount = mCore->getMaxBufferCountLocked(async);
+        if (async && mCore->mOverrideMaxBufferCount) {
+            // FIXME: Some drivers are manually setting the buffer count
+            // (which they shouldn't), so we do this extra test here to
+            // handle that case. This is TEMPORARY until we get this fixed.
+            if (mCore->mOverrideMaxBufferCount < maxBufferCount) {
+                BQ_LOGE("queueBuffer: async mode is invalid with "
+                        "buffer count override");
+                return BAD_VALUE;
+            }
+        }
+
+        if (slot < 0 || slot >= maxBufferCount) {
+            BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
+                    slot, maxBufferCount);
+            return -EINVAL;
+        } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
+            BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
+                    "(state = %d)", slot, mSlots[slot].mBufferState);
+            return -EINVAL;
+        } else if (!mSlots[slot].mRequestBufferCalled) {
+            BQ_LOGE("queueBuffer: slot %d was queued without requesting "
+                    "a buffer", slot);
+            return -EINVAL;
+        }
+
+        BQ_LOGV("queueBuffer: slot=%d/%llu time=%llu crop=[%d,%d,%d,%d] "
+                "transform=%#x scale=%s",
+                slot, mCore->mFrameCounter + 1, timestamp,
+                crop.left, crop.top, crop.right, crop.bottom,
+                transform, BufferItem::scalingModeName(scalingMode));
+
+        const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
+        Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
+        Rect croppedRect;
+        crop.intersect(bufferRect, &croppedRect);
+        if (croppedRect != crop) {
+            BQ_LOGE("queueBuffer: crop rect is not contained within the "
+                    "buffer in slot %d", slot);
+            return -EINVAL;
+        }
+
+        mSlots[slot].mFence = fence;
+        mSlots[slot].mBufferState = BufferSlot::QUEUED;
+        ++mCore->mFrameCounter;
+        mSlots[slot].mFrameNumber = mCore->mFrameCounter;
+
+        BufferItem item;
+        item.mAcquireCalled = mSlots[slot].mAcquireCalled;
+        item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
+        item.mCrop = crop;
+        item.mTransform = transform & ~NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY;
+        item.mTransformToDisplayInverse =
+                bool(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
+        item.mScalingMode = scalingMode;
+        item.mTimestamp = timestamp;
+        item.mIsAutoTimestamp = isAutoTimestamp;
+        item.mFrameNumber = mCore->mFrameCounter;
+        item.mSlot = slot;
+        item.mFence = fence;
+        item.mIsDroppable = mCore->mDequeueBufferCannotBlock || async;
+
+        if (mCore->mQueue.empty()) {
+            // When the queue is empty, we can ignore mDequeueBufferCannotBlock
+            // and simply queue this buffer
+            mCore->mQueue.push_back(item);
+            listener = mCore->mConsumerListener;
+        } else {
+            // When the queue is not empty, we need to look at the front buffer
+            // state to see if we need to replace it
+            BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
+            if (front->mIsDroppable) {
+                // 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;
+                    // Reset the frame number of the freed buffer so that it is
+                    // the first in line to be dequeued again
+                    mSlots[front->mSlot].mFrameNumber = 0;
+                }
+                // Overwrite the droppable buffer with the incoming one
+                *front = item;
+            } else {
+                mCore->mQueue.push_back(item);
+                listener = mCore->mConsumerListener;
+            }
+        }
+
+        mCore->mBufferHasBeenQueued = true;
+        mCore->mDequeueCondition.broadcast();
+
+        output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
+                mCore->mTransformHint, mCore->mQueue.size());
+
+        ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
+    } // Autolock scope
+
+    // Call back without lock held
+    if (listener != NULL) {
+        listener->onFrameAvailable();
+    }
+
+    return NO_ERROR;
+}
+
+void BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) {
+    ATRACE_CALL();
+    BQ_LOGV("cancelBuffer: slot %d", slot);
+    Mutex::Autolock lock(mCore->mMutex);
+
+    if (mCore->mIsAbandoned) {
+        BQ_LOGE("cancelBuffer: BufferQueue has been abandoned");
+        return;
+    }
+
+    if (slot < 0 || slot >= BufferQueueCore::NUM_BUFFER_SLOTS) {
+        BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)",
+                slot, BufferQueueCore::NUM_BUFFER_SLOTS);
+        return;
+    } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
+        BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
+                "(state = %d)", slot, mSlots[slot].mBufferState);
+        return;
+    } else if (fence == NULL) {
+        BQ_LOGE("cancelBuffer: fence is NULL");
+        return;
+    }
+
+    mSlots[slot].mBufferState = BufferSlot::FREE;
+    mSlots[slot].mFrameNumber = 0;
+    mSlots[slot].mFence = fence;
+    mCore->mDequeueCondition.broadcast();
+}
+
+int BufferQueueProducer::query(int what, int *outValue) {
+    ATRACE_CALL();
+    Mutex::Autolock lock(mCore->mMutex);
+
+    if (outValue == NULL) {
+        BQ_LOGE("query: outValue was NULL");
+        return BAD_VALUE;
+    }
+
+    if (mCore->mIsAbandoned) {
+        BQ_LOGE("query: BufferQueue has been abandoned");
+        return NO_INIT;
+    }
+
+    int value;
+    switch (what) {
+        case NATIVE_WINDOW_WIDTH:
+            value = mCore->mDefaultWidth;
+            break;
+        case NATIVE_WINDOW_HEIGHT:
+            value = mCore->mDefaultHeight;
+            break;
+        case NATIVE_WINDOW_FORMAT:
+            value = mCore->mDefaultBufferFormat;
+            break;
+        case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
+            value = mCore->getMinUndequeuedBufferCountLocked(false);
+            break;
+        case NATIVE_WINDOW_CONSUMER_RUNNING_BEHIND:
+            value = (mCore->mQueue.size() > 1);
+            break;
+        case NATIVE_WINDOW_CONSUMER_USAGE_BITS:
+            value = mCore->mConsumerUsageBits;
+            break;
+        default:
+            return BAD_VALUE;
+    }
+
+    BQ_LOGV("query: %d? %d", what, value);
+    *outValue = value;
+    return NO_ERROR;
+}
+
+status_t BufferQueueProducer::connect(const sp<android::IBinder> &token,
+        int api, bool producerControlledByApp, QueueBufferOutput *output) {
+    ATRACE_CALL();
+    Mutex::Autolock lock(mCore->mMutex);
+    mConsumerName = mCore->mConsumerName;
+    BQ_LOGV("connect(P): api=%d producerControlledByApp=%s", api,
+            producerControlledByApp ? "true" : "false");
+
+    // If we disconnect and reconnect quickly, we can be in a state where our
+    // slots are empty but we have many buffers in the queue. This can cause us
+    // to run out of memory if we outrun the consumer. Wait here if it looks
+    // like we have too many buffers queued up.
+    while (true) {
+        if (mCore->mIsAbandoned) {
+            BQ_LOGE("connect(P): BufferQueue has been abandoned");
+            return NO_INIT;
+        }
+
+        if (mCore->mConsumerListener == NULL) {
+            BQ_LOGE("connect(P): BufferQueue has no consumer");
+            return NO_INIT;
+        }
+
+        if (output == NULL) {
+            BQ_LOGE("connect(P): output was NULL");
+            return BAD_VALUE;
+        }
+
+        if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
+            BQ_LOGE("connect(P): already connected (cur=%d req=%d)",
+                    mCore->mConnectedApi, api);
+            return BAD_VALUE;
+        }
+
+        size_t maxBufferCount = mCore->getMaxBufferCountLocked(false);
+        if (mCore->mQueue.size() <= maxBufferCount) {
+            // The queue size seems small enough to proceed
+            // TODO: Make this bound tighter?
+            break;
+        }
+
+        BQ_LOGV("connect(P): queue size is %d, waiting", mCore->mQueue.size());
+        mCore->mDequeueCondition.wait(mCore->mMutex);
+    }
+
+    int status = NO_ERROR;
+    switch (api) {
+        case NATIVE_WINDOW_API_EGL:
+        case NATIVE_WINDOW_API_CPU:
+        case NATIVE_WINDOW_API_MEDIA:
+        case NATIVE_WINDOW_API_CAMERA:
+            mCore->mConnectedApi = api;
+            output->inflate(mCore->mDefaultWidth, mCore->mDefaultHeight,
+                    mCore->mTransformHint, mCore->mQueue.size());
+
+            // Set up a death notification so that we can disconnect
+            // automatically if the remote producer dies
+            if (token != NULL && token->remoteBinder() != NULL) {
+                status = token->linkToDeath(
+                        static_cast<IBinder::DeathRecipient*>(this));
+                if (status == NO_ERROR) {
+                    mCore->mConnectedProducerToken = token;
+                } else {
+                    BQ_LOGE("connect(P): linkToDeath failed: %s (%d)",
+                            strerror(-status), status);
+                }
+            }
+            break;
+        default:
+            BQ_LOGE("connect(P): unknown API %d", api);
+            status = BAD_VALUE;
+            break;
+    }
+
+    mCore->mBufferHasBeenQueued = false;
+    mCore->mDequeueBufferCannotBlock =
+            mCore->mConsumerControlledByApp && producerControlledByApp;
+
+    return status;
+}
+
+status_t BufferQueueProducer::disconnect(int api) {
+    ATRACE_CALL();
+    BQ_LOGV("disconnect(P): api %d", api);
+
+    int status = NO_ERROR;
+    sp<IConsumerListener> listener;
+    { // Autolock scope
+        Mutex::Autolock lock(mCore->mMutex);
+
+        if (mCore->mIsAbandoned) {
+            // It's not really an error to disconnect after the surface has
+            // been abandoned; it should just be a no-op.
+            return NO_ERROR;
+        }
+
+        switch (api) {
+            case NATIVE_WINDOW_API_EGL:
+            case NATIVE_WINDOW_API_CPU:
+            case NATIVE_WINDOW_API_MEDIA:
+            case NATIVE_WINDOW_API_CAMERA:
+                if (mCore->mConnectedApi == api) {
+                    mCore->freeAllBuffersLocked();
+
+                    // Remove our death notification callback if we have one
+                    sp<IBinder> token = mCore->mConnectedProducerToken;
+                    if (token != NULL) {
+                        // This can fail if we're here because of the death
+                        // notification, but we just ignore it
+                        token->unlinkToDeath(
+                                static_cast<IBinder::DeathRecipient*>(this));
+                    }
+                    mCore->mConnectedProducerToken = NULL;
+                    mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
+                    mCore->mDequeueCondition.broadcast();
+                    listener = mCore->mConsumerListener;
+                } else {
+                    BQ_LOGE("disconnect(P): connected to another API "
+                            "(cur=%d req=%d)", mCore->mConnectedApi, api);
+                    status = -EINVAL;
+                }
+                break;
+            default:
+                BQ_LOGE("disconnect(P): unknown API %d", api);
+                status = -EINVAL;
+                break;
+        }
+    } // Autolock scope
+
+    // Call back without lock held
+    if (listener != NULL) {
+        listener->onBuffersReleased();
+    }
+
+    return status;
+}
+
+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
+    // this callback upon disconnect. It's therefore safe to read mConnectedApi
+    // without synchronization here.
+    int api = mCore->mConnectedApi;
+    disconnect(api);
+}
+
+} // namespace android
diff --git a/libs/gui/BufferSlot.cpp b/libs/gui/BufferSlot.cpp
new file mode 100644 (file)
index 0000000..6b5d77a
--- /dev/null
@@ -0,0 +1,15 @@
+#include <gui/BufferSlot.h>
+
+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";
+        default: return "Unknown";
+    }
+}
+
+} // namespace android