生产者和消费者模型是编程中运用比较广泛的模型。在SurfaceFlinger图像绘制、合成、显示也用到了该模型。利用该模型合理的管理图像缓冲区buffer。让整个android系统从绘制到显示的过程有条不紊的进行。
BufferQueue图像缓冲管理者。其成员函数createBufferQueue创建一个缓冲区队列。
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
sp<IGraphicBufferConsumer>* outConsumer,
const sp<IGraphicBufferAlloc>& allocator) {
LOG_ALWAYS_FATAL_IF(outProducer == NULL,
"BufferQueue: outProducer must not be NULL");
LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
"BufferQueue: outConsumer must not be NULL");
sp<BufferQueueCore> core(new BufferQueueCore(allocator));
LOG_ALWAYS_FATAL_IF(core == NULL,
"BufferQueue: failed to create BufferQueueCore");
sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core));
LOG_ALWAYS_FATAL_IF(producer == NULL,
"BufferQueue: failed to create BufferQueueProducer");
sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
LOG_ALWAYS_FATAL_IF(consumer == NULL,
"BufferQueue: failed to create BufferQueueConsumer");
*outProducer = producer;
*outConsumer = consumer;
}
class BufferQueueCore : public virtual RefBase {
friend class BufferQueueProducer;
friend class BufferQueueConsumer;
public:
// Used as a placeholder slot number when the value isn't pointing to an
// existing buffer.
enum { INVALID_BUFFER_SLOT = BufferItem::INVALID_BUFFER_SLOT };
// We reserve two slots in order to guarantee that the producer and
// consumer can run asynchronously.
enum { MAX_MAX_ACQUIRED_BUFFERS = BufferQueueDefs::NUM_BUFFER_SLOTS - 2 };
// The default API number used to indicate that no producer is connected
enum { NO_CONNECTED_API = 0 };
typedef Vector<BufferItem> Fifo;
// BufferQueueCore manages a pool of gralloc memory slots to be used by
// producers and consumers. allocator is used to allocate all the needed
// gralloc buffers.
BufferQueueCore(const sp<IGraphicBufferAlloc>& allocator = NULL);
virtual ~BufferQueueCore();
private:
// Dump our state in a string
void dump(String8& result, const char* prefix) const;
// getMinUndequeuedBufferCountLocked returns the minimum number of buffers
// that must remain in a state other than DEQUEUED. The async parameter
// tells whether we're in asynchronous mode.
int getMinUndequeuedBufferCountLocked(bool async) const;
// getMinMaxBufferCountLocked returns the minimum number of buffers allowed
// given the current BufferQueue state. The async parameter tells whether
// we're in asynchonous mode.
int getMinMaxBufferCountLocked(bool async) const;
// getMaxBufferCountLocked returns the maximum number of buffers that can be
// allocated at once. This value depends on the following member variables:
//
// mDequeueBufferCannotBlock
// mMaxAcquiredBufferCount
// mDefaultMaxBufferCount
// mOverrideMaxBufferCount
// async parameter
//
// Any time one of these member variables is changed while a producer is
// connected, mDequeueCondition must be broadcast.
int getMaxBufferCountLocked(bool async) const;
// setDefaultMaxBufferCountLocked sets the maximum number of buffer slots
// that will be used if the producer does not override the buffer slot
// count. The count must be between 2 and NUM_BUFFER_SLOTS, inclusive. The
// initial default is 2.
status_t setDefaultMaxBufferCountLocked(int count);
// freeBufferLocked frees the GraphicBuffer and sync resources for the
// given slot.
void freeBufferLocked(int slot);
// freeAllBuffersLocked frees the GraphicBuffer and sync resources for
// all slots.
void freeAllBuffersLocked();
// stillTracking returns true iff the buffer item is still being tracked
// in one of the slots.
bool stillTracking(const BufferItem* item) const;
// waitWhileAllocatingLocked blocks until mIsAllocating is false.
void waitWhileAllocatingLocked() const;
// validateConsistencyLocked ensures that the free lists are in sync with
// the information stored in mSlots
void validateConsistencyLocked() const;
// mAllocator is the connection to SurfaceFlinger that is used to allocate
// new GraphicBuffer objects.
sp<IGraphicBufferAlloc> mAllocator;
// mMutex is the mutex used to prevent concurrent access to the member
// variables of BufferQueueCore objects. It must be locked whenever any
// member variable is accessed.
mutable Mutex mMutex;
// mIsAbandoned indicates that the BufferQueue will no longer be used to
// consume image buffers pushed to it using the IGraphicBufferProducer
// interface. It is initialized to false, and set to true in the
// consumerDisconnect method. A BufferQueue that is abandoned will return
// the NO_INIT error from all IGraphicBufferProducer methods capable of
// returning an error.
bool mIsAbandoned;
// mConsumerControlledByApp indicates whether the connected consumer is
// controlled by the application.
bool mConsumerControlledByApp;
// mConsumerName is a string used to identify the BufferQueue in log
// messages. It is set by the IGraphicBufferConsumer::setConsumerName
// method.
String8 mConsumerName;
// mConsumerListener is used to notify the connected consumer of
// asynchronous events that it may wish to react to. It is initially
// set to NULL and is written by consumerConnect and consumerDisconnect.
sp<IConsumerListener> mConsumerListener;
// mConsumerUsageBits contains flags that the consumer wants for
// GraphicBuffers.
uint32_t mConsumerUsageBits;
// mConnectedApi indicates the producer API that is currently connected
// to this BufferQueue. It defaults to NO_CONNECTED_API, and gets updated
// by the connect and disconnect methods.
int mConnectedApi;
// mConnectedProducerToken is used to set a binder death notification on
// the producer.
sp<IProducerListener> mConnectedProducerListener;
// mSlots is an array of buffer slots that must be mirrored on the producer
// side. This allows buffer ownership to be transferred between the producer
// and consumer without sending a GraphicBuffer over Binder. The entire
// array is initialized to NULL at construction time, and buffers are
// allocated for a slot when requestBuffer is called with that slot's index.
BufferQueueDefs::SlotsType mSlots;
// mQueue is a FIFO of queued buffers used in synchronous mode.
Fifo mQueue;
// mFreeSlots contains all of the slots which are FREE and do not currently
// have a buffer attached
std::set<int> mFreeSlots;
// mFreeBuffers contains all of the slots which are FREE and currently have
// a buffer attached
std::list<int> mFreeBuffers;
// mOverrideMaxBufferCount is the limit on the number of buffers that will
// be allocated at one time. This value is set by the producer by calling
// setBufferCount. The default is 0, which means that the producer doesn't
// care about the number of buffers in the pool. In that case,
// mDefaultMaxBufferCount is used as the limit.
int mOverrideMaxBufferCount;
// mDequeueCondition is a condition variable used for dequeueBuffer in
// synchronous mode.
mutable Condition mDequeueCondition;
// mUseAsyncBuffer indicates whether an extra buffer is used in async mode
// to prevent dequeueBuffer from blocking.
bool mUseAsyncBuffer;
// mDequeueBufferCannotBlock indicates whether dequeueBuffer is allowed to
// block. This flag is set during connect when both the producer and
// consumer are controlled by the application.
bool mDequeueBufferCannotBlock;
// mDefaultBufferFormat can be set so it will override the buffer format
// when it isn't specified in dequeueBuffer.
PixelFormat mDefaultBufferFormat;
// mDefaultWidth holds the default width of allocated buffers. It is used
// in dequeueBuffer if a width and height of 0 are specified.
uint32_t mDefaultWidth;
// mDefaultHeight holds the default height of allocated buffers. It is used
// in dequeueBuffer if a width and height of 0 are specified.
uint32_t mDefaultHeight;
// mDefaultBufferDataSpace holds the default dataSpace of queued buffers.
// It is used in queueBuffer if a dataspace of 0 (HAL_DATASPACE_UNKNOWN)
// is specified.
android_dataspace mDefaultBufferDataSpace;
// mDefaultMaxBufferCount is the default limit on the number of buffers that
// will be allocated at one time. This default limit is set by the consumer.
// The limit (as opposed to the default limit) may be overriden by the
// producer.
int mDefaultMaxBufferCount;
// mMaxAcquiredBufferCount is the number of buffers that the consumer may
// acquire at one time. It defaults to 1, and can be changed by the consumer
// via setMaxAcquiredBufferCount, but this may only be done while no
// producer is connected to the BufferQueue. This value is used to derive
// the value returned for the MIN_UNDEQUEUED_BUFFERS query to the producer.
int mMaxAcquiredBufferCount;
// mBufferHasBeenQueued is true once a buffer has been queued. It is reset
// when something causes all buffers to be freed (e.g., changing the buffer
// count).
bool mBufferHasBeenQueued;
// mFrameCounter is the free running counter, incremented on every
// successful queueBuffer call and buffer allocation.
uint64_t mFrameCounter;
// mTransformHint is used to optimize for screen rotations.
uint32_t mTransformHint;
// mSidebandStream is a handle to the sideband buffer stream, if any
sp<NativeHandle> mSidebandStream;
// mIsAllocating indicates whether a producer is currently trying to allocate buffers (which
// releases mMutex while doing the allocation proper). Producers should not modify any of the
// FREE slots while this is true. mIsAllocatingCondition is signaled when this value changes to
// false.
bool mIsAllocating;
// mIsAllocatingCondition is a condition variable used by producers to wait until mIsAllocating
// becomes false.
mutable Condition mIsAllocatingCondition;
// mAllowAllocation determines whether dequeueBuffer is allowed to allocate
// new buffers
bool mAllowAllocation;
// mBufferAge tracks the age of the contents of the most recently dequeued
// buffer as the number of frames that have elapsed since it was last queued
uint64_t mBufferAge;
// mGenerationNumber stores the current generation number of the attached
// producer. Any attempt to attach a buffer with a different generation
// number will fail.
uint32_t mGenerationNumber;
}; // class BufferQueueCore
struct BufferSlot {
BufferSlot()
: mEglDisplay(EGL_NO_DISPLAY),
mBufferState(BufferSlot::FREE),
mRequestBufferCalled(false),
mFrameNumber(0),
mEglFence(EGL_NO_SYNC_KHR),
mAcquireCalled(false),
mNeedsCleanupOnRelease(false),
mAttachedByConsumer(false) {
}
// mGraphicBuffer points to the buffer allocated for this slot or is NULL
// if no buffer has been allocated.
sp<GraphicBuffer> mGraphicBuffer;
// 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 = 0,
DEQUEUED = 1,
QUEUED = 2,
ACQUIRED = 3
};
static const char* bufferStateName(BufferState state);
// mBufferState is the current state of this buffer slot.
BufferState mBufferState;
// mRequestBufferCalled is used for validating that the producer did
// call requestBuffer() when told to do so. Technically this is not
// needed but useful for debugging and catching producer bugs.
bool mRequestBufferCalled;
// mFrameNumber is the number of the queued frame for this slot. This
// is used to dequeue buffers in LRU order (useful because buffers
// may be released before their release fence is signaled).
uint64_t mFrameNumber;
// mEglFence is the EGL sync object that must signal before the buffer
// associated with this buffer slot may be dequeued. It is initialized
// to EGL_NO_SYNC_KHR when the buffer is created and may be set to a
// new sync object in releaseBuffer. (This is deprecated in favor of
// mFence, below.)
EGLSyncKHR mEglFence;
// mFence is a fence which will signal when work initiated by the
// previous owner of the buffer is finished. When the buffer is FREE,
// the fence indicates when the consumer has finished reading
// from the buffer, or when the producer has finished writing if it
// called cancelBuffer after queueing some writes. When the buffer is
// QUEUED, it indicates when the producer has finished filling the
// buffer. When the buffer is DEQUEUED or ACQUIRED, the fence has been
// passed to the consumer or producer along with ownership of the
// buffer, and mFence is set to NO_FENCE.
sp<Fence> mFence;
// Indicates whether this buffer has been seen by a consumer yet
bool mAcquireCalled;
// Indicates whether this buffer needs to be cleaned up by the
// consumer. This is set when a buffer in ACQUIRED state is freed.
// It causes releaseBuffer to return STALE_BUFFER_SLOT.
bool mNeedsCleanupOnRelease;
// Indicates whether the buffer was attached on the consumer side.
// If so, it needs to set the BUFFER_NEEDS_REALLOCATION flag when dequeued
// to prevent the producer from using a stale cached buffer.
bool mAttachedByConsumer;
};
BufferQueue的生产步骤(生产者接口BufferQueueProducer):
BufferQueue消费步骤(消费者接口BufferQueueConsumer):
生产者在缓冲区填充完成后,调用queueBuffer方法将缓冲区加入队列,并且通知消费者。
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
.......
frameAvailableListener = mCore->mConsumerListener;
......
frameAvailableListener->onFrameAvailable(item);
......
}
那么问题又来了,mConsumerListener又是在哪里给设置的呢?
status_t BufferQueueConsumer::connect(
const sp<IConsumerListener>& consumerListener, bool controlledByApp) {
ATRACE_CALL();
if (consumerListener == NULL) {
BQ_LOGE("connect(C): consumerListener may not be NULL");
return BAD_VALUE;
}
BQ_LOGV("connect(C): controlledByApp=%s",
controlledByApp ? "true" : "false");
Mutex::Autolock lock(mCore->mMutex);
if (mCore->mIsAbandoned) {
BQ_LOGE("connect(C): BufferQueue has been abandoned");
return NO_INIT;
}
mCore->mConsumerListener = consumerListener;
mCore->mConsumerControlledByApp = controlledByApp;
return NO_ERROR;
}
该类是对图像缓冲区消费者接口的进一步封装,看看它的构造函数
ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
mAbandoned(false),
mConsumer(bufferQueue) {
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
// Note that we can't create an sp<...>(this) in a ctor that will not keep a
// reference once the ctor ends, as that would cause the refcount of 'this'
// dropping to 0 at the end of the ctor. Since all we need is a wp<...>
// that's what we create.
wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);
status_t err = mConsumer->consumerConnect(proxy, controlledByApp);
if (err != NO_ERROR) {
CB_LOGE("ConsumerBase: error connecting to BufferQueue: %s (%d)",
strerror(-err), err);
} else {
mConsumer->setConsumerName(mName);
}
}
// frameworks/native/include/gui/BufferQueueConsumer.h
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer,
bool controlledByApp) {
return connect(consumer, controlledByApp);
}
// frameworks/native/libs/gui/BufferQueue.cpp
BufferQueue::ProxyConsumerListener::ProxyConsumerListener(
const wp<ConsumerListener>& consumerListener):
mConsumerListener(consumerListener) {}
void BufferQueue::ProxyConsumerListener::onFrameAvailable(
const BufferItem& item) {
sp<ConsumerListener> listener(mConsumerListener.promote());
if (listener != NULL) {
listener->onFrameAvailable(item);
}
}
由以上分析,可以得出一个结论ConsumerBase类在其构造函数中会注册消费监听,且其回调函数为它的成员函数onFrameAvailable,那么在来看看ConsumerBase::onFrameAvailable函数
void ConsumerBase::onFrameAvailable(const BufferItem& item) {
CB_LOGV("onFrameAvailable");
sp<FrameAvailableListener> listener;
{ // scope for the lock
Mutex::Autolock lock(mMutex);
listener = mFrameAvailableListener.promote();
}
if (listener != NULL) {
CB_LOGV("actually calling onFrameAvailable");
listener->onFrameAvailable(item);
}
}
void ConsumerBase::setFrameAvailableListener(
const wp<FrameAvailableListener>& listener) {
CB_LOGV("setFrameAvailableListener");
Mutex::Autolock lock(mMutex);
mFrameAvailableListener = listener;
}