BLASTBufferQueue01-BLASTBufferQueue架构概述

1,305 阅读27分钟

本文基于Android 15梳理。

前言

Android图形架构中,对所有图形数据流的处理,是以"生产者-消费者"模式实现,并通过一个图形数据缓冲队列,实现图形数据的传递。

  • 图形流生产者:指生成Graphic Buffer内容的组件,如OpenGL ES、Canvas、CameraPreview、多媒体视频解码器;
  • 图形流消费者:指对生产者提供的Graphic Buffer进行使用的组件,一般即指surfaceflinger,有些场景下,也可以是其他应用;
  • 图形数据缓冲区队列:作为Android图形组件的粘合剂,用来在生产者-消费者之间进行Graphic Buffer的传递,Graphic Buffer的移动必须在该队列中进行。

这个图形数据缓冲区队列便是本篇中的主角前身——BufferQueue。

在BufferQueue机制中,定义了BufferQueue类作为Graphic Buffer队列的抽象和封装,同时定义了三个类:

  • BufferQueueCore:Buffer队列实现类,内部定义了多个Buffer数组和列表,负责Graphic Buffer的传递、状态管理;
  • IGraphicBufferProducer:IPC接口,用于Buffer Queue和图形流生产者连接;
  • IGraphicBufferConsumer:IPC接口,用于Buffer Queue和图形流消费者连接。
流程图_2.jpg

在Android P及以前版本中,图层渲染合成部分对Buffer Queue的机制使用方式是集成在了surfaceflinger进程Layer中,在初始化BufferQueueLayer时创建BufferQueue,并将IGraphicBufferProducer接口对象传递给了Surface,将IGraphicBufferConsumer接口对象传递给了BufferLayerConsumer。

android.googlesource.com/platform/fr…

这种实现方式缺点显而易见:

  1. sufaceflinger进程变得臃肿:Layer中包含了太多获取Buffer过程的操作,而不是直接使用Buffer;
  2. Layer中接收数据的方式不一:Graphic Buffer通过Buffer Queue队列获得,其他几何属性通过Transaction提交;
  3. 几何属性和Buffer同步可能存在延时:由于传递方式的不同,某些场景下Graphic Buffer和几何属性需要同时更新时,可能存在无法一次性处理或延时的问题;
  4. 多个进程间无法做到帧同步:在一些需要两个进程同步执行动画时,无法做到同一帧提交不同进程的Buffer。
流程图_1.jpg

因此,在此后的版本中,AOSP开始尝试逐步将BufferQueue从Layer中剥离出去,并在Android S上,所有应用端图层绘制合成彻底废弃Buffer Queue机制,采用了新的机制——BLAST Buffer Queue机制。

BLAST Buffer Queue机制完全填补了Buffer Queue机制的缺点:

  1. BufferQueue的创建放在了业务进程,surfaceflinger中只接受Buffer,同时所有图层属性的提交都通过Transaction进行,大大减轻了surfaceflinger的负担;
  2. 支持Transaction的合并操作,多个Transaction可以合并为一个提交给surfaceflinger进程,从而可以保证Buffer的提交和几何属性的提交都可在同一帧完成;
  3. 支持多进程间的协同能力(BLAST SyncEngine),可以对Transaction跨进程传递,携带不同进程的Buffer一起提交给surfaceflinger,实现进程间的帧同步。

"BLAST"的正式含义AOSP中没有具体给出,个人认为应该是"Buffer Layer All Sync by Transaction",即Layer属性全部由Transaction进行提交和更新。

流程图.jpg

BBQ架构中Graphic Buffer流表示如下图所示:

本篇文章中,对BLAST Buffer Queue架构的实现原理和基本概念进行总结梳理。

一、BBQ架构

BLAST Buffer Queue机制相比Buffer Queue机制,主要在行为和能力上发生了变化:

  • BufferQueue在应用进程创建;
  • Graphic Buffer的传递方式发生变化;
  • 增加了跨进程同步Buffer的能力。

其核心组件和本质并没有太大变化,主要还是由三部分组成:

  • 生产者接口:当Surface需要更新显示内容时,生产者接口会为Surface从Buffer队列中提供来自Gralloc分配的Graphic Buffer,Surface获得Graphic Buffer后便可给到GPU/CPU进行绘制操作,并在完成绘制后继续通过生产者接口将Graphic Buffer放入Buffer队列;
  • 消费者接口:当Surface完成绘制操作并且Graphic Buffer重新入队后,会通过消费者接口将Graphic Buffer传递给surfaceflinger进程,对Graphic Buffer进行实际的消费,完成图层的合成与显示。
  • BBQ核心组建:统一维护一个实际的Graphic Buffer队列,管理Graphic Buffer在生产者和消费者间的移动,同时也负责整个Buffer队列的参数配置和更新。

类图关系如下:

1.1、生产者接口

1.1.1、IGraphicBufferProducer

IGraphicBufferProducer接口定义了BBQ架构中GraphicBuffer生产者类的IPC接口和通用方法,是BBQ中生产者类的最顶层接口。

1.1.2、BufferQueueProducer

BufferQueueProducer类实现了IGraphicBufferProducer接口,除了重写IGraphicBufferProducer接口定义的方法,还定义了和Consumer、BufferQueueCore等BBQ架构中其他组件产生依赖和交互的方法或对象:

// frameworks/native/include/gui/BufferQueueProducer.h

class BufferQueueProducer : public BnGraphicBufferProducer,
                            private IBinder::DeathRecipient {
    ......
    
    // 当Buffer队列中没有可用Buffer时,通过该方法进入等待状态
    status_t waitForFreeSlotThenRelock(FreeSlotCaller caller, std::unique_lock<std::mutex>& lock,
            int* found) const;
    // BufferQueueCore对象
    sp<BufferQueueCore> mCore;
    
    // Buffer队列,Buffer槽(BufferSlot数组)
    BufferQueueDefs::SlotsType& mSlots;
    
};

1.1.3、BBQBufferQueueProducer

BBQBufferQueueProducer类继承于BufferQueueProducer,它主要是为了实现异步执行IProducerListener接口的回调方法而定义的包裹类。它定义在BLASTBufferQueue类中:

// frameworks/native/libs/gui/BLASTBufferQueue.cpp

