diff options
31 files changed, 763 insertions, 213 deletions
diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index e46005941b..de514194fb 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -693,7 +693,7 @@ int main(int argc, char *argv[]) { /* switch to non-root user and group */ gid_t groups[] = { AID_LOG, AID_SDCARD_R, AID_SDCARD_RW, - AID_MOUNT, AID_INET, AID_NET_BW_STATS }; + AID_MOUNT, AID_INET, AID_NET_BW_STATS, AID_READPROC }; if (setgroups(sizeof(groups)/sizeof(groups[0]), groups) != 0) { ALOGE("Unable to setgroups, aborting: %s\n", strerror(errno)); return -1; diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc index 7154fabcc7..a6a4d0316b 100644 --- a/cmds/servicemanager/servicemanager.rc +++ b/cmds/servicemanager/servicemanager.rc @@ -1,7 +1,7 @@ service servicemanager /system/bin/servicemanager class core user system - group system + group system readproc critical onrestart restart healthd onrestart restart zygote diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h index ae76ffbb38..430c3ff00c 100644 --- a/include/binder/Parcel.h +++ b/include/binder/Parcel.h @@ -125,6 +125,8 @@ public: status_t writeCharVector(const std::vector<char16_t>& val); status_t writeString16Vector(const std::vector<String16>& val); + status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val); + template<typename T> status_t write(const Flattenable<T>& val); @@ -202,7 +204,9 @@ public: wp<IBinder> readWeakBinder() const; template<typename T> - status_t readStrongBinder(sp<T>* val) const; + status_t readStrongBinder(sp<T>* val) const; + + status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const; status_t readByteVector(std::vector<int8_t>* val) const; status_t readInt32Vector(std::vector<int32_t>* val) const; diff --git a/include/gui/BufferItem.h b/include/gui/BufferItem.h index 504ac64e38..370f5d52f7 100644 --- a/include/gui/BufferItem.h +++ b/include/gui/BufferItem.h @@ -118,6 +118,13 @@ class BufferItem : public Flattenable<BufferItem> { // Describes the portion of the surface that has been modified since the // previous frame Region mSurfaceDamage; + + // Indicates that the BufferQueue is in single buffer mode + bool mSingleBufferMode; + + // Indicates that this buffer was queued by the producer. When in single + // buffer mode acquire() can return a BufferItem that wasn't in the queue. + bool mQueuedBuffer; }; } // namespace android diff --git a/include/gui/BufferQueueCore.h b/include/gui/BufferQueueCore.h index 36cd238f29..fbd5114c1d 100644 --- a/include/gui/BufferQueueCore.h +++ b/include/gui/BufferQueueCore.h @@ -107,10 +107,10 @@ private: // freeBufferLocked frees the GraphicBuffer and sync resources for the // given slot. - void freeBufferLocked(int slot); + void freeBufferLocked(int slot, bool validate = true); // freeAllBuffersLocked frees the GraphicBuffer and sync resources for - // all slots. + // all slots, even if they're currently dequeued, queued, or acquired. void freeAllBuffersLocked(); // stillTracking returns true iff the buffer item is still being tracked @@ -271,6 +271,32 @@ private: // enqueue buffers without blocking. bool mAsyncMode; + // mSingleBufferMode indicates whether or not single buffer mode is enabled. + // In single buffer mode, the last buffer that was dequeued is cached and + // returned to all calls to dequeueBuffer and acquireBuffer. This allows the + // consumer and producer to access the same buffer simultaneously. + bool mSingleBufferMode; + + // When single buffer mode is enabled, this tracks which slot contains the + // shared buffer. + int mSingleBufferSlot; + + // Cached data about the shared buffer in single buffer mode + struct SingleBufferCache { + SingleBufferCache(Rect _crop, uint32_t _transform, int _scalingMode, + android_dataspace _dataspace) + : crop(_crop), + transform(_transform), + scalingMode(_scalingMode), + dataspace(_dataspace) { + }; + + Rect crop; + uint32_t transform; + uint32_t scalingMode; + android_dataspace dataspace; + } mSingleBufferCache; + }; // class BufferQueueCore } // namespace android diff --git a/include/gui/BufferQueueProducer.h b/include/gui/BufferQueueProducer.h index 322f2ec986..5fe5ce0433 100644 --- a/include/gui/BufferQueueProducer.h +++ b/include/gui/BufferQueueProducer.h @@ -173,6 +173,9 @@ public: // See IGraphicBufferProducer::getNextFrameNumber virtual uint64_t getNextFrameNumber() const override; + // See IGraphicBufferProducer::setSingleBufferMode + virtual status_t setSingleBufferMode(bool singleBufferMode); + private: // This is required by the IBinder::DeathRecipient interface virtual void binderDied(const wp<IBinder>& who); diff --git a/include/gui/BufferSlot.h b/include/gui/BufferSlot.h index 6085e116a3..17a654a89b 100644 --- a/include/gui/BufferSlot.h +++ b/include/gui/BufferSlot.h @@ -29,11 +29,153 @@ namespace android { class Fence; +// BufferState tracks the states in which a buffer slot can be. +struct BufferState { + + // All slots are initially FREE (not dequeued, queued, acquired, or shared). + BufferState() + : mDequeueCount(0), + mQueueCount(0), + mAcquireCount(0), + mShared(false) { + } + + uint32_t mDequeueCount; + uint32_t mQueueCount; + uint32_t mAcquireCount; + bool mShared; + + // A buffer can be in one of five states, represented as below: + // + // | mShared | mDequeueCount | mQueueCount | mAcquireCount | + // --------|---------|---------------|-------------|---------------| + // FREE | false | 0 | 0 | 0 | + // DEQUEUED| false | 1 | 0 | 0 | + // QUEUED | false | 0 | 1 | 0 | + // ACQUIRED| false | 0 | 0 | 1 | + // SHARED | true | any | any | any | + // + // FREE indicates that the buffer is available to be dequeued by the + // producer. The slot is "owned" by BufferQueue. It transitions to DEQUEUED + // when dequeueBuffer is called. + // + // DEQUEUED indicates that the buffer has been dequeued by the producer, but + // has not yet been queued or canceled. The producer may modify the + // buffer's contents as soon as the associated release fence is signaled. + // The slot is "owned" by the producer. It can transition to QUEUED (via + // queueBuffer or attachBuffer) or back to FREE (via cancelBuffer or + // detachBuffer). + // + // QUEUED indicates that the buffer has been filled by the producer and + // queued for use by the consumer. The buffer contents may continue to be + // modified for a finite time, so the contents must not be accessed until + // the associated fence is signaled. The slot is "owned" by BufferQueue. It + // can transition to ACQUIRED (via acquireBuffer) or to FREE (if another + // buffer is queued in asynchronous mode). + // + // ACQUIRED indicates that the buffer has been acquired by the consumer. As + // with QUEUED, the contents must not be accessed by the consumer until the + // acquire fence is signaled. The slot is "owned" by the consumer. It + // transitions to FREE when releaseBuffer (or detachBuffer) is called. A + // detached buffer can also enter the ACQUIRED state via attachBuffer. + // + // SHARED indicates that this buffer is being used in single-buffer + // mode. It can be in any combination of the other states at the same time, + // except for FREE (since that excludes being in any other state). It can + // also be dequeued, queued, or acquired multiple times. + + inline bool isFree() const { + return !isAcquired() && !isDequeued() && !isQueued(); + } + + inline bool isDequeued() const { + return mDequeueCount > 0; + } + + inline bool isQueued() const { + return mQueueCount > 0; + } + + inline bool isAcquired() const { + return mAcquireCount > 0; + } + + inline bool isShared() const { + return mShared; + } + + inline void reset() { + *this = BufferState(); + } + + const char* string() const; + + inline void dequeue() { + mDequeueCount++; + } + + inline void detachProducer() { + if (mDequeueCount > 0) { + mDequeueCount--; + } + } + + inline void attachProducer() { + mDequeueCount++; + } + + inline void queue() { + if (mDequeueCount > 0) { + mDequeueCount--; + } + mQueueCount++; + } + + inline void cancel() { + if (mDequeueCount > 0) { + mDequeueCount--; + } + } + + inline void freeQueued() { + if (mQueueCount > 0) { + mQueueCount--; + } + } + + inline void acquire() { + if (mQueueCount > 0) { + mQueueCount--; + } + mAcquireCount++; + } + + inline void acquireNotInQueue() { + mAcquireCount++; + } + + inline void release() { + if (mAcquireCount > 0) { + mAcquireCount--; + } + } + + inline void detachConsumer() { + if (mAcquireCount > 0) { + mAcquireCount--; + } + } + + inline void attachConsumer() { + mAcquireCount++; + } +}; + struct BufferSlot { BufferSlot() : mEglDisplay(EGL_NO_DISPLAY), - mBufferState(BufferSlot::FREE), + mBufferState(), mRequestBufferCalled(false), mFrameNumber(0), mEglFence(EGL_NO_SYNC_KHR), @@ -49,47 +191,6 @@ struct BufferSlot { // mEglDisplay is the EGLDisplay used to create EGLSyncKHR objects. EGLDisplay mEglDisplay; - // BufferState represents the different states in which a buffer slot - // can be. All slots are initially FREE. - enum BufferState { - // FREE indicates that the buffer is available to be dequeued - // by the producer. The buffer may be in use by the consumer for - // a finite time, so the buffer must not be modified until the - // associated fence is signaled. - // - // The slot is "owned" by BufferQueue. It transitions to DEQUEUED - // when dequeueBuffer is called. - FREE = 0, - - // DEQUEUED indicates that the buffer has been dequeued by the - // producer, but has not yet been queued or canceled. The - // producer may modify the buffer's contents as soon as the - // associated ready fence is signaled. - // - // The slot is "owned" by the producer. It can transition to - // QUEUED (via queueBuffer) or back to FREE (via cancelBuffer). - DEQUEUED = 1, - - // QUEUED indicates that the buffer has been filled by the - // producer and queued for use by the consumer. The buffer - // contents may continue to be modified for a finite time, so - // the contents must not be accessed until the associated fence - // is signaled. - // - // The slot is "owned" by BufferQueue. It can transition to - // ACQUIRED (via acquireBuffer) or to FREE (if another buffer is - // queued in asynchronous mode). - QUEUED = 2, - - // ACQUIRED indicates that the buffer has been acquired by the - // consumer. As with QUEUED, the contents must not be accessed - // by the consumer until the fence is signaled. - // - // The slot is "owned" by the consumer. It transitions to FREE - // when releaseBuffer is called. - ACQUIRED = 3 - }; - static const char* bufferStateName(BufferState state); // mBufferState is the current state of this buffer slot. diff --git a/include/gui/IGraphicBufferProducer.h b/include/gui/IGraphicBufferProducer.h index 4586839c48..d6daca7659 100644 --- a/include/gui/IGraphicBufferProducer.h +++ b/include/gui/IGraphicBufferProducer.h @@ -511,6 +511,13 @@ public: // Returns the number of the next frame which will be dequeued. virtual uint64_t getNextFrameNumber() const = 0; + + // Used to enable/disable single buffer mode. + // + // In single buffer mode the last buffer that was dequeued will be cached + // and returned to all calls to dequeueBuffer and acquireBuffer. This allows + // the producer and consumer to simultaneously access the same buffer. + virtual status_t setSingleBufferMode(bool singleBufferMode) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/gui/Surface.h b/include/gui/Surface.h index 3f46460748..f9fc6df472 100644 --- a/include/gui/Surface.h +++ b/include/gui/Surface.h @@ -159,6 +159,7 @@ private: int dispatchSetSidebandStream(va_list args); int dispatchSetBuffersDataSpace(va_list args); int dispatchSetSurfaceDamage(va_list args); + int dispatchSetSingleBufferMode(va_list args); protected: virtual int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd); @@ -188,6 +189,7 @@ protected: public: virtual int setMaxDequeuedBufferCount(int maxDequeuedBuffers); virtual int setAsyncMode(bool async); + virtual int setSingleBufferMode(bool singleBufferMode); virtual int lock(ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds); virtual int unlockAndPost(); diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h index 1711071144..5cf316f31f 100644 --- a/include/private/gui/LayerState.h +++ b/include/private/gui/LayerState.h @@ -59,7 +59,7 @@ struct layer_state_t { : what(0), x(0), y(0), z(0), w(0), h(0), layerStack(0), alpha(0), flags(0), mask(0), - reserved(0), crop(Rect::INVALID_RECT) + reserved(0), crop(Rect::INVALID_RECT), frameNumber(0) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index 3af48e58f8..56cde8abf6 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -1071,6 +1071,56 @@ status_t Parcel::writeStrongBinder(const sp<IBinder>& val) return flatten_binder(ProcessState::self(), val, this); } +status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val) +{ + if (val.size() > std::numeric_limits<int32_t>::max()) { + return BAD_VALUE; + } + + status_t status = writeInt32(val.size()); + + if (status != OK) { + return status; + } + + for (const auto& item : val) { + status = writeStrongBinder(item); + + if (status != OK) { + return status; + } + } + + return OK; +} + +status_t Parcel::readStrongBinderVector(std::vector<sp<IBinder>>* val) const { + val->clear(); + + int32_t size; + status_t status = readInt32(&size); + + if (status != OK) { + return status; + } + + if (size < 0) { + return BAD_VALUE; + } + + val->resize(size); + + for (auto& v : *val) { + status = readStrongBinder(&v); + + if (status != OK) { + return status; + } + } + + return OK; +} + status_t Parcel::writeWeakBinder(const wp<IBinder>& val) { return flatten_binder(ProcessState::self(), val, this); diff --git a/libs/gui/BufferItem.cpp b/libs/gui/BufferItem.cpp index 6a883cf807..de8ff70846 100644 --- a/libs/gui/BufferItem.cpp +++ b/libs/gui/BufferItem.cpp @@ -24,6 +24,8 @@ namespace android { BufferItem::BufferItem() : + mGraphicBuffer(NULL), + mFence(NULL), mCrop(Rect::INVALID_RECT), mTransform(0), mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), @@ -34,7 +36,10 @@ BufferItem::BufferItem() : mSlot(INVALID_BUFFER_SLOT), mIsDroppable(false), mAcquireCalled(false), - mTransformToDisplayInverse(false) { + mTransformToDisplayInverse(false), + mSurfaceDamage(), + mSingleBufferMode(false), + mQueuedBuffer(true) { } BufferItem::~BufferItem() {} diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index d52b47f3b0..6f9f21f296 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -50,7 +50,7 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, // buffer before releasing the old one. int numAcquiredBuffers = 0; for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) { + if (mSlots[s].mBufferState.isAcquired()) { ++numAcquiredBuffers; } } @@ -60,10 +60,13 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, return INVALID_OPERATION; } - // Check if the queue is empty. + bool sharedBufferAvailable = mCore->mSingleBufferMode && + mCore->mSingleBufferSlot != + BufferQueueCore::INVALID_BUFFER_SLOT; + // In asynchronous mode the list is guaranteed to be one buffer deep, // while in synchronous mode we use the oldest buffer. - if (mCore->mQueue.empty()) { + if (mCore->mQueue.empty() && !sharedBufferAvailable) { return NO_BUFFER_AVAILABLE; } @@ -72,7 +75,9 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, // If expectedPresent is specified, we may not want to return a buffer yet. // If it's specified and there's more than one buffer queued, we may want // to drop a buffer. - if (expectedPresent != 0) { + // Skip this if we're in single buffer mode and the queue is empty, + // since in that case we'll just return the shared buffer. + if (expectedPresent != 0 && !mCore->mQueue.empty()) { const int MAX_REASONABLE_NSEC = 1000000000ULL; // 1 second // The 'expectedPresent' argument indicates when the buffer is expected @@ -130,8 +135,19 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, desiredPresent, expectedPresent, mCore->mQueue.size()); if (mCore->stillTracking(front)) { // Front buffer is still in mSlots, so mark the slot as free - mSlots[front->mSlot].mBufferState = BufferSlot::FREE; - mCore->mFreeBuffers.push_back(front->mSlot); + mSlots[front->mSlot].mBufferState.freeQueued(); + + // After leaving single buffer mode, the shared buffer will + // still be around. Mark it as no longer shared if this + // operation causes it to be free. + if (!mCore->mSingleBufferMode && + mSlots[front->mSlot].mBufferState.isFree()) { + mSlots[front->mSlot].mBufferState.mShared = false; + } + // Don't put the shared buffer on the free list. + if (!mSlots[front->mSlot].mBufferState.isShared()) { + mCore->mFreeBuffers.push_back(front->mSlot); + } listener = mCore->mConnectedProducerListener; ++numDroppedBuffers; } @@ -162,17 +178,56 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, systemTime(CLOCK_MONOTONIC)); } - int slot = front->mSlot; - *outBuffer = *front; + int slot = BufferQueueCore::INVALID_BUFFER_SLOT; + + if (sharedBufferAvailable && mCore->mQueue.empty()) { + // make sure the buffer has finished allocating before acquiring it + mCore->waitWhileAllocatingLocked(); + + slot = mCore->mSingleBufferSlot; + + // Recreate the BufferItem for the shared buffer from the data that + // was cached when it was last queued. + outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer; + outBuffer->mFence = Fence::NO_FENCE; + outBuffer->mCrop = mCore->mSingleBufferCache.crop; + outBuffer->mTransform = mCore->mSingleBufferCache.transform & + ~static_cast<uint32_t>( + NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY); + outBuffer->mScalingMode = mCore->mSingleBufferCache.scalingMode; + outBuffer->mDataSpace = mCore->mSingleBufferCache.dataspace; + outBuffer->mFrameNumber = mCore->mFrameCounter; + outBuffer->mSlot = slot; + outBuffer->mAcquireCalled = mSlots[slot].mAcquireCalled; + outBuffer->mTransformToDisplayInverse = + (mCore->mSingleBufferCache.transform & + NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0; + outBuffer->mSurfaceDamage = Region::INVALID_REGION; + outBuffer->mSingleBufferMode = true; + outBuffer->mQueuedBuffer = false; + } else { + slot = front->mSlot; + *outBuffer = *front; + } + + outBuffer->mSingleBufferMode = mCore->mSingleBufferMode; + ATRACE_BUFFER_INDEX(slot); BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }", - slot, front->mFrameNumber, front->mGraphicBuffer->handle); + slot, outBuffer->mFrameNumber, outBuffer->mGraphicBuffer->handle); // If the front buffer is still being tracked, update its slot state - if (mCore->stillTracking(front)) { + if (mCore->stillTracking(outBuffer)) { mSlots[slot].mAcquireCalled = true; mSlots[slot].mNeedsCleanupOnRelease = false; - mSlots[slot].mBufferState = BufferSlot::ACQUIRED; + // Don't decrease the queue count if the BufferItem wasn't + // previously in the queue. This happens in single buffer mode when + // the queue is empty and the BufferItem is created above. + if (mCore->mQueue.empty()) { + mSlots[slot].mBufferState.acquireNotInQueue(); + } else { + mSlots[slot].mBufferState.acquire(); + } mSlots[slot].mFence = Fence::NO_FENCE; } @@ -207,24 +262,31 @@ status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer, status_t BufferQueueConsumer::detachBuffer(int slot) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); - BQ_LOGV("detachBuffer(C): slot %d", slot); + BQ_LOGV("detachBuffer: slot %d", slot); Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { - BQ_LOGE("detachBuffer(C): BufferQueue has been abandoned"); + BQ_LOGE("detachBuffer: BufferQueue has been abandoned"); return NO_INIT; } + if (mCore->mSingleBufferMode) { + BQ_LOGE("detachBuffer: detachBuffer not allowed in single buffer" + "mode"); + return BAD_VALUE; + } + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - BQ_LOGE("detachBuffer(C): slot index %d out of range [0, %d)", + BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; - } else if (mSlots[slot].mBufferState != BufferSlot::ACQUIRED) { - BQ_LOGE("detachBuffer(C): slot %d is not owned by the consumer " - "(state = %d)", slot, mSlots[slot].mBufferState); + } else if (!mSlots[slot].mBufferState.isAcquired()) { + BQ_LOGE("detachBuffer: slot %d is not owned by the consumer " + "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } + mSlots[slot].mBufferState.detachConsumer(); mCore->freeBufferLocked(slot); mCore->mDequeueCondition.broadcast(); mCore->validateConsistencyLocked(); @@ -237,25 +299,31 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot, ATRACE_CALL(); if (outSlot == NULL) { - BQ_LOGE("attachBuffer(P): outSlot must not be NULL"); + BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; } else if (buffer == NULL) { - BQ_LOGE("attachBuffer(P): cannot attach NULL buffer"); + BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } Mutex::Autolock lock(mCore->mMutex); + if (mCore->mSingleBufferMode) { + BQ_LOGE("attachBuffer: cannot attach a buffer in single buffer" + "mode"); + return BAD_VALUE; + } + // Make sure we don't have too many acquired buffers int numAcquiredBuffers = 0; for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - if (mSlots[s].mBufferState == BufferSlot::ACQUIRED) { + if (mSlots[s].mBufferState.isAcquired()) { ++numAcquiredBuffers; } } if (numAcquiredBuffers >= mCore->mMaxAcquiredBufferCount + 1) { - BQ_LOGE("attachBuffer(P): max acquired buffer count reached: %d " + BQ_LOGE("attachBuffer: max acquired buffer count reached: %d " "(max %d)", numAcquiredBuffers, mCore->mMaxAcquiredBufferCount); return INVALID_OPERATION; @@ -279,16 +347,16 @@ status_t BufferQueueConsumer::attachBuffer(int* outSlot, mCore->mFreeBuffers.remove(found); } if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { - BQ_LOGE("attachBuffer(P): could not find free buffer slot"); + BQ_LOGE("attachBuffer: could not find free buffer slot"); return NO_MEMORY; } *outSlot = found; ATRACE_BUFFER_INDEX(*outSlot); - BQ_LOGV("attachBuffer(C): returning slot %d", *outSlot); + BQ_LOGV("attachBuffer: returning slot %d", *outSlot); mSlots[*outSlot].mGraphicBuffer = buffer; - mSlots[*outSlot].mBufferState = BufferSlot::ACQUIRED; + mSlots[*outSlot].mBufferState.attachConsumer(); mSlots[*outSlot].mAttachedByConsumer = true; mSlots[*outSlot].mNeedsCleanupOnRelease = false; mSlots[*outSlot].mFence = Fence::NO_FENCE; @@ -334,38 +402,45 @@ status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber, Mutex::Autolock lock(mCore->mMutex); // If the frame number has changed because the buffer has been reallocated, - // we can ignore this releaseBuffer for the old buffer - if (frameNumber != mSlots[slot].mFrameNumber) { + // we can ignore this releaseBuffer for the old buffer. + // Ignore this for the shared buffer where the frame number can easily + // get out of sync due to the buffer being queued and acquired at the + // same time. + if (frameNumber != mSlots[slot].mFrameNumber && + !mSlots[slot].mBufferState.isShared()) { return STALE_BUFFER_SLOT; } - // Make sure this buffer hasn't been queued while acquired by the consumer - BufferQueueCore::Fifo::iterator current(mCore->mQueue.begin()); - while (current != mCore->mQueue.end()) { - if (current->mSlot == slot) { - BQ_LOGE("releaseBuffer: buffer slot %d pending release is " - "currently queued", slot); - return BAD_VALUE; - } - ++current; - } - if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) { + if (mSlots[slot].mBufferState.isAcquired()) { mSlots[slot].mEglDisplay = eglDisplay; mSlots[slot].mEglFence = eglFence; mSlots[slot].mFence = releaseFence; - mSlots[slot].mBufferState = BufferSlot::FREE; - mCore->mFreeBuffers.push_back(slot); + mSlots[slot].mBufferState.release(); + + // After leaving single buffer mode, the shared buffer will + // still be around. Mark it as no longer shared if this + // operation causes it to be free. + if (!mCore->mSingleBufferMode && + mSlots[slot].mBufferState.isFree()) { + mSlots[slot].mBufferState.mShared = false; + } + // Don't put the shared buffer on the free list. + if (!mSlots[slot].mBufferState.isShared()) { + mCore->mFreeBuffers.push_back(slot); + } + listener = mCore->mConnectedProducerListener; BQ_LOGV("releaseBuffer: releasing slot %d", slot); } else if (mSlots[slot].mNeedsCleanupOnRelease) { BQ_LOGV("releaseBuffer: releasing a stale buffer slot %d " - "(state = %d)", slot, mSlots[slot].mBufferState); + "(state = %s)", slot, mSlots[slot].mBufferState.string()); mSlots[slot].mNeedsCleanupOnRelease = false; return STALE_BUFFER_SLOT; } else { BQ_LOGE("releaseBuffer: attempted to release buffer slot %d " - "but its state was %d", slot, mSlots[slot].mBufferState); + "but its state was %s", slot, + mSlots[slot].mBufferState.string()); return BAD_VALUE; } @@ -386,17 +461,17 @@ status_t BufferQueueConsumer::connect( ATRACE_CALL(); if (consumerListener == NULL) { - BQ_LOGE("connect(C): consumerListener may not be NULL"); + BQ_LOGE("connect: consumerListener may not be NULL"); return BAD_VALUE; } - BQ_LOGV("connect(C): controlledByApp=%s", + BQ_LOGV("connect: controlledByApp=%s", controlledByApp ? "true" : "false"); Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { - BQ_LOGE("connect(C): BufferQueue has been abandoned"); + BQ_LOGE("connect: BufferQueue has been abandoned"); return NO_INIT; } @@ -409,12 +484,12 @@ status_t BufferQueueConsumer::connect( status_t BufferQueueConsumer::disconnect() { ATRACE_CALL(); - BQ_LOGV("disconnect(C)"); + BQ_LOGV("disconnect"); Mutex::Autolock lock(mCore->mMutex); if (mCore->mConsumerListener == NULL) { - BQ_LOGE("disconnect(C): no consumer is connected"); + BQ_LOGE("disconnect: no consumer is connected"); return BAD_VALUE; } diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp index b1cbc86068..c24ad19897 100644 --- a/libs/gui/BufferQueueCore.cpp +++ b/libs/gui/BufferQueueCore.cpp @@ -69,7 +69,11 @@ BufferQueueCore::BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator) : mAllowAllocation(true), mBufferAge(0), mGenerationNumber(0), - mAsyncMode(false) + mAsyncMode(false), + mSingleBufferMode(false), + mSingleBufferSlot(INVALID_BUFFER_SLOT), + mSingleBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE, + HAL_DATASPACE_UNKNOWN) { if (allocator == NULL) { sp<ISurfaceComposer> composer(ComposerService::getComposerService()); @@ -113,7 +117,7 @@ void BufferQueueCore::dump(String8& result, const char* prefix) const { int maxBufferCount = 0; for (int s = BufferQueueDefs::NUM_BUFFER_SLOTS - 1; s >= 0; --s) { const BufferSlot& slot(mSlots[s]); - if (slot.mBufferState != BufferSlot::FREE || + if (!slot.mBufferState.isFree() || slot.mGraphicBuffer != NULL) { maxBufferCount = s + 1; break; @@ -124,9 +128,9 @@ void BufferQueueCore::dump(String8& result, const char* prefix) const { const BufferSlot& slot(mSlots[s]); const sp<GraphicBuffer>& buffer(slot.mGraphicBuffer); result.appendFormat("%s%s[%02d:%p] state=%-8s", prefix, - (slot.mBufferState == BufferSlot::ACQUIRED) ? ">" : " ", + (slot.mBufferState.isAcquired()) ? ">" : " ", s, buffer.get(), - BufferSlot::bufferStateName(slot.mBufferState)); + slot.mBufferState.string()); if (buffer != NULL) { result.appendFormat(", %p [%4ux%4u:%4u,%3X]", buffer->handle, @@ -164,8 +168,8 @@ int BufferQueueCore::getMaxBufferCountLocked() const { // will temporarily keep the max buffer count up until the slots no longer // need to be preserved. for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - BufferSlot::BufferState state = mSlots[s].mBufferState; - if (state == BufferSlot::QUEUED || state == BufferSlot::DEQUEUED) { + BufferState state = mSlots[s].mBufferState; + if (state.isQueued() || state.isDequeued()) { maxBufferCount = s + 1; } } @@ -173,14 +177,14 @@ int BufferQueueCore::getMaxBufferCountLocked() const { return maxBufferCount; } -void BufferQueueCore::freeBufferLocked(int slot) { +void BufferQueueCore::freeBufferLocked(int slot, bool validate) { BQ_LOGV("freeBufferLocked: slot %d", slot); bool hadBuffer = mSlots[slot].mGraphicBuffer != NULL; mSlots[slot].mGraphicBuffer.clear(); - if (mSlots[slot].mBufferState == BufferSlot::ACQUIRED) { + if (mSlots[slot].mBufferState.isAcquired()) { mSlots[slot].mNeedsCleanupOnRelease = true; } - if (mSlots[slot].mBufferState != BufferSlot::FREE) { + if (!mSlots[slot].mBufferState.isFree()) { mFreeSlots.insert(slot); } else if (hadBuffer) { // If the slot was FREE, but we had a buffer, we need to move this slot @@ -188,7 +192,6 @@ void BufferQueueCore::freeBufferLocked(int slot) { mFreeBuffers.remove(slot); mFreeSlots.insert(slot); } - mSlots[slot].mBufferState = BufferSlot::FREE; mSlots[slot].mAcquireCalled = false; mSlots[slot].mFrameNumber = 0; @@ -198,14 +201,19 @@ void BufferQueueCore::freeBufferLocked(int slot) { mSlots[slot].mEglFence = EGL_NO_SYNC_KHR; } mSlots[slot].mFence = Fence::NO_FENCE; - validateConsistencyLocked(); + if (validate) { + validateConsistencyLocked(); + } } void BufferQueueCore::freeAllBuffersLocked() { mBufferHasBeenQueued = false; for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - freeBufferLocked(s); + freeBufferLocked(s, false); + mSlots[s].mBufferState.reset(); } + mSingleBufferSlot = INVALID_BUFFER_SLOT; + validateConsistencyLocked(); } bool BufferQueueCore::stillTracking(const BufferItem* item) const { @@ -238,7 +246,8 @@ void BufferQueueCore::validateConsistencyLocked() const { bool isInFreeBuffers = std::find(mFreeBuffers.cbegin(), mFreeBuffers.cend(), slot) != mFreeBuffers.cend(); - if (mSlots[slot].mBufferState == BufferSlot::FREE) { + if (mSlots[slot].mBufferState.isFree() && + !mSlots[slot].mBufferState.isShared()) { if (mSlots[slot].mGraphicBuffer == NULL) { if (!isInFreeSlots) { BQ_LOGE("Slot %d is FREE but is not in mFreeSlots", slot); @@ -262,13 +271,13 @@ void BufferQueueCore::validateConsistencyLocked() const { } } else { if (isInFreeSlots) { - BQ_LOGE("Slot %d is in mFreeSlots but is not FREE (%d)", - slot, mSlots[slot].mBufferState); + BQ_LOGE("Slot %d is in mFreeSlots but is not FREE (%s)", + slot, mSlots[slot].mBufferState.string()); usleep(PAUSE_TIME); } if (isInFreeBuffers) { - BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE (%d)", - slot, mSlots[slot].mBufferState); + BQ_LOGE("Slot %d is in mFreeBuffers but is not FREE (%s)", + slot, mSlots[slot].mBufferState.string()); usleep(PAUSE_TIME); } } diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 9ef8ff715f..ef01745355 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -66,9 +66,9 @@ status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) { BQ_LOGE("requestBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; - } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { + } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("requestBuffer: slot %d is not owned by the producer " - "(state = %d)", slot, mSlots[slot].mBufferState); + "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } @@ -96,7 +96,7 @@ status_t BufferQueueProducer::setMaxDequeuedBufferCount( // There must be no dequeued buffers when changing the buffer count. for (int s = 0; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - if (mSlots[s].mBufferState == BufferSlot::DEQUEUED) { + if (mSlots[s].mBufferState.isDequeued()) { BQ_LOGE("setMaxDequeuedBufferCount: buffer owned by producer"); return BAD_VALUE; } @@ -196,7 +196,7 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller, // Free up any buffers that are in slots beyond the max buffer count for (int s = maxBufferCount; s < BufferQueueDefs::NUM_BUFFER_SLOTS; ++s) { - assert(mSlots[s].mBufferState == BufferSlot::FREE); + assert(mSlots[s].mBufferState.isFree()); if (mSlots[s].mGraphicBuffer != NULL) { mCore->freeBufferLocked(s); *returnFlags |= RELEASE_ALL_BUFFERS; @@ -206,15 +206,11 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller, int dequeuedCount = 0; int acquiredCount = 0; for (int s = 0; s < maxBufferCount; ++s) { - switch (mSlots[s].mBufferState) { - case BufferSlot::DEQUEUED: - ++dequeuedCount; - break; - case BufferSlot::ACQUIRED: - ++acquiredCount; - break; - default: - break; + if (mSlots[s].mBufferState.isDequeued()) { + ++dequeuedCount; + } + if (mSlots[s].mBufferState.isAcquired()) { + ++acquiredCount; } } @@ -240,7 +236,12 @@ status_t BufferQueueProducer::waitForFreeSlotThenRelock(const char* caller, BQ_LOGV("%s: queue size is %zu, waiting", caller, mCore->mQueue.size()); } else { - if (!mCore->mFreeBuffers.empty()) { + // If in single buffer mode and a shared buffer exists, always + // return it. + if (mCore->mSingleBufferMode && mCore->mSingleBufferSlot != + BufferQueueCore::INVALID_BUFFER_SLOT) { + *found = mCore->mSingleBufferSlot; + } else if (!mCore->mFreeBuffers.empty()) { auto slot = mCore->mFreeBuffers.begin(); *found = *slot; mCore->mFreeBuffers.erase(slot); @@ -348,6 +349,13 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, // requested attributes, we free it and attempt to get another one. if (!mCore->mAllowAllocation) { if (buffer->needsReallocation(width, height, format, usage)) { + if (mCore->mSingleBufferMode && + mCore->mSingleBufferSlot == found) { + BQ_LOGE("dequeueBuffer: cannot re-allocate a shared" + "buffer"); + return BAD_VALUE; + } + mCore->freeBufferLocked(found); found = BufferItem::INVALID_BUFFER_SLOT; continue; @@ -360,7 +368,15 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, attachedByConsumer = mSlots[found].mAttachedByConsumer; - mSlots[found].mBufferState = BufferSlot::DEQUEUED; + mSlots[found].mBufferState.dequeue(); + + // If single buffer mode has just been enabled, cache the slot of the + // first buffer that is dequeued and mark it as the shared buffer. + if (mCore->mSingleBufferMode && mCore->mSingleBufferSlot == + BufferQueueCore::INVALID_BUFFER_SLOT) { + mCore->mSingleBufferSlot = found; + mSlots[found].mBufferState.mShared = true; + } const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer); if ((buffer == NULL) || @@ -373,6 +389,7 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, mSlots[found].mEglFence = EGL_NO_SYNC_KHR; mSlots[found].mFence = Fence::NO_FENCE; mCore->mBufferAge = 0; + mCore->mIsAllocating = true; returnFlags |= BUFFER_NEEDS_REALLOCATION; } else { @@ -405,21 +422,26 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot); sp<GraphicBuffer> graphicBuffer(mCore->mAllocator->createGraphicBuffer( width, height, format, usage, &error)); - if (graphicBuffer == NULL) { - BQ_LOGE("dequeueBuffer: createGraphicBuffer failed"); - return error; - } - { // Autolock scope Mutex::Autolock lock(mCore->mMutex); + if (graphicBuffer != NULL && !mCore->mIsAbandoned) { + graphicBuffer->setGenerationNumber(mCore->mGenerationNumber); + mSlots[*outSlot].mGraphicBuffer = graphicBuffer; + } + + mCore->mIsAllocating = false; + mCore->mIsAllocatingCondition.broadcast(); + + if (graphicBuffer == NULL) { + BQ_LOGE("dequeueBuffer: createGraphicBuffer failed"); + return error; + } + if (mCore->mIsAbandoned) { BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned"); return NO_INIT; } - - graphicBuffer->setGenerationNumber(mCore->mGenerationNumber); - mSlots[*outSlot].mGraphicBuffer = graphicBuffer; } // Autolock scope } @@ -453,33 +475,40 @@ status_t BufferQueueProducer::dequeueBuffer(int *outSlot, status_t BufferQueueProducer::detachBuffer(int slot) { ATRACE_CALL(); ATRACE_BUFFER_INDEX(slot); - BQ_LOGV("detachBuffer(P): slot %d", slot); + BQ_LOGV("detachBuffer: slot %d", slot); Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { - BQ_LOGE("detachBuffer(P): BufferQueue has been abandoned"); + BQ_LOGE("detachBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { - BQ_LOGE("detachBuffer(P): BufferQueue has no connected producer"); + BQ_LOGE("detachBuffer: BufferQueue has no connected producer"); return NO_INIT; } + if (mCore->mSingleBufferMode) { + BQ_LOGE("detachBuffer: cannot detach a buffer in single buffer" + "mode"); + return BAD_VALUE; + } + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { - BQ_LOGE("detachBuffer(P): slot index %d out of range [0, %d)", + BQ_LOGE("detachBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; - } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { - BQ_LOGE("detachBuffer(P): slot %d is not owned by the producer " - "(state = %d)", slot, mSlots[slot].mBufferState); + } else if (!mSlots[slot].mBufferState.isDequeued()) { + BQ_LOGE("detachBuffer: slot %d is not owned by the producer " + "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } else if (!mSlots[slot].mRequestBufferCalled) { - BQ_LOGE("detachBuffer(P): buffer in slot %d has not been requested", + BQ_LOGE("detachBuffer: buffer in slot %d has not been requested", slot); return BAD_VALUE; } + mSlots[slot].mBufferState.detachProducer(); mCore->freeBufferLocked(slot); mCore->mDequeueCondition.broadcast(); mCore->validateConsistencyLocked(); @@ -511,6 +540,12 @@ status_t BufferQueueProducer::detachNextBuffer(sp<GraphicBuffer>* outBuffer, return NO_INIT; } + if (mCore->mSingleBufferMode) { + BQ_LOGE("detachNextBuffer: cannot detach a buffer in single buffer" + "mode"); + return BAD_VALUE; + } + mCore->waitWhileAllocatingLocked(); if (mCore->mFreeBuffers.empty()) { @@ -535,25 +570,30 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot, ATRACE_CALL(); if (outSlot == NULL) { - BQ_LOGE("attachBuffer(P): outSlot must not be NULL"); + BQ_LOGE("attachBuffer: outSlot must not be NULL"); return BAD_VALUE; } else if (buffer == NULL) { - BQ_LOGE("attachBuffer(P): cannot attach NULL buffer"); + BQ_LOGE("attachBuffer: cannot attach NULL buffer"); return BAD_VALUE; } Mutex::Autolock lock(mCore->mMutex); if (mCore->mIsAbandoned) { - BQ_LOGE("attachBuffer(P): BufferQueue has been abandoned"); + BQ_LOGE("attachBuffer: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) { - BQ_LOGE("attachBuffer(P): BufferQueue has no connected producer"); + BQ_LOGE("attachBuffer: BufferQueue has no connected producer"); return NO_INIT; } + if (mCore->mSingleBufferMode) { + BQ_LOGE("attachBuffer: cannot atach a buffer in single buffer mode"); + return BAD_VALUE; + } + if (buffer->getGenerationNumber() != mCore->mGenerationNumber) { BQ_LOGE("attachBuffer: generation number mismatch [buffer %u] " "[queue %u]", buffer->getGenerationNumber(), @@ -565,7 +605,7 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot, status_t returnFlags = NO_ERROR; int found; - status_t status = waitForFreeSlotThenRelock("attachBuffer(P)", &found, + status_t status = waitForFreeSlotThenRelock("attachBuffer", &found, &returnFlags); if (status != NO_ERROR) { return status; @@ -573,17 +613,17 @@ status_t BufferQueueProducer::attachBuffer(int* outSlot, // This should not happen if (found == BufferQueueCore::INVALID_BUFFER_SLOT) { - BQ_LOGE("attachBuffer(P): no available buffer slots"); + BQ_LOGE("attachBuffer: no available buffer slots"); return -EBUSY; } *outSlot = found; ATRACE_BUFFER_INDEX(*outSlot); - BQ_LOGV("attachBuffer(P): returning slot %d flags=%#x", + BQ_LOGV("attachBuffer: returning slot %d flags=%#x", *outSlot, returnFlags); mSlots[*outSlot].mGraphicBuffer = buffer; - mSlots[*outSlot].mBufferState = BufferSlot::DEQUEUED; + mSlots[*outSlot].mBufferState.attachProducer(); mSlots[*outSlot].mEglFence = EGL_NO_SYNC_KHR; mSlots[*outSlot].mFence = Fence::NO_FENCE; mSlots[*outSlot].mRequestBufferCalled = true; @@ -649,9 +689,9 @@ status_t BufferQueueProducer::queueBuffer(int slot, BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)", slot, maxBufferCount); return BAD_VALUE; - } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { + } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("queueBuffer: slot %d is not owned by the producer " - "(state = %d)", slot, mSlots[slot].mBufferState); + "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } else if (!mSlots[slot].mRequestBufferCalled) { BQ_LOGE("queueBuffer: slot %d was queued without requesting " @@ -681,7 +721,8 @@ status_t BufferQueueProducer::queueBuffer(int slot, } mSlots[slot].mFence = fence; - mSlots[slot].mBufferState = BufferSlot::QUEUED; + mSlots[slot].mBufferState.queue(); + ++mCore->mFrameCounter; mSlots[slot].mFrameNumber = mCore->mFrameCounter; @@ -700,11 +741,23 @@ status_t BufferQueueProducer::queueBuffer(int slot, item.mSlot = slot; item.mFence = fence; item.mIsDroppable = mCore->mAsyncMode || - mCore->mDequeueBufferCannotBlock; + mCore->mDequeueBufferCannotBlock || + (mCore->mSingleBufferMode && mCore->mSingleBufferSlot == slot); item.mSurfaceDamage = surfaceDamage; + item.mSingleBufferMode = mCore->mSingleBufferMode; + item.mQueuedBuffer = true; mStickyTransform = stickyTransform; + // Cache the shared buffer data so that the BufferItem can be recreated. + if (mCore->mSingleBufferMode) { + mCore->mSingleBufferCache.crop = crop; + mCore->mSingleBufferCache.transform = transform; + mCore->mSingleBufferCache.scalingMode = static_cast<uint32_t>( + scalingMode); + mCore->mSingleBufferCache.dataspace = dataSpace; + } + if (mCore->mQueue.empty()) { // When the queue is empty, we can ignore mDequeueBufferCannotBlock // and simply queue this buffer @@ -718,8 +771,19 @@ status_t BufferQueueProducer::queueBuffer(int slot, // If the front queued buffer is still being tracked, we first // mark it as freed if (mCore->stillTracking(front)) { - mSlots[front->mSlot].mBufferState = BufferSlot::FREE; - mCore->mFreeBuffers.push_front(front->mSlot); + mSlots[front->mSlot].mBufferState.freeQueued(); + + // After leaving single buffer mode, the shared buffer will + // still be around. Mark it as no longer shared if this + // operation causes it to be free. + if (!mCore->mSingleBufferMode && + mSlots[front->mSlot].mBufferState.isFree()) { + mSlots[front->mSlot].mBufferState.mShared = false; + } + // Don't put the shared buffer on the free list. + if (!mSlots[front->mSlot].mBufferState.isShared()) { + mCore->mFreeBuffers.push_front(front->mSlot); + } } // Overwrite the droppable buffer with the incoming one *front = item; @@ -795,21 +859,36 @@ status_t BufferQueueProducer::cancelBuffer(int slot, const sp<Fence>& fence) { return NO_INIT; } + if (mCore->mSingleBufferMode) { + BQ_LOGE("cancelBuffer: cannot cancel a buffer in single buffer mode"); + return BAD_VALUE; + } + if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) { BQ_LOGE("cancelBuffer: slot index %d out of range [0, %d)", slot, BufferQueueDefs::NUM_BUFFER_SLOTS); return BAD_VALUE; - } else if (mSlots[slot].mBufferState != BufferSlot::DEQUEUED) { + } else if (!mSlots[slot].mBufferState.isDequeued()) { BQ_LOGE("cancelBuffer: slot %d is not owned by the producer " - "(state = %d)", slot, mSlots[slot].mBufferState); + "(state = %s)", slot, mSlots[slot].mBufferState.string()); return BAD_VALUE; } else if (fence == NULL) { BQ_LOGE("cancelBuffer: fence is NULL"); return BAD_VALUE; } - mCore->mFreeBuffers.push_front(slot); - mSlots[slot].mBufferState = BufferSlot::FREE; + mSlots[slot].mBufferState.cancel(); + + // After leaving single buffer mode, the shared buffer will still be around. + // Mark it as no longer shared if this operation causes it to be free. + if (!mCore->mSingleBufferMode && mSlots[slot].mBufferState.isFree()) { + mSlots[slot].mBufferState.mShared = false; + } + + // Don't put the shared buffer on the free list. + if (!mSlots[slot].mBufferState.isShared()) { + mCore->mFreeBuffers.push_front(slot); + } mSlots[slot].mFence = fence; mCore->mDequeueCondition.broadcast(); mCore->validateConsistencyLocked(); @@ -878,26 +957,26 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, ATRACE_CALL(); Mutex::Autolock lock(mCore->mMutex); mConsumerName = mCore->mConsumerName; - BQ_LOGV("connect(P): api=%d producerControlledByApp=%s", api, + BQ_LOGV("connect: api=%d producerControlledByApp=%s", api, producerControlledByApp ? "true" : "false"); if (mCore->mIsAbandoned) { - BQ_LOGE("connect(P): BufferQueue has been abandoned"); + BQ_LOGE("connect: BufferQueue has been abandoned"); return NO_INIT; } if (mCore->mConsumerListener == NULL) { - BQ_LOGE("connect(P): BufferQueue has no consumer"); + BQ_LOGE("connect: BufferQueue has no consumer"); return NO_INIT; } if (output == NULL) { - BQ_LOGE("connect(P): output was NULL"); + BQ_LOGE("connect: output was NULL"); return BAD_VALUE; } if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { - BQ_LOGE("connect(P): already connected (cur=%d req=%d)", + BQ_LOGE("connect: already connected (cur=%d req=%d)", mCore->mConnectedApi, api); return BAD_VALUE; } @@ -920,14 +999,14 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, status = IInterface::asBinder(listener)->linkToDeath( static_cast<IBinder::DeathRecipient*>(this)); if (status != NO_ERROR) { - BQ_LOGE("connect(P): linkToDeath failed: %s (%d)", + BQ_LOGE("connect: linkToDeath failed: %s (%d)", strerror(-status), status); } } mCore->mConnectedProducerListener = listener; break; default: - BQ_LOGE("connect(P): unknown API %d", api); + BQ_LOGE("connect: unknown API %d", api); status = BAD_VALUE; break; } @@ -942,7 +1021,7 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, status_t BufferQueueProducer::disconnect(int api) { ATRACE_CALL(); - BQ_LOGV("disconnect(P): api %d", api); + BQ_LOGV("disconnect: api %d", api); int status = NO_ERROR; sp<IConsumerListener> listener; @@ -979,13 +1058,13 @@ status_t BufferQueueProducer::disconnect(int api) { mCore->mDequeueCondition.broadcast(); listener = mCore->mConsumerListener; } else if (mCore->mConnectedApi != BufferQueueCore::NO_CONNECTED_API) { - BQ_LOGE("disconnect(P): still connected to another API " + BQ_LOGE("disconnect: still connected to another API " "(cur=%d req=%d)", mCore->mConnectedApi, api); status = BAD_VALUE; } break; default: - BQ_LOGE("disconnect(P): unknown API %d", api); + BQ_LOGE("disconnect: unknown API %d", api); status = BAD_VALUE; break; } @@ -1038,7 +1117,7 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, if (mSlots[slot].mGraphicBuffer != NULL) { ++currentBufferCount; } else { - if (mSlots[slot].mBufferState != BufferSlot::FREE) { + if (!mSlots[slot].mBufferState.isFree()) { BQ_LOGE("allocateBuffers: slot %d without buffer is not FREE", slot); continue; @@ -1101,7 +1180,7 @@ void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height, for (size_t i = 0; i < newBufferCount; ++i) { int slot = freeSlots[i]; - if (mSlots[slot].mBufferState != BufferSlot::FREE) { + if (!mSlots[slot].mBufferState.isFree()) { // A consumer allocated the FREE slot with attachBuffer. Discard the buffer we // allocated. BQ_LOGV("allocateBuffers: slot %d was acquired while allocating. " @@ -1159,6 +1238,19 @@ uint64_t BufferQueueProducer::getNextFrameNumber() const { return nextFrameNumber; } +status_t BufferQueueProducer::setSingleBufferMode(bool singleBufferMode) { + ATRACE_CALL(); + BQ_LOGV("setSingleBufferMode: %d", singleBufferMode); + + Mutex::Autolock lock(mCore->mMutex); + if (!singleBufferMode) { + mCore->mSingleBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT; + } + mCore->mSingleBufferMode = singleBufferMode; + + return NO_ERROR; +} + void BufferQueueProducer::binderDied(const wp<android::IBinder>& /* who */) { // If we're here, it means that a producer we were connected to died. // We're guaranteed that we are still connected to it because we remove diff --git a/libs/gui/BufferSlot.cpp b/libs/gui/BufferSlot.cpp index 01595de448..b1cdc5d5f3 100644 --- a/libs/gui/BufferSlot.cpp +++ b/libs/gui/BufferSlot.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2014 The Android Open Source Project + * Copyright 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,29 @@ 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 diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 1572a89222..83e4d66412 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -424,6 +424,11 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item) mCurrentTextureImage->graphicBufferHandle() : 0, slot, mSlots[slot].mGraphicBuffer->handle); + // Hang onto the pointer so that it isn't freed in the call to + // releaseBufferLocked() if we're in single buffer mode and both buffers are + // the same. + sp<EglImage> nextTextureImage = mEglSlots[slot].mEglImage; + // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { status_t status = releaseBufferLocked( @@ -439,7 +444,7 @@ status_t GLConsumer::updateAndReleaseLocked(const BufferItem& item) // Update the GLConsumer state. mCurrentTexture = slot; - mCurrentTextureImage = mEglSlots[slot].mEglImage; + mCurrentTextureImage = nextTextureImage; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index 640a9f2136..d5310bdccd 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -50,7 +50,8 @@ enum { GET_CONSUMER_NAME, SET_MAX_DEQUEUED_BUFFER_COUNT, SET_ASYNC_MODE, - GET_NEXT_FRAME_NUMBER + GET_NEXT_FRAME_NUMBER, + SET_SINGLE_BUFFER_MODE }; class BpGraphicBufferProducer : public BpInterface<IGraphicBufferProducer> @@ -339,6 +340,19 @@ public: uint64_t frameNumber = reply.readUint64(); return frameNumber; } + + virtual status_t setSingleBufferMode(bool singleBufferMode) { + Parcel data, reply; + data.writeInterfaceToken( + IGraphicBufferProducer::getInterfaceDescriptor()); + data.writeInt32(singleBufferMode); + status_t result = remote()->transact(SET_SINGLE_BUFFER_MODE, data, + &reply); + if (result == NO_ERROR) { + result = reply.readInt32(); + } + return result; + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -527,6 +541,13 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeUint64(frameNumber); return NO_ERROR; } + case SET_SINGLE_BUFFER_MODE: { + CHECK_INTERFACE(IGraphicBufferProducer, data, reply); + bool singleBufferMode = data.readInt32(); + status_t result = setSingleBufferMode(singleBufferMode); + reply->writeInt32(result); + return NO_ERROR; + } } return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 9191b2a379..7578a3db29 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -550,6 +550,9 @@ int Surface::perform(int operation, va_list args) case NATIVE_WINDOW_SET_SURFACE_DAMAGE: res = dispatchSetSurfaceDamage(args); break; + case NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE: + res = dispatchSetSingleBufferMode(args); + break; default: res = NAME_NOT_FOUND; break; @@ -660,6 +663,12 @@ int Surface::dispatchSetSurfaceDamage(va_list args) { return NO_ERROR; } +int Surface::dispatchSetSingleBufferMode(va_list args) { + bool singleBufferMode = va_arg(args, int); + setSingleBufferMode(singleBufferMode); + return NO_ERROR; +} + int Surface::connect(int api) { static sp<IProducerListener> listener = new DummyProducerListener(); return connect(api, listener); @@ -861,6 +870,19 @@ int Surface::setAsyncMode(bool async) { return err; } +int Surface::setSingleBufferMode(bool singleBufferMode) { + ATRACE_CALL(); + ALOGV("Surface::setSingleBufferMode (%d)", singleBufferMode); + Mutex::Autolock lock(mMutex); + + status_t err = mGraphicBufferProducer->setSingleBufferMode( + singleBufferMode); + ALOGE_IF(err, "IGraphicsBufferProducer::setSingleBufferMode(%d) returned" + "%s", singleBufferMode, strerror(-err)); + + return err; +} + int Surface::setBuffersDimensions(uint32_t width, uint32_t height) { ATRACE_CALL(); diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 5244d82831..a32a90c75f 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -465,4 +465,74 @@ TEST_F(BufferQueueTest, TestGenerationNumbers) { ASSERT_EQ(OK, mConsumer->attachBuffer(&outSlot, buffer)); } +TEST_F(BufferQueueTest, TestSingleBufferMode) { + createBufferQueue(); + sp<DummyConsumer> dc(new DummyConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + IGraphicBufferProducer::QueueBufferOutput output; + ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, + NATIVE_WINDOW_API_CPU, true, &output)); + + ASSERT_EQ(OK, mProducer->setSingleBufferMode(true)); + + // Get a buffer + int singleSlot; + sp<Fence> fence; + sp<GraphicBuffer> buffer; + ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, + mProducer->dequeueBuffer(&singleSlot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(OK, mProducer->requestBuffer(singleSlot, &buffer)); + + // Queue the buffer + IGraphicBufferProducer::QueueBufferInput input(0, false, + HAL_DATASPACE_UNKNOWN, Rect(0, 0, 1, 1), + NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); + ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output)); + + // Repeatedly acquire and release a buffer from the consumer side, it should + // always return the same one. + BufferItem item; + for (int i = 0; i < 5; i++) { + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + ASSERT_EQ(singleSlot, item.mSlot); + ASSERT_EQ(0, item.mTimestamp); + ASSERT_EQ(false, item.mIsAutoTimestamp); + ASSERT_EQ(HAL_DATASPACE_UNKNOWN, item.mDataSpace); + ASSERT_EQ(Rect(0, 0, 1, 1), item.mCrop); + ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode); + ASSERT_EQ(0, item.mTransform); + ASSERT_EQ(Fence::NO_FENCE, item.mFence); + + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + } + + // Repeatedly queue and dequeue a buffer from the producer side, it should + // always return the same one. + int slot; + for (int i = 0; i < 5; i++) { + ASSERT_EQ(OK, mProducer->dequeueBuffer(&slot, &fence, 0, 0, 0, 0)); + ASSERT_EQ(singleSlot, slot); + ASSERT_EQ(OK, mProducer->queueBuffer(singleSlot, input, &output)); + } + + // Repeatedly acquire and release a buffer from the consumer side, it should + // always return the same one. First grabbing them from the queue and then + // when the queue is empty, returning the single buffer. + for (int i = 0; i < 10; i++) { + ASSERT_EQ(OK, mConsumer->acquireBuffer(&item, 0)); + ASSERT_EQ(singleSlot, item.mSlot); + ASSERT_EQ(0, item.mTimestamp); + ASSERT_EQ(false, item.mIsAutoTimestamp); + ASSERT_EQ(HAL_DATASPACE_UNKNOWN, item.mDataSpace); + ASSERT_EQ(Rect(0, 0, 1, 1), item.mCrop); + ASSERT_EQ(NATIVE_WINDOW_SCALING_MODE_FREEZE, item.mScalingMode); + ASSERT_EQ(0, item.mTransform); + ASSERT_EQ(Fence::NO_FENCE, item.mFence); + + ASSERT_EQ(OK, mConsumer->releaseBuffer(item.mSlot, item.mFrameNumber, + EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE)); + } +} + } // namespace android diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp index fb292769fc..5bd7464920 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -1186,6 +1186,14 @@ EGLBoolean eglSurfaceAttrib( return setError(EGL_BAD_SURFACE, EGL_FALSE); egl_surface_t const * const s = get_surface(surface); + + //XXX: temporary hack for the EGL hook-up for single buffer mode + if (attribute == EGL_RENDER_BUFFER && (value == EGL_BACK_BUFFER || + value == EGL_SINGLE_BUFFER)) { + return (native_window_set_single_buffer_mode(s->win.get(), + value == EGL_SINGLE_BUFFER)) ? EGL_TRUE : EGL_FALSE; + } + if (s->cnx->egl.eglSurfaceAttrib) { return s->cnx->egl.eglSurfaceAttrib( dp->disp.dpy, s->surface, attribute, value); diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 67edcf3eee..ab21c8f116 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -178,25 +178,21 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { mExtensionString.setTo(gBuiltinExtensionString); char const* start = gExtensionString; - char const* end; do { - // find the space separating this extension for the next one - end = strchr(start, ' '); - if (end) { - // length of the extension string - const size_t len = end - start; - if (len) { - // NOTE: we could avoid the copy if we had strnstr. - const String8 ext(start, len); - if (findExtension(disp.queryString.extensions, ext.string(), - len)) { - mExtensionString.append(start, len+1); - } + // length of the extension name + size_t len = strcspn(start, " "); + if (len) { + // NOTE: we could avoid the copy if we had strnstr. + const String8 ext(start, len); + if (findExtension(disp.queryString.extensions, ext.string(), + len)) { + mExtensionString.append(ext + " "); } - // process the next extension string, and skip the space. - start = end + 1; + // advance to the next extension name, skipping the space. + start += len; + start += (*start == ' ') ? 1 : 0; } - } while (end); + } while (*start != '\0'); egl_cache_t::get()->initialize(this); diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index 5b85481e3a..f8fe8e1ae2 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -547,6 +547,10 @@ uint64_t VirtualDisplaySurface::getNextFrameNumber() const { return 0; } +status_t VirtualDisplaySurface::setSingleBufferMode(bool singleBufferMode) { + return mSource[SOURCE_SINK]->setSingleBufferMode(singleBufferMode); +} + void VirtualDisplaySurface::updateQueueBufferOutput( const QueueBufferOutput& qbo) { uint32_t w, h, transformHint, numPendingBuffers; diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h index fb37373470..bde7fcf1d0 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h @@ -120,6 +120,7 @@ private: virtual status_t setGenerationNumber(uint32_t generationNumber); virtual String8 getConsumerName() const override; virtual uint64_t getNextFrameNumber() const override; + virtual status_t setSingleBufferMode(bool singleBufferMode); // // Utility methods diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index a7b167fbc3..ad53226c64 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -1254,7 +1254,7 @@ void Layer::useEmptyDamage() { // ---------------------------------------------------------------------------- bool Layer::shouldPresentNow(const DispSync& dispSync) const { - if (mSidebandStreamChanged) { + if (mSidebandStreamChanged || mSingleBufferMode) { return true; } @@ -1278,7 +1278,7 @@ bool Layer::shouldPresentNow(const DispSync& dispSync) const { bool Layer::onPreComposition() { mRefreshPending = false; - return mQueuedFrames > 0 || mSidebandStreamChanged; + return mQueuedFrames > 0 || mSidebandStreamChanged || mSingleBufferMode; } void Layer::onPostComposition() { @@ -1335,7 +1335,7 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) } Region outDirtyRegion; - if (mQueuedFrames > 0) { + if (mQueuedFrames > 0 || mSingleBufferMode) { // if we've already called updateTexImage() without going through // a composition step, we have to skip this layer at this point @@ -1492,8 +1492,14 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) } } + // This boolean is used to make sure that SurfaceFlinger's shadow copy + // of the buffer queue isn't modified when the buffer queue is returning + // BufferItem's that weren't actually queued. This can happen in single + // buffer mode. + bool queuedBuffer = false; status_t updateResult = mSurfaceFlingerConsumer->updateTexImage(&r, - mFlinger->mPrimaryDispSync, maxFrameNumber); + mFlinger->mPrimaryDispSync, &mSingleBufferMode, &queuedBuffer, + maxFrameNumber); if (updateResult == BufferQueue::PRESENT_LATER) { // Producer doesn't want buffer to be displayed yet. Signal a // layer update so we check again at the next opportunity. @@ -1502,16 +1508,18 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) } else if (updateResult == SurfaceFlingerConsumer::BUFFER_REJECTED) { // If the buffer has been rejected, remove it from the shadow queue // and return early - Mutex::Autolock lock(mQueueItemLock); - mQueueItems.removeAt(0); - android_atomic_dec(&mQueuedFrames); + if (queuedBuffer) { + Mutex::Autolock lock(mQueueItemLock); + mQueueItems.removeAt(0); + android_atomic_dec(&mQueuedFrames); + } return outDirtyRegion; } else if (updateResult != NO_ERROR || mUpdateTexImageFailed) { // This can occur if something goes wrong when trying to create the // EGLImage for this buffer. If this happens, the buffer has already // been released, so we need to clean up the queue and bug out // early. - { + if (queuedBuffer) { Mutex::Autolock lock(mQueueItemLock); mQueueItems.clear(); android_atomic_and(0, &mQueuedFrames); @@ -1526,7 +1534,8 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) return outDirtyRegion; } - { // Autolock scope + if (queuedBuffer) { + // Autolock scope auto currentFrameNumber = mSurfaceFlingerConsumer->getFrameNumber(); Mutex::Autolock lock(mQueueItemLock); @@ -1544,7 +1553,8 @@ Region Layer::latchBuffer(bool& recomputeVisibleRegions) // Decrement the queued-frames count. Signal another event if we // have more frames pending. - if (android_atomic_dec(&mQueuedFrames) > 1) { + if ((queuedBuffer && android_atomic_dec(&mQueuedFrames) > 1) + || mSingleBufferMode) { mFlinger->signalLayerUpdate(); } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 4ff900672b..9e3c4db434 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -306,7 +306,8 @@ public: /* * Returns if a frame is queued. */ - bool hasQueuedFrame() const { return mQueuedFrames > 0 || mSidebandStreamChanged; } + bool hasQueuedFrame() const { return mQueuedFrames > 0 || + mSidebandStreamChanged || mSingleBufferMode; } // ----------------------------------------------------------------------- @@ -489,6 +490,8 @@ private: Vector<BufferItem> mQueueItems; uint64_t mLastFrameNumberReceived; bool mUpdateTexImageFailed; // This is only modified from the main thread + + bool mSingleBufferMode; }; // --------------------------------------------------------------------------- diff --git a/services/surfaceflinger/MonitoredProducer.cpp b/services/surfaceflinger/MonitoredProducer.cpp index 68b6b86308..16879ca570 100644 --- a/services/surfaceflinger/MonitoredProducer.cpp +++ b/services/surfaceflinger/MonitoredProducer.cpp @@ -131,6 +131,10 @@ uint64_t MonitoredProducer::getNextFrameNumber() const { return mProducer->getNextFrameNumber(); } +status_t MonitoredProducer::setSingleBufferMode(bool singleBufferMode) { + return mProducer->setSingleBufferMode(singleBufferMode); +} + IBinder* MonitoredProducer::onAsBinder() { return IInterface::asBinder(mProducer).get(); } diff --git a/services/surfaceflinger/MonitoredProducer.h b/services/surfaceflinger/MonitoredProducer.h index 4d2ed6eed7..c2b2e243cd 100644 --- a/services/surfaceflinger/MonitoredProducer.h +++ b/services/surfaceflinger/MonitoredProducer.h @@ -59,6 +59,7 @@ public: virtual String8 getConsumerName() const override; virtual uint64_t getNextFrameNumber() const override; virtual IBinder* onAsBinder(); + virtual status_t setSingleBufferMode(bool singleBufferMode); private: sp<IGraphicBufferProducer> mProducer; diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.cpp b/services/surfaceflinger/SurfaceFlingerConsumer.cpp index 930e7c764c..5722fb446c 100644 --- a/services/surfaceflinger/SurfaceFlingerConsumer.cpp +++ b/services/surfaceflinger/SurfaceFlingerConsumer.cpp @@ -32,7 +32,8 @@ namespace android { // --------------------------------------------------------------------------- status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter, - const DispSync& dispSync, uint64_t maxFrameNumber) + const DispSync& dispSync, bool* singleBufferMode, bool* queuedBuffer, + uint64_t maxFrameNumber) { ATRACE_CALL(); ALOGV("updateTexImage"); @@ -68,7 +69,6 @@ status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter, return err; } - // We call the rejecter here, in case the caller has a reason to // not accept this buffer. This is used by SurfaceFlinger to // reject buffers which have the wrong size @@ -78,6 +78,14 @@ status_t SurfaceFlingerConsumer::updateTexImage(BufferRejecter* rejecter, return BUFFER_REJECTED; } + if (singleBufferMode) { + *singleBufferMode = item.mSingleBufferMode; + } + + if (queuedBuffer) { + *queuedBuffer = item.mQueuedBuffer; + } + // Release the previous buffer. err = updateAndReleaseLocked(item); if (err != NO_ERROR) { diff --git a/services/surfaceflinger/SurfaceFlingerConsumer.h b/services/surfaceflinger/SurfaceFlingerConsumer.h index 779e5b77b2..207c243c12 100644 --- a/services/surfaceflinger/SurfaceFlingerConsumer.h +++ b/services/surfaceflinger/SurfaceFlingerConsumer.h @@ -57,6 +57,7 @@ public: // this does not guarantee that the buffer has been bound to the GL // texture. status_t updateTexImage(BufferRejecter* rejecter, const DispSync& dispSync, + bool* singleBufferMode, bool* queuedBuffer, uint64_t maxFrameNumber = 0); // See GLConsumer::bindTextureImageLocked(). diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc index 59a43e2190..eb9bd25a6b 100644 --- a/services/surfaceflinger/surfaceflinger.rc +++ b/services/surfaceflinger/surfaceflinger.rc @@ -1,6 +1,6 @@ service surfaceflinger /system/bin/surfaceflinger class core user system - group graphics drmrpc + group graphics drmrpc readproc onrestart restart zygote writepid /dev/cpuset/system-background/tasks |