Add a BufferItem parameter to onFrameAvailable

Passes the BufferItem for the queued buffer to the onFrameAvailable
callback so the consumer can track the BufferQueue's contents. Also
adds an onFrameReplaced callback, which is necessary if the consumer
wants to do anything more than simple queue length tracking.

Bug: 18111837
Change-Id: If9d07229c9b586c668e5f99074e9b63b0468feb0
This commit is contained in:
Dan Stoza 2014-11-04 11:37:46 -08:00
parent 793fc0e13d
commit 8dc55396fc
23 changed files with 99 additions and 38 deletions

View File

@ -44,6 +44,7 @@ class BufferItem : public Flattenable<BufferItem> {
// The default value of mBuf, used to indicate this doesn't correspond to a slot. // The default value of mBuf, used to indicate this doesn't correspond to a slot.
enum { INVALID_BUFFER_SLOT = -1 }; enum { INVALID_BUFFER_SLOT = -1 };
BufferItem(); BufferItem();
~BufferItem();
operator IGraphicBufferConsumer::BufferItem() const; operator IGraphicBufferConsumer::BufferItem() const;
static const char* scalingModeName(uint32_t scalingMode); static const char* scalingModeName(uint32_t scalingMode);

View File

@ -62,7 +62,7 @@ public:
public: public:
ProxyConsumerListener(const wp<ConsumerListener>& consumerListener); ProxyConsumerListener(const wp<ConsumerListener>& consumerListener);
virtual ~ProxyConsumerListener(); virtual ~ProxyConsumerListener();
virtual void onFrameAvailable(); virtual void onFrameAvailable(const android::BufferItem& item);
virtual void onBuffersReleased(); virtual void onBuffersReleased();
virtual void onSidebandStreamChanged(); virtual void onSidebandStreamChanged();
private: private:

View File

@ -203,6 +203,16 @@ private:
// since the previous buffer might have already been acquired. // since the previous buffer might have already been acquired.
sp<Fence> mLastQueueBufferFence; sp<Fence> mLastQueueBufferFence;
// Take-a-ticket system for ensuring that onFrame* callbacks are called in
// the order that frames are queued. While the BufferQueue lock
// (mCore->mMutex) is held, a ticket is retained by the producer. After
// dropping the BufferQueue lock, the producer must wait on the condition
// variable until the current callback ticket matches its retained ticket.
Mutex mCallbackMutex;
int mNextCallbackTicket; // Protected by mCore->mMutex
int mCurrentCallbackTicket; // Protected by mCallbackMutex
Condition mCallbackCondition;
}; // class BufferQueueProducer }; // class BufferQueueProducer
} // namespace android } // namespace android

View File

@ -46,7 +46,7 @@ public:
// //
// This is called without any lock held and can be called concurrently // This is called without any lock held and can be called concurrently
// by multiple threads. // by multiple threads.
virtual void onFrameAvailable() = 0; virtual void onFrameAvailable(const BufferItem& item) = 0;
}; };
virtual ~ConsumerBase(); virtual ~ConsumerBase();
@ -106,7 +106,7 @@ protected:
// the ConsumerBase implementation must be called from the derived class. // the ConsumerBase implementation must be called from the derived class.
// The ConsumerBase version of onSidebandStreamChanged does nothing and can // The ConsumerBase version of onSidebandStreamChanged does nothing and can
// be overriden by derived classes if they want the notification. // be overriden by derived classes if they want the notification.
virtual void onFrameAvailable(); virtual void onFrameAvailable(const BufferItem& item);
virtual void onBuffersReleased(); virtual void onBuffersReleased();
virtual void onSidebandStreamChanged(); virtual void onSidebandStreamChanged();

View File