class BBQBufferQueueProducer : public BufferQueueProducer {
public:
    BBQBufferQueueProducer(const sp<BufferQueueCore>& core, wp<BLASTBufferQueue> bbq)
          : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/),
            mBLASTBufferQueue(std::move(bbq)) {}

    status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp,
                     QueueBufferOutput* output) override {
        // 如果listener为空,则将listener传递给BufferQueueProducer
        if (!listener) {
            return BufferQueueProducer::connect(listener, api, producerControlledByApp, output);
        }
        // 如果listener不为空,则会将listener包裹成AsyncProducerListener后传递给BufferQueueProducer
        return BufferQueueProducer::connect(new AsyncProducerListener(listener), api,
                                            producerControlledByApp, output);
    }
    
    ......
};

Surface创建后,会通过IGraphicBufferProducer->connect()方法将Surface和BufferQueueProducer进行连接。

1.1.4、IProducerListener

IProducerListener接口是BBQ向生产者组件通知Buffer事件的接口,主要有两个作用:

  1. BBQ通知BufferQueueProducer事件,BufferQueueProducer中可将事件通过IProducerListener通知给Client端;
  2. BufferQueueProducer中监听Client端进程是否死亡。

它也是通过IGraphicBufferProducer->connect()方法由Buffer的使用方向BBQ中完成注册。

在Surface中,会在跟BufferQueueProducer连接时,传入一个StubProducerListener对象:

// frameworks/native/libs/gui/Surface.cpp

int Surface::connect(int api) {
    static sp<IProducerListener> listener = new StubProducerListener();
    return connect(api, listener);
}

StubProducerListener类是一个空的实现类,其作用是Server端在BufferQueueProducer中对Client端进程的监听。

1.2、消费者接口

1.2.1、IGraphicBufferConsumer

IGraphicBufferConsumer接口对应于IGraphicBufferProducer接口,定义了BBQ架构中GraphicBuffer生产者类的IPC接口和通用方法。

IGraphicBufferProducer通过queueBuffer将GraphicBuffer放入Buffer队列后,IGraphicBufferConsumer便可通过acquireBuffer操作从Buffer队列中获取GraphicBuffer的所有权,并逐步将GraphicBuffer传递给surfaceflinger进行合成与显示。

1.2.2、BufferQueueConsumer

BufferQueueConsumer类实现了IGraphicBufferConsumer接口,是BBQ中读取GraphicBuffer的实际Consumer对象,内部持有一个BufferQueueCore对象,可以用来对BBQ进行配置:

// frameworks/native/libs/gui/include/gui/BufferQueueConsumer.h

class BufferQueueConsumer : public BnGraphicBufferConsumer {
......
private:
    sp<BufferQueueCore> mCore;

    // This references mCore->mSlots. Lock mCore->mMutex while accessing.
    BufferQueueDefs::SlotsType& mSlots;

    // This is a cached copy of the name stored in the BufferQueueCore.
    // It's updated during setConsumerName.
    String8 mConsumerName;

}; // class BufferQueueConsumer

1.2.3、IConsumerListener

IConsumerListener是用于BBQ向消费者组件通知Buffer操作事件的接口。比如:当BufferQueueProducer执行完queueBuffer()后,便通过IConsumerListener::onFrameAvailable()通知消费者组件从Buffer队列中获取可用Buffer。

1.2.4、ConsumerBase

ConsumerBase实现了IConsumerListener接口,是消费者组件中处理通用任务的基类。

BBQ不会和BufferQueueConsumer直接进行交互,而是通过ConsumerBase向BufferQueueConsumer发起调用,ConsumerBase在初始化时会传入BufferQueueConsumer对象:

// frameworks/native/libs/gui/ConsumerBase.cpp

ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
        mAbandoned(false),
        mConsumer(bufferQueue),
        mPrevFinalReleaseFence(Fence::NO_FENCE) {
    wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
    sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);
    // BBQ和mConsumer进行连接
    status_t err = mConsumer->consumerConnect(proxy, controlledByApp);
    ......
}

使用ConsumerBase时,一般会定义一个子类跟BufferQueueConsumer类交互,如在BBQ中定义了BufferItemConsumer类,surfaceflinger中定义了FrameBufferSurface类用于Client合成过程中获取GraphicBuffer。

1.2.5、BufferItemConsumer

BufferItemConsumer类是ConsumerBase的子类,在BBQ架构中作为BBQ和BufferQueueConsumer进行交互的中间层。

// frameworks/base/libs/hostgraphics/gui/BufferItemConsumer.h

class BufferItemConsumer : public ConsumerBase {
public:
    BufferItemConsumer(
        const sp<IGraphicBufferConsumer>& consumer,
        uint64_t consumerUsage,
        int bufferCount,
        bool controlledByApp) : mConsumer(consumer) {
    }
    ......
};

1.2.6、BLASTBufferItemConsumer

BLASTBufferItemConsumer是BufferItemConsumer的子类,主要作用是增加了一些帧数据的统计的方法和对象。

1.3、BBQ核心

1.3.1、BLASTBufferQueue

BLASTBufferQueue是BBQ架构的抽象,也是生产者和消费者组组件的创建者。当初始化一个BLASTBufferQueue对象后,便意味着创建了一个Buffer队列、BufferQueueProducer对象和BufferQueueConsumer对象。

应用进程中,在ViewRootImpl中创建BLASTBufferQueue对象,并返回Surface对象:

// frameworks/base/core/java/android/view/ViewRootImpl.java

void updateBlastSurfaceIfNeeded() {
    ......
    // 创建BLASTBufferQueue对象
    mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
            mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);

    // 从mBlastBufferQueue中获取Surface对象
    Surface blastSurface = mBlastBufferQueue.createSurface();
    // Only call transferFrom if the surface has changed to prevent inc the generation ID and
    // causing EGL resources to be recreated.
    mSurface.transferFrom(blastSurface);
    ......
}

1.3.2、BufferQueueCore

BufferQueueCore是Buffer队列真正的实现之处,它负责Graphic Buffer的所有权转移、Buffer队列状态管理、以及队列参数配置。内部定义了多个Buffer索引列表和一个BufferSlot对象数组用来存放GraphicBuffer的索引以及GraphicBuffer对象。

BufferQueueCore对象创建完成后,会作为参数进行BufferQueueProducer和BufferQueueConsumer的创建,

BufferQueueProducer和BufferQueueConsumer中都可以通过BufferQueueCore对象获取Graphic Buffer,以及对Buffer队列配置进行更新。

