diff options
| author | 2012-03-15 14:01:24 -0700 | |
|---|---|---|
| committer | 2012-03-19 15:09:09 -0700 | |
| commit | fa5b40ebb8923133df12dc70591bfe35b3f1c9b3 (patch) | |
| tree | 8f8c8612361f0e1a9a1fdf73a78796df032c154b /libs/gui/BufferQueue.cpp | |
| parent | 9eeebe39807e65e29b3bb05e7d0e4094bdb564c8 (diff) | |
libgui: add BQ consumer buffer free notifications
This change adds a new callback for BufferQueue consumers to be notified
when the BufferQueue frees some or all of its buffers. This is needed
to retain SurfaceTexture behavior where all buffers would be freed when
the producer disconnects. This change also modifies the
SurfaceTextureGLToGLTest.EglDestroySurfaceUnrefsBuffers test to catch
when the buffers are not freed.
The implementation is a little complicated because it needs to avoid
circular sp<> references across what will be a binder interface (so wp<>
can't be used directly). It also needs to avoid the possibility of
locking the BufferQueue and consumer (e.g. SurfaceTexture) mutexes in
the wrong order.
This change also includes a few additional fixes and test cleanups.
Change-Id: I27b77d0af15cb4b135f4b63573f634f5f0da2182
Diffstat (limited to 'libs/gui/BufferQueue.cpp')
| -rw-r--r-- | libs/gui/BufferQueue.cpp | 158 |
1 files changed, 113 insertions, 45 deletions
diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index 0f9c7c5c4a..ffc5fa02a9 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -15,8 +15,8 @@ */ #define LOG_TAG "BufferQueue" -//#define LOG_NDEBUG 0 #define ATRACE_TAG ATRACE_TAG_GRAPHICS +//#define LOG_NDEBUG 0 #define GL_GLEXT_PROTOTYPES #define EGL_EGLEXT_PROTOTYPES @@ -146,13 +146,6 @@ void BufferQueue::setConsumerName(const String8& name) { mConsumerName = name; } -void BufferQueue::setFrameAvailableListener( - const sp<FrameAvailableListener>& listener) { - ST_LOGV("setFrameAvailableListener"); - Mutex::Autolock lock(mMutex); - mFrameAvailableListener = listener; -} - status_t BufferQueue::setDefaultBufferFormat(uint32_t defaultFormat) { Mutex::Autolock lock(mMutex); mDefaultBufferFormat = defaultFormat; @@ -531,7 +524,7 @@ status_t BufferQueue::queueBuffer(int buf, int64_t timestamp, ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp); - sp<FrameAvailableListener> listener; + sp<ConsumerListener> listener; { // scope for the lock Mutex::Autolock lock(mMutex); @@ -559,7 +552,7 @@ status_t BufferQueue::queueBuffer(int buf, int64_t timestamp, // Synchronous mode always signals that an additional frame should // be consumed. - listener = mFrameAvailableListener; + listener = mConsumerListener; } else { // In asynchronous mode we only keep the most recent buffer. if (mQueue.empty()) { @@ -568,7 +561,7 @@ status_t BufferQueue::queueBuffer(int buf, int64_t timestamp, // Asynchronous mode only signals that a frame should be // consumed if no previous frame was pending. If a frame were // pending then the consumer would have already been notified. - listener = mFrameAvailableListener; + listener = mConsumerListener; } else { Fifo::iterator front(mQueue.begin()); // buffer currently queued is freed @@ -682,6 +675,11 @@ status_t BufferQueue::connect(int api, return NO_INIT; } + if (mConsumerListener == NULL) { + ST_LOGE("connect: BufferQueue has no consumer!"); + return NO_INIT; + } + int err = NO_ERROR; switch (api) { case NATIVE_WINDOW_API_EGL: @@ -712,38 +710,49 @@ status_t BufferQueue::connect(int api, status_t BufferQueue::disconnect(int api) { ATRACE_CALL(); ST_LOGV("disconnect: api=%d", api); - Mutex::Autolock lock(mMutex); - - if (mAbandoned) { - // it is not really an error to disconnect after the surface - // has been abandoned, it should just be a no-op. - return NO_ERROR; - } int err = NO_ERROR; - switch (api) { - case NATIVE_WINDOW_API_EGL: - case NATIVE_WINDOW_API_CPU: - case NATIVE_WINDOW_API_MEDIA: - case NATIVE_WINDOW_API_CAMERA: - if (mConnectedApi == api) { - drainQueueAndFreeBuffersLocked(); - mConnectedApi = NO_CONNECTED_API; - mNextCrop.makeInvalid(); - mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; - mNextTransform = 0; - mDequeueCondition.broadcast(); - } else { - ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)", - mConnectedApi, api); + sp<ConsumerListener> listener; + + { // Scope for the lock + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + // it is not really an error to disconnect after the surface + // has been abandoned, it should just be a no-op. + return NO_ERROR; + } + + switch (api) { + case NATIVE_WINDOW_API_EGL: + case NATIVE_WINDOW_API_CPU: + case NATIVE_WINDOW_API_MEDIA: + case NATIVE_WINDOW_API_CAMERA: + if (mConnectedApi == api) { + drainQueueAndFreeBuffersLocked(); + mConnectedApi = NO_CONNECTED_API; + mNextCrop.makeInvalid(); + mNextScalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE; + mNextTransform = 0; + mDequeueCondition.broadcast(); + listener = mConsumerListener; + } else { + ST_LOGE("disconnect: connected to another api (cur=%d, req=%d)", + mConnectedApi, api); + err = -EINVAL; + } + break; + default: + ST_LOGE("disconnect: unknown API %d", api); err = -EINVAL; - } - break; - default: - ST_LOGE("disconnect: unknown API %d", api); - err = -EINVAL; - break; + break; + } } + + if (listener != NULL) { + listener->onBuffersReleased(); + } + return err; } @@ -841,7 +850,7 @@ void BufferQueue::freeAllBuffersLocked() { } } -status_t BufferQueue::acquire(BufferItem *buffer) { +status_t BufferQueue::acquireBuffer(BufferItem *buffer) { ATRACE_CALL(); Mutex::Autolock _l(mMutex); // check if queue is empty @@ -855,8 +864,7 @@ status_t BufferQueue::acquire(BufferItem *buffer) { if (mSlots[buf].mAcquireCalled) { buffer->mGraphicBuffer = NULL; - } - else { + } else { buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer; } buffer->mCrop = mSlots[buf].mCrop; @@ -872,8 +880,7 @@ status_t BufferQueue::acquire(BufferItem *buffer) { mDequeueCondition.broadcast(); ATRACE_INT(mConsumerName.string(), mQueue.size()); - } - else { + } else { // should be a better return code? return -EINVAL; } @@ -907,17 +914,58 @@ status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display, return OK; } +status_t BufferQueue::consumerConnect(const sp<ConsumerListener>& consumerListener) { + ST_LOGV("consumerConnect"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("consumerConnect: BufferQueue has been abandoned!"); + return NO_INIT; + } + + mConsumerListener = consumerListener; + + return OK; +} + status_t BufferQueue::consumerDisconnect() { + ST_LOGV("consumerDisconnect"); Mutex::Autolock lock(mMutex); - mAbandoned = true; + if (mConsumerListener == NULL) { + ST_LOGE("consumerDisconnect: No consumer is connected!"); + return -EINVAL; + } + mAbandoned = true; + mConsumerListener = NULL; mQueue.clear(); freeAllBuffersLocked(); mDequeueCondition.broadcast(); return OK; } +status_t BufferQueue::getReleasedBuffers(uint32_t* slotMask) { + ST_LOGV("getReleasedBuffers"); + Mutex::Autolock lock(mMutex); + + if (mAbandoned) { + ST_LOGE("getReleasedBuffers: BufferQueue has been abandoned!"); + return NO_INIT; + } + + uint32_t mask = 0; + for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { + if (!mSlots[i].mAcquireCalled) { + mask |= 1 << i; + } + } + *slotMask = mask; + + ST_LOGV("getReleasedBuffers: returning mask %#x", mask); + return NO_ERROR; +} + status_t BufferQueue::setDefaultBufferSize(uint32_t w, uint32_t h) { ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h); @@ -982,4 +1030,24 @@ status_t BufferQueue::drainQueueAndFreeBuffersLocked() { return err; } +BufferQueue::ProxyConsumerListener::ProxyConsumerListener( + const wp<BufferQueue::ConsumerListener>& consumerListener): + mConsumerListener(consumerListener) {} + +BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {} + +void BufferQueue::ProxyConsumerListener::onFrameAvailable() { + sp<BufferQueue::ConsumerListener> listener(mConsumerListener.promote()); + if (listener != NULL) { + listener->onFrameAvailable(); + } +} + +void BufferQueue::ProxyConsumerListener::onBuffersReleased() { + sp<BufferQueue::ConsumerListener> listener(mConsumerListener.promote()); + if (listener != NULL) { + listener->onBuffersReleased(); + } +} + }; // namespace android |