@ -28,6 +28,8 @@
namespace android { namespace android {
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
class BufferItem;
// ConsumerListener is the interface through which the BufferQueue notifies // ConsumerListener is the interface through which the BufferQueue notifies
// the consumer of events that the consumer may wish to react to. Because // the consumer of events that the consumer may wish to react to. Because
// the consumer will generally have a mutex that is locked during calls from // the consumer will generally have a mutex that is locked during calls from
@ -43,11 +45,24 @@ public:
// frame becomes available for consumption. This means that frames that // frame becomes available for consumption. This means that frames that
// are queued while in asynchronous mode only trigger the callback if no // are queued while in asynchronous mode only trigger the callback if no
// previous frames are pending. Frames queued while in synchronous mode // previous frames are pending. Frames queued while in synchronous mode
// always trigger the callback. // always trigger the callback. The item passed to the callback will contain
// all of the information about the queued frame except for its
// GraphicBuffer pointer, which will always be null.
// //
// This is called without any lock held and can be called concurrently // This is called without any lock held and can be called concurrently
// by multiple threads. // by multiple threads.
virtual void onFrameAvailable() = 0; /* Asynchronous */ virtual void onFrameAvailable(const BufferItem& item) = 0; /* Asynchronous */
// onFrameReplaced is called from queueBuffer if the frame being queued is
// replacing an existing slot in the queue. Any call to queueBuffer that
// doesn't call onFrameAvailable will call this callback instead. The item
// passed to the callback will contain all of the information about the
// queued frame except for its GraphicBuffer pointer, which will always be
// null.
//
// This is called without any lock held and can be called concurrently
// by multiple threads.
virtual void onFrameReplaced(const BufferItem& item) {} /* Asynchronous */
// onBuffersReleased is called to notify the buffer consumer that the // onBuffersReleased is called to notify the buffer consumer that the
// BufferQueue has released its references to one or more GraphicBuffers // BufferQueue has released its references to one or more GraphicBuffers

View File

@ -74,7 +74,7 @@ private:
// can block if there are too many outstanding buffers. If it blocks, it // can block if there are too many outstanding buffers. If it blocks, it
// will resume when onBufferReleasedByOutput releases a buffer back to the // will resume when onBufferReleasedByOutput releases a buffer back to the
// input. // input.
virtual void onFrameAvailable(); virtual void onFrameAvailable(const BufferItem& item);
// From IConsumerListener // From IConsumerListener
// We don't care about released buffers because we detach each buffer as // We don't care about released buffers because we detach each buffer as

View File

@ -36,6 +36,8 @@ BufferItem::BufferItem() :
mCrop.makeInvalid(); mCrop.makeInvalid();
} }
BufferItem::~BufferItem() {}
BufferItem::operator IGraphicBufferConsumer::BufferItem() const { BufferItem::operator IGraphicBufferConsumer::BufferItem() const {
IGraphicBufferConsumer::BufferItem bufferItem; IGraphicBufferConsumer::BufferItem bufferItem;
bufferItem.mGraphicBuffer = mGraphicBuffer; bufferItem.mGraphicBuffer = mGraphicBuffer;

View File

@ -31,10 +31,11 @@ BufferQueue::ProxyConsumerListener::ProxyConsumerListener(
BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {} BufferQueue::ProxyConsumerListener::~ProxyConsumerListener() {}
void BufferQueue::ProxyConsumerListener::onFrameAvailable() { void BufferQueue::ProxyConsumerListener::onFrameAvailable(
const android::BufferItem& item) {
sp<ConsumerListener> listener(mConsumerListener.promote()); sp<ConsumerListener> listener(mConsumerListener.promote());
if (listener != NULL) { if (listener != NULL) {
listener->onFrameAvailable(); listener->onFrameAvailable(item);
} }
} }

View File

@ -39,7 +39,11 @@ BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core) :
mSlots(core->mSlots), mSlots(core->mSlots),
mConsumerName(), mConsumerName(),
mStickyTransform(0), mStickyTransform(0),
mLastQueueBufferFence(Fence::NO_FENCE) {} mLastQueueBufferFence(Fence::NO_FENCE),
mCallbackMutex(),
mNextCallbackTicket(0),
mCurrentCallbackTicket(0),
mCallbackCondition() {}
BufferQueueProducer::~BufferQueueProducer() {} BufferQueueProducer::~BufferQueueProducer() {}
@ -542,7 +546,10 @@ status_t BufferQueueProducer::queueBuffer(int slot,
return BAD_VALUE; return BAD_VALUE;
} }
sp<IConsumerListener> listener; sp<IConsumerListener> frameAvailableListener;
sp<IConsumerListener> frameReplacedListener;
int callbackTicket = 0;
BufferItem item;
{ // Autolock scope { // Autolock scope
Mutex::Autolock lock(mCore->mMutex); Mutex::Autolock lock(mCore->mMutex);
@ -598,7 +605,6 @@ status_t BufferQueueProducer::queueBuffer(int slot,
++mCore->mFrameCounter; ++mCore->mFrameCounter;
mSlots[slot].mFrameNumber = mCore->mFrameCounter; mSlots[slot].mFrameNumber = mCore->mFrameCounter;
BufferItem item;
item.mAcquireCalled = mSlots[slot].mAcquireCalled; item.mAcquireCalled = mSlots[slot].mAcquireCalled;
item.mGraphicBuffer = mSlots[slot].mGraphicBuffer; item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
item.mCrop = crop; item.mCrop = crop;
@ -619,7 +625,7 @@ status_t BufferQueueProducer::queueBuffer(int slot,
// When the queue is empty, we can ignore mDequeueBufferCannotBlock // When the queue is empty, we can ignore mDequeueBufferCannotBlock
// and simply queue this buffer // and simply queue this buffer
mCore->mQueue.push_back(item); mCore->mQueue.push_back(item);
listener = mCore->mConsumerListener; frameAvailableListener = mCore->mConsumerListener;
} else { } else {
// When the queue is not empty, we need to look at the front buffer // When the queue is not empty, we need to look at the front buffer
// state to see if we need to replace it // state to see if we need to replace it
@ -635,9 +641,10 @@ status_t BufferQueueProducer::queueBuffer(int slot,
} }
// Overwrite the droppable buffer with the incoming one // Overwrite the droppable buffer with the incoming one
*front = item; *front = item;
frameReplacedListener = mCore->mConsumerListener;
} else { } else {
mCore->mQueue.push_back(item); mCore->mQueue.push_back(item);
listener = mCore->mConsumerListener; frameAvailableListener = mCore->mConsumerListener;
} }
} }
@ -648,6 +655,9 @@ status_t BufferQueueProducer::queueBuffer(int slot,
mCore->mTransformHint, mCore->mQueue.size()); mCore->mTransformHint, mCore->mQueue.size());
ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size()); ATRACE_INT(mCore->mConsumerName.string(), mCore->mQueue.size());
// Take a ticket for the callback functions
callbackTicket = mNextCallbackTicket++;
} // Autolock scope } // Autolock scope
// Wait without lock held // Wait without lock held
@ -659,9 +669,27 @@ status_t BufferQueueProducer::queueBuffer(int slot,
mLastQueueBufferFence = fence; mLastQueueBufferFence = fence;
} }
// Call back without lock held // Don't send the GraphicBuffer through the callback, and don't send
if (listener != NULL) { // the slot number, since the consumer shouldn't need it
listener->onFrameAvailable(); item.mGraphicBuffer.clear();
item.mSlot = BufferItem::INVALID_BUFFER_SLOT;
// Call back without the main BufferQueue lock held, but with the callback
// lock held so we can ensure that callbacks occur in order
{
Mutex::Autolock lock(mCallbackMutex);
while (callbackTicket != mCurrentCallbackTicket) {
mCallbackCondition.wait(mCallbackMutex);
}
if (frameAvailableListener != NULL) {
frameAvailableListener->onFrameAvailable(item);
} else if (frameReplacedListener != NULL) {
frameReplacedListener->onFrameReplaced(item);
}
++mCurrentCallbackTicket;
mCallbackCondition.broadcast();
} }
return NO_ERROR; return NO_ERROR;

