BLASTBufferQueue 详解

知其所以然

建议配置这下面三篇文章一起阅读:
BBQ 机制介绍:https://www.jianshu.com/p/50a30fa6952e
BBQ 原理解读:https://www.jianshu.com/p/cdc60627df90
BBQ 运用场景:https://www.jianshu.com/p/384a5cd2e304

背景

在 Android S 之前,提起 BufferQueue,最经典的一张图就是下面这张:

BufferQueue

BLAST Buffer Queue 完全颠覆了 Android R 版本之前的实现,现在 BufferQueue 完全存在于 App 进程,dequeue,queue,acquire,release Buffer 的操作均由 App 进行,当 App 绘制完一帧后,只传 Buffer 给 SF 用于合成,上屏,SF 显示完成后回调告诉 App 进程当前显示完成,让 App 执行 release 操作。

在之前的 BufferQueue 中,Buffer 是通过 IGBP 接口传递的,几何变动(Geometry changes)是通过 Transaction 传递给 SurfaceFlinger 的;BLASTBufferQueue 中,Buffer 和几何变动都是通过 Transaction 传递给 SurfaceFlinger 的。

弊端:存在多种同步问题,BufferQueue 对应多个 SurfaceControl 时,他们之间的 Transaction 同步会存在问题

初始化

计数

现在在 SurfaceFlinger 里,显示各个 Layer Buffer 的个数从之前的 BufferQueue 变成了 BufferCountTracker

// Keeps track of pending buffers per layer handle in the transaction queue or current/drawing
// state before the buffers are latched. The layer owns the atomic counters and decrements the
// count in the main thread when dropping or latching a buffer.
//
// The binder threads increment the same counter when a new transaction containing a buffer is
// added to the transaction queue. The map is updated with the layer handle lifecycle updates.
// This is done to avoid lock contention with the main thread.
class BufferCountTracker {
public:
    void increment(BBinder* layerHandle) {
        std::lock_guard<std::mutex> lock(mLock);
        auto it = mCounterByLayerHandle.find(layerHandle);
        if (it != mCounterByLayerHandle.end()) {
            auto [name, pendingBuffers] = it->second;
            int32_t count = ++(*pendingBuffers);
            ATRACE_INT(name.c_str(), count);
        } else {
            ALOGW("Handle not found! %p", layerHandle);
        }
    }

    void add(BBinder* layerHandle, const std::string& name, std::atomic<int32_t>* counter) {
        std::lock_guard<std::mutex> lock(mLock);
        mCounterByLayerHandle[layerHandle] = std::make_pair(name, counter);
    }

    void remove(BBinder* layerHandle) {
        std::lock_guard<std::mutex> lock(mLock);
        mCounterByLayerHandle.erase(layerHandle);
    }

private:
    std::mutex mLock;
    std::unordered_map<BBinder*, std::pair<std::string, std::atomic<int32_t>*>>
            mCounterByLayerHandle GUARDED_BY(mLock);
};

这个 BufferCountTracker 是 SurfaceFlinger 的一个内部类,主要是用来记录跟踪每个 Layer 的 pending buffer 的数量(还没有被 SurfaceFlinger 消费的 Buffer)。

在 SurfaceFlinger 创建 Layer 的时候,会往 BufferCountTracker 追加一个实例和空的计数器:

