// freeBufferLocked frees the GraphicBuffer and sync resources for the
// given slot.
- void freeBufferLocked(int slot);
+ void freeBufferLocked(int slot, bool validate = true);
// freeAllBuffersLocked frees the GraphicBuffer and sync resources for
- // all slots.
+ // all slots, even if they're currently dequeued, queued, or acquired.
void freeAllBuffersLocked();
// stillTracking returns true iff the buffer item is still being tracked
// enqueue buffers without blocking.
bool mAsyncMode;
+ // mSingleBufferMode indicates whether or not single buffer mode is enabled.
+ // In single buffer mode, the last buffer that was dequeued is cached and
+ // returned to all calls to dequeueBuffer and acquireBuffer. This allows the
+ // consumer and producer to access the same buffer simultaneously.
+ bool mSingleBufferMode;
+
+ // When single buffer mode is enabled, this tracks which slot contains the
+ // shared buffer.
+ int mSingleBufferSlot;
+
+ // Cached data about the shared buffer in single buffer mode
+ struct SingleBufferCache {
+ SingleBufferCache(Rect _crop, uint32_t _transform, int _scalingMode,
+ android_dataspace _dataspace)
+ : crop(_crop),
+ transform(_transform),
+ scalingMode(_scalingMode),
+ dataspace(_dataspace) {
+ };
+
+ Rect crop;
+ uint32_t transform;
+ uint32_t scalingMode;
+ android_dataspace dataspace;
+ } mSingleBufferCache;
+
}; // class BufferQueueCore
} // namespace android
// See IGraphicBufferProducer::getNextFrameNumber
virtual uint64_t getNextFrameNumber() const override;
+ // See IGraphicBufferProducer::setSingleBufferMode
+ virtual status_t setSingleBufferMode(bool singleBufferMode);
+
private:
// This is required by the IBinder::DeathRecipient interface
virtual void binderDied(const wp<IBinder>& who);
class Fence;
+// BufferState tracks the states in which a buffer slot can be.
+struct BufferState {
+
+ // All slots are initially FREE (not dequeued, queued, acquired, or shared).
+ BufferState()
+ : mDequeueCount(0),
+ mQueueCount(0),
+ mAcquireCount(0),
+ mShared(false) {
+ }
+
+ uint32_t mDequeueCount;
+ uint32_t mQueueCount;
+ uint32_t mAcquireCount;
+ bool mShared;
+
+ // A buffer can be in one of five states, represented as below:
+ //
+ // | mShared | mDequeueCount | mQueueCount | mAcquireCount |
+ // --------|---------|---------------|-------------|---------------|
+ // FREE | false | 0 | 0 | 0 |
+ // DEQUEUED| false | 1 | 0 | 0 |
+ // QUEUED | false | 0 | 1 | 0 |
+ // ACQUIRED| false | 0 | 0 | 1 |
+ // SHARED | true | any | any | any |
+ //
+ // FREE indicates that the buffer is available to be dequeued by the
+ // producer. The slot is "owned" by BufferQueue. It transitions to DEQUEUED
+ // when dequeueBuffer is called.
+ //
+ // DEQUEUED indicates that the buffer has been dequeued by the producer, but
+ // has not yet been queued or canceled. The producer may modify the
+ // buffer's contents as soon as the associated release fence is signaled.
+ // The slot is "owned" by the producer. It can transition to QUEUED (via
+ // queueBuffer or attachBuffer) or back to FREE (via cancelBuffer or
+ // detachBuffer).
+ //
+ // QUEUED indicates that the buffer has been filled by the producer and
+ // queued for use by the consumer. The buffer contents may continue to be
+ // modified for a finite time, so the contents must not be accessed until
+ // the associated fence is signaled. The slot is "owned" by BufferQueue. It
+ // can transition to ACQUIRED (via acquireBuffer) or to FREE (if another
+ // buffer is queued in asynchronous mode).
+ //
+ // ACQUIRED indicates that the buffer has been acquired by the consumer. As
+ // with QUEUED, the contents must not be accessed by the consumer until the
+ // acquire fence is signaled. The slot is "owned" by the consumer. It
+ // transitions to FREE when releaseBuffer (or detachBuffer) is called. A
+ // detached buffer can also enter the ACQUIRED state via attachBuffer.
+ //
+ // SHARED indicates that this buffer is being used in single-buffer
+ // mode. It can be in any combination of the other states at the same time,
+ // except for FREE (since that excludes being in any other state). It can
+ // also be dequeued, queued, or acquired multiple times.
+
+ inline bool isFree() const {
+ return !isAcquired() && !isDequeued() && !isQueued();
+ }
+
+ inline bool isDequeued() const {
+ return mDequeueCount > 0;
+ }
+
+ inline bool isQueued() const {
+ return mQueueCount > 0;
+ }
+
+ inline bool isAcquired() const {
+ return mAcquireCount > 0;
+ }
+
+ inline bool isShared() const {
+ return mShared;
+ }
+
+ inline void reset() {
+ *this = BufferState();
+ }
+
+ const char* string() const;
+
+ inline void dequeue() {
+ mDequeueCount++;
+ }
+
+ inline void detachProducer() {
+ if (mDequeueCount > 0) {
+ mDequeueCount--;
+ }
+ }
+
+ inline void attachProducer() {
+ mDequeueCount++;
+ }
+
+ inline void queue() {
+ if (mDequeueCount > 0) {
+ mDequeueCount--;
+ }
+ mQueueCount++;
+ }
+
+ inline void cancel() {
+ if (mDequeueCount > 0) {
+ mDequeueCount--;
+ }
+ }
+
+ inline void freeQueued() {
+ if (mQueueCount > 0) {
+ mQueueCount--;
+ }
+ }
+
+ inline void acquire() {
+ if (mQueueCount > 0) {
+ mQueueCount--;
+ }
+ mAcquireCount++;
+ }
+
+ inline void acquireNotInQueue() {
+ mAcquireCount++;
+ }
+
+ inline void release() {
+ if (mAcquireCount > 0) {
+ mAcquireCount--;
+ }
+ }
+
+ inline void detachConsumer() {
+ if (mAcquireCount > 0) {
+ mAcquireCount--;
+ }
+ }
+
+ inline void attachConsumer() {
+ mAcquireCount++;
+ }
+};
+
struct BufferSlot {
BufferSlot()
: mEglDisplay(EGL_NO_DISPLAY),
- mBufferState(BufferSlot::FREE),
+ mBufferState(),
mRequestBufferCalled(false),
mFrameNumber(0),
mEglFence(EGL_NO_SYNC_KHR),
// 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.
// Returns the number of the next frame which will be dequeued.
virtual uint64_t getNextFrameNumber() const = 0;
+
+ // Used to enable/disable single buffer mode.
+ //
+ // In single buffer mode the last buffer that was dequeued will be cached
+ // and returned to all calls to dequeueBuffer and acquireBuffer. This allows
+ // the producer and consumer to simultaneously access the same buffer.
+ virtual status_t setSingleBufferMode(bool singleBufferMode) = 0;
};
// ----------------------------------------------------------------------------
int dispatchSetSidebandStream(va_list args);
int dispatchSetBuffersDataSpace(va_list args);
int dispatchSetSurfaceDamage(va_list args);
+ int dispatchSetSingleBufferMode(va_list args);
protected:
virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
public:
virtual int setMaxDequeuedBufferCount(int maxDequeuedBuffers);
virtual int setAsyncMode(bool async);
+ virtual int setSingleBufferMode(bool singleBufferMode);
virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds);
virtual int unlockAndPost();
: what(0),
x(0), y(0), z(0), w(0), h(0), layerStack(0),
alpha(0), flags(0), mask(0),
- reserved(0), crop(Rect::INVALID_RECT)
+ reserved(0), crop(Rect::INVALID_RECT), frameNumber(0)
{
matrix.dsdx = matrix.dtdy = 1.0f;
matrix.dsdy = matrix.dtdx = 0.0f;
namespace android {
BufferItem::BufferItem() :
+ mGraphicBuffer(NULL),
+ mFence(NULL),
mCrop(Rect::INVALID_RECT),
mTransform(0),
mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mSlot(INVALID_BUFFER_SLOT),
mIsDroppable(false),
mAcquireCalled(false),
- mTransformToDisplayInverse(false) {
+ mTransformToDisplayInverse(false),
+ mSurfaceDamage() {
}
BufferItem::~BufferItem() {}
// buffer before releasing the old one.
int numAcquiredBuffers = 0;
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
- if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
+ if (mSlots[s].mBufferState.isAcquired()) {
++numAcquiredBuffers;
}
}
return INVALID_OPERATION;
}
- // Check if the queue is empty.
+ bool sharedBufferAvailable = mCore->mSingleBufferMode &&
+ mCore->mSingleBufferSlot !=
+ BufferQueueCore::INVALID_BUFFER_SLOT;
+
// In asynchronous mode the list is guaranteed to be one buffer deep,
// while in synchronous mode we use the oldest buffer.
- if (mCore->mQueue.empty()) {
+ if (mCore->mQueue.empty() && !sharedBufferAvailable) {
return NO_BUFFER_AVAILABLE;
}
// If expectedPresent is specified, we may not want to return a buffer yet.
// If it's specified and there's more than one buffer queued, we may want
// to drop a buffer.
- if (expectedPresent != 0) {
+ // Skip this if we're in single buffer mode and the queue is empty,
+ // since in that case we'll just return the shared buffer.
+ if (expectedPresent != 0 && !mCore->mQueue.empty()) {
const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second
// The 'expectedPresent' argument indicates when the buffer is expected
desiredPresent, expectedPresent, mCore->mQueue.size());
if (mCore->stillTracking(front)) {
// Front buffer is still in mSlots, so mark the slot as free
- mSlots[front->mSlot].mBufferState = BufferSlot::FREE;
- mCore->mFreeBuffers.push_back(front->mSlot);
+ mSlots[front->mSlot].mBufferState.freeQueued();
+
+ // After leaving single buffer mode, the shared buffer will
+ // still be around. Mark it as no longer shared if this
+ // operation causes it to be free.
+ if (!mCore->mSingleBufferMode &&
+ mSlots[front->mSlot].mBufferState.isFree()) {
+ mSlots[front->mSlot].mBufferState.mShared = false;
+ }
+ // Don't put the shared buffer on the free list.
+ if (!mSlots[front->mSlot].mBufferState.isShared()) {
+ mCore->mFreeBuffers.push_back(front->mSlot);
+ }
listener = mCore->mConnectedProducerListener;
++numDroppedBuffers;
}
systemTime(CLOCK_MONOTONIC));
}
- int slot = front->mSlot;
- *outBuffer = *front;
+ int slot = BufferQueueCore::INVALID_BUFFER_SLOT;
+
+ if (sharedBufferAvailable && mCore->mQueue.empty()) {
+ // make sure the buffer has finished allocating before acquiring it
+ mCore->waitWhileAllocatingLocked();
+
+ slot = mCore->mSingleBufferSlot;
+
+ // Recreate the BufferItem for the shared buffer from the data that
+ // was cached when it was last queued.
+ outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer;
+ outBuffer->mFence = Fence::NO_FENCE;
+ outBuffer->mCrop = mCore->mSingleBufferCache.crop;
+ outBuffer->mTransform = mCore->mSingleBufferCache.transform &
+ ~static_cast<uint32_t>(
+ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
+ outBuffer->mScalingMode = mCore->mSingleBufferCache.scalingMode;
+ outBuffer->mDataSpace = mCore->mSingleBufferCache.dataspace;
+ outBuffer->mFrameNumber = mCore->mFrameCounter;
+ outBuffer->mSlot = slot;
+ outBuffer->mAcquireCalled = mSlots[slot].mAcquireCalled;
+ outBuffer->mTransformToDisplayInverse =
+ (mCore->mSingleBufferCache.transform &
+ NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
+ outBuffer->mSurfaceDamage = Region::INVALID_REGION;
+ } else {
+ slot = front->mSlot;
+ *outBuffer = *front;
+ }
+
ATRACE_BUFFER_INDEX(slot);
BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
- slot, front->mFrameNumber, front->mGraphicBuffer->handle);
+ slot, outBuffer->mFrameNumber, outBuffer->mGraphicBuffer->handle);
// If the front buffer is still being tracked, update its slot state
- if (mCore->stillTracking(front)) {
+ if (mCore->stillTracking(outBuffer)) {
mSlots[slot].mAcquireCalled = true;
mSlots[slot].mNeedsCleanupOnRelease = false;
- mSlots[slot].mBufferState = BufferSlot::ACQUIRED;
+ // Don't decrease the queue count if the BufferItem wasn't
+ // previously in the queue. This happens in single buffer mode when
+ // the queue is empty and the BufferItem is created above.
+ if (mCore->mQueue.empty()) {
+ mSlots[slot].mBufferState.acquireNotInQueue();
+ } else {
+ mSlots[slot].mBufferState.acquire();
+ }
mSlots[slot].mFence = Fence::NO_FENCE;
}
status_t BufferQueueConsumer::detachBuffer(int slot) {
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
- BQ_LOGV("detachBuffer(C): slot %d", slot);
+ BQ_LOGV("detachBuffer: slot %d", slot);
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
- BQ_LOGE("detachBuffer(C): BufferQueue has been abandoned");
+ BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
+ if (mCore->mSingleBufferMode) {
+ BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer"
+ "mode");
+ return BAD_VALUE;
+ }
+
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer(C): slot index %d out of range [0, %d)",
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
- } else if (mSlots[slot].mBufferState != BufferSlot::ACQUIRED) {
- BQ_LOGE("detachBuffer(C): slot %d is not owned by the consumer "
- "(state = %d)", slot, mSlots[slot].mBufferState);
+ } else if (!mSlots[slot].mBufferState.isAcquired()) {
+ BQ_LOGE("detachBuffer: slot %d is not owned by the consumer "
+ "(state = %s)", slot, mSlots[slot].mBufferState.string());
return BAD_VALUE;
}
+ mSlots[slot].mBufferState.detachConsumer();
mCore->freeBufferLocked(slot);
mCore->mDequeueCondition.broadcast();
mCore->validateConsistencyLocked();
ATRACE_CALL();
if (outSlot == NULL) {
- BQ_LOGE("attachBuffer(P): outSlot must not be NULL");
+ BQ_LOGE("attachBuffer: outSlot must not be NULL");
return BAD_VALUE;
} else if (buffer == NULL) {
- BQ_LOGE("attachBuffer(P): cannot attach NULL buffer");
+ BQ_LOGE("attachBuffer: cannot attach NULL buffer");
return BAD_VALUE;
}
Mutex::Autolock lock(mCore->mMutex);
+ if (mCore->mSingleBufferMode) {
+ BQ_LOGE("attachBuffer: cannot attach a buffer in single buffer"
+ "mode");
+ return BAD_VALUE;
+ }
+
// Make sure we don't have too many acquired buffers
int numAcquiredBuffers = 0;
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
- if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) {
+ if (mSlots[s].mBufferState.isAcquired()) {
++numAcquiredBuffers;
}
}
if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) {
- BQ_LOGE("attachBuffer(P): max acquired buffer count reached: %d "
+ BQ_LOGE("attachBuffer: max acquired buffer count reached: %d "
"(max %d)", numAcquiredBuffers,
mCore->mMaxAcquiredBufferCount);
return INVALID_OPERATION;
mCore->mFreeBuffers.remove(found);
}
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
- BQ_LOGE("attachBuffer(P): could not find free buffer slot");
+ BQ_LOGE("attachBuffer: could not find free buffer slot");
return NO_MEMORY;
}
*outSlot = found;
ATRACE_BUFFER_INDEX(*outSlot);
- BQ_LOGV("attachBuffer(C): returning slot %d", *outSlot);
+ BQ_LOGV("attachBuffer: returning slot %d", *outSlot);
mSlots[*outSlot].mGraphicBuffer = buffer;
- mSlots[*outSlot].mBufferState = BufferSlot::ACQUIRED;
+ mSlots[*outSlot].mBufferState.attachConsumer();
mSlots[*outSlot].mAttachedByConsumer = true;
mSlots[*outSlot].mNeedsCleanupOnRelease = false;
mSlots[*outSlot].mFence = Fence::NO_FENCE;
Mutex::Autolock lock(mCore->mMutex);
// If the frame number has changed because the buffer has been reallocated,
- // we can ignore this releaseBuffer for the old buffer
- if (frameNumber != mSlots[slot].mFrameNumber) {
+ // we can ignore this releaseBuffer for the old buffer.
+ // Ignore this for the shared buffer where the frame number can easily
+ // get out of sync due to the buffer being queued and acquired at the
+ // same time.
+ if (frameNumber != mSlots[slot].mFrameNumber &&
+ !mSlots[slot].mBufferState.isShared()) {
return STALE_BUFFER_SLOT;
}
- // Make sure this buffer hasn't been queued while acquired by the consumer
- BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin());
- while (current != mCore->mQueue.end()) {
- if (current->mSlot == slot) {
- BQ_LOGE("releaseBuffer: buffer slot %d pending release is "
- "currently queued", slot);
- return BAD_VALUE;
- }
- ++current;
- }
- if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
+ if (mSlots[slot].mBufferState.isAcquired()) {
mSlots[slot].mEglDisplay = eglDisplay;
mSlots[slot].mEglFence = eglFence;
mSlots[slot].mFence = releaseFence;
- mSlots[slot].mBufferState = BufferSlot::FREE;
- mCore->mFreeBuffers.push_back(slot);
+ mSlots[slot].mBufferState.release();
+
+ // After leaving single buffer mode, the shared buffer will
+ // still be around. Mark it as no longer shared if this
+ // operation causes it to be free.
+ if (!mCore->mSingleBufferMode &&
+ mSlots[slot].mBufferState.isFree()) {
+ mSlots[slot].mBufferState.mShared = false;
+ }
+ // Don't put the shared buffer on the free list.
+ if (!mSlots[slot].mBufferState.isShared()) {
+ mCore->mFreeBuffers.push_back(slot);
+ }
+
listener = mCore->mConnectedProducerListener;
BQ_LOGV("releaseBuffer: releasing slot %d", slot);
} else if (mSlots[slot].mNeedsCleanupOnRelease) {
BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d "
- "(state = %d)", slot, mSlots[slot].mBufferState);
+ "(state = %s)", slot, mSlots[slot].mBufferState.string());
mSlots[slot].mNeedsCleanupOnRelease = false;
return STALE_BUFFER_SLOT;
} else {
BQ_LOGE("releaseBuffer: attempted to release buffer slot %d "
- "but its state was %d", slot, mSlots[slot].mBufferState);
+ "but its state was %s", slot,
+ mSlots[slot].mBufferState.string());
return BAD_VALUE;
}
ATRACE_CALL();
if (consumerListener == NULL) {
- BQ_LOGE("connect(C): consumerListener may not be NULL");
+ BQ_LOGE("connect: consumerListener may not be NULL");
return BAD_VALUE;
}
- BQ_LOGV("connect(C): controlledByApp=%s",
+ BQ_LOGV("connect: controlledByApp=%s",
controlledByApp ? "true" : "false");
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
- BQ_LOGE("connect(C): BufferQueue has been abandoned");
+ BQ_LOGE("connect: BufferQueue has been abandoned");
return NO_INIT;
}
status_t BufferQueueConsumer::disconnect() {
ATRACE_CALL();
- BQ_LOGV("disconnect(C)");
+ BQ_LOGV("disconnect");
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mConsumerListener == NULL) {
- BQ_LOGE("disconnect(C): no consumer is connected");
+ BQ_LOGE("disconnect: no consumer is connected");
return BAD_VALUE;
}
mAllowAllocation(true),
mBufferAge(0),
mGenerationNumber(0),
- mAsyncMode(false)
+ mAsyncMode(false),
+ mSingleBufferMode(false),
+ mSingleBufferSlot(INVALID_BUFFER_SLOT),
+ mSingleBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
+ HAL_DATASPACE_UNKNOWN)
{
if (allocator == NULL) {
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
int maxBufferCount = 0;
for (int s = BufferQueueDefs::NUM_BUFFER_SLOTS - 1; s >= 0; --s) {
const BufferSlot& slot(mSlots[s]);
- if (slot.mBufferState != BufferSlot::FREE ||
+ if (!slot.mBufferState.isFree() ||
slot.mGraphicBuffer != NULL) {
maxBufferCount = s + 1;
break;
const BufferSlot& slot(mSlots[s]);
const sp<GraphicBuffer>& buffer(slot.mGraphicBuffer);
result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix,
- (slot.mBufferState == BufferSlot::ACQUIRED) ? ">" : " ",
+ (slot.mBufferState.isAcquired()) ? ">" : " ",
s, buffer.get(),
- BufferSlot::bufferStateName(slot.mBufferState));
+ slot.mBufferState.string());
if (buffer != NULL) {
result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle,
// will temporarily keep the max buffer count up until the slots no longer
// need to be preserved.
for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
- BufferSlot::BufferState state = mSlots[s].mBufferState;
- if (state == BufferSlot::QUEUED || state == BufferSlot::DEQUEUED) {
+ BufferState state = mSlots[s].mBufferState;
+ if (state.isQueued() || state.isDequeued()) {
maxBufferCount = s + 1;
}
}
return maxBufferCount;
}
-void BufferQueueCore::freeBufferLocked(int slot) {
+void BufferQueueCore::freeBufferLocked(int slot, bool validate) {
BQ_LOGV("freeBufferLocked: slot %d", slot);
bool hadBuffer = mSlots[slot].mGraphicBuffer != NULL;
mSlots[slot].mGraphicBuffer.clear();
- if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) {
+ if (mSlots[slot].mBufferState.isAcquired()) {
mSlots[slot].mNeedsCleanupOnRelease = true;
}
- if (mSlots[slot].mBufferState != BufferSlot::FREE) {
+ if (!mSlots[slot].mBufferState.isFree()) {
mFreeSlots.insert(slot);
} else if (hadBuffer) {
// If the slot was FREE, but we had a buffer, we need to move this slot
mFreeBuffers.remove(slot);
mFreeSlots.insert(slot);
}
- mSlots[slot].mBufferState = BufferSlot::FREE;
mSlots[slot].mAcquireCalled = false;
mSlots[slot].mFrameNumber = 0;
mSlots[slot].mEglFence = EGL_NO_SYNC_KHR;
}
mSlots[slot].mFence = Fence::NO_FENCE;
- validateConsistencyLocked();
+ if (validate) {
+ validateConsistencyLocked();
+ }
}
void BufferQueueCore::freeAllBuffersLocked() {
mBufferHasBeenQueued = false;
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
- freeBufferLocked(s);
+ freeBufferLocked(s, false);
+ mSlots[s].mBufferState.reset();
}
+ mSingleBufferSlot = INVALID_BUFFER_SLOT;
+ validateConsistencyLocked();
}
bool BufferQueueCore::stillTracking(const BufferItem* item) const {
bool isInFreeBuffers =
std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) !=
mFreeBuffers.cend();
- if (mSlots[slot].mBufferState == BufferSlot::FREE) {
+ if (mSlots[slot].mBufferState.isFree() &&
+ !mSlots[slot].mBufferState.isShared()) {
if (mSlots[slot].mGraphicBuffer == NULL) {
if (!isInFreeSlots) {
BQ_LOGE("Slot %d is FREE but is not in mFreeSlots", slot);
}
} else {
if (isInFreeSlots) {
- BQ_LOGE("Slot %d is in mFreeSlots but is not FREE (%d)",
- slot, mSlots[slot].mBufferState);
+ BQ_LOGE("Slot %d is in mFreeSlots but is not FREE (%s)",
+ slot, mSlots[slot].mBufferState.string());
usleep(PAUSE_TIME);
}
if (isInFreeBuffers) {
- BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE (%d)",
- slot, mSlots[slot].mBufferState);
+ BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE (%s)",
+ slot, mSlots[slot].mBufferState.string());
usleep(PAUSE_TIME);
}
}
BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)",
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
- } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
+ } else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("requestBuffer: slot %d is not owned by the producer "
- "(state = %d)", slot, mSlots[slot].mBufferState);
+ "(state = %s)", slot, mSlots[slot].mBufferState.string());
return BAD_VALUE;
}
// There must be no dequeued buffers when changing the buffer count.
for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
- if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) {
+ if (mSlots[s].mBufferState.isDequeued()) {
BQ_LOGE("setMaxDequeuedBufferCount: buffer owned by producer");
return BAD_VALUE;
}
// Free up any buffers that are in slots beyond the max buffer count
for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) {
- assert(mSlots[s].mBufferState == BufferSlot::FREE);
+ assert(mSlots[s].mBufferState.isFree());
if (mSlots[s].mGraphicBuffer != NULL) {
mCore->freeBufferLocked(s);
*returnFlags |= RELEASE_ALL_BUFFERS;
int dequeuedCount = 0;
int acquiredCount = 0;
for (int s = 0; s < maxBufferCount; ++s) {
- switch (mSlots[s].mBufferState) {
- case BufferSlot::DEQUEUED:
- ++dequeuedCount;
- break;
- case BufferSlot::ACQUIRED:
- ++acquiredCount;
- break;
- default:
- break;
+ if (mSlots[s].mBufferState.isDequeued()) {
+ ++dequeuedCount;
+ }
+ if (mSlots[s].mBufferState.isAcquired()) {
+ ++acquiredCount;
}
}
BQ_LOGV("%s: queue size is %zu, waiting", caller,
mCore->mQueue.size());
} else {
- if (!mCore->mFreeBuffers.empty()) {
+ // If in single buffer mode and a shared buffer exists, always
+ // return it.
+ if (mCore->mSingleBufferMode && mCore->mSingleBufferSlot !=
+ BufferQueueCore::INVALID_BUFFER_SLOT) {
+ *found = mCore->mSingleBufferSlot;
+ } else if (!mCore->mFreeBuffers.empty()) {
auto slot = mCore->mFreeBuffers.begin();
*found = *slot;
mCore->mFreeBuffers.erase(slot);
// requested attributes, we free it and attempt to get another one.
if (!mCore->mAllowAllocation) {
if (buffer->needsReallocation(width, height, format, usage)) {
+ if (mCore->mSingleBufferMode &&
+ mCore->mSingleBufferSlot == found) {
+ BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
+ "buffer");
+ return BAD_VALUE;
+ }
+
mCore->freeBufferLocked(found);
found = BufferItem::INVALID_BUFFER_SLOT;
continue;
attachedByConsumer = mSlots[found].mAttachedByConsumer;
- mSlots[found].mBufferState = BufferSlot::DEQUEUED;
+ mSlots[found].mBufferState.dequeue();
+
+ // If single buffer mode has just been enabled, cache the slot of the
+ // first buffer that is dequeued and mark it as the shared buffer.
+ if (mCore->mSingleBufferMode && mCore->mSingleBufferSlot ==
+ BufferQueueCore::INVALID_BUFFER_SLOT) {
+ mCore->mSingleBufferSlot = found;
+ mSlots[found].mBufferState.mShared = true;
+ }
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
if ((buffer == NULL) ||
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE;
mCore->mBufferAge = 0;
+ mCore->mIsAllocating = true;
returnFlags |= BUFFER_NEEDS_REALLOCATION;
} else {
BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer(
width, height, format, usage, &error));
- if (graphicBuffer == NULL) {
- BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
- return error;
- }
-
{ // Autolock scope
Mutex::Autolock lock(mCore->mMutex);
+ if (graphicBuffer != NULL && !mCore->mIsAbandoned) {
+ graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
+ mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
+ }
+
+ mCore->mIsAllocating = false;
+ mCore->mIsAllocatingCondition.broadcast();
+
+ if (graphicBuffer == NULL) {
+ BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
+ return error;
+ }
+
if (mCore->mIsAbandoned) {
BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
-
- graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
- mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
} // Autolock scope
}
status_t BufferQueueProducer::detachBuffer(int slot) {
ATRACE_CALL();
ATRACE_BUFFER_INDEX(slot);
- BQ_LOGV("detachBuffer(P): slot %d", slot);
+ BQ_LOGV("detachBuffer: slot %d", slot);
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
- BQ_LOGE("detachBuffer(P): BufferQueue has been abandoned");
+ BQ_LOGE("detachBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("detachBuffer(P): BufferQueue has no connected producer");
+ BQ_LOGE("detachBuffer: BufferQueue has no connected producer");
return NO_INIT;
}
+ if (mCore->mSingleBufferMode) {
+ BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer"
+ "mode");
+ return BAD_VALUE;
+ }
+
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
- BQ_LOGE("detachBuffer(P): slot index %d out of range [0, %d)",
+ BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)",
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
- } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
- BQ_LOGE("detachBuffer(P): slot %d is not owned by the producer "
- "(state = %d)", slot, mSlots[slot].mBufferState);
+ } else if (!mSlots[slot].mBufferState.isDequeued()) {
+ BQ_LOGE("detachBuffer: slot %d is not owned by the producer "
+ "(state = %s)", slot, mSlots[slot].mBufferState.string());
return BAD_VALUE;
} else if (!mSlots[slot].mRequestBufferCalled) {
- BQ_LOGE("detachBuffer(P): buffer in slot %d has not been requested",
+ BQ_LOGE("detachBuffer: buffer in slot %d has not been requested",
slot);
return BAD_VALUE;
}
+ mSlots[slot].mBufferState.detachProducer();
mCore->freeBufferLocked(slot);
mCore->mDequeueCondition.broadcast();
mCore->validateConsistencyLocked();
return NO_INIT;
}
+ if (mCore->mSingleBufferMode) {
+ BQ_LOGE("detachNextBuffer: cannot detach a buffer in single buffer"
+ "mode");
+ return BAD_VALUE;
+ }
+
mCore->waitWhileAllocatingLocked();
if (mCore->mFreeBuffers.empty()) {
ATRACE_CALL();
if (outSlot == NULL) {
- BQ_LOGE("attachBuffer(P): outSlot must not be NULL");
+ BQ_LOGE("attachBuffer: outSlot must not be NULL");
return BAD_VALUE;
} else if (buffer == NULL) {
- BQ_LOGE("attachBuffer(P): cannot attach NULL buffer");
+ BQ_LOGE("attachBuffer: cannot attach NULL buffer");
return BAD_VALUE;
}
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
- BQ_LOGE("attachBuffer(P): BufferQueue has been abandoned");
+ BQ_LOGE("attachBuffer: BufferQueue has been abandoned");
return NO_INIT;
}
if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("attachBuffer(P): BufferQueue has no connected producer");
+ BQ_LOGE("attachBuffer: BufferQueue has no connected producer");
return NO_INIT;
}
+ if (mCore->mSingleBufferMode) {
+ BQ_LOGE("attachBuffer: cannot atach a buffer in single buffer mode");
+ return BAD_VALUE;
+ }
+
if (buffer->getGenerationNumber() != mCore->mGenerationNumber) {
BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] "
"[queue %u]", buffer->getGenerationNumber(),
status_t returnFlags = NO_ERROR;
int found;
- status_t status = waitForFreeSlotThenRelock("attachBuffer(P)", &found,
+ status_t status = waitForFreeSlotThenRelock("attachBuffer", &found,
&returnFlags);
if (status != NO_ERROR) {
return status;
// This should not happen
if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
- BQ_LOGE("attachBuffer(P): no available buffer slots");
+ BQ_LOGE("attachBuffer: no available buffer slots");
return -EBUSY;
}
*outSlot = found;
ATRACE_BUFFER_INDEX(*outSlot);
- BQ_LOGV("attachBuffer(P): returning slot %d flags=%#x",
+ BQ_LOGV("attachBuffer: returning slot %d flags=%#x",
*outSlot, returnFlags);
mSlots[*outSlot].mGraphicBuffer = buffer;
- mSlots[*outSlot].mBufferState = BufferSlot::DEQUEUED;
+ mSlots[*outSlot].mBufferState.attachProducer();
mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR;
mSlots[*outSlot].mFence = Fence::NO_FENCE;
mSlots[*outSlot].mRequestBufferCalled = true;
BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
slot, maxBufferCount);
return BAD_VALUE;
- } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
+ } else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
- "(state = %d)", slot, mSlots[slot].mBufferState);
+ "(state = %s)", slot, mSlots[slot].mBufferState.string());
return BAD_VALUE;
} else if (!mSlots[slot].mRequestBufferCalled) {
BQ_LOGE("queueBuffer: slot %d was queued without requesting "
}
mSlots[slot].mFence = fence;
- mSlots[slot].mBufferState = BufferSlot::QUEUED;
+ mSlots[slot].mBufferState.queue();
+
++mCore->mFrameCounter;
mSlots[slot].mFrameNumber = mCore->mFrameCounter;
item.mSlot = slot;
item.mFence = fence;
item.mIsDroppable = mCore->mAsyncMode ||
- mCore->mDequeueBufferCannotBlock;
+ mCore->mDequeueBufferCannotBlock ||
+ (mCore->mSingleBufferMode && mCore->mSingleBufferSlot == slot);
item.mSurfaceDamage = surfaceDamage;
mStickyTransform = stickyTransform;
+ // Cache the shared buffer data so that the BufferItem can be recreated.
+ if (mCore->mSingleBufferMode) {
+ mCore->mSingleBufferCache.crop = crop;
+ mCore->mSingleBufferCache.transform = transform;
+ mCore->mSingleBufferCache.scalingMode = static_cast<uint32_t>(
+ scalingMode);
+ mCore->mSingleBufferCache.dataspace = dataSpace;
+ }
+
if (mCore->mQueue.empty()) {
// When the queue is empty, we can ignore mDequeueBufferCannotBlock
// and simply queue this buffer
// If the front queued buffer is still being tracked, we first
// mark it as freed
if (mCore->stillTracking(front)) {
- mSlots[front->mSlot].mBufferState = BufferSlot::FREE;
- mCore->mFreeBuffers.push_front(front->mSlot);
+ mSlots[front->mSlot].mBufferState.freeQueued();
+
+ // After leaving single buffer mode, the shared buffer will
+ // still be around. Mark it as no longer shared if this
+ // operation causes it to be free.
+ if (!mCore->mSingleBufferMode &&
+ mSlots[front->mSlot].mBufferState.isFree()) {
+ mSlots[front->mSlot].mBufferState.mShared = false;
+ }
+ // Don't put the shared buffer on the free list.
+ if (!mSlots[front->mSlot].mBufferState.isShared()) {
+ mCore->mFreeBuffers.push_front(front->mSlot);
+ }
}
// Overwrite the droppable buffer with the incoming one
*front = item;
return NO_INIT;
}
+ if (mCore->mSingleBufferMode) {
+ BQ_LOGE("cancelBuffer: cannot cancel a buffer in single buffer mode");
+ return BAD_VALUE;
+ }
+
if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)",
slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
return BAD_VALUE;
- } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) {
+ } else if (!mSlots[slot].mBufferState.isDequeued()) {
BQ_LOGE("cancelBuffer: slot %d is not owned by the producer "
- "(state = %d)", slot, mSlots[slot].mBufferState);
+ "(state = %s)", slot, mSlots[slot].mBufferState.string());
return BAD_VALUE;
} else if (fence == NULL) {
BQ_LOGE("cancelBuffer: fence is NULL");
return BAD_VALUE;
}
- mCore->mFreeBuffers.push_front(slot);
- mSlots[slot].mBufferState = BufferSlot::FREE;
+ mSlots[slot].mBufferState.cancel();
+
+ // After leaving single buffer mode, the shared buffer will still be around.
+ // Mark it as no longer shared if this operation causes it to be free.
+ if (!mCore->mSingleBufferMode && mSlots[slot].mBufferState.isFree()) {
+ mSlots[slot].mBufferState.mShared = false;
+ }
+
+ // Don't put the shared buffer on the free list.
+ if (!mSlots[slot].mBufferState.isShared()) {
+ mCore->mFreeBuffers.push_front(slot);
+ }
mSlots[slot].mFence = fence;
mCore->mDequeueCondition.broadcast();
mCore->validateConsistencyLocked();
ATRACE_CALL();
Mutex::Autolock lock(mCore->mMutex);
mConsumerName = mCore->mConsumerName;
- BQ_LOGV("connect(P): api=%d producerControlledByApp=%s", api,
+ BQ_LOGV("connect: api=%d producerControlledByApp=%s", api,
producerControlledByApp ? "true" : "false");
if (mCore->mIsAbandoned) {
- BQ_LOGE("connect(P): BufferQueue has been abandoned");
+ BQ_LOGE("connect: BufferQueue has been abandoned");
return NO_INIT;
}
if (mCore->mConsumerListener == NULL) {
- BQ_LOGE("connect(P): BufferQueue has no consumer");
+ BQ_LOGE("connect: BufferQueue has no consumer");
return NO_INIT;
}
if (output == NULL) {
- BQ_LOGE("connect(P): output was NULL");
+ BQ_LOGE("connect: output was NULL");
return BAD_VALUE;
}
if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("connect(P): already connected (cur=%d req=%d)",
+ BQ_LOGE("connect: already connected (cur=%d req=%d)",
mCore->mConnectedApi, api);
return BAD_VALUE;
}
status = IInterface::asBinder(listener)->linkToDeath(
static_cast<IBinder::DeathRecipient*>(this));
if (status != NO_ERROR) {
- BQ_LOGE("connect(P): linkToDeath failed: %s (%d)",
+ BQ_LOGE("connect: linkToDeath failed: %s (%d)",
strerror(-status), status);
}
}
mCore->mConnectedProducerListener = listener;
break;
default:
- BQ_LOGE("connect(P): unknown API %d", api);
+ BQ_LOGE("connect: unknown API %d", api);
status = BAD_VALUE;
break;
}
status_t BufferQueueProducer::disconnect(int api) {
ATRACE_CALL();
- BQ_LOGV("disconnect(P): api %d", api);
+ BQ_LOGV("disconnect: api %d", api);
int status = NO_ERROR;
sp<IConsumerListener> listener;
mCore->mDequeueCondition.broadcast();
listener = mCore->mConsumerListener;
} else if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) {
- BQ_LOGE("disconnect(P): still connected to another API "
+ BQ_LOGE("disconnect: still connected to another API "
"(cur=%d req=%d)", mCore->mConnectedApi, api);
status = BAD_VALUE;
}
break;
default:
- BQ_LOGE("disconnect(P): unknown API %d", api);
+ BQ_LOGE("disconnect: unknown API %d", api);
status = BAD_VALUE;
break;
}
if (mSlots[slot].mGraphicBuffer != NULL) {
++currentBufferCount;
} else {
- if (mSlots[slot].mBufferState != BufferSlot::FREE) {
+ if (!mSlots[slot].mBufferState.isFree()) {
BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE",
slot);
continue;
for (size_t i = 0; i < newBufferCount; ++i) {
int slot = freeSlots[i];
- if (mSlots[slot].mBufferState != BufferSlot::FREE) {
+ if (!mSlots[slot].mBufferState.isFree()) {
// A consumer allocated the FREE slot with attachBuffer. Discard the buffer we
// allocated.
BQ_LOGV("allocateBuffers: slot %d was acquired while allocating. "
return nextFrameNumber;
}
+status_t BufferQueueProducer::setSingleBufferMode(bool singleBufferMode) {
+ ATRACE_CALL();
+ BQ_LOGV("setSingleBufferMode: %d", singleBufferMode);
+
+ Mutex::Autolock lock(mCore->mMutex);
+ if (!singleBufferMode) {
+ mCore->mSingleBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
+ }
+ mCore->mSingleBufferMode = singleBufferMode;
+
+ return NO_ERROR;
+}
+
void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) {
// If we're here, it means that a producer we were connected to died.
// We're guaranteed that we are still connected to it because we remove
/*
- * Copyright 2014 The Android Open Source Project
+ * Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
namespace android {
-const char* BufferSlot::bufferStateName(BufferState state) {
- switch (state) {
- case BufferSlot::DEQUEUED: return "DEQUEUED";
- case BufferSlot::QUEUED: return "QUEUED";
- case BufferSlot::FREE: return "FREE";
- case BufferSlot::ACQUIRED: return "ACQUIRED";
+
+const char* BufferState::string() const {
+
+ if (isShared()) {
+ return "SHARED";
+ }
+
+ if (isFree()) {
+ return "FREE";
+ }
+
+ if (isAcquired()) {
+ return "ACQUIRED";
+ }
+
+ if (isDequeued()) {
+ return "DEQUEUED";
+ }
+
+ if (isQueued()) {
+ return "QUEUED";
}
- return "Unknown";
+ return "UNKNOWN";
}
} // namespace android
mCurrentTextureImage->graphicBufferHandle() : 0,
slot, mSlots[slot].mGraphicBuffer->handle);
+ // Hang onto the pointer so that it isn't freed in the call to
+ // releaseBufferLocked() if we're in single buffer mode and both buffers are
+ // the same.
+ sp<EglImage> nextTextureImage = mEglSlots[slot].mEglImage;
+
// release old buffer
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
status_t status = releaseBufferLocked(
// Update the GLConsumer state.
mCurrentTexture = slot;
- mCurrentTextureImage = mEglSlots[slot].mEglImage;
+ mCurrentTextureImage = nextTextureImage;
mCurrentCrop = item.mCrop;
mCurrentTransform = item.mTransform;
mCurrentScalingMode = item.mScalingMode;
GET_CONSUMER_NAME,
SET_MAX_DEQUEUED_BUFFER_COUNT,
SET_ASYNC_MODE,
- GET_NEXT_FRAME_NUMBER
+ GET_NEXT_FRAME_NUMBER,
+ SET_SINGLE_BUFFER_MODE
};
class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer>
uint64_t frameNumber = reply.readUint64();
return frameNumber;
}
+
+ virtual status_t setSingleBufferMode(bool singleBufferMode) {
+ Parcel data, reply;
+ data.writeInterfaceToken(
+ IGraphicBufferProducer::getInterfaceDescriptor());
+ data.writeInt32(singleBufferMode);
+ status_t result = remote()->transact(SET_SINGLE_BUFFER_MODE, data,
+ &reply);
+ if (result == NO_ERROR) {
+ result = reply.readInt32();
+ }
+ return result;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
reply->writeUint64(frameNumber);
return NO_ERROR;
}
+ case SET_SINGLE_BUFFER_MODE: {
+ CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
+ bool singleBufferMode = data.readInt32();
+ status_t result = setSingleBufferMode(singleBufferMode);
+ reply->writeInt32(result);
+ return NO_ERROR;
+ }
}
return BBinder::onTransact(code, data, reply, flags);
}
case NATIVE_WINDOW_SET_SURFACE_DAMAGE:
res = dispatchSetSurfaceDamage(args);
break;
+ case NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE:
+ res = dispatchSetSingleBufferMode(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
return NO_ERROR;
}
+int Surface::dispatchSetSingleBufferMode(va_list args) {
+ bool singleBufferMode = va_arg(args, int);
+ setSingleBufferMode(singleBufferMode);
+ return NO_ERROR;
+}
+
int Surface::connect(int api) {
static sp<IProducerListener> listener = new DummyProducerListener();
return connect(api, listener);
return err;
}
+int Surface::setSingleBufferMode(bool singleBufferMode) {
+ ATRACE_CALL();
+ ALOGV("Surface::setSingleBufferMode (%d)", singleBufferMode);
+ Mutex::Autolock lock(mMutex);
+
+ status_t err = mGraphicBufferProducer->setSingleBufferMode(
+ singleBufferMode);
+ ALOGE_IF(err, "IGraphicsBufferProducer::setSingleBufferMode(%d) returned"
+ "%s", singleBufferMode, strerror(-err));
+
+ return err;
+}
+
int Surface::setBuffersDimensions(uint32_t width, uint32_t height)
{
ATRACE_CALL();
ASSERT_EQ(OK, mConsumer->attachBuffer(&outSlot, buffer));
}
+TEST_F(BufferQueueTest, TestSingleBufferMode) {
+ createBufferQueue();
+ sp<DummyConsumer> dc(new DummyConsumer);
+ ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true));
+ IGraphicBufferProducer::QueueBufferOutput output;
+ ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener,
+ NATIVE_WINDOW_API_CPU, true, &output));
+
+ ASSERT_EQ(OK, mProducer->setSingleBufferMode(true));
+
+ // Get a buffer
+ int singleSlot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buffer;
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION,
+ mProducer->dequeueBuffer(&singleSlot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(OK, mProducer->requestBuffer(singleSlot, &buffer));
+
+ // Queue the buffer
+ IGraphicBufferProducer::QueueBufferInput input(0, false,
+ HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE);
+ ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+
+ // Repeatedly acquire and release a buffer from the consumer side, it should
+ // always return the same one.
+ BufferItem item;
+ for (int i = 0; i < 5; i++) {
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(singleSlot, item.mSlot);
+ ASSERT_EQ(0, item.mTimestamp);
+ ASSERT_EQ(false, item.mIsAutoTimestamp);
+ ASSERT_EQ(HAL_DATASPACE_UNKNOWN, item.mDataSpace);
+ ASSERT_EQ(Rect(0, 0, 1, 1), item.mCrop);
+ ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode);
+ ASSERT_EQ(0, item.mTransform);
+ ASSERT_EQ(Fence::NO_FENCE, item.mFence);
+
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ }
+
+ // Repeatedly queue and dequeue a buffer from the producer side, it should
+ // always return the same one.
+ int slot;
+ for (int i = 0; i < 5; i++) {
+ ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0));
+ ASSERT_EQ(singleSlot, slot);
+ ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output));
+ }
+
+ // Repeatedly acquire and release a buffer from the consumer side, it should
+ // always return the same one. First grabbing them from the queue and then
+ // when the queue is empty, returning the single buffer.
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0));
+ ASSERT_EQ(singleSlot, item.mSlot);
+ ASSERT_EQ(0, item.mTimestamp);
+ ASSERT_EQ(false, item.mIsAutoTimestamp);
+ ASSERT_EQ(HAL_DATASPACE_UNKNOWN, item.mDataSpace);
+ ASSERT_EQ(Rect(0, 0, 1, 1), item.mCrop);
+ ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode);
+ ASSERT_EQ(0, item.mTransform);
+ ASSERT_EQ(Fence::NO_FENCE, item.mFence);
+
+ ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber,
+ EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE));
+ }
+}
+
} // namespace android
return setError(EGL_BAD_SURFACE, EGL_FALSE);
egl_surface_t const * const s = get_surface(surface);
+
+ //XXX: temporary hack for the EGL hook-up for single buffer mode
+ if (attribute == EGL_RENDER_BUFFER && (value == EGL_BACK_BUFFER ||
+ value == EGL_SINGLE_BUFFER)) {
+ return (native_window_set_single_buffer_mode(s->win.get(),
+ value == EGL_SINGLE_BUFFER)) ? EGL_TRUE : EGL_FALSE;
+ }
+
if (s->cnx->egl.eglSurfaceAttrib) {
return s->cnx->egl.eglSurfaceAttrib(
dp->disp.dpy, s->surface, attribute, value);
return 0;
}
+status_t VirtualDisplaySurface::setSingleBufferMode(bool singleBufferMode) {
+ return mSource[SOURCE_SINK]->setSingleBufferMode(singleBufferMode);
+}
+
void VirtualDisplaySurface::updateQueueBufferOutput(
const QueueBufferOutput& qbo) {
uint32_t w, h, transformHint, numPendingBuffers;
virtual status_t setGenerationNumber(uint32_t generationNumber);
virtual String8 getConsumerName() const override;
virtual uint64_t getNextFrameNumber() const override;
+ virtual status_t setSingleBufferMode(bool singleBufferMode);
//
// Utility methods
return mProducer->getNextFrameNumber();
}
+status_t MonitoredProducer::setSingleBufferMode(bool singleBufferMode) {
+ return mProducer->setSingleBufferMode(singleBufferMode);
+}
+
IBinder* MonitoredProducer::onAsBinder() {
return IInterface::asBinder(mProducer).get();
}
virtual String8 getConsumerName() const override;
virtual uint64_t getNextFrameNumber() const override;
virtual IBinder* onAsBinder();
+ virtual status_t setSingleBufferMode(bool singleBufferMode);
private:
sp<IGraphicBufferProducer> mProducer;