二、BBQ内部队列结构

1.1、Graphic Buffer的状态周期

BBQ中Graphic Buffer根据其所有权持有关系和流转阶段,共定义了4种状态,状态流转周期如下图所示:

流程图_3.jpg
  1. BBQ在创建时,会预先向Gralloc申请一定数量的Buffer,此时Buffer状态为FREE状态;
  2. 当生产者需要Buffer时,会通过IGBP接口向Buffer Queue请求空闲Buffer,并指定其宽、高、pixel格式、flag等属性。这步通过dequeueBuffer()方法进行,操作成功后Buffer的所有权将给到生产者,此时Buffer状态变为DEQUEUED状态;
  3. 申请到空闲Buffer后,生产者端会对Buffer进行绘制、填充,绘制完毕后重新放入队列中,并通知消费者进行消费。这步通过queueBuffer()方法进行,操作成功后Buffer的所有权回到Buffer Queue中,此时Buffer状态变为QUEUED状态;
  4. 消费者端收到消费回调后,会通过IGBC接口向Buffer Queue中获取QUEUED状态的Buffer,并将Buffer发送给surfaceflinger。这一步通过acquireBuffer()方法进行,操作成功后Buffer的所有权将给到消费者,此时Buffer状态变为ACQUIRED状态;
  5. surfaceflinger中获得新的Buffer后,不再需要上一帧Buffer,会回调给Buffer Queue中,并通过IGBC接口将Buffer重新放入队列中。这步通过releaseBuffer()方法进行,操作成功后Buffer所有权回到Buffer Queue中,Buffer状态重新变为FREE状态。

在应用进程中,除releaseBuffer由binder线程释放外,其他三个方法都执行在RenderThread中执行。

1.2、Buffer队列数据结构

在整个状态变化和Buffer所有权的转移过程中,不会发生数据的移动和拷贝,这跟系统内架构存实现原理有关。此外由于移动大量Graphic Buffer数据很低效,所以在跟surfaceflinger进程的传递过程中,采用Graphic Buffer句柄的方式进行传递。

BBQ中将Graphic Buffer放在Buffer槽(BufferSlot)中,在Producer和Consumer的移动过程中,只传递BufferSlot的索引。

在BufferQueueCore中,定义了多个列表来实现Buffer在Producer和Consumer间的移动,图示描述如下:

流程图_4.jpg

这里结合代码,对BufferSlotBufferItem进行详细了解,它们贯穿整个BBQ流程中。

1.2.1、BufferSlot

BufferSlot表示一个"Buffer槽",是Buffer在BBQ内部Producer和Consumer之间传递的基本单位,它封装了GraphicBuffer、BufferState、Fence、EGLDisplay等内容,管理GraphicBuffer的状态。

// frameworks/native/libs/gui/include/gui/BufferSlot.h

struct BufferSlot {
    .......
    // GraphicBuffer对象,初始化时为nullptr,allocateBuffer过程中会对它赋值
    sp<GraphicBuffer> mGraphicBuffer;

    // EGLDisplay对象,用于创建EGLSyncKHR
    EGLDisplay mEglDisplay;

    // BufferState用于描述该BufferSlot状态,如FREE、DEQUEUED、QUEUED、ACQUIRED、SHARED状态都由它来描述
    BufferState mBufferState;

    // 用于标记producer是否对该BufferSlot进行过requestBuffer()操作
    bool mRequestBufferCalled;

    // 帧计数器,每次queueBuffer时会递增+1,可以理解为每queueBuffer成功一次,该值+1
    uint64_t mFrameNumber;

    // EGLSyncKHR用于同步EGL和OpenGL/Vulkan之间的操作,可以确保进行渲染图形或交换缓冲区
    // 时,所有图形命令已经完成
    EGLSyncKHR mEglFence;

    // Fence对象,Fence主要用于处理GPU和CPU之间的Buffer同步问题
    sp<Fence> mFence;

    // 用于描述该BufferSlot是否已经被consumer进行过acquireBuffer操作
    bool mAcquireCalled;

    // 用于描述该BufferSlot的mGraphicBuffer是否进行重新分配
    // 该值为true时说明发生过reallocate操作,会在dequeuebuffer时,设置BUFFER_NEEDS_REALLOCATION标记,
    // 通知producer防止使用过时的GraphicBuffer。
    // 重新申请Buffer时,BufferQueueCore::clearBufferSlotLocked()中将该值设置为true 
    // 添加BUFFER_NEEDS_REALLOCATION后,在dequeueBuffer/attachBuffer时重置为false
    bool mNeedsReallocation;
};

BBQ中定义了一个BufferSlot数组,这个数组便是实际的Buffer Queue:

// frameworks/native/include/gui/BufferQueueDefs.h

    namespace BufferQueueDefs {
        // SlotsType表示一个包含NUM_BUFFER_SLOTS个BufferSlot 元素的数组
        typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
    } // namespace BufferQueueDefs

NUM_BUFFER_SLOTS值为64,说明一个BBQ队列中最多只能申请64个Graphic Buffer。

流程图_5.jpg

1.2.2、BufferState

  1. BufferState用于记录BufferSlot的状态,通过四个属性描述BufferSlot的五种状态:
// frameworks/native/libs/gui/include/gui/BufferSlot.h
struct BufferState {

    // 当前BufferSlot处于DEQUEUED状态的个数
    uint32_t mDequeueCount;
    // 当前BufferSlot处于QUEUED状态的个数
    uint32_t mQueueCount;
    // 当前BufferSlot处于ACQUIRED状态的个数
    uint32_t mAcquireCount;
    // 该BufferSlot是否为Share Buffer模式
    bool mShared;
    ....
};

其状态跟属性对应关系如下表:

mSharedmDequeueCountmQueueCountmAcquireCount含义
FREEFALSE000Buffer处于空闲状态,可以被Producer进行dequeue操作
DEQUEUEDFALSE100Buffer处于"出队"状态,已经被Producer进行了dequeue操作,但还没有进行queue操作
QUEUEDFALSE010Buffer处于"入队"状态,已经被Producer填充且重新入对,等待Consumer消费
ACQUIREDFALSE001Buffer已经被Consumer获取,用于对buffer内容进行合成,完成并释放后,重新恢复FREE状态
SHAREDTRUEanyanyanyBuffer以共享方式使用,可以同时进行dequeue、queue、acquired等操作