status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
                                     uint32_t h, PixelFormat format, uint32_t flags,
                                     LayerMetadata metadata, sp<IBinder>* handle,
                                     sp<IGraphicBufferProducer>* gbp,
                                     const sp<IBinder>& parentHandle, int32_t* outLayerId,
                                     const sp<Layer>& parentLayer, uint32_t* outTransformHint) {
	......

    sp<Layer> layer;

    std::string uniqueName = getUniqueLayerName(name.string());

    switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
        case ISurfaceComposerClient::eFXSurfaceBufferQueue:
        case ISurfaceComposerClient::eFXSurfaceBufferState: {
            result = createBufferStateLayer(client, std::move(uniqueName), w, h, flags,
                                            std::move(metadata), handle, &layer);
            std::atomic<int32_t>* pendingBufferCounter = layer->getPendingBufferCounter();
            if (pendingBufferCounter) {
                std::string counterName = layer->getPendingBufferCounterName();
                mBufferCountTracker.add((*handle)->localBinder(), counterName,
                                        pendingBufferCounter);
            }

当 Layer 销毁的时候,会移除该计数器:

void SurfaceFlinger::onHandleDestroyed(sp<Layer>& layer) {
    Mutex::Autolock lock(mStateLock);
    // If a layer has a parent, we allow it to out-live it's handle
    // with the idea that the parent holds a reference and will eventually
    // be cleaned up. However no one cleans up the top-level so we do so
    // here.
    if (layer->isAtRoot()) {
        layer->setIsAtRoot(false);
        mCurrentState.layersSortedByZ.remove(layer);
    }
    markLayerPendingRemovalLocked(layer);

    auto it = mLayersByLocalBinderToken.begin();
    while (it != mLayersByLocalBinderToken.end()) {
        if (it->second == layer) {
            mBufferCountTracker.remove(it->first->localBinder());
            it = mLayersByLocalBinderToken.erase(it);
        } else {
            it++;
        }
    }

    layer.clear();
}

当 Layer 的 Buffer queue 上来以后,最终会走到 SurfaceFlinger::setTransactionState(),在这里会增加计数器:

status_t SurfaceFlinger::setTransactionState(
        const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
        const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
        const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
        bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
        const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
	......

    // Check for incoming buffer updates and increment the pending buffer count.
    state.traverseStatesWithBuffers([&](const layer_state_t& state) {
        mBufferCountTracker.increment(state.surface->localBinder());
    });

latchBuffer() 里减少计数:

sequenceDiagram
	SurfaceFlinger ->> BufferLayer: latchBuffer()
	BufferLayer ->> BufferStateLayer: updateActiveBuffer()
	BufferStateLayer ->> BufferStateLayer: decrementPendingBufferCount()

processNextBufferLocked() 在下面的这个 change 里改名为 acquireNextBufferLocked()

DO NOT MERGE: Move blast sync handling to BBQ

Add logic in BBQ so it can handle waiting the transaction callback vs a
sync transaction request. The following behavior will occur

1. If a nextTransaction (sync) was set, we will wait until the
transaction callback for that frame before continuing to acquire
new frames. Once the transaction callback for the sync transaction is
invoked, BBQ will flush the shadow queue. It will try to process
as many frames as it can that were queued up during the time BBQ was
blocked from processing.

2. If BBQ is waiting on a sync transaction callback and then another
sync transaction is requested afterwards, BBQ will allow it to acquire
a buffer instead of just adding to the shadow queue. It will acquire
the new frame in the new sync transaction and allow the caller that
requested the sync to apply it. At this point, it's up to the callers
to ensure they apply the two sync transactions in order to ensure
frames are applied in order.

3. Similar to 2, but if there are queue requests in between the two
sync requests that aren't trying to be synced. When the second sync
frame is getting acquired, BBQ will acquire and release any frames
that were requested in between. This is so we don't skip or
have to wait in the first sync transaction callback.

Test: BLASTBufferQueueTest
Bug: 200285149
Change-Id: I8da8de1a3fe2a44ca2199ff92cfd4b60c7f01183
(cherry picked from commit d7deef7278f934a1750738b600c11c1771ae7ac6)
chaviw@google.com (author)
racarr@google.com (committer)
Committed on 2021-11-04 1:53 AM

2d2150edc25cdd03bdb5b25ec1a4cc5fb7aa0f33

BLASTBufferQueue 调用 onFrameAvalable()

BLASTBufferQueue 在初始化的时候,会将自己设置为 BufferQueueCore 的 consumerListener:

// frameworks/native/libs/gui/BLASTBufferQueue.cpp
BLASTBufferQueue::BLASTBufferQueue(const std::string& name)
      : mSurfaceControl(nullptr),
        mSize(1, 1),
        mRequestedSize(mSize),
        mFormat(PIXEL_FORMAT_RGBA_8888),
        mNextTransaction(nullptr) {
    createBufferQueue(&mProducer, &mConsumer);
    // since the adapter is in the client process, set dequeue timeout
    // explicitly so that dequeueBuffer will block
    mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());

    // safe default, most producers are expected to override this
    mProducer->setMaxDequeuedBufferCount(2);
    mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
                                                      GraphicBuffer::USAGE_HW_COMPOSER |
                                                              GraphicBuffer::USAGE_HW_TEXTURE,
                                                      1, false, this);
    mBufferItemConsumer->setFrameAvailableListener(this);

// frameworks/native/libs/gui/BufferItemConsumer.cpp
void BufferItemConsumer::setBufferFreedListener(
        const wp<BufferFreedListener>& listener) {
    Mutex::Autolock _l(mMutex);
    mBufferFreedListener = listener;
}

BufferQueueProducer::queueBuffer() 中,将 BufferQueue.mSlots 的 Buffer 封装为 BufferItem 以后,会插入到 BufferQueueCore.mQueue 里,会调用 frameAvailableListener->onFrameAvailable() 去消耗该 Buffer,而这个 frameAvailableListener 来自 BufferQueueCoremConsumerListener

status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
		......
        if (mCore->mQueue.empty()) {
            // When the queue is empty, we can ignore mDequeueBufferCannotBlock
            // and simply queue this buffer
            mCore->mQueue.push_back(item);
            frameAvailableListener = mCore->mConsumerListener;
		......
        if (frameAvailableListener != nullptr) {
            frameAvailableListener->onFrameAvailable(item);

BufferQueueCore.mConsumerListener 是在哪里赋值的呢?看看定义:

// frameworks/native/libs/gui/include/gui/BufferQueueCore.h
class BufferQueueCore : public virtual RefBase {
	......
    // 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;
// frameworks/native/libs/gui/BufferQueueConsumer.cpp
status_t BufferQueueConsumer::connect(
        const sp<IConsumerListener>& consumerListener, bool controlledByApp) {
	......
    mCore->mConsumerListener = consumerListener;

而这个 BufferQueueConsumer::connect() 的调用流程是:

sequenceDiagram
	autoNumber
	BLASTBufferQueue ->> +BLASTBufferQueue: BLASTBufferQueue()
	BLASTBufferQueue ->> BufferItemConsumer: "new BufferItemConsumer()"
	BufferItemConsumer ->> ConsumerBase: ConsumerBase()
	ConsumerBase ->> BufferQueueConsumer: consumerConnect()
	BufferQueueConsumer ->> BufferQueueConsumer: connect()
	BLASTBufferQueue ->> -BLASTBufferQueue: BLASTBufferQueue()

而上面的 consumerListener 实际上是 ProxyConsumerListener,在下面这里构造的:

ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
        mAbandoned(false),
        mConsumer(bufferQueue),
        mPrevFinalReleaseFence(Fence::NO_FENCE) {
    // 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);

	// 这个 consumerConnect 最终就调用到 BufferQueueConsumer::connect()
    status_t err = mConsumer->consumerConnect(proxy, controlledByApp);

上面的 listenerConsumerBase,被作为参数传递到 BufferQueue::ProxyConsumerListener 的构造函数里:

// frameworks/native/libs/gui/BufferQueue.cpp
BufferQueue::ProxyConsumerListener::ProxyConsumerListener(
        const wp<ConsumerListener>& consumerListener):
        mConsumerListener(consumerListener) {}

所以,当 queueBuffer() 调用 onFrameAvailable() 的时候,实际上调用的就是 BufferQueue::ProxyConsumerListener::onFrameAvailable()

void BufferQueue::ProxyConsumerListener::onFrameAvailable(
        const BufferItem& item) {
    sp<ConsumerListener> listener(mConsumerListener.promote());
    if (listener != nullptr) {
        listener->onFrameAvailable(item);
    }
}

这个 listener 就是 BufferQueue::ProxyConsumerListener 构造函数传进来的 ConsumerBase,所以转而调到 ConsumerBase::onFrameAvailable()

void ConsumerBase::onFrameAvailable(const BufferItem& item) {
    CB_LOGV("onFrameAvailable");

    sp<FrameAvailableListener> listener;
    { // scope for the lock
        Mutex::Autolock lock(mFrameAvailableMutex);
        listener = mFrameAvailableListener.promote();
    }

    if (listener != nullptr) {
        CB_LOGV("actually calling onFrameAvailable");
        listener->onFrameAvailable(item);
    }
}

现在就差最后一步了,这个 mFrameAvailableListener 是何方神圣,在哪赋值的?

// mFrameAvailableListener is the listener object that will be called when a
// new frame becomes available. If it is not NULL it will be called from
// queueBuffer. The listener object is protected by mFrameAvailableMutex
// (not mMutex).
Mutex mFrameAvailableMutex;
wp<FrameAvailableListener> mFrameAvailableListener;

他的赋值是通过 ConsumerBase::setFrameAvailableListener()

void ConsumerBase::setFrameAvailableListener(
        const wp<FrameAvailableListener>& listener) {
    CB_LOGV("setFrameAvailableListener");
    Mutex::Autolock lock(mFrameAvailableMutex);
    mFrameAvailableListener = listener;
}

这个 ConsumerBase::setFrameAvailableListener() 有没有看着很熟悉?让我们回到 [[#BLASTBufferQueue 调用 onFrameAvalable]] 开头 BLASTBufferQueue() 的构造函数,在创建完 BufferItemConsumer 以后,随即就调用了 mBufferItemConsumer->setFrameAvailableListener(this);,也就是说,把 BLASTBufferQueue 给设置为 ConsumerBasemFrameAvailableListener

所以,我们终于弄清楚了,queueBuffer()onFrameAvailable() 最终是调用到 BLASTBufferQueue::onFrameAvailable(),也就是说,是在 BLASTBufferQueue 里完成对 BufferItem 的 acquire 操作

而这个 acquireBuffer() 的调用流程是:BufferItemConsumer::acquireBuffer()ConsumerBase::acquireBufferLocked()BufferQueueConsumer::acquireBuffer()

流程

sequenceDiagram
	Surface ->> +Surface: queueBuffer()
	Surface ->> BufferQueueProducer: queueBuffer()
	BufferQueueProducer ->> BLASTBufferQueue: onFrameAvailable ()
	BLASTBufferQueue ->> BLASTBufferQueue: processNextBufferLocked()
	BLASTBufferQueue ->> SurfaceComposerClient.Transaction : setApplyToken().apply()
	SurfaceComposerClient.Transaction ->> SurfaceFlinger: setTransactionState()
	SurfaceFlinger ->> SurfaceFlinger: setTransactionFlags(eTransactionFlushNeeded)
	Surface ->> -Surface: queueBuffer()

Author: simowce

Permalink: https://blog.simowce.com/all-about-blastbbq/

知识共享许可协议
本作品采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。