Split BufferQueue into core + producer + consumer
Change-Id: Idc39f1e511d68ce4f02202d35425a419bc0bcd92
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index c14c950..0a77317 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -5,8 +5,13 @@
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
index 0000000..cccb38e
--- /dev/null
+++ b/libs/gui/BufferItem.cpp
@@ -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
index 0000000..7b70df1
--- /dev/null
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -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
index 0000000..edce3e0
--- /dev/null
+++ b/libs/gui/BufferQueueCore.cpp
@@ -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
index 0000000..9d569ad
--- /dev/null
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -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(×tamp, &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
index 0000000..6b5d77a
--- /dev/null
+++ b/libs/gui/BufferSlot.cpp
@@ -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