View File

@ -98,7 +98,7 @@ void ConsumerBase::freeBufferLocked(int slotIndex) {
mSlots[slotIndex].mFrameNumber = 0; mSlots[slotIndex].mFrameNumber = 0;
} }
void ConsumerBase::onFrameAvailable() { void ConsumerBase::onFrameAvailable(const BufferItem& item) {
CB_LOGV("onFrameAvailable"); CB_LOGV("onFrameAvailable");
sp<FrameAvailableListener> listener; sp<FrameAvailableListener> listener;
@ -109,7 +109,7 @@ void ConsumerBase::onFrameAvailable() {
if (listener != NULL) { if (listener != NULL) {
CB_LOGV("actually calling onFrameAvailable"); CB_LOGV("actually calling onFrameAvailable");
listener->onFrameAvailable(); listener->onFrameAvailable(item);
} }
} }

View File

@ -21,6 +21,7 @@
#include <binder/Parcel.h> #include <binder/Parcel.h>
#include <gui/IConsumerListener.h> #include <gui/IConsumerListener.h>
#include <gui/BufferItem.h>
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
namespace android { namespace android {
@ -39,9 +40,10 @@ public:
: BpInterface<IConsumerListener>(impl) { : BpInterface<IConsumerListener>(impl) {
} }
virtual void onFrameAvailable() { virtual void onFrameAvailable(const BufferItem& item) {
Parcel data, reply; Parcel data, reply;
data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor()); data.writeInterfaceToken(IConsumerListener::getInterfaceDescriptor());
data.write(item);
remote()->transact(ON_FRAME_AVAILABLE, data, &reply, IBinder::FLAG_ONEWAY); remote()->transact(ON_FRAME_AVAILABLE, data, &reply, IBinder::FLAG_ONEWAY);
} }
@ -66,18 +68,20 @@ status_t BnConsumerListener::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{ {
switch(code) { switch(code) {
case ON_FRAME_AVAILABLE: case ON_FRAME_AVAILABLE: {
CHECK_INTERFACE(IConsumerListener, data, reply); CHECK_INTERFACE(IConsumerListener, data, reply);
onFrameAvailable(); BufferItem item;
return NO_ERROR; data.read(item);
case ON_BUFFER_RELEASED: onFrameAvailable(item);
return NO_ERROR; }
case ON_BUFFER_RELEASED: {
CHECK_INTERFACE(IConsumerListener, data, reply); CHECK_INTERFACE(IConsumerListener, data, reply);
onBuffersReleased(); onBuffersReleased();
return NO_ERROR; return NO_ERROR; }
case ON_SIDEBAND_STREAM_CHANGED: case ON_SIDEBAND_STREAM_CHANGED: {
CHECK_INTERFACE(IConsumerListener, data, reply); CHECK_INTERFACE(IConsumerListener, data, reply);
onSidebandStreamChanged(); onSidebandStreamChanged();
return NO_ERROR; return NO_ERROR; }
} }
return BBinder::onTransact(code, data, reply, flags); return BBinder::onTransact(code, data, reply, flags);
} }

View File

@ -98,7 +98,7 @@ void StreamSplitter::setName(const String8 &name) {
mInput->setConsumerName(name); mInput->setConsumerName(name);
} }
void StreamSplitter::onFrameAvailable() { void StreamSplitter::onFrameAvailable(const BufferItem& /* item */) {
ATRACE_CALL(); ATRACE_CALL();
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);

View File

@ -67,7 +67,7 @@ protected:
}; };
struct DummyConsumer : public BnConsumerListener { struct DummyConsumer : public BnConsumerListener {
virtual void onFrameAvailable() {} virtual void onFrameAvailable(const BufferItem& /* item */) {}
virtual void onBuffersReleased() {} virtual void onBuffersReleased() {}
virtual void onSidebandStreamChanged() {} virtual void onSidebandStreamChanged() {}
}; };