之所以以引用计数的方式标记状态,是因为在Shared Buffer mode状态时,可以同时有多个状态。

1.2.3、BufferItem

BufferItem是Buffer队列mCore->mQueue的元素,它是真正入队的数据,它封装了所有来自客户端输入的图形数据,传递给surfaceflinger的数据基本也全都是BufferItem中的属性。

// frameworks/native/libs/gui/include/gui/BufferItem.h

class BufferItem : public Flattenable<BufferItem> {
    ......
    // 引用自BufferSlot中的mGraphicBuffer对象
    sp<GraphicBuffer> mGraphicBuffer;

    // fence对象,用于CPU/GPU间GB状态的同步
    sp<Fence> mFence;

    // Fence的包裹类,可记录变为有信号状态的时间戳
    std::shared_ptr<FenceTime> mFenceTime{FenceTime::NO_FENCE};

    // 当前GraphicBuffer对象的裁剪区域
    Rect mCrop;

    // 当前GraphicBuffer的方向标记,值参考window.h中的NATIVE_WINDOW_TRANSFORM_*
    uint32_t mTransform;

    // 当前GraphicBuffer的缩放模式,值参考window.h中的NATIVE_WINDOW_SCALING_*
    uint32_t mScalingMode;

    // queueBuffer时刻的时间戳
    int64_t mTimestamp;

    // 表示mTimestamp是否为自动生成
    bool mIsAutoTimestamp;

    // 当前GraphicBuffer的dataspace
    android_dataspace mDataSpace;

    // 当前GraphicBuffer的HDR元数据
    HdrMetadata mHdrMetadata;

    // 当前GraphicBuffer的帧数,在queueBuffer时标记
    uint64_t mFrameNumber;

    // 当前BufferItem所使用GraphicBuffer的slot索引
    int mSlot;

    // 是否可丢弃,用于非阻塞模式的dequeueBuffer操作时
    bool mIsDroppable;

    // 当前Buffer已经执行acquiredBuffer操作
    bool mAcquireCalled;

    // 转换方向标记,表示是否需要用屏幕转置矩阵进行变换
    bool mTransformToDisplayInverse;

    // Surface内容发生变化的区域
    Region mSurfaceDamage;

    // 共享模式时,Consumer是否可以在不用等待Buffer可用时就可以获取下一帧
    bool mAutoRefresh;

    // 表示该Buffer是否被Producer执行过queuedBuffer操作
    bool mQueuedBuffer;

    // 表示是否已经是过期的Buffer
    bool mIsStale;

    // Producer API,表示Buffer的来源
    int mApi;
};

三、BBQ使用流程

BBQ的使用过程可分解为四步:

  1. 创建BBQ;
  2. Surface和BBQ连接;
  3. 生产和消费Graphic Buffer;
  4. 销毁BBQ。

3.1、创建BBQ

Java层可通过BLASTBufferQueue类初始化BBQ。在应用进程中,BBQ的创建是在ViewRootImpl中进行:

// frameworks/base/core/java/android/view/ViewRootImpl.java

void updateBlastSurfaceIfNeeded() {
    ......
    // 创建BBQ对象
    mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
            mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
    // 从BBQ获取Surface对象
    Surface blastSurface = mBlastBufferQueue.createSurface();
    
}

system_server中也可以直接创建BBQ。

创建过程中,会通过JNI层进入Native层进行BLASTBufferQueue对象及其组件的初始化操作。

Native层创建BBQ通过SurfaceControl::getSurface()方法创建:

// frameworks/native/libs/gui/SurfaceControl.cpp

sp<Surface> SurfaceControl::getSurface()
{
    Mutex::Autolock _l(mLock);
    if (mSurfaceData == nullptr) {
        return generateSurfaceLocked();
    }
    return mSurfaceData;
}

sp<Surface> SurfaceControl::generateSurfaceLocked()
{
    uint32_t ignore;
    auto flags = mCreateFlags & (ISurfaceComposerClient::eCursorWindow |
                                 ISurfaceComposerClient::eOpaque);
    // 创建SurfaceControl对象
    mBbqChild = mClient->createSurface(String8("bbq-wrapper"), 0, 0, mFormat,
                                       flags, mHandle, {}, &ignore);
    // 创建BBQ对象
    mBbq = sp<BLASTBufferQueue>::make("bbq-adapter", mBbqChild, mWidth, mHeight, mFormat);

    // This surface is always consumed by SurfaceFlinger, so the
    // producerControlledByApp value doesn't matter; using false.
    mSurfaceData = mBbq->getSurface(true);

    return mSurfaceData;
}

最终会创建一个"bbq-adapter"命名的BBQ。

下面从BBQ构造方法开始,看下BBQ中各个组件的初始化流程。

// frameworks/native/libs/gui/BLASTBufferQueue.cpp

BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame)
      : mSurfaceControl(nullptr),        // mSurfaceControl用于和sf中的Layer关联
        // Surface宽高,也是申请GraphicBuffer时的宽高
        mSize(1, 1),
        mRequestedSize(mSize),
        mFormat(PIXEL_FORMAT_RGBA_8888),
        // 用于跨进程同步帧,用于监听sf中Transaction的commit操作是否执行
        mTransactionReadyCallback(nullptr),  
        // 用于跨进程同步帧,mSyncTransaction不为空时,会将Transaction传递给VRI进行apply
        mSyncTransaction(nullptr),           
        mUpdateDestinationFrame(updateDestinationFrame) {
    // 创建BufferQueue队列
    createBufferQueue(&mProducer, &mConsumer);
    // 设置dequeueBuffer的超时时间
    mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());

    // 设置同时处于DEQUEUED状态buffer的最大个数,默认为2
    mProducer->setMaxDequeuedBufferCount(2);
    // 创建BufferItemConsumer对象
    mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
                                                      GraphicBuffer::USAGE_HW_COMPOSER |
                                                              GraphicBuffer::USAGE_HW_TEXTURE,
                                                      1, false, this);
    static std::atomic<uint32_t> nextId = 0;
    mProducerId = nextId++;
    // 设置Consumer name
    mName = name + "#" + std::to_string(mProducerId);
    auto consumerName = mName + "(BLAST Consumer)" + std::to_string(mProducerId);
    // "QueuedBuffer - "用于trace分析
    mQueuedBufferTrace = "QueuedBuffer - " + mName + "BLAST#" + std::to_string(mProducerId);
    mBufferItemConsumer->setName(String8(consumerName.c_str()));
    // 向Consumer设置FrameAvailable回调,当Producer端完成queueBuffer时,通过该回调通知Consumer
    mBufferItemConsumer->setFrameAvailableListener(this);
    // 从sf进程获取acquirebuffer的最大个数
    ComposerServiceAIDL::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
    // 设置设置同时处于ACQUIRED状态buffer的最大个数
    mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
    mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
    mNumAcquired = 0;
    mNumFrameAvailable = 0;
    // 设置TransactionCompletedListener监听
    TransactionCompletedListener::getInstance()->addQueueStallListener(
            [&](const std::string& reason) {
                std::function<void(const std::string&)> callbackCopy;
                {
                    std::unique_lock _lock{mMutex};
                    callbackCopy = mTransactionHangCallback;
                }
                if (callbackCopy) callbackCopy(reason);
            },
            this);
}

