Bug 1098970 - Part 1: Add new LL native files. r=sotaro

Copied from aosp/framework/native/gui.
This commit is contained in:
Boris Chiou 2014-12-17 18:26:00 +01:00
parent 5cfd1b5156
commit 33b88b3371
17 changed files with 4441 additions and 0 deletions

View File

@ -0,0 +1,192 @@
/*
* 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.mGraphicBuffer = mGraphicBuffer;
bufferItem.mFence = mFence;
bufferItem.mCrop = mCrop;
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;
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

View File

@ -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

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2012 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 LOG_TAG "BufferQueue"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#include <gui/BufferQueue.h>
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
#include <gui/BufferQueueProducer.h>
namespace android {
BufferQueue::ProxyConsumerListener::ProxyConsumerListener(
const wp<ConsumerListener>& consumerListener):
mConsumerListener(consumerListener) {}
BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {}
void BufferQueue::ProxyConsumerListener::onFrameAvailable() {
sp<ConsumerListener> listener(mConsumerListener.promote());
if (listener != NULL) {
listener->onFrameAvailable();
}
}
void BufferQueue::ProxyConsumerListener::onBuffersReleased() {
sp<ConsumerListener> listener(mConsumerListener.promote());
if (listener != NULL) {
listener->onBuffersReleased();
}
}
void BufferQueue::ProxyConsumerListener::onSidebandStreamChanged() {
sp<ConsumerListener> listener(mConsumerListener.promote());
if (listener != NULL) {
listener->onSidebandStreamChanged();
}
}
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
const sp<IGraphicBufferAlloc>& allocator) {
LOG_ALWAYS_FATAL_IF(outProducer == NULL,
"BufferQueue: outProducer must not be NULL");
LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
"BufferQueue: outConsumer must not be NULL");
sp<BufferQueueCore> core(new BufferQueueCore(allocator));
LOG_ALWAYS_FATAL_IF(core == NULL,
"BufferQueue: failed to create BufferQueueCore");
sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core));
LOG_ALWAYS_FATAL_IF(producer == NULL,
"BufferQueue: failed to create BufferQueueProducer");
sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
LOG_ALWAYS_FATAL_IF(consumer == NULL,
"BufferQueue: failed to create BufferQueueConsumer");
*outProducer = producer;
*outConsumer = consumer;
}
}; // namespace android

View File

@ -0,0 +1,88 @@
/*
* Copyright (C) 2012 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_BUFFERQUEUE_H
#define ANDROID_GUI_BUFFERQUEUE_H
#include <gui/BufferQueueDefs.h>
#include <gui/IGraphicBufferConsumer.h>
#include <gui/IGraphicBufferProducer.h>
#include <gui/IConsumerListener.h>
// These are only required to keep other parts of the framework with incomplete
// dependencies building successfully
#include <gui/IGraphicBufferAlloc.h>
namespace android {
class BufferQueue {
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 = BufferQueueDefs::NUM_BUFFER_SLOTS };
// Used as a placeholder slot# when the value isn't pointing to an existing buffer.
enum { INVALID_BUFFER_SLOT = IGraphicBufferConsumer::BufferItem::INVALID_BUFFER_SLOT };
// Alias to <IGraphicBufferConsumer.h> -- please scope from there in future code!
enum {
NO_BUFFER_AVAILABLE = IGraphicBufferConsumer::NO_BUFFER_AVAILABLE,
PRESENT_LATER = IGraphicBufferConsumer::PRESENT_LATER,
};
// When in async mode 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 };
// for backward source compatibility
typedef ::android::ConsumerListener ConsumerListener;
typedef IGraphicBufferConsumer::BufferItem BufferItem;
// ProxyConsumerListener is a ConsumerListener implementation that keeps a weak
// reference to the actual consumer object. It forwards all calls to that
// consumer object so long as it exists.
//
// This class exists to avoid having a circular reference between the
// BufferQueue object and the consumer object. The reason this can't be a weak
// reference in the BufferQueue class is because we're planning to expose the
// consumer side of a BufferQueue as a binder interface, which doesn't support
// weak references.
class ProxyConsumerListener : public BnConsumerListener {
public:
ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
virtual ~ProxyConsumerListener();
virtual void onFrameAvailable();
virtual void onBuffersReleased();
virtual void onSidebandStreamChanged();
private:
// mConsumerListener is a weak reference to the IConsumerListener. This is
// the raison d'etre of ProxyConsumerListener.
wp<ConsumerListener> mConsumerListener;
};
// BufferQueue manages a pool of gralloc memory slots to be used by
// producers and consumers. allocator is used to allocate all the
// needed gralloc buffers.
static void createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
const sp<IGraphicBufferAlloc>& allocator = NULL);
private:
BufferQueue(); // Create through createBufferQueue
};
// ----------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_GUI_BUFFERQUEUE_H

View File

@ -0,0 +1,523 @@
/*
* 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 <inttypes.h>
#define LOG_TAG "BufferQueueConsumer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#include <gui/BufferItem.h>
#include <gui/BufferQueueConsumer.h>
#include <gui/BufferQueueCore.h>
#include <gui/IConsumerListener.h>
#include <gui/IProducerListener.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 < BufferQueueDefs::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=%" PRId64 " expect=%"
PRId64 " (%" PRId64 ") now=%" PRId64,
desiredPresent, expectedPresent,
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC));
break;
}
BQ_LOGV("acquireBuffer: drop desire=%" PRId64 " expect=%" PRId64
" size=%zu",
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=%" PRId64 " expect=%" PRId64
" (%" PRId64 ") now=%" PRId64,
desiredPresent, expectedPresent,
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC));
return PRESENT_LATER;
}
BQ_LOGV("acquireBuffer: accept desire=%" PRId64 " expect=%" PRId64 " "
"(%" PRId64 ") now=%" PRId64, desiredPresent, expectedPresent,
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC));
}
int slot = front->mSlot;
*outBuffer = *front;
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " 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);
// We might have freed a slot while dropping old buffers, or the producer
// may be blocked waiting for the number of buffers in the queue to
// decrease.
mCore->mDequeueCondition.broadcast();
ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
return NO_ERROR;
}
status_t BufferQueueConsumer::detachBuffer(int slot) {
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("detachBuffer(C): slot %d", slot);
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("detachBuffer(C): BufferQueue has been abandoned");
return NO_INIT;
}
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
BQ_LOGE("detachBuffer(C): slot index %d out of range [0, %d)",
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
} else if (mSlots[slot].mBufferState != BufferSlot::ACQUIRED) {
BQ_LOGE("detachBuffer(C): slot %d is not owned by the consumer "
"(state = %d)", slot, mSlots[slot].mBufferState);
return BAD_VALUE;
}
mCore->freeBufferLocked(slot);
mCore->mDequeueCondition.broadcast();
return NO_ERROR;
}
status_t BufferQueueConsumer::attachBuffer(int* outSlot,
const sp<android::GraphicBuffer>& buffer) {
ATRACE_CALL();
if (outSlot == NULL) {
BQ_LOGE("attachBuffer(P): outSlot must not be NULL");
return BAD_VALUE;
} else if (buffer == NULL) {
BQ_LOGE("attachBuffer(P): cannot attach NULL buffer");
return BAD_VALUE;
}
Mutex::Autolock lock(mCore->mMutex);
// Make sure we don't have too many acquired buffers and find a free slot
// to put the buffer into (the oldest if there are multiple).
int numAcquiredBuffers = 0;
int found = BufferQueueCore::INVALID_BUFFER_SLOT;
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
++numAcquiredBuffers;
} else if (mSlots[s].mBufferState == BufferSlot::FREE) {
if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
found = s;
}
}
}
if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
BQ_LOGE("attachBuffer(P): max acquired buffer count reached: %d "
"(max %d)", numAcquiredBuffers,
mCore->mMaxAcquiredBufferCount);
return INVALID_OPERATION;
}
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
BQ_LOGE("attachBuffer(P): could not find free buffer slot");
return NO_MEMORY;
}
*outSlot = found;
ATRACE_BUFFER_INDEX(*outSlot);
BQ_LOGV("attachBuffer(C): returning slot %d", *outSlot);
mSlots[*outSlot].mGraphicBuffer = buffer;
mSlots[*outSlot].mBufferState = BufferSlot::ACQUIRED;
mSlots[*outSlot].mAttachedByConsumer = true;
mSlots[*outSlot].mNeedsCleanupOnRelease = false;
mSlots[*outSlot].mFence = Fence::NO_FENCE;
mSlots[*outSlot].mFrameNumber = 0;
// mAcquireCalled tells BufferQueue that it doesn't need to send a valid
// GraphicBuffer pointer on the next acquireBuffer call, which decreases
// Binder traffic by not un/flattening the GraphicBuffer. However, it
// requires that the consumer maintain a cached copy of the slot <--> buffer
// mappings, which is why the consumer doesn't need the valid pointer on
// acquire.
//
// The StreamSplitter is one of the primary users of the attach/detach
// logic, and while it is running, all buffers it acquires are immediately
// detached, and all buffers it eventually releases are ones that were
// attached (as opposed to having been obtained from acquireBuffer), so it
// doesn't make sense to maintain the slot/buffer mappings, which would
// become invalid for every buffer during detach/attach. By setting this to
// false, the valid GraphicBuffer pointer will always be sent with acquire
// for attached buffers.
mSlots[*outSlot].mAcquireCalled = false;
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 < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS ||
releaseFence == NULL) {
return BAD_VALUE;
}
sp<IProducerListener> listener;
{ // Autolock scope
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 BAD_VALUE;
}
++current;
}
if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
mSlots[slot].mEglDisplay = eglDisplay;
mSlots[slot].mEglFence = eglFence;
mSlots[slot].mFence = releaseFence;
mSlots[slot].mBufferState = BufferSlot::FREE;
listener = mCore->mConnectedProducerListener;
BQ_LOGV("releaseBuffer: releasing slot %d", slot);
} else if (mSlots[slot].mNeedsCleanupOnRelease) {
BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d "
"(state = %d)", slot, mSlots[slot].mBufferState);
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 BAD_VALUE;
}
mCore->mDequeueCondition.broadcast();
} // Autolock scope
// Call back without lock held
if (listener != NULL) {
listener->onBufferReleased();
}
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 BAD_VALUE;
}
mCore->mIsAbandoned = true;
mCore->mConsumerListener = NULL;
mCore->mQueue.clear();
mCore->freeAllBuffersLocked();
mCore->mDequeueCondition.broadcast();
return NO_ERROR;
}
status_t BufferQueueConsumer::getReleasedBuffers(uint64_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;
}
uint64_t mask = 0;
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
if (!mSlots[s].mAcquireCalled) {
mask |= (1ULL << 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 &= ~(1ULL << current->mSlot);
}
++current;
}
BQ_LOGV("getReleasedBuffers: returning mask %#" PRIx64, 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;
}
sp<NativeHandle> BufferQueueConsumer::getSidebandStream() const {
return mCore->mSidebandStream;
}
void BufferQueueConsumer::dump(String8& result, const char* prefix) const {
mCore->dump(result, prefix);
}
} // namespace android

View File

@ -0,0 +1,181 @@
/*
* 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/BufferQueueDefs.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);
// See IGraphicBufferConsumer::detachBuffer
virtual status_t detachBuffer(int slot);
// See IGraphicBufferConsumer::attachBuffer
virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer);
// 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(uint64_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);
// Retrieve the sideband buffer stream, if any.
virtual sp<NativeHandle> getSidebandStream() const;
// dump our state in a String
virtual void dump(String8& result, const char* prefix) const;
// Functions required for backwards compatibility.
// These will be modified/renamed in IGraphicBufferConsumer and will be
// removed from this class at that time. See b/13306289.
virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
EGLDisplay display, EGLSyncKHR fence,
const sp<Fence>& releaseFence) {
return releaseBuffer(buf, frameNumber, releaseFence, display, fence);
}
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer,
bool controlledByApp) {
return connect(consumer, controlledByApp);
}
virtual status_t consumerDisconnect() { return disconnect(); }
// End functions required for backwards compatibility
private:
sp<BufferQueueCore> mCore;
// This references mCore->mSlots. Lock mCore->mMutex while accessing.
BufferQueueDefs::SlotsType& mSlots;
// This is a cached copy of the name stored in the BufferQueueCore.
// It's updated during setConsumerName.
String8 mConsumerName;
}; // class BufferQueueConsumer
} // namespace android
#endif

View File

@ -0,0 +1,238 @@
/*
* 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 LOG_TAG "BufferQueueCore"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#define EGL_EGLEXT_PROTOTYPES
#include <inttypes.h>
#include <gui/BufferItem.h>
#include <gui/BufferQueueCore.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferAlloc.h>
#include <gui/IProducerListener.h>
#include <gui/ISurfaceComposer.h>
#include <private/gui/ComposerService.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),
mConnectedProducerListener(),
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),
mIsAllocating(false),
mIsAllocatingCondition()
{
if (allocator == NULL) {
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mAllocator = composer->createGraphicBufferAlloc();
if (mAllocator == NULL) {
BQ_LOGE("createGraphicBufferAlloc failed");
}
}
}
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=%#" PRIx64 ", 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(%zu)={%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 = BufferQueueDefs::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 < BufferQueueDefs::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 > BufferQueueDefs::NUM_BUFFER_SLOTS) {
BQ_LOGV("setDefaultMaxBufferCount: invalid count %d, should be in "
"[%d, %d]",
count, minBufferCount, BufferQueueDefs::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 = UINT32_MAX;
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 < BufferQueueDefs::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/%" PRIu64 " buffer=%p } "
"slot { slot=%d/%" PRIu64 " 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);
}
void BufferQueueCore::waitWhileAllocatingLocked() const {
ATRACE_CALL();
while (mIsAllocating) {
mIsAllocatingCondition.wait(mMutex);
}
}
} // namespace android

View File

@ -0,0 +1,253 @@
/*
* 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/BufferQueueDefs.h>
#include <gui/BufferSlot.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/NativeHandle.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 IConsumerListener;
class IGraphicBufferAlloc;
class IProducerListener;
class BufferQueueCore : public virtual RefBase {
friend class BufferQueueProducer;
friend class BufferQueueConsumer;
public:
// 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 = BufferQueueDefs::NUM_BUFFER_SLOTS - 2 };
// The default API number used to indicate that no producer is connected
enum { NO_CONNECTED_API = 0 };
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:
// Dump our state in a string
void dump(String8& result, const char* prefix) const;
// getMinUndequeuedBufferCountLocked returns the minimum number of buffers
// that must remain in a state other than DEQUEUED. The async parameter
// tells whether we're in asynchronous mode.
int getMinUndequeuedBufferCountLocked(bool async) const;
// getMinMaxBufferCountLocked returns the minimum number of buffers allowed
// given the current BufferQueue state. The async parameter tells whether
// we're in asynchonous mode.
int getMinMaxBufferCountLocked(bool async) const;
// getMaxBufferCountLocked returns the maximum number of buffers that can be
// allocated at once. This value depends on the following member variables:
//
// mDequeueBufferCannotBlock
// mMaxAcquiredBufferCount
// mDefaultMaxBufferCount
// mOverrideMaxBufferCount
// async parameter
//
// Any time one of these member variables is changed while a producer is
// connected, mDequeueCondition must be broadcast.
int getMaxBufferCountLocked(bool async) const;
// setDefaultMaxBufferCountLocked sets the maximum number of buffer slots
// that will be used if the producer does not override the buffer slot
// count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. The
// initial default is 2.
status_t setDefaultMaxBufferCountLocked(int count);
// freeBufferLocked frees the GraphicBuffer and sync resources for the
// given slot.
void freeBufferLocked(int slot);
// freeAllBuffersLocked frees the GraphicBuffer and sync resources for
// all slots.
void freeAllBuffersLocked();
// stillTracking returns true iff the buffer item is still being tracked
// in one of the slots.
bool stillTracking(const BufferItem* item) const;
// waitWhileAllocatingLocked blocks until mIsAllocating is false.
void waitWhileAllocatingLocked() const;
// mAllocator is the connection to SurfaceFlinger that is used to allocate
// new GraphicBuffer objects.
sp<IGraphicBufferAlloc> mAllocator;
// mMutex is the mutex used to prevent concurrent access to the member
// variables of BufferQueueCore objects. It must be locked whenever any
// member variable is accessed.
mutable Mutex mMutex;
// mIsAbandoned indicates that the BufferQueue will no longer be used to
// consume image buffers pushed to it using the IGraphicBufferProducer
// interface. It is initialized to false, and set to true in the
// consumerDisconnect method. A BufferQueue that is abandoned will return
// the NO_INIT error from all IGraphicBufferProducer methods capable of
// returning an error.
bool mIsAbandoned;
// mConsumerControlledByApp indicates whether the connected consumer is
// controlled by the application.
bool mConsumerControlledByApp;
// mConsumerName is a string used to identify the BufferQueue in log
// messages. It is set by the IGraphicBufferConsumer::setConsumerName
// method.
String8 mConsumerName;
// mConsumerListener is used to notify the connected consumer of
// asynchronous events that it may wish to react to. It is initially
// set to NULL and is written by consumerConnect and consumerDisconnect.
sp<IConsumerListener> mConsumerListener;
// mConsumerUsageBits contains flags that the consumer wants for
// GraphicBuffers.
uint32_t mConsumerUsageBits;
// mConnectedApi indicates the producer API that is currently connected
// to this BufferQueue. It defaults to NO_CONNECTED_API, and gets updated
// by the connect and disconnect methods.
int mConnectedApi;
// mConnectedProducerToken is used to set a binder death notification on
// the producer.
sp<IProducerListener> mConnectedProducerListener;
// mSlots is an array of buffer slots that must be mirrored on the producer
// side. This allows buffer ownership to be transferred between the producer
// and consumer without sending a GraphicBuffer over Binder. The entire
// array is initialized to NULL at construction time, and buffers are
// allocated for a slot when requestBuffer is called with that slot's index.
BufferQueueDefs::SlotsType mSlots;
// mQueue is a FIFO of queued buffers used in synchronous mode.
Fifo mQueue;
// mOverrideMaxBufferCount is the limit on the number of buffers that will
// be allocated at one time. This value is set by the producer by calling
// setBufferCount. The default is 0, which means that the producer doesn't
// care about the number of buffers in the pool. In that case,
// mDefaultMaxBufferCount is used as the limit.
int mOverrideMaxBufferCount;
// mDequeueCondition is a condition variable used for dequeueBuffer in
// synchronous mode.
mutable Condition mDequeueCondition;
// mUseAsyncBuffer indicates whether an extra buffer is used in async mode
// to prevent dequeueBuffer from blocking.
bool mUseAsyncBuffer;
// mDequeueBufferCannotBlock indicates whether dequeueBuffer is allowed to
// block. This flag is set during connect when both the producer and
// consumer are controlled by the application.
bool mDequeueBufferCannotBlock;
// mDefaultBufferFormat can be set so it will override the buffer format
// when it isn't specified in dequeueBuffer.
uint32_t mDefaultBufferFormat;
// mDefaultWidth holds the default width of allocated buffers. It is used
// in dequeueBuffer if a width and height of 0 are specified.
int mDefaultWidth;
// mDefaultHeight holds the default height of allocated buffers. It is used
// in dequeueBuffer if a width and height of 0 are specified.
int mDefaultHeight;
// mDefaultMaxBufferCount is the default limit on the number of buffers that
// will be allocated at one time. This default limit is set by the consumer.
// The limit (as opposed to the default limit) may be overriden by the
// producer.
int mDefaultMaxBufferCount;
// mMaxAcquiredBufferCount is the number of buffers that the consumer may
// acquire at one time. It defaults to 1, and can be changed by the consumer
// via setMaxAcquiredBufferCount, but this may only be done while no
// producer is connected to the BufferQueue. This value is used to derive
// the value returned for the MIN_UNDEQUEUED_BUFFERS query to the producer.
int mMaxAcquiredBufferCount;
// mBufferHasBeenQueued is true once a buffer has been queued. It is reset
// when something causes all buffers to be freed (e.g., changing the buffer
// count).
bool mBufferHasBeenQueued;
// mFrameCounter is the free running counter, incremented on every
// successful queueBuffer call and buffer allocation.
uint64_t mFrameCounter;
// mTransformHint is used to optimize for screen rotations.
uint32_t mTransformHint;
// mSidebandStream is a handle to the sideband buffer stream, if any
sp<NativeHandle> mSidebandStream;
// mIsAllocating indicates whether a producer is currently trying to allocate buffers (which
// releases mMutex while doing the allocation proper). Producers should not modify any of the
// FREE slots while this is true. mIsAllocatingCondition is signaled when this value changes to
// false.
bool mIsAllocating;
// mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating
// becomes false.
mutable Condition mIsAllocatingCondition;
}; // class BufferQueueCore
} // namespace android
#endif

View File

@ -0,0 +1,35 @@
/*
* 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_BUFFERQUEUECOREDEFS_H
#define ANDROID_GUI_BUFFERQUEUECOREDEFS_H
#include <gui/BufferSlot.h>
namespace android {
class BufferQueueCore;
namespace BufferQueueDefs {
// 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 = 64 };
typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
} // namespace BufferQueueDefs
} // namespace android
#endif

View File

@ -0,0 +1,982 @@
/*
* 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 <inttypes.h>
#define LOG_TAG "BufferQueueProducer"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#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 <gui/IProducerListener.h>
#include <utils/Log.h>
#include <utils/Trace.h>
namespace android {
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) :
mCore(core),
mSlots(core->mSlots),
mConsumerName(),
mStickyTransform(0) {}
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 >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
} else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
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);
mCore->waitWhileAllocatingLocked();
if (mCore->mIsAbandoned) {
BQ_LOGE("setBufferCount: BufferQueue has been abandoned");
return NO_INIT;
}
if (bufferCount > BufferQueueDefs::NUM_BUFFER_SLOTS) {
BQ_LOGE("setBufferCount: bufferCount %d too large (max %d)",
bufferCount, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
}
// There must be no dequeued buffers when changing the buffer count.
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) {
BQ_LOGE("setBufferCount: buffer owned by producer");
return BAD_VALUE;
}
}
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::waitForFreeSlotThenRelock(const char* caller,
bool async, int* found, status_t* returnFlags) const {
bool tryAgain = true;
while (tryAgain) {
if (mCore->mIsAbandoned) {
BQ_LOGE("%s: BufferQueue has been abandoned", caller);
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("%s: async mode is invalid with buffer count override",
caller);
return BAD_VALUE;
}
}
// Free up any buffers that are in slots beyond the max buffer count
for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
assert(mSlots[s].mBufferState == BufferSlot::FREE);
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("%s: can't dequeue multiple buffers without setting the "
"buffer count", caller);
return INVALID_OPERATION;
}
// 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("%s: min undequeued buffer count (%d) exceeded "
"(dequeued=%d undequeued=%d)",
caller, minUndequeuedCount,
dequeuedCount, newUndequeuedCount);
return INVALID_OPERATION;
}
}
// 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.
bool tooManyBuffers = mCore->mQueue.size()
> static_cast<size_t>(maxBufferCount);
if (tooManyBuffers) {
BQ_LOGV("%s: queue size is %zu, waiting", caller,
mCore->mQueue.size());
}
// If no buffer is found, or if the queue has too many buffers
// outstanding, wait for a buffer to be acquired or released, or for the
// max buffer count to change.
tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) ||
tooManyBuffers;
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)
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;
bool attachedByConsumer = false;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked();
if (format == 0) {
format = mCore->mDefaultBufferFormat;
}
// Enable the usage bits the consumer requested
usage |= mCore->mConsumerUsageBits;
int found;
status_t status = waitForFreeSlotThenRelock("dequeueBuffer", async,
&found, &returnFlags);
if (status != NO_ERROR) {
return status;
}
// 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);
attachedByConsumer = mSlots[found].mAttachedByConsumer;
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;
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
width, height, format, usage, &error));
if (graphicBuffer == NULL) {
BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
return error;
}
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
mSlots[*outSlot].mFrameNumber = UINT32_MAX;
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
} // Autolock scope
}
if (attachedByConsumer) {
returnFlags |= BUFFER_NEEDS_REALLOCATION;
}
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/%" PRIu64 " buf=%p flags=%#x",
*outSlot,
mSlots[*outSlot].mFrameNumber,
mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);
return returnFlags;
}
status_t BufferQueueProducer::detachBuffer(int slot) {
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("detachBuffer(P): slot %d", slot);
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("detachBuffer(P): BufferQueue has been abandoned");
return NO_INIT;
}
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
BQ_LOGE("detachBuffer(P): slot index %d out of range [0, %d)",
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
} else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
BQ_LOGE("detachBuffer(P): slot %d is not owned by the producer "
"(state = %d)", slot, mSlots[slot].mBufferState);
return BAD_VALUE;
} else if (!mSlots[slot].mRequestBufferCalled) {
BQ_LOGE("detachBuffer(P): buffer in slot %d has not been requested",
slot);
return BAD_VALUE;
}
mCore->freeBufferLocked(slot);
mCore->mDequeueCondition.broadcast();
return NO_ERROR;
}
status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence) {
ATRACE_CALL();
if (outBuffer == NULL) {
BQ_LOGE("detachNextBuffer: outBuffer must not be NULL");
return BAD_VALUE;
} else if (outFence == NULL) {
BQ_LOGE("detachNextBuffer: outFence must not be NULL");
return BAD_VALUE;
}
Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked();
if (mCore->mIsAbandoned) {
BQ_LOGE("detachNextBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
// Find the oldest valid slot
int found = BufferQueueCore::INVALID_BUFFER_SLOT;
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
if (mSlots[s].mBufferState == BufferSlot::FREE &&
mSlots[s].mGraphicBuffer != NULL) {
if (found == BufferQueueCore::INVALID_BUFFER_SLOT ||
mSlots[s].mFrameNumber < mSlots[found].mFrameNumber) {
found = s;
}
}
}
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
return NO_MEMORY;
}
BQ_LOGV("detachNextBuffer detached slot %d", found);
*outBuffer = mSlots[found].mGraphicBuffer;
*outFence = mSlots[found].mFence;
mCore->freeBufferLocked(found);
return NO_ERROR;
}
status_t BufferQueueProducer::attachBuffer(int* outSlot,
const sp<android::GraphicBuffer>& buffer) {
ATRACE_CALL();
if (outSlot == NULL) {
BQ_LOGE("attachBuffer(P): outSlot must not be NULL");
return BAD_VALUE;
} else if (buffer == NULL) {
BQ_LOGE("attachBuffer(P): cannot attach NULL buffer");
return BAD_VALUE;
}
Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked();
status_t returnFlags = NO_ERROR;
int found;
// TODO: Should we provide an async flag to attachBuffer? It seems
// unlikely that buffers which we are attaching to a BufferQueue will
// be asynchronous (droppable), but it may not be impossible.
status_t status = waitForFreeSlotThenRelock("attachBuffer(P)", false,
&found, &returnFlags);
if (status != NO_ERROR) {
return status;
}
// This should not happen
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
BQ_LOGE("attachBuffer(P): no available buffer slots");
return -EBUSY;
}
*outSlot = found;
ATRACE_BUFFER_INDEX(*outSlot);
BQ_LOGV("attachBuffer(P): returning slot %d flags=%#x",
*outSlot, returnFlags);
mSlots[*outSlot].mGraphicBuffer = buffer;
mSlots[*outSlot].mBufferState = BufferSlot::DEQUEUED;
mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR;
mSlots[*outSlot].mFence = Fence::NO_FENCE;
mSlots[*outSlot].mRequestBufferCalled = true;
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;
uint32_t stickyTransform;
bool async;
sp<Fence> fence;
input.deflate(&timestamp, &isAutoTimestamp, &crop, &scalingMode, &transform,
&async, &fence, &stickyTransform);
if (fence == NULL) {
BQ_LOGE("queueBuffer: fence is NULL");
// Temporary workaround for b/17946343: soldier-on instead of returning an error. This
// prevents the client from dying, at the risk of visible corruption due to hwcomposer
// reading the buffer before the producer is done rendering it. Unless the buffer is the
// last frame of an animation, the corruption will be transient.
fence = Fence::NO_FENCE;
// 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 BAD_VALUE;
}
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 BAD_VALUE;
} 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 BAD_VALUE;
} else if (!mSlots[slot].mRequestBufferCalled) {
BQ_LOGE("queueBuffer: slot %d was queued without requesting "
"a buffer", slot);
return BAD_VALUE;
}
BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64
" 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 BAD_VALUE;
}
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;
mStickyTransform = stickyTransform;
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 >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)",
slot, BufferQueueDefs::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_STICKY_TRANSFORM:
value = static_cast<int>(mStickyTransform);
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<IProducerListener>& listener,
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 (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;
}
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 (listener != NULL &&
listener->asBinder()->remoteBinder() != NULL) {
status = listener->asBinder()->linkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
if (status != NO_ERROR) {
BQ_LOGE("connect(P): linkToDeath failed: %s (%d)",
strerror(-status), status);
}
}
mCore->mConnectedProducerListener = listener;
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);
mCore->waitWhileAllocatingLocked();
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
if (mCore->mConnectedProducerListener != NULL) {
sp<IBinder> token =
mCore->mConnectedProducerListener->asBinder();
// 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->mConnectedProducerListener = NULL;
mCore->mConnectedApi = BufferQueueCore::NO_CONNECTED_API;
mCore->mSidebandStream.clear();
mCore->mDequeueCondition.broadcast();
listener = mCore->mConsumerListener;
} else {
BQ_LOGE("disconnect(P): connected to another API "
"(cur=%d req=%d)", mCore->mConnectedApi, api);
status = BAD_VALUE;
}
break;
default:
BQ_LOGE("disconnect(P): unknown API %d", api);
status = BAD_VALUE;
break;
}
} // Autolock scope
// Call back without lock held
if (listener != NULL) {
listener->onBuffersReleased();
}
return status;
}
status_t BufferQueueProducer::setSidebandStream(const sp<NativeHandle>& stream) {
sp<IConsumerListener> listener;
{ // Autolock scope
Mutex::Autolock _l(mCore->mMutex);
mCore->mSidebandStream = stream;
listener = mCore->mConsumerListener;
} // Autolock scope
if (listener != NULL) {
listener->onSidebandStreamChanged();
}
return NO_ERROR;
}
void BufferQueueProducer::allocateBuffers(bool async, uint32_t width,
uint32_t height, uint32_t format, uint32_t usage) {
ATRACE_CALL();
while (true) {
Vector<int> freeSlots;
size_t newBufferCount = 0;
uint32_t allocWidth = 0;
uint32_t allocHeight = 0;
uint32_t allocFormat = 0;
uint32_t allocUsage = 0;
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
mCore->waitWhileAllocatingLocked();
int currentBufferCount = 0;
for (int slot = 0; slot < BufferQueueDefs::NUM_BUFFER_SLOTS; ++slot) {
if (mSlots[slot].mGraphicBuffer != NULL) {
++currentBufferCount;
} else {
if (mSlots[slot].mBufferState != BufferSlot::FREE) {
BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE",
slot);
continue;
}
freeSlots.push_back(slot);
}
}
int maxBufferCount = mCore->getMaxBufferCountLocked(async);
BQ_LOGV("allocateBuffers: allocating from %d buffers up to %d buffers",
currentBufferCount, maxBufferCount);
if (maxBufferCount <= currentBufferCount)
return;
newBufferCount = maxBufferCount - currentBufferCount;
if (freeSlots.size() < newBufferCount) {
BQ_LOGE("allocateBuffers: ran out of free slots");
return;
}
allocWidth = width > 0 ? width : mCore->mDefaultWidth;
allocHeight = height > 0 ? height : mCore->mDefaultHeight;
allocFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
allocUsage = usage | mCore->mConsumerUsageBits;
mCore->mIsAllocating = true;
} // Autolock scope
Vector<sp<GraphicBuffer> > buffers;
for (size_t i = 0; i < newBufferCount; ++i) {
status_t result = NO_ERROR;
sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
allocWidth, allocHeight, allocFormat, allocUsage, &result));
if (result != NO_ERROR) {
BQ_LOGE("allocateBuffers: failed to allocate buffer (%u x %u, format"
" %u, usage %u)", width, height, format, usage);
Mutex::Autolock lock(mCore->mMutex);
mCore->mIsAllocating = false;
mCore->mIsAllocatingCondition.broadcast();
return;
}
buffers.push_back(graphicBuffer);
}
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
uint32_t checkWidth = width > 0 ? width : mCore->mDefaultWidth;
uint32_t checkHeight = height > 0 ? height : mCore->mDefaultHeight;
uint32_t checkFormat = format != 0 ? format : mCore->mDefaultBufferFormat;
uint32_t checkUsage = usage | mCore->mConsumerUsageBits;
if (checkWidth != allocWidth || checkHeight != allocHeight ||
checkFormat != allocFormat || checkUsage != allocUsage) {
// Something changed while we released the lock. Retry.
BQ_LOGV("allocateBuffers: size/format/usage changed while allocating. Retrying.");
mCore->mIsAllocating = false;
mCore->mIsAllocatingCondition.broadcast();
continue;
}
for (size_t i = 0; i < newBufferCount; ++i) {
int slot = freeSlots[i];
if (mSlots[slot].mBufferState != BufferSlot::FREE) {
// A consumer allocated the FREE slot with attachBuffer. Discard the buffer we
// allocated.
BQ_LOGV("allocateBuffers: slot %d was acquired while allocating. "
"Dropping allocated buffer.", slot);
continue;
}
mCore->freeBufferLocked(slot); // Clean up the slot first
mSlots[slot].mGraphicBuffer = buffers[i];
mSlots[slot].mFrameNumber = 0;
mSlots[slot].mFence = Fence::NO_FENCE;
BQ_LOGV("allocateBuffers: allocated a new buffer in slot %d", slot);
}
mCore->mIsAllocating = false;
mCore->mIsAllocatingCondition.broadcast();
} // Autolock scope
}
}
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

View File

@ -0,0 +1,204 @@
/*
* 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/BufferQueueDefs.h>
#include <gui/IGraphicBufferProducer.h>
namespace android {
class BufferSlot;
class BufferQueueProducer : public BnGraphicBufferProducer,
private IBinder::DeathRecipient {
public:
friend class BufferQueue; // Needed to access binderDied
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);
// See IGraphicBufferProducer::detachBuffer
virtual status_t detachBuffer(int slot);
// See IGraphicBufferProducer::detachNextBuffer
virtual status_t detachNextBuffer(sp<GraphicBuffer>* outBuffer,
sp<Fence>* outFence);
// See IGraphicBufferProducer::attachBuffer
virtual status_t attachBuffer(int* outSlot, const sp<GraphicBuffer>& buffer);
// 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<IProducerListener>& listener,
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);
// Attaches a sideband buffer stream to the IGraphicBufferProducer.
//
// A sideband stream is a device-specific mechanism for passing buffers
// from the producer to the consumer without using dequeueBuffer/
// queueBuffer. If a sideband stream is present, the consumer can choose
// whether to acquire buffers from the sideband stream or from the queued
// buffers.
//
// Passing NULL or a different stream handle will detach the previous
// handle if any.
virtual status_t setSidebandStream(const sp<NativeHandle>& stream);
// See IGraphicBufferProducer::allocateBuffers
virtual void allocateBuffers(bool async, uint32_t width, uint32_t height,
uint32_t format, uint32_t usage);
private:
// This is required by the IBinder::DeathRecipient interface
virtual void binderDied(const wp<IBinder>& who);
// waitForFreeSlotThenRelock finds the oldest slot in the FREE state. It may
// block if there are no available slots and we are not in non-blocking
// mode (producer and consumer controlled by the application). If it blocks,
// it will release mCore->mMutex while blocked so that other operations on
// the BufferQueue may succeed.
status_t waitForFreeSlotThenRelock(const char* caller, bool async,
int* found, status_t* returnFlags) const;
sp<BufferQueueCore> mCore;
// This references mCore->mSlots. Lock mCore->mMutex while accessing.
BufferQueueDefs::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;
uint32_t mStickyTransform;
}; // class BufferQueueProducer
} // namespace android
#endif

View File

@ -0,0 +1,31 @@
/*
* 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/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

View File

@ -0,0 +1,142 @@
/*
* 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),
mAttachedByConsumer(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;
// Indicates whether the buffer was attached on the consumer side.
// If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when dequeued
// to prevent the producer from using a stale cached buffer.
bool mAttachedByConsumer;
};
} // namespace android
#endif

View File

@ -0,0 +1,269 @@
/*
* Copyright (C) 2010 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 <inttypes.h>
#define LOG_TAG "ConsumerBase"
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
//#define LOG_NDEBUG 0
#define EGL_EGLEXT_PROTOTYPES
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <hardware/hardware.h>
#include <gui/IGraphicBufferAlloc.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <gui/ConsumerBase.h>
#include <private/gui/ComposerService.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include <utils/Trace.h>
// Macros for including the ConsumerBase name in log messages
#define CB_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
#define CB_LOGD(x, ...) ALOGD("[%s] "x, mName.string(), ##__VA_ARGS__)
#define CB_LOGI(x, ...) ALOGI("[%s] "x, mName.string(), ##__VA_ARGS__)
#define CB_LOGW(x, ...) ALOGW("[%s] "x, mName.string(), ##__VA_ARGS__)
#define CB_LOGE(x, ...) ALOGE("[%s] "x, mName.string(), ##__VA_ARGS__)
namespace android {
// Get an ID that's unique within this process.
static int32_t createProcessUniqueId() {
static volatile int32_t globalCounter = 0;
return android_atomic_inc(&globalCounter);
}
ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
mAbandoned(false),
mConsumer(bufferQueue) {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
// Note that we can't create an sp<...>(this) in a ctor that will not keep a
// reference once the ctor ends, as that would cause the refcount of 'this'
// dropping to 0 at the end of the ctor. Since all we need is a wp<...>
// that's what we create.
wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);
status_t err = mConsumer->consumerConnect(proxy, controlledByApp);
if (err != NO_ERROR) {
CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
strerror(-err), err);
} else {
mConsumer->setConsumerName(mName);
}
}
ConsumerBase::~ConsumerBase() {
CB_LOGV("~ConsumerBase");
Mutex::Autolock lock(mMutex);
// Verify that abandon() has been called before we get here. This should
// be done by ConsumerBase::onLastStrongRef(), but it's possible for a
// derived class to override that method and not call
// ConsumerBase::onLastStrongRef().
LOG_ALWAYS_FATAL_IF(!mAbandoned, "[%s] ~ConsumerBase was called, but the "
"consumer is not abandoned!", mName.string());
}
void ConsumerBase::onLastStrongRef(const void* id __attribute__((unused))) {
abandon();
}
void ConsumerBase::freeBufferLocked(int slotIndex) {
CB_LOGV("freeBufferLocked: slotIndex=%d", slotIndex);
mSlots[slotIndex].mGraphicBuffer = 0;
mSlots[slotIndex].mFence = Fence::NO_FENCE;
mSlots[slotIndex].mFrameNumber = 0;
}
void ConsumerBase::onFrameAvailable() {
CB_LOGV("onFrameAvailable");
sp<FrameAvailableListener> listener;
{ // scope for the lock
Mutex::Autolock lock(mMutex);
listener = mFrameAvailableListener.promote();
}
if (listener != NULL) {
CB_LOGV("actually calling onFrameAvailable");
listener->onFrameAvailable();
}
}
void ConsumerBase::onBuffersReleased() {
Mutex::Autolock lock(mMutex);
CB_LOGV("onBuffersReleased");
if (mAbandoned) {
// Nothing to do if we're already abandoned.
return;
}
uint64_t mask = 0;
mConsumer->getReleasedBuffers(&mask);
for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
if (mask & (1ULL << i)) {
freeBufferLocked(i);
}
}
}
void ConsumerBase::onSidebandStreamChanged() {
}
void ConsumerBase::abandon() {
CB_LOGV("abandon");
Mutex::Autolock lock(mMutex);
if (!mAbandoned) {
abandonLocked();
mAbandoned = true;
}
}
void ConsumerBase::abandonLocked() {
CB_LOGV("abandonLocked");
for (int i =0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) {
freeBufferLocked(i);
}
// disconnect from the BufferQueue
mConsumer->consumerDisconnect();
mConsumer.clear();
}
void ConsumerBase::setFrameAvailableListener(
const wp<FrameAvailableListener>& listener) {
CB_LOGV("setFrameAvailableListener");
Mutex::Autolock lock(mMutex);
mFrameAvailableListener = listener;
}
void ConsumerBase::dump(String8& result) const {
dump(result, "");
}
void ConsumerBase::dump(String8& result, const char* prefix) const {
Mutex::Autolock _l(mMutex);
dumpLocked(result, prefix);
}
void ConsumerBase::dumpLocked(String8& result, const char* prefix) const {
result.appendFormat("%smAbandoned=%d\n", prefix, int(mAbandoned));
if (!mAbandoned) {
mConsumer->dump(result, prefix);
}
}
status_t ConsumerBase::acquireBufferLocked(BufferQueue::BufferItem *item,
nsecs_t presentWhen) {
status_t err = mConsumer->acquireBuffer(item, presentWhen);
if (err != NO_ERROR) {
return err;
}
if (item->mGraphicBuffer != NULL) {
mSlots[item->mBuf].mGraphicBuffer = item->mGraphicBuffer;
}
mSlots[item->mBuf].mFrameNumber = item->mFrameNumber;
mSlots[item->mBuf].mFence = item->mFence;
CB_LOGV("acquireBufferLocked: -> slot=%d/%" PRIu64,
item->mBuf, item->mFrameNumber);
return OK;
}
status_t ConsumerBase::addReleaseFence(int slot,
const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) {
Mutex::Autolock lock(mMutex);
return addReleaseFenceLocked(slot, graphicBuffer, fence);
}
status_t ConsumerBase::addReleaseFenceLocked(int slot,
const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence) {
CB_LOGV("addReleaseFenceLocked: slot=%d", slot);
// If consumer no longer tracks this graphicBuffer, we can safely
// drop this fence, as it will never be received by the producer.
if (!stillTracking(slot, graphicBuffer)) {
return OK;
}
if (!mSlots[slot].mFence.get()) {
mSlots[slot].mFence = fence;
} else {
sp<Fence> mergedFence = Fence::merge(
String8::format("%.28s:%d", mName.string(), slot),
mSlots[slot].mFence, fence);
if (!mergedFence.get()) {
CB_LOGE("failed to merge release fences");
// synchronization is broken, the best we can do is hope fences
// signal in order so the new fence will act like a union
mSlots[slot].mFence = fence;
return BAD_VALUE;
}
mSlots[slot].mFence = mergedFence;
}
return OK;
}
status_t ConsumerBase::releaseBufferLocked(
int slot, const sp<GraphicBuffer> graphicBuffer,
EGLDisplay display, EGLSyncKHR eglFence) {
// If consumer no longer tracks this graphicBuffer (we received a new
// buffer on the same slot), the buffer producer is definitely no longer
// tracking it.
if (!stillTracking(slot, graphicBuffer)) {
return OK;
}
CB_LOGV("releaseBufferLocked: slot=%d/%" PRIu64,
slot, mSlots[slot].mFrameNumber);
status_t err = mConsumer->releaseBuffer(slot, mSlots[slot].mFrameNumber,
display, eglFence, mSlots[slot].mFence);
if (err == IGraphicBufferConsumer::STALE_BUFFER_SLOT) {
freeBufferLocked(slot);
}
mSlots[slot].mFence = Fence::NO_FENCE;
return err;
}
bool ConsumerBase::stillTracking(int slot,
const sp<GraphicBuffer> graphicBuffer) {
if (slot < 0 || slot >= BufferQueue::NUM_BUFFER_SLOTS) {
return false;
}
return (mSlots[slot].mGraphicBuffer != NULL &&
mSlots[slot].mGraphicBuffer->handle == graphicBuffer->handle);
}
} // namespace android

View File

@ -0,0 +1,241 @@
/*
* Copyright (C) 2010 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_CONSUMERBASE_H
#define ANDROID_GUI_CONSUMERBASE_H
#include <gui/BufferQueue.h>
#include <ui/GraphicBuffer.h>
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/threads.h>
#include <gui/IConsumerListener.h>
namespace android {
// ----------------------------------------------------------------------------
class String8;
// ConsumerBase is a base class for BufferQueue consumer end-points. It
// handles common tasks like management of the connection to the BufferQueue
// and the buffer pool.
class ConsumerBase : public virtual RefBase,
protected ConsumerListener {
public:
struct FrameAvailableListener : public virtual RefBase {
// onFrameAvailable() is called each time an additional frame becomes
// available for consumption. This means that frames that are queued
// while in asynchronous mode only trigger the callback if no previous
// frames are pending. Frames queued while in synchronous mode always
// trigger the callback.
//
// This is called without any lock held and can be called concurrently
// by multiple threads.
virtual void onFrameAvailable() = 0;
};
virtual ~ConsumerBase();
// abandon frees all the buffers and puts the ConsumerBase into the
// 'abandoned' state. Once put in this state the ConsumerBase can never
// leave it. When in the 'abandoned' state, all methods of the
// IGraphicBufferProducer interface will fail with the NO_INIT error.
//
// Note that while calling this method causes all the buffers to be freed
// from the perspective of the the ConsumerBase, if there are additional
// references on the buffers (e.g. if a buffer is referenced by a client
// or by OpenGL ES as a texture) then those buffer will remain allocated.
void abandon();
// set the name of the ConsumerBase that will be used to identify it in
// log messages.
void setName(const String8& name);
// dump writes the current state to a string. Child classes should add
// their state to the dump by overriding the dumpLocked method, which is
// called by these methods after locking the mutex.
void dump(String8& result) const;
void dump(String8& result, const char* prefix) const;
// setFrameAvailableListener sets the listener object that will be notified
// when a new frame becomes available.
void setFrameAvailableListener(const wp<FrameAvailableListener>& listener);
private:
ConsumerBase(const ConsumerBase&);
void operator=(const ConsumerBase&);
protected:
// ConsumerBase constructs a new ConsumerBase object to consume image
// buffers from the given IGraphicBufferConsumer.
// The controlledByApp flag indicates that this consumer is under the application's
// control.
ConsumerBase(const sp<IGraphicBufferConsumer>& consumer, bool controlledByApp = false);
// onLastStrongRef gets called by RefBase just before the dtor of the most
// derived class. It is used to clean up the buffers so that ConsumerBase
// can coordinate the clean-up by calling into virtual methods implemented
// by the derived classes. This would not be possible from the
// ConsuemrBase dtor because by the time that gets called the derived
// classes have already been destructed.
//
// This methods should not need to be overridden by derived classes, but
// if they are overridden the ConsumerBase implementation must be called
// from the derived class.
virtual void onLastStrongRef(const void* id);
// Implementation of the IConsumerListener interface. These
// calls are used to notify the ConsumerBase of asynchronous events in the
// BufferQueue. The onFrameAvailable and onBuffersReleased methods should
// not need to be overridden by derived classes, but if they are overridden
// the ConsumerBase implementation must be called from the derived class.
// The ConsumerBase version of onSidebandStreamChanged does nothing and can
// be overriden by derived classes if they want the notification.
virtual void onFrameAvailable();
virtual void onBuffersReleased();
virtual void onSidebandStreamChanged();
// freeBufferLocked frees up the given buffer slot. If the slot has been
// initialized this will release the reference to the GraphicBuffer in that
// slot. Otherwise it has no effect.
//
// Derived classes should override this method to clean up any state they
// keep per slot. If it is overridden, the derived class's implementation
// must call ConsumerBase::freeBufferLocked.
//
// This method must be called with mMutex locked.
virtual void freeBufferLocked(int slotIndex);
// abandonLocked puts the BufferQueue into the abandoned state, causing
// all future operations on it to fail. This method rather than the public
// abandon method should be overridden by child classes to add abandon-
// time behavior.
//
// Derived classes should override this method to clean up any object
// state they keep (as opposed to per-slot state). If it is overridden,
// the derived class's implementation must call ConsumerBase::abandonLocked.
//
// This method must be called with mMutex locked.
virtual void abandonLocked();
// dumpLocked dumps the current state of the ConsumerBase object to the
// result string. Each line is prefixed with the string pointed to by the
// prefix argument. The buffer argument points to a buffer that may be
// used for intermediate formatting data, and the size of that buffer is
// indicated by the size argument.
//
// Derived classes should override this method to dump their internal
// state. If this method is overridden the derived class's implementation
// should call ConsumerBase::dumpLocked.
//
// This method must be called with mMutex locked.
virtual void dumpLocked(String8& result, const char* prefix) const;
// acquireBufferLocked fetches the next buffer from the BufferQueue and
// updates the buffer slot for the buffer returned.
//
// Derived classes should override this method to perform any
// initialization that must take place the first time a buffer is assigned
// to a slot. If it is overridden the derived class's implementation must
// call ConsumerBase::acquireBufferLocked.
virtual status_t acquireBufferLocked(IGraphicBufferConsumer::BufferItem *item,
nsecs_t presentWhen);
// releaseBufferLocked relinquishes control over a buffer, returning that
// control to the BufferQueue.
//
// Derived classes should override this method to perform any cleanup that
// must take place when a buffer is released back to the BufferQueue. If
// it is overridden the derived class's implementation must call
// ConsumerBase::releaseBufferLocked.e
virtual status_t releaseBufferLocked(int slot,
const sp<GraphicBuffer> graphicBuffer,
EGLDisplay display, EGLSyncKHR eglFence);
// returns true iff the slot still has the graphicBuffer in it.
bool stillTracking(int slot, const sp<GraphicBuffer> graphicBuffer);
// addReleaseFence* adds the sync points associated with a fence to the set
// of sync points that must be reached before the buffer in the given slot
// may be used after the slot has been released. This should be called by
// derived classes each time some asynchronous work is kicked off that
// references the buffer.
status_t addReleaseFence(int slot,
const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence);
status_t addReleaseFenceLocked(int slot,
const sp<GraphicBuffer> graphicBuffer, const sp<Fence>& fence);
// Slot contains the information and object references that
// ConsumerBase maintains about a BufferQueue buffer slot.
struct Slot {
// mGraphicBuffer is the Gralloc buffer store in the slot or NULL if
// no Gralloc buffer is in the slot.
sp<GraphicBuffer> mGraphicBuffer;
// mFence is a fence which will signal when the buffer associated with
// this buffer slot is no longer being used by the consumer and can be
// overwritten. The buffer can be dequeued before the fence signals;
// the producer is responsible for delaying writes until it signals.
sp<Fence> mFence;
// the frame number of the last acquired frame for this slot
uint64_t mFrameNumber;
};
// mSlots stores the buffers that have been allocated by the BufferQueue
// for each buffer slot. It is initialized to null pointers, and gets
// filled in with the result of BufferQueue::acquire when the
// client dequeues a buffer from a
// slot that has not yet been used. The buffer allocated to a slot will also
// be replaced if the requested buffer usage or geometry differs from that
// of the buffer allocated to a slot.
Slot mSlots[BufferQueue::NUM_BUFFER_SLOTS];
// mAbandoned indicates that the BufferQueue will no longer be used to
// consume images buffers pushed to it using the IGraphicBufferProducer
// interface. It is initialized to false, and set to true in the abandon
// method. A BufferQueue that has been abandoned will return the NO_INIT
// error from all IConsumerBase methods capable of returning an error.
bool mAbandoned;
// mName is a string used to identify the ConsumerBase in log messages.
// It can be set by the setName method.
String8 mName;
// mFrameAvailableListener is the listener object that will be called when a
// new frame becomes available. If it is not NULL it will be called from
// queueBuffer.
wp<FrameAvailableListener> mFrameAvailableListener;
// The ConsumerBase has-a BufferQueue and is responsible for creating this object
// if none is supplied
sp<IGraphicBufferConsumer> mConsumer;
// mMutex is the mutex used to prevent concurrent access to the member
// variables of ConsumerBase objects. It must be locked whenever the
// member variables are accessed or when any of the *Locked methods are
// called.
//
// This mutex is intended to be locked by derived classes.
mutable Mutex mMutex;
};
// ----------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_GUI_CONSUMERBASE_H

View File

@ -0,0 +1,553 @@
/*
* Copyright (C) 2013 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 <stdint.h>
#include <sys/types.h>
#include <utils/Errors.h>
#include <utils/NativeHandle.h>
#include <binder/Parcel.h>
#include <binder/IInterface.h>
#include <gui/IConsumerListener.h>
#include <gui/IGraphicBufferConsumer.h>
#include <ui/GraphicBuffer.h>
#include <ui/Fence.h>
#include <system/window.h>
namespace android {
// ---------------------------------------------------------------------------
IGraphicBufferConsumer::BufferItem::BufferItem() :
mTransform(0),
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mTimestamp(0),
mIsAutoTimestamp(false),
mFrameNumber(0),
mBuf(INVALID_BUFFER_SLOT),
mIsDroppable(false),
mAcquireCalled(false),
mTransformToDisplayInverse(false) {
mCrop.makeInvalid();
}
size_t IGraphicBufferConsumer::BufferItem::getPodSize() const {
size_t c = sizeof(mCrop) +
sizeof(mTransform) +
sizeof(mScalingMode) +
sizeof(mTimestamp) +
sizeof(mIsAutoTimestamp) +
sizeof(mFrameNumber) +
sizeof(mBuf) +
sizeof(mIsDroppable) +
sizeof(mAcquireCalled) +
sizeof(mTransformToDisplayInverse);
return c;
}
size_t IGraphicBufferConsumer::BufferItem::getFlattenedSize() const {
size_t c = 0;
if (mGraphicBuffer != 0) {
c += mGraphicBuffer->getFlattenedSize();
c = FlattenableUtils::align<4>(c);
}
if (mFence != 0) {
c += mFence->getFlattenedSize();
c = FlattenableUtils::align<4>(c);
}
return sizeof(int32_t) + c + getPodSize();
}
size_t IGraphicBufferConsumer::BufferItem::getFdCount() const {
size_t c = 0;
if (mGraphicBuffer != 0) {
c += mGraphicBuffer->getFdCount();
}
if (mFence != 0) {
c += mFence->getFdCount();
}
return c;
}
static void writeBoolAsInt(void*& buffer, size_t& size, bool b) {
FlattenableUtils::write(buffer, size, static_cast<int32_t>(b));
}
static bool readBoolFromInt(void const*& buffer, size_t& size) {
int32_t i;
FlattenableUtils::read(buffer, size, i);
return static_cast<bool>(i);
}
status_t IGraphicBufferConsumer::BufferItem::flatten(
void*& buffer, size_t& size, int*& fds, size_t& count) const {
// make sure we have enough space
if (size < 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);
writeBoolAsInt(buffer, size, mIsAutoTimestamp);
FlattenableUtils::write(buffer, size, mFrameNumber);
FlattenableUtils::write(buffer, size, mBuf);
writeBoolAsInt(buffer, size, mIsDroppable);
writeBoolAsInt(buffer, size, mAcquireCalled);
writeBoolAsInt(buffer, size, mTransformToDisplayInverse);
return NO_ERROR;
}
status_t IGraphicBufferConsumer::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);
mIsAutoTimestamp = readBoolFromInt(buffer, size);
FlattenableUtils::read(buffer, size, mFrameNumber);
FlattenableUtils::read(buffer, size, mBuf);
mIsDroppable = readBoolFromInt(buffer, size);
mAcquireCalled = readBoolFromInt(buffer, size);
mTransformToDisplayInverse = readBoolFromInt(buffer, size);
return NO_ERROR;
}
// ---------------------------------------------------------------------------
enum {
ACQUIRE_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
DETACH_BUFFER,
ATTACH_BUFFER,
RELEASE_BUFFER,
CONSUMER_CONNECT,
CONSUMER_DISCONNECT,
GET_RELEASED_BUFFERS,
SET_DEFAULT_BUFFER_SIZE,
SET_DEFAULT_MAX_BUFFER_COUNT,
DISABLE_ASYNC_BUFFER,
SET_MAX_ACQUIRED_BUFFER_COUNT,
SET_CONSUMER_NAME,
SET_DEFAULT_BUFFER_FORMAT,
SET_CONSUMER_USAGE_BITS,
SET_TRANSFORM_HINT,
GET_SIDEBAND_STREAM,
DUMP,
};
class BpGraphicBufferConsumer : public BpInterface<IGraphicBufferConsumer>
{
public:
BpGraphicBufferConsumer(const sp<IBinder>& impl)
: BpInterface<IGraphicBufferConsumer>(impl)
{
}
virtual status_t acquireBuffer(BufferItem *buffer, nsecs_t presentWhen) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeInt64(presentWhen);
status_t result = remote()->transact(ACQUIRE_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
result = reply.read(*buffer);
if (result != NO_ERROR) {
return result;
}
return reply.readInt32();
}
virtual status_t detachBuffer(int slot) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeInt32(slot);
status_t result = remote()->transact(DETACH_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
result = reply.readInt32();
return result;
}
virtual status_t attachBuffer(int* slot, const sp<GraphicBuffer>& buffer) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.write(*buffer.get());
status_t result = remote()->transact(ATTACH_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
*slot = reply.readInt32();
result = reply.readInt32();
return result;
}
virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
EGLDisplay display __attribute__((unused)), EGLSyncKHR fence __attribute__((unused)),
const sp<Fence>& releaseFence) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeInt32(buf);
data.writeInt64(frameNumber);
data.write(*releaseFence);
status_t result = remote()->transact(RELEASE_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
return reply.readInt32();
}
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeStrongBinder(consumer->asBinder());
data.writeInt32(controlledByApp);
status_t result = remote()->transact(CONSUMER_CONNECT, data, &reply);
if (result != NO_ERROR) {
return result;
}
return reply.readInt32();
}
virtual status_t consumerDisconnect() {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
status_t result = remote()->transact(CONSUMER_DISCONNECT, data, &reply);
if (result != NO_ERROR) {
return result;
}
return reply.readInt32();
}
virtual status_t getReleasedBuffers(uint64_t* slotMask) {
Parcel data, reply;
if (slotMask == NULL) {
ALOGE("getReleasedBuffers: slotMask must not be NULL");
return BAD_VALUE;
}
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
status_t result = remote()->transact(GET_RELEASED_BUFFERS, data, &reply);
if (result != NO_ERROR) {
return result;
}
*slotMask = reply.readInt64();
return reply.readInt32();
}
virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeInt32(w);
data.writeInt32(h);
status_t result = remote()->transact(SET_DEFAULT_BUFFER_SIZE, data, &reply);
if (result != NO_ERROR) {
return result;
}
return reply.readInt32();
}
virtual status_t setDefaultMaxBufferCount(int bufferCount) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeInt32(bufferCount);
status_t result = remote()->transact(SET_DEFAULT_MAX_BUFFER_COUNT, data, &reply);
if (result != NO_ERROR) {
return result;
}
return reply.readInt32();
}
virtual status_t disableAsyncBuffer() {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
status_t result = remote()->transact(DISABLE_ASYNC_BUFFER, data, &reply);
if (result != NO_ERROR) {
return result;
}
return reply.readInt32();
}
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeInt32(maxAcquiredBuffers);
status_t result = remote()->transact(SET_MAX_ACQUIRED_BUFFER_COUNT, data, &reply);
if (result != NO_ERROR) {
return result;
}
return reply.readInt32();
}
virtual void setConsumerName(const String8& name) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeString8(name);
remote()->transact(SET_CONSUMER_NAME, data, &reply);
}
virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeInt32(defaultFormat);
status_t result = remote()->transact(SET_DEFAULT_BUFFER_FORMAT, data, &reply);
if (result != NO_ERROR) {
return result;
}
return reply.readInt32();
}
virtual status_t setConsumerUsageBits(uint32_t usage) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeInt32(usage);
status_t result = remote()->transact(SET_CONSUMER_USAGE_BITS, data, &reply);
if (result != NO_ERROR) {
return result;
}
return reply.readInt32();
}
virtual status_t setTransformHint(uint32_t hint) {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeInt32(hint);
status_t result = remote()->transact(SET_TRANSFORM_HINT, data, &reply);
if (result != NO_ERROR) {
return result;
}
return reply.readInt32();
}
virtual sp<NativeHandle> getSidebandStream() const {
Parcel data, reply;
status_t err;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
if ((err = remote()->transact(GET_SIDEBAND_STREAM, data, &reply)) != NO_ERROR) {
return NULL;
}
sp<NativeHandle> stream;
if (reply.readInt32()) {
stream = NativeHandle::create(reply.readNativeHandle(), true);
}
return stream;
}
virtual void dump(String8& result, const char* prefix) const {
Parcel data, reply;
data.writeInterfaceToken(IGraphicBufferConsumer::getInterfaceDescriptor());
data.writeString8(result);
data.writeString8(String8(prefix ? prefix : ""));
remote()->transact(DUMP, data, &reply);
reply.readString8();
}
};
IMPLEMENT_META_INTERFACE(GraphicBufferConsumer, "android.gui.IGraphicBufferConsumer");
// ----------------------------------------------------------------------
status_t BnGraphicBufferConsumer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch(code) {
case ACQUIRE_BUFFER: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
BufferItem item;
int64_t presentWhen = data.readInt64();
status_t result = acquireBuffer(&item, presentWhen);
status_t err = reply->write(item);
if (err) return err;
reply->writeInt32(result);
return NO_ERROR;
} break;
case DETACH_BUFFER: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
int slot = data.readInt32();
int result = detachBuffer(slot);
reply->writeInt32(result);
return NO_ERROR;
} break;
case ATTACH_BUFFER: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
sp<GraphicBuffer> buffer = new GraphicBuffer();
data.read(*buffer.get());
int slot;
int result = attachBuffer(&slot, buffer);
reply->writeInt32(slot);
reply->writeInt32(result);
return NO_ERROR;
} break;
case RELEASE_BUFFER: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
int buf = data.readInt32();
uint64_t frameNumber = data.readInt64();
sp<Fence> releaseFence = new Fence();
status_t err = data.read(*releaseFence);
if (err) return err;
status_t result = releaseBuffer(buf, frameNumber,
EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, releaseFence);
reply->writeInt32(result);
return NO_ERROR;
} break;
case CONSUMER_CONNECT: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
sp<IConsumerListener> consumer = IConsumerListener::asInterface( data.readStrongBinder() );
bool controlledByApp = data.readInt32();
status_t result = consumerConnect(consumer, controlledByApp);
reply->writeInt32(result);
return NO_ERROR;
} break;
case CONSUMER_DISCONNECT: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
status_t result = consumerDisconnect();
reply->writeInt32(result);
return NO_ERROR;
} break;
case GET_RELEASED_BUFFERS: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
uint64_t slotMask;
status_t result = getReleasedBuffers(&slotMask);
reply->writeInt64(slotMask);
reply->writeInt32(result);
return NO_ERROR;
} break;
case SET_DEFAULT_BUFFER_SIZE: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
uint32_t w = data.readInt32();
uint32_t h = data.readInt32();
status_t result = setDefaultBufferSize(w, h);
reply->writeInt32(result);
return NO_ERROR;
} break;
case SET_DEFAULT_MAX_BUFFER_COUNT: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
uint32_t bufferCount = data.readInt32();
status_t result = setDefaultMaxBufferCount(bufferCount);
reply->writeInt32(result);
return NO_ERROR;
} break;
case DISABLE_ASYNC_BUFFER: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
status_t result = disableAsyncBuffer();
reply->writeInt32(result);
return NO_ERROR;
} break;
case SET_MAX_ACQUIRED_BUFFER_COUNT: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
uint32_t maxAcquiredBuffers = data.readInt32();
status_t result = setMaxAcquiredBufferCount(maxAcquiredBuffers);
reply->writeInt32(result);
return NO_ERROR;
} break;
case SET_CONSUMER_NAME: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
setConsumerName( data.readString8() );
return NO_ERROR;
} break;
case SET_DEFAULT_BUFFER_FORMAT: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
uint32_t defaultFormat = data.readInt32();
status_t result = setDefaultBufferFormat(defaultFormat);
reply->writeInt32(result);
return NO_ERROR;
} break;
case SET_CONSUMER_USAGE_BITS: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
uint32_t usage = data.readInt32();
status_t result = setConsumerUsageBits(usage);
reply->writeInt32(result);
return NO_ERROR;
} break;
case SET_TRANSFORM_HINT: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
uint32_t hint = data.readInt32();
status_t result = setTransformHint(hint);
reply->writeInt32(result);
return NO_ERROR;
} break;
case DUMP: {
CHECK_INTERFACE(IGraphicBufferConsumer, data, reply);
String8 result = data.readString8();
String8 prefix = data.readString8();
static_cast<IGraphicBufferConsumer*>(this)->dump(result, prefix);
reply->writeString8(result);
return NO_ERROR;
}
}
return BBinder::onTransact(code, data, reply, flags);
}
}; // namespace android

View File

@ -0,0 +1,327 @@
/*
* Copyright (C) 2013 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_IGRAPHICBUFFERCONSUMER_H
#define ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H
#include <stdint.h>
#include <sys/types.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <binder/IInterface.h>
#include <ui/Rect.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
namespace android {
// ----------------------------------------------------------------------------
class Fence;
class GraphicBuffer;
class IConsumerListener;
class NativeHandle;
class IGraphicBufferConsumer : public IInterface {
public:
// public facing structure for BufferSlot
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();
// 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;
// mBuf is the slot index of this buffer (default INVALID_BUFFER_SLOT).
int mBuf;
// 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;
};
enum {
// Returned by releaseBuffer, after which the consumer must
// free any references to the just-released buffer that it might have.
STALE_BUFFER_SLOT = 1,
// Returned by dequeueBuffer if there are no pending buffers available.
NO_BUFFER_AVAILABLE,
// Returned by dequeueBuffer if it's too early for the buffer to be acquired.
PRESENT_LATER,
};
// 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 presentWhen is non-zero, 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.
//
// Return of NO_ERROR means the operation completed as normal.
//
// Return of a positive value means the operation could not be completed
// at this time, but the user should try again later:
// * NO_BUFFER_AVAILABLE - no buffer is pending (nothing queued by producer)
// * PRESENT_LATER - the buffer's timestamp is farther in the future
//
// Return of a negative value means an error has occurred:
// * INVALID_OPERATION - too many buffers have been acquired
virtual status_t acquireBuffer(BufferItem* buffer, nsecs_t presentWhen) = 0;
// detachBuffer attempts to remove all ownership of the buffer in the given
// slot from the buffer queue. If this call succeeds, the slot will be
// freed, and there will be no way to obtain the buffer from this interface.
// The freed slot will remain unallocated until either it is selected to
// hold a freshly allocated buffer in dequeueBuffer or a buffer is attached
// to the slot. The buffer must have already been acquired.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - the given slot number is invalid, either because it is
// out of the range [0, NUM_BUFFER_SLOTS) or because the slot
// it refers to is not currently acquired.
virtual status_t detachBuffer(int slot) = 0;
// attachBuffer attempts to transfer ownership of a buffer to the buffer
// queue. If this call succeeds, it will be as if this buffer was acquired
// from the returned slot number. As such, this call will fail if attaching
// this buffer would cause too many buffers to be simultaneously acquired.
//
// If the buffer is successfully attached, its frameNumber is initialized
// to 0. This must be passed into the releaseBuffer call or else the buffer
// will be deallocated as stale.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - outSlot or buffer were NULL
// * INVALID_OPERATION - cannot attach the buffer because it would cause too
// many buffers to be acquired.
// * NO_MEMORY - no free slots available
virtual status_t attachBuffer(int *outSlot,
const sp<GraphicBuffer>& buffer) = 0;
// 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.
//
// Return of NO_ERROR means the operation completed as normal.
//
// Return of a positive value means the operation could not be completed
// at this time, but the user should try again later:
// * STALE_BUFFER_SLOT - see above (second paragraph)
//
// Return of a negative value means an error has occurred:
// * BAD_VALUE - one of the following could've happened:
// * the buffer slot was invalid
// * the fence was NULL
// * the buffer slot specified is not in the acquired state
virtual status_t releaseBuffer(int buf, uint64_t frameNumber,
EGLDisplay display, EGLSyncKHR fence,
const sp<Fence>& releaseFence) = 0;
// consumerConnect 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.
//
// consumer may not be NULL.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned
// * BAD_VALUE - a NULL consumer was provided
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer, bool controlledByApp) = 0;
// consumerDisconnect 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.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - no consumer is currently connected
virtual status_t consumerDisconnect() = 0;
// getReleasedBuffers sets the value pointed to by slotMask to a bit set.
// Each bit index with a 1 corresponds to a released buffer slot with that
// index value. In particular, a released buffer is one that has
// been released by the BufferQueue but have not yet been released by the consumer.
//
// This should be called from the onBuffersReleased() callback.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * NO_INIT - the buffer queue has been abandoned.
virtual status_t getReleasedBuffers(uint64_t* slotMask) = 0;
// setDefaultBufferSize is used to set the size of buffers returned by
// dequeueBuffer when a width and height of zero is requested. Default
// is 1x1.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - either w or h was zero
virtual status_t setDefaultBufferSize(uint32_t w, uint32_t h) = 0;
// 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.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - bufferCount was out of range (see above).
virtual status_t setDefaultMaxBufferCount(int bufferCount) = 0;
// disableAsyncBuffer disables the extra buffer used in async mode
// (when both producer and consumer have set their "isControlledByApp"
// flag) and has dequeueBuffer() return WOULD_BLOCK instead.
//
// This can only be called before consumerConnect().
//
// Return of a value other than NO_ERROR means an error has occurred:
// * INVALID_OPERATION - attempting to call this after consumerConnect.
virtual status_t disableAsyncBuffer() = 0;
// 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.
//
// maxAcquiredBuffers must be (inclusive) between 1 and MAX_MAX_ACQUIRED_BUFFERS.
//
// Return of a value other than NO_ERROR means an error has occurred:
// * BAD_VALUE - maxAcquiredBuffers was out of range (see above).
// * INVALID_OPERATION - attempting to call this after a producer connected.
virtual status_t setMaxAcquiredBufferCount(int maxAcquiredBuffers) = 0;
// setConsumerName sets the name used in logging
virtual void setConsumerName(const String8& name) = 0;
// 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.
//
// Return of a value other than NO_ERROR means an unknown error has occurred.
virtual status_t setDefaultBufferFormat(uint32_t defaultFormat) = 0;
// 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.
//
// Return of a value other than NO_ERROR means an unknown error has occurred.
virtual status_t setConsumerUsageBits(uint32_t usage) = 0;
// 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).
//
// Return of a value other than NO_ERROR means an unknown error has occurred.
virtual status_t setTransformHint(uint32_t hint) = 0;
// Retrieve the sideband buffer stream, if any.
virtual sp<NativeHandle> getSidebandStream() const = 0;
// dump state into a string
virtual void dump(String8& result, const char* prefix) const = 0;
public:
DECLARE_META_INTERFACE(GraphicBufferConsumer);
};
// ----------------------------------------------------------------------------
class BnGraphicBufferConsumer : public BnInterface<IGraphicBufferConsumer>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
// ----------------------------------------------------------------------------
}; // namespace android
#endif // ANDROID_GUI_IGRAPHICBUFFERCONSUMER_H