View File

@ -44,7 +44,7 @@ public:
mPendingFrames--; mPendingFrames--;
} }
virtual void onFrameAvailable() { virtual void onFrameAvailable(const BufferItem& /* item */) {
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
mPendingFrames++; mPendingFrames++;
mFrameCondition.signal(); mFrameCondition.signal();

View File

@ -35,7 +35,7 @@ public:
mPendingFrames--; mPendingFrames--;
} }
virtual void onFrameAvailable() { virtual void onFrameAvailable(const BufferItem& /* item */) {
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
mPendingFrames++; mPendingFrames++;
mCondition.signal(); mCondition.signal();

View File

@ -65,7 +65,7 @@ const sp<Fence> QUEUE_BUFFER_INPUT_FENCE = Fence::NO_FENCE;
}; // namespace anonymous }; // namespace anonymous
struct DummyConsumer : public BnConsumerListener { struct DummyConsumer : public BnConsumerListener {
virtual void onFrameAvailable() {} virtual void onFrameAvailable(const BufferItem& /* item */) {}
virtual void onBuffersReleased() {} virtual void onBuffersReleased() {}
virtual void onSidebandStreamChanged() {} virtual void onSidebandStreamChanged() {}
}; };

View File

@ -46,7 +46,7 @@ protected:
}; };
struct DummyListener : public BnConsumerListener { struct DummyListener : public BnConsumerListener {
virtual void onFrameAvailable() {} virtual void onFrameAvailable(const BufferItem& /* item */) {}
virtual void onBuffersReleased() {} virtual void onBuffersReleased() {}
virtual void onSidebandStreamChanged() {} virtual void onSidebandStreamChanged() {}
}; };