在BBQ构造方法内部,就会对BufferQueueCore、BufferQueueProducer和BufferQueueConsumer全部完成创建,并对BBQ中各个状态的最大Buffer数完成设置。

createBufferQueue()方法中,会完成IGraphicBufferQueueProducer、IGraphicBufferQueueConsumer相关对象的初始化:

// frameworks/native/libs/gui/BLASTBufferQueue.cpp

void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
                                         sp<IGraphicBufferConsumer>* outConsumer) {
    // 创建BufferQueueCore对象
    sp<BufferQueueCore> core(new BufferQueueCore());
    // 创建BBQBufferQueueProducer对象
    sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core, this));
    // 创建BufferQueueConsumer对象
    sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core));
    consumer->setAllowExtraAcquire(true);

    *outProducer = producer;
    *outConsumer = consumer;
}

3.1.1、创建BufferQueueCore对象

BufferQueueCore是Buffer队列实现之处,同时定义了接口允许Producer对象和Consumer对象进行配置参数的调整,构造方法如下:

// frameworks/native/libs/gui/BufferQueueCore.cpp

BufferQueueCore::BufferQueueCore()
      : mMutex(),
        // Buffer队列是否已经废弃,Consumer断开连接后将变为废弃状态
        mIsAbandoned(false),         
        // 表示与Buffer队列连接的Consumer是否可以由应用自己控制,一般都为false     
        mConsumerControlledByApp(false),  
        // Consumer名称
        mConsumerName(getUniqueName()), 
        // IConsumerListener监听对象,在Consumer连接时由Consumer对象设置
        mConsumerListener(),
        // Consumer指定的GraphicBuffer的使用标记
        mConsumerUsageBits(0),
        // Consumer是否开始处理protected buffer
        mConsumerIsProtected(false),
        // 表示Producer API,在Producer连接时设置,默认NO_CONNECTED_API
        mConnectedApi(NO_CONNECTED_API),
        // 用于Cliend端进程死亡监听,也是一个IProducerListener对象
        mLinkedToDeath(),
        // IProducerListener对象,用于通知Client端Buffer相关事件
        mConnectedProducerListener(),
        // 是否允许回调IProducerListener::onBufferReleased()方法
        mBufferReleasedCbEnabled(false),
        // BufferSlot对象数组,就是"Buffer Queue",在Producer和Consumer之间传递Buffer所有权的基本单位
        mSlots(),
        // BufferItem列表,用于保存已经完成绘制操作后入队的GraphicBuffer
        mQueue(),
        // 索引值列表,保存所有未绑定GraphicBuffer的空闲BufferSlot在mSlots列表中的索引值
        mFreeSlots(),
        // 索引值列表,保存所有绑定了GraphicBuffer的空闲BufferSlot在mSlots列表中的索引值
        mFreeBuffers(),
        // 索引值列表,保存所有
        mUnusedSlots(),
        // 索引值列表,保存所有非空闲状态BufferSlot在mSlots列表中的索引值
        mActiveBuffers(),
        // std::condition_variable锁,用于在同步模式下dequeueBuffer时等待空闲Buffer
        mDequeueCondition(),
        // 表示BufferQueueProducer在dequeueBuffer时BBQ处理模式,默认为false(阻塞模式),
        // 只有当Producer和Consumer都由应用端控制时,该值为true(非阻塞模式),
        // 即没有空闲Buffer时不会等待,而是直接返回
        mDequeueBufferCannotBlock(false),
        // 表示BufferQueueProducer在queueBuffer时BBQ处理模式,默认为false(非丢弃模式),
        // 只有当Producer和Consumer都由应用端控制时,该值为true(丢弃模式),
        // 即如果有未消费Buffer,会直接丢弃掉
        mQueueBufferCanDrop(false),
        mLegacyBufferDrop(true),
        // 默认Buffer格式,由Consumer设置,Producer在dequeueBuffer时如果未指定格式,默认使用该值
        mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
        // Buffer的默认宽高,由Consumer设置,Producer在dequeueBuffer时如果未指定宽高,默认使用该值
        mDefaultWidth(1),
        mDefaultHeight(1),
        // Buffer的默认DataSpace,由Consumer设置,Producer在queueBuffer时如果未指定,默认使用该值
        mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
        // 队列最大可申请的Buffer数
        mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
        // Consumer最多能acquireBuffer的个数,默认为1,可通过Consumer设置
        mMaxAcquiredBufferCount(1),
        // Producer最多能dequeueBuffer的个数, 默认为1,可通过Producer设置
        mMaxDequeuedBufferCount(1),
        // 当发生queueBuffer后,该值将变为true
        mBufferHasBeenQueued(false),
        // 帧数统计,每次queueBuffer后+1
        mFrameCounter(0),
        // 屏幕方向提示,由Consumer设置
        mTransformHint(0),
        // 表示producer正在进行分配新的GraphicBuffer,此时不允许对任何队列进行修改
        mIsAllocating(false),
        // 条件锁,当mIsAllocating为false后唤醒,用于分配Graphic Buffer过程
        mIsAllocatingCondition(),
        // 是否允许分配Buffer
        mAllowAllocation(true),
        // 用于记录最近一次dequeueBuffer返回Buffer的年龄,自上次queueBuffer后经过的帧数,总帧数 - 该Buffer帧数
        mBufferAge(0),
        // 由Producer设置,在attachBuffer操作时跟Buffer::mGenerationNumber做匹配
        mGenerationNumber(0),
        // 该BBQ是否开启了异步模式
        mAsyncMode(false),
        // shareBuffer模式
        mSharedBufferMode(false),
        mAutoRefresh(false),
        mSharedBufferSlot(INVALID_BUFFER_SLOT),
        mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
                           HAL_DATASPACE_UNKNOWN),
        mLastQueuedSlot(INVALID_BUFFER_SLOT),
        mUniqueId(getUniqueId()),
        mAutoPrerotation(false),
        mTransformHintInUse(0) {
    // 填充mFreeSlots
    int numStartingBuffers = getMaxBufferCountLocked();
    for (int s = 0; s < numStartingBuffers; s++) {
        mFreeSlots.insert(s);
    }
    // 填充mUnusedSlots
    for (int s = numStartingBuffers; s < BufferQueueDefs::NUM_BUFFER_SLOTS;
            s++) {
        mUnusedSlots.push_front(s);
    }
}

3.1.2、创建IGraphicBufferProducer对象

BBQ中,IGraphicBufferProducer的实现类是BufferQueueProducer,其构造方法如下:

// frameworks/native/libs/gui/BufferQueueProducer.cpp

BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
        bool consumerIsSurfaceFlinger) :
    // BufferQueueCore对象
    mCore(core),
    // BufferSlot对象数组,通过该数组获取BufferSlot及其绑定的GraphicBuffer的所有权
    mSlots(core->mSlots),
    // Consumer名称
    mConsumerName(),
    // 指定Buffer队列中Buffer的旋转方向,除Camera相关场景外,该值一般不会使用到
    mStickyTransform(0),
    // Consumer是否为sf
    mConsumerIsSurfaceFlinger(consumerIsSurfaceFlinger),
    // 最近一次入队Buffer的acquire Fence
    mLastQueueBufferFence(Fence::NO_FENCE),
    // 最近一次入队Buffer的Transform值
    mLastQueuedTransform(0),
    // 跟CallbackTicket用于保证onFrame* 回调方法的顺序
    mCallbackMutex(),
    mNextCallbackTicket(0),
    mCurrentCallbackTicket(0),
    mCallbackCondition(),
    // dequeueBuffer和attachBuffer的等待空闲Buffer的Block时长,
    // 当队列中没有空闲Buffer时,dequeueBuffer会进入wait状态,直到有空闲Buffer或者到达该timeout,
    // -1表示永久等待
    mDequeueTimeout(-1),
    // 表示是否在dequeueBuffer时等待allocatBuffer完成
    mDequeueWaitingForAllocation(false) {
}

3.1.3、创建IGraphicBufferConsumer对象

IGraphicBufferConsumer的实现类是BufferQueueConsumer,其构造方法如下:

// frameworks/native/libs/gui/BufferQueueConsumer.cpp

BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
    mCore(core),
    mSlots(core->mSlots),
    mConsumerName() {}

3.1.4、创建ConsumerBase对象

BBQ中ConsumerBase的实现类是BufferItemConsumer,在完成Producer和Consumer对象创建后,接着便会创建

BufferItemConsumer对象,其构造方法如下:

// frameworks/native/libs/gui/BufferItemConsumer.cpp

BufferItemConsumer::BufferItemConsumer(
        const sp<IGraphicBufferConsumer>& consumer, uint64_t consumerUsage,
        int bufferCount, bool controlledByApp) :
    ConsumerBase(consumer, controlledByApp)
{
    // 设置GraphicBuffer usage标记
    status_t err = mConsumer->setConsumerUsageBits(consumerUsage);
    // 设置acquireBuffer最大个数
    if (bufferCount != DEFAULT_MAX_BUFFERS) {
        err = mConsumer->setMaxAcquiredBufferCount(bufferCount);
    }
}

ConsumerBase构造方法如下:

// frameworks/native/libs/gui/ConsumerBase.cpp

ConsumerBase::ConsumerBase(const sp<IGraphicBufferConsumer>& bufferQueue, bool controlledByApp) :
        // BBQ队列是否已经废弃
        mAbandoned(false),
        // IGraphicBufferConsumer对象
        mConsumer(bufferQueue),
        // 上一次释放Buffer时对应的release Fence对象
        mPrevFinalReleaseFence(Fence::NO_FENCE) {

    mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
    // 创建ProxyConsumerListener对象
    wp<ConsumerListener> listener = static_cast<ConsumerListener*>(this);
    sp<IConsumerListener> proxy = new BufferQueue::ProxyConsumerListener(listener);
    // 对Consumer进行连接
    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);
    }
}

在以上方法中,会同时完成Consumer和BBQ的连接:

// frameworks/native/libs/gui/BufferQueueConsumer.cpp

status_t BufferQueueConsumer::connect(
        const sp<IConsumerListener>& consumerListener, bool controlledByApp) {

    ......
    // ConsumerListener对象
    mCore->mConsumerListener = consumerListener;
    // Consumer是否由应用端控制
    mCore->mConsumerControlledByApp = controlledByApp;

    return NO_ERROR;
}

3.2、Surface和BBQ的连接过程

完成BBQ的创建后,BBQ需要通过IGraphicBufferProducer与实际的图形生产者进行连接后,才能接收来自生产者的Graphic Buffer。

连接时需要指定负责生产和提供图形数据的具体图形流生产者(即Producer API)。Android图形系统中,规定了四类Producer API:

// frameworks/native/libs/nativewindow/include/system/window.h

enum {
    // 生产者为渲染引擎,如opengl、Vulkan,通过渲染引擎对Buffer入队
    NATIVE_WINDOW_API_EGL = 1,

    // 生产者为CPU,即软件绘制,绘制完成后直接入队
    NATIVE_WINDOW_API_CPU = 2,

    // 生产者为视频解码器,通过Stagefright操作对Buffer入队
    NATIVE_WINDOW_API_MEDIA = 3,

    // 生产者为Camera预览,通过Camera HAL对Buffer入队
    NATIVE_WINDOW_API_CAMERA = 4,
};

Surface在图形渲染中作为生产者和消费者交换Buffer的接口,会在适当的流程中执行IGBP接口与BBQ的连接。

如果启用了硬件加速绘制,会在CanvasContext中通过CanvasContext::setupPipelineSurface()方法设置RenderPipeline时,会设置Producer API并回调Surface::connect()方法,和BBQ完成连接:

// frameworks/native/libs/gui/Surface.cpp

int Surface::connect(
        int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) {
    ......
    // 跟BBQ连接图形生产者
    int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
    ......

    return err;
}

如果是软件绘制,会在Surface::lock()时和BBQ进行连接:

// frameworks/native/libs/gui/Surface.cpp

status_t Surface::lock(
        ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds)
{
    ......
    if (!mConnectedToCpu) {
        // Producer和BBQ进行连接
        int err = Surface::connect(NATIVE_WINDOW_API_CPU);
        if (err) {
            return err;
        }
    ......
}

进入BufferQueueProducer中的流程如下:

// frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
        int api, bool producerControlledByApp, QueueBufferOutput *output) {
    ......
    
    int status = NO_ERROR;
    switch (api) {
        case NATIVE_WINDOW_API_EGL:
        case NATIVE_WINDOW_API_CPU:
        case NATIVE_WINDOW_API_MEDIA:
        case NATIVE_WINDOW_API_CAMERA:
            mCore->mConnectedApi = api;
            ......
            if (listener != nullptr) {
                // 设置Binder died监听
                if (IInterface::asBinder(listener)->remoteBinder() != nullptr) {
                    status = IInterface::asBinder(listener)->linkToDeath(
                            static_cast<IBinder::DeathRecipient*>(this));
                    mCore->mLinkedToDeath = listener;
                }
            }
            break;
        default:
            status = BAD_VALUE;
            break;
    }
    ......
    return status;
}

BufferQueueProducer::connect()方法中,除了指定Producer API之外,还传入了IProducerListener接口对象,作为BBQ中用于监听Surface所在进程死亡状态的接口使用。

需要注意的是,一个Surface只能和一个BBQ连接,一个BBQ也只能和一个Surface连接,否则就会出现如下异常:

E BufferQueueProducer: bbq-adapter#975(BLAST Consumer)975(id:13f400000552,api:1,p:5108,c:5108) connect: already connected (cur=1 req=1)
E libEGL  : eglCreateWindowSurface: native_window_api_connect (win=0xb400007318a49010) failed (0xffffffea) (already connected to another API?)

3.3、生产和消费Graphic Buffer

当Surface和BBQ完成连接后,便可进行Graphic Buffer的传递。

  • 获取Graphic Buffer:当需要渲染新的图像时,调用IGBP::dequeueBuffer()方法,从BBQ返回一个可用的Graphic Buffer;
  • 填充Graphic Buffer:一旦获取到缓冲区,通过OpenGL ES进行渲染,将图像数据写入该缓冲区;
  • 交换Graphic Buffer:渲染完成后,调用IGBP::queueBuffer()方法,将填充完的缓冲区提交到BBQ中。

软件绘制时这个过程如下:

// frameworks/base/core/java/android/view/ViewRootImpl.java

private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
        boolean scalingRequired, Rect dirty, Rect surfaceInsets) {

    // Draw with software renderer.
    final Canvas canvas;

    try {
        // 获取Graphc Buffer:直接调用dequeueBuffer()
        canvas = mSurface.lockCanvas(dirty);
    } 
    try {
        // 填充Graphc Buffer: 执行各种绘制
        if (!canvas.isOpaque() || yoff != 0 || xoff != 0) {
            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
        }
        canvas.translate(-xoff, -yoff);
        canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);
        mView.draw(canvas);
    } finally {
        try {
            // 交换Graphc Buffer: 直接调用queueBuffer()
            surface.unlockCanvasAndPost(canvas);
        } catch (IllegalArgumentException e) {
            return false;
        }
    }
    return true;
}

硬件加锁渲染时,通过绘制引擎管道进行dequeueBufferqueueBuffer

// frameworks/base/libs/hwui/renderthread/CanvasContext.cpp

void CanvasContext::draw(bool solelyTextureViewUpdates) {
    // 获取Graphic Buffer
    Frame frame = getFrame();
    ......
    IRenderPipeline::DrawResult drawResult;
    {
        // 填充Graphic Buffer:进行绘制
        std::scoped_lock lock(mFrameMetricsReporterMutex);
        drawResult = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry,
                                           &mLayerUpdateQueue, mContentDrawBounds, mOpaque,
                                           mLightInfo, mRenderNodes, &(profiler()), mBufferParams);
    }


    // 交换Graphic Buffer: 通过eglSwapBuffer/VulkanManager执行queueBuffer
    bool didSwap = mRenderPipeline->swapBuffers(frame, drawResult, windowDirty, mCurrentFrameInfo,
                                                &requireSwap);


    mIsDirty = false;
    .......

    return;
}

执行完毕queueBuffer后,接下来会通过BufferQueueConsumer::acquireBuffer()将Graphic Buffer从队列中取出,并通过Transaction提交给surfaceflinger:

// frameworks/native/libs/gui/BLASTBufferQueue.cpp

status_t BLASTBufferQueue::acquireNextBufferLocked(
        const std::optional<SurfaceComposerClient::Transaction*> transaction) {
    ......
    BufferItem bufferItem;
    // 从队列中获取Graphic Buffer
    status_t status =
            mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
    ......
    // 将Buffer设置给Transaction
    t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, mProducerId,
                 releaseBufferCallback);
    // 提交Transaction
    if (applyTransaction) {
        t->setApplyToken(mApplyToken).apply(false, true);
    } 
    ......
    return OK;
}

3.4、销毁BBQ

当BBQ不再使用时,需要主动进行销毁,避免出现内存泄漏。

Java层中,通过BLASTBufferQueue.destory()方法销毁BBQ:

// frameworks/base/graphics/java/android/graphics/BLASTBufferQueue.java

public void destroy() {
    nativeDestroy(mNativeObject);
    mNativeObject = 0;
}

并在完成BLASTBuffeQueue对象的析构后,陆续完成其他组件对象的析构。

四、BBQ模式设置

4.1、阻塞模式VS非阻塞模式

mCore->mDequeueBufferCannotBlock表示BBQ的阻塞模式或非阻塞模式。

该模式决定了BufferQueueProduer::dequeueBuffer操作未能获取到空闲Buffer时,是否需要阻塞等待,直到获得空闲Buffer。

默认为阻塞模式,只有同时满足以下三个条件时,此BBQ队列将为非阻塞模式:

  1. mCore->mConsumerControlledByApp为true;
  2. BufferQueueProducer::mProducerControlledByApp为true;
  3. mDequeueTimeout < 0,即未指定dequeueBuffer超时时长。
// frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener,
        int api, bool producerControlledByApp, QueueBufferOutput *output) {
    ......
    
    mCore->mDequeueBufferCannotBlock = false;
    mCore->mQueueBufferCanDrop = false;
    // 设置BBQ是否为非阻塞模式
    if (mCore->mConsumerControlledByApp && producerControlledByApp) {
        mCore->mDequeueBufferCannotBlock = mDequeueTimeout < 0;
        mCore->mQueueBufferCanDrop = mDequeueTimeout <= 0;
    }

    return status;
}

mCore->mConsumerControlledByAppproducerControlledByApp表示BBQ中的Producer和Consumer行为是否由业务侧进行控制,从BBQ内部来看,其实就涉及到了两种模式。

4.2、丢弃模式

丢弃模式针对于某一个单一BufferItem对象,或者说是针对单个GraphicBuffer的属性,由BufferItem->mIsDroppable表示,在BufferQueueProducer::queueBuffer时设置:

// frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
    .....
    { // Autolock scope
        std::lock_guard<std::mutex> lock(mCore->mMutex);
        // 设置是否可丢弃
        item.mIsDroppable = mCore->mAsyncMode ||
                (mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) ||
                (mCore->mLegacyBufferDrop && mCore->mQueueBufferCanDrop) ||
                (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
        if (mCore->mQueue.empty()) {
            ......
        } else {
            // When the queue is not empty, we need to look at the last buffer
            // in the queue to see if we need to replace it
            const BufferItem& last = mCore->mQueue.itemAt(
                    mCore->mQueue.size() - 1);
            if (last.mIsDroppable) {
                if (!last.mIsStale) {
                    // 丢弃上一个未出队的BufferItem
                }
  
    }

    ......
    return NO_ERROR;
}

根据以上代码可知,满足以下任一条件mIsDroppable即可为true:

  1. mCore->mAsyncMode为true,即当前BBQ为异步模式;
  2. mCore->mQueueBufferCanDrop为true;
  3. mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot:SharedBufferMode模式开启时,且恰好是SharedBufferSlot时。

mConsumerIsSurfaceFlinger是旧BufferQueue方案遗留的属性,BLAST方案中已全部弃用,恒为false;

mCore->mLegacyBufferDrop默认为true;

mCore->mQueueBufferCanDrop值的设置也跟类似,必须同时满足以下条件:

  1. mCore->mConsumerControlledByApp为true;

  2. BufferQueueProducer::mProducerControlledByApp为true;

  3. mDequeueTimeout <= 0,即未指定dequeueBuffer超时时长或指定时长为0。

当有新的BufferItem入队时,如果存在还未出队的BufferItem且是可丢弃的,则会直接丢弃:

// frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
    .....
    { // Autolock scope
        ......
        if (mCore->mQueue.empty()) {
            .....
        } else {
            const BufferItem& last = mCore->mQueue.itemAt(
                    mCore->mQueue.size() - 1);
            if (last.mIsDroppable) {
                // 丢弃上一个未出队的BufferItem
                if (!last.mIsStale) {
                    mSlots[last.mSlot].mBufferState.freeQueued();
                    
                    if (!mSlots[last.mSlot].mBufferState.isShared()) {
                        mCore->mActiveBuffers.erase(last.mSlot);
                        mCore->mFreeBuffers.push_back(last.mSlot);
                        output->bufferReplaced = true;
                    }
                }
                // 用item替换last
                mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
                frameReplacedListener = mCore->mConsumerListener;
            } else {
            }
        }
  
    }
    { // scope for the lock
        if (frameAvailableListener != nullptr) {
            frameAvailableListener->onFrameAvailable(item);
        } else if (frameReplacedListener != nullptr) {
            frameReplacedListener->onFrameReplaced(item);
        }
    }
    ......
    return NO_ERROR;
}

4.3、异步模式(Async Mode)

mCore->mAsyncMode表示BBQ的异步模式是否开启。

该值通过BufferQueueProducer::setAsyncMode()方法设置。

默认为同步模式,当异步模式开启后:

  1. Consumer最大ACQUIRED Buffer数+1;
  2. queueBuffer时所有Buffer变为可丢弃模式;
  3. dequeuBuffer时变为非阻塞模式。

所有的虚拟屏关联Surface均为Async,目的是不阻塞sf。

4.4、共享Buffer模式(Shared Buffer Mode)

mCore->mSharedBufferMode表示BBQ的共享Buffer模式是否开启,较早版本或称为Single Buffer模式;

该值通过BufferQueueProducer::setSharedBufferMode()方法设置:

// frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::setSharedBufferMode(bool sharedBufferMode) {

    std::lock_guard<std::mutex> lock(mCore->mMutex);
    if (!sharedBufferMode) {
        mCore->mSharedBufferSlot = BufferQueueCore::INVALID_BUFFER_SLOT;
    }
    mCore->mSharedBufferMode = sharedBufferMode;
    return NO_ERROR;
}

当开启SharedBufferMode后,dequeueBuffer时第一次被获得的BufferSlot会被标记为共享Buffer,将mCore->mSlot的索引指赋值给mCore->mSharedBufferSlot,并在此后一直使用该Buffer。

mCore->mSharedBufferSlot代表共享Buffer的索引,在dequeueBufferqueueBuffer时设置:

// frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
        ......
        // 当SharedBufferMode开启后,第一次进行dequeue/queue的Buffer作为sharedBuffer
        if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
                BufferQueueCore::INVALID_BUFFER_SLOT) {
            mCore->mSharedBufferSlot = slot;
            mSlots[slot].mBufferState.mShared = true;
        }
    ......
}

SharedBufferMode开启后,后续的dequeueBuffer、queueBuffer將仅使用标记的Buffer进行,如在dequeueBuffer时,恒返回mCore->mSharedBufferSlot

// frameworks/native/libs/gui/BufferQueueProducer.cpp

status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
        std::unique_lock<std::mutex>& lock, int* found) const {

    while (tryAgain) {
        if (tooManyBuffers) {
            ......
        } else {
            // 开启Shared buffer mode时,恒返回mCore->mSharedBufferSlot
            if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot !=
                    BufferQueueCore::INVALID_BUFFER_SLOT) {
                *found = mCore->mSharedBufferSlot;
            } else {
                ......
            }
        }
    }
}