summaryrefslogtreecommitdiff
path: root/libs/gui/BufferQueue.cpp
diff options
context:
space:
mode:
author Jamie Gennis <jgennis@google.com> 2012-03-15 14:01:24 -0700
committer Jamie Gennis <jgennis@google.com> 2012-03-19 15:09:09 -0700
commitfa5b40ebb8923133df12dc70591bfe35b3f1c9b3 (patch)
tree8f8c8612361f0e1a9a1fdf73a78796df032c154b /libs/gui/BufferQueue.cpp
parent9eeebe39807e65e29b3bb05e7d0e4094bdb564c8 (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.cpp158
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