View File

@ -130,7 +130,7 @@ protected:
} }
// This should be called by GLConsumer on the producer thread. // This should be called by GLConsumer on the producer thread.
virtual void onFrameAvailable() { virtual void onFrameAvailable(const BufferItem& /* item */) {
Mutex::Autolock lock(mMutex); Mutex::Autolock lock(mMutex);
ALOGV("+onFrameAvailable"); ALOGV("+onFrameAvailable");
mFrameAvailable = true; mFrameAvailable = true;

View File

@ -101,7 +101,7 @@ TEST_F(EGLTest, EGLTerminateSucceedsWithRemainingObjects) {
EXPECT_TRUE(eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs)); EXPECT_TRUE(eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs));
struct DummyConsumer : public BnConsumerListener { struct DummyConsumer : public BnConsumerListener {
virtual void onFrameAvailable() {} virtual void onFrameAvailable(const BufferItem& /* item */) {}
virtual void onBuffersReleased() {} virtual void onBuffersReleased() {}
virtual void onSidebandStreamChanged() {} virtual void onSidebandStreamChanged() {}
}; };

View File

@ -122,7 +122,7 @@ status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>&
} }
// Overrides ConsumerBase::onFrameAvailable(), does not call base class impl. // Overrides ConsumerBase::onFrameAvailable(), does not call base class impl.
void FramebufferSurface::onFrameAvailable() { void FramebufferSurface::onFrameAvailable(const BufferItem& /* item */) {
sp<GraphicBuffer> buf; sp<GraphicBuffer> buf;
sp<Fence> acquireFence; sp<Fence> acquireFence;
status_t err = nextBuffer(buf, acquireFence); status_t err = nextBuffer(buf, acquireFence);

View File

@ -56,7 +56,7 @@ public:
private: private:
virtual ~FramebufferSurface() { }; // this class cannot be overloaded virtual ~FramebufferSurface() { }; // this class cannot be overloaded
virtual void onFrameAvailable(); virtual void onFrameAvailable(const BufferItem& item);
virtual void freeBufferLocked(int slotIndex); virtual void freeBufferLocked(int slotIndex);
virtual void dumpLocked(String8& result, const char* prefix) const; virtual void dumpLocked(String8& result, const char* prefix) const;

View File

@ -159,7 +159,7 @@ void Layer::onLayerDisplayed(const sp<const DisplayDevice>& /* hw */,
} }
} }
void Layer::onFrameAvailable() { void Layer::onFrameAvailable(const BufferItem& /* item */) {
android_atomic_inc(&mQueuedFrames); android_atomic_inc(&mQueuedFrames);
mFlinger->signalLayerUpdate(); mFlinger->signalLayerUpdate();
} }

View File

@ -330,7 +330,7 @@ protected:
private: private:
// Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener // Interface implementation for SurfaceFlingerConsumer::ContentsChangedListener
virtual void onFrameAvailable(); virtual void onFrameAvailable(const BufferItem& item);
virtual void onSidebandStreamChanged(); virtual void onSidebandStreamChanged();
void commitTransaction(); void commitTransaction();