Android SurfaceFlinger服務(一) ----- BufferQueue分析

生產者和消費者模型是編程中運用比較廣泛的模型。在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;
}
  • outProducer outConsumer爲輸出參數,輸出對應的生產者接口,和消費者接口
  • allocator爲輸入參數,輸入一個圖像緩衝區內存分配接口,用於內存的申請
  • 首先通用allocator構造一個BufferQueueCore對象,然後在用該對象構造生產者對象(BufferQueueProducer)和消費者對象(BufferQueueConsumer)
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
  • 這個類是一個比較關鍵的類
  • 成員變量mSlots管理着隊列中各個緩衝區的狀態
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;
};
  • BufferState定義了緩衝區的五種狀態
  • 該結構體描述了每個緩衝區的狀態與屬性

BufferQueue的生產步驟(生產者接口BufferQueueProducer):

  1. dequeueBuffer
  2. requestBuffer
  3. queueBuffer

BufferQueue消費步驟(消費者接口BufferQueueConsumer):

  1. acquireBuffer
  2. releaseBuffer

生產者在緩衝區填充完成後,調用queueBuffer方法將緩衝區加入隊列,並且通知消費者。

status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
    .......
    
    frameAvailableListener = mCore->mConsumerListener;
    
    ......
    
    frameAvailableListener->onFrameAvailable(item);
    
    ......    
}
  • 獲取BufferQueueCore內的成員變量mConsumerListener(消費者監聽接口)
  • 回調接口方法onFrameAvailable

那麼問題又來了,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;
}
  • BufferQueueConsumer::connect函數中會傳入consumerListener接口指針,並設置給mCore->mConsumerListener
  • 最終決定在哪消費就那BufferQueueConsumer接口調用connect函數時傳入的接口對象類型

ConsumerBase消費者基類

該類是對圖像緩衝區消費者接口的進一步封裝,看看它的構造函數

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);       
    }  
}
  • 用該類的this指定構造一個消費者監聽代碼對象BufferQueue::ProxyConsumerListener
  • 調用消費者接口的consumerConnect函數連接服務端,並註冊消費監聽
// frameworks/native/include/gui/BufferQueueConsumer.h
virtual status_t consumerConnect(const sp<IConsumerListener>& consumer,
            bool controlledByApp) {
        return connect(consumer, controlledByApp);
    }
  • consumerConnect函數還是調用我們前面說的connect函數,那個數據到來回調就調用剛剛他建的BufferQueue::ProxyConsumerListener對象的onFrameAvailable函數
// 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);
    }
} 
  • ProxyConsumerListener構造函數中,將參數consumerListener賦值給mConsumerListener。(既前面說的ConsumerBase對象)
  • 在onFrameAvailable函數中調用mConsumerListener的onFrameAvailable函數

由以上分析,可以得出一個結論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;
}
  • ConsumerBase::onFrameAvailable函數最終調用成員變量mFrameAvailableListener的onFrameAvailable函數。
  • 其成員函數 ConsumerBase::setFrameAvailableListener給mFrameAvailableListener賦值
  • 所以,如果圖像緩衝區消費者類是繼承於ConsumerBase時,要看消費者數據回調最終跑在哪裏就要查看setFrameAvailableListener設置的監聽器
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章