图像显示框架八——BufferQueue与BLASTBufferQueue(基于android 15源码分析)

一.背景

前一篇我们已经讲解了createSurface的流程,也在SurfaceFlinger中创建了图层,但是没有看到BufferQueue,其实在android 12中已经将BufferQueue的流程移除了SurfaceFlinger中,然后在看看我们之前的应用代码,看下BufferQueue如何引入的,以及如何工作的

二.创建BufferQueue

我们继续从创建的demo应用入手,相关内容参考:https://gonglipeng.blog.csdn.net/article/details/154405481?spm=1001.2014.3001.5502https://gonglipeng.blog.csdn.net/article/details/154405481?spm=1001.2014.3001.5502参考代码:https://github.com/yrzroger/NativeSFDemo/tree/android_u

创建Native的surface后接下来就是去绘图,流程走到drawNativeSurface()这个方法中,如下:

drawNativeSurface这个方法中首先去调用了我们定义的NativeSurfaceWrapper::getSurface方法:

复制代码
sp<ANativeWindow> NativeSurfaceWrapper::getSurface() const {
    sp<ANativeWindow> anw = mSurfaceControl->getSurface();
    return anw;
}

在getSurface函数中,又是使用mSurfaceControl的getSurface函数,mSurfaceControl在上一篇createSurface创建得到的,它封装了SurfaceFlinger创建的BufferStateLayer信息,然后我们继续看SurfaceControl的getSurface函数

framework/native/libs/gui/SurfaceControl.cpp

复制代码
sp<Surface> SurfaceControl::generateSurfaceLocked()
{
    uint32_t ignore;
    auto flags = mCreateFlags & (ISurfaceComposerClient::eCursorWindow |
                                 ISurfaceComposerClient::eOpaque);
    mBbqChild = mClient->createSurface(String8("bbq-wrapper"), 0, 0, mFormat,
                                       flags, mHandle, {}, &ignore);
    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;
}

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

上述可以看出来在getSurface函数中,第一次调用mSurfaceData是空的,然后会走到generateSurfaceLocked函数中,我们先看下SurfaceControl.h头文件,路径:framework/native/libs/gui/include/gui/SurfaceControl.h

复制代码
/**
 * SurfaceControl 是对屏幕上一个"层(Layer)"的句柄。
 * 它允许应用控制该层的属性(如位置、缩放、透明度等),这些操作通常通过 SurfaceComposerClient 的 Transaction 提交。
 */
class SurfaceControl : public RefBase
{

...

    // --- 成员变量 ---
    sp<SurfaceComposerClient>   mClient;       // 负责与 SurfaceFlinger 通信的连接
    sp<IBinder> mHandle;                       // 在 SurfaceFlinger 中的唯一标识 Token
    mutable Mutex               mLock;         // 线程锁
    mutable sp<Surface>         mSurfaceData;  // 缓存生成的 Surface 对象
    
    // BLAST (Buffer-Like-As-Surface-Transactions) 机制相关
    // Android 11+ 引入,旨在将 Buffer 更新同步到 SurfaceFlinger 事务中
    mutable sp<BLASTBufferQueue> mBbq;         // BLASTBufferQueue对象实例
    mutable sp<SurfaceControl> mBbqChild;      // child layer,它会和mBbq相关联

...
};

然后我们再看看generateSurfaceLocked函数做了哪些操作:

总结 SurfaceControl::generateSurfaceLocked() 的核心逻辑,该函数利用 BLAST 架构将图层管理与数据渲染对接,具体步骤如下:

  1. 筛选属性标识 (mCreateFlags):mCreateFlags表示createSurface的一些属性标识,这个值其实就是我们调用surfaceComposerClient->createSurface时new SurfaceControl传递下来的,然后从原始创建参数中提取不透明度(Opaque)等关键属性,确保后续创建的子图层继承必要的物理特性。
  2. 创建子图层 (bbq-wrapper) :mClient,类型是sp 这个值也是我们调用surfaceComposerClient->createSurface时new SurfaceControl传递下来的,利用 mClient 在当前图层(如 "NativeSFDemo")下创建一个名为 "bbq-wrapper" 的子图层,并将其 SurfaceControl 保存在 mBbqChild 中,建立父子层级关系。
  3. 构建 BLAST 适配器 (mBbq) :实例化一个 BLASTBufferQueue 对象,它负责将应用产生的缓冲区(Buffer)数据封装进 SurfaceFlinger 的事务(Transaction)中。
  4. 获取渲染接口 (getSurface) :通过 mBbq 生成并返回一个 sp<Surface> 对象,让应用层可以直接通过这个"画布"操作底层的 BufferQueue 流程。

一句话总结其本质:

该函数通过在主图层下挂载一个名为 bbq-wrapper 的隐藏子图层,并利用 BLASTBufferQueue 将传统的 Surface 绘图模式无缝转换到现代的 BLAST 事务合成架构 中。

然后继续来看BLASTBufferQueue的构建

先看下构造函数

复制代码
/**
 * BLASTBufferQueue 构造函数
 * @param name 调试用的名称
 * @param updateDestinationFrame 是否根据 Buffer 大小自动更新目标区域坐标
 */
BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame)
      : mSurfaceControl(nullptr),
        mSize(1, 1),                 // 默认初始大小为 1x1
        mRequestedSize(mSize),
        mFormat(PIXEL_FORMAT_RGBA_8888), // 默认格式为 RGBA_8888
        mTransactionReadyCallback(nullptr),
        mSyncTransaction(nullptr),
        mUpdateDestinationFrame(updateDestinationFrame) {

    // 1. 创建核心 BufferQueue 
    // mProducer 是给 App 绘图用的(生产者接口)
    // mConsumer 是给当前类使用的(消费者接口)
    createBufferQueue(&mProducer, &mConsumer);

    // 2. 配置生产者:设置 dequeueBuffer 为阻塞模式
    // 因为适配器在客户端进程,显式设置超时为无限,直到有可用 Buffer
    mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max());

    // 3. 设置默认最大出队 Buffer 数量为 2(典型双缓冲配置)
    mProducer->setMaxDequeuedBufferCount(2);

    // 4. 创建 BLAST 专用的消费者对象
    // 这个对象负责监听 BufferQueue 中的数据,并将其转化为 SurfaceFlinger 的 Transaction
    mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
                                                      GraphicBuffer::USAGE_HW_COMPOSER |
                                                              GraphicBuffer::USAGE_HW_TEXTURE,
                                                      1, false, this);

    // 5. 生成唯一 ID 和名称,用于 Systrace 性能追踪和调试
    static std::atomic<uint32_t> nextId = 0;
    mProducerId = nextId++;
    mName = name + "#" + std::to_string(mProducerId);
    auto consumerName = mName + "(BLAST Consumer)" + std::to_string(mProducerId);
    mQueuedBufferTrace = "QueuedBuffer - " + mName + "BLAST#" + std::to_string(mProducerId);
    
    mBufferItemConsumer->setName(String8(consumerName.c_str()));

    // 6. 核心步骤:设置监听器
    // 当 App 完成绘图并 queueBuffer 时,会触发 this->onFrameAvailable()
    mBufferItemConsumer->setFrameAvailableListener(this);

    // 7. 查询系统允许的最大 Acquired Buffer 数量(通常由 HWC 硬件限制决定)
    ComposerServiceAIDL::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
    mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
    mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
    mNumAcquired = 0;
    mNumFrameAvailable = 0;

    // 8. 注册队列卡死(Stall)监听器
    // 如果事务长时间未完成,会触发回调进行异常处理(如日志记录或监控)
    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);

    BQA_LOGV("BLASTBufferQueue created");

    /* QTI_BEGIN */
    // 9. 高通 (Qualcomm) 平台扩展逻辑:初始化高通专属的 BBQ 优化插件
    if (!mQtiBBQExtn) {
        mQtiBBQExtn = new libguiextension::QtiBLASTBufferQueueExtension(this, name);
    }
    /* QTI_END */
}

这段代码的核心任务是**"搭建舞台"**。它在 App 进程里初始化了一个复杂的缓冲转换机制。具体体现在以下三个维度:

A. 建立缓冲区链路 (Internal BufferQueue)

它调用 createBufferQueue 生成了一对生产者和消费者。

  • 生产者 (mProducer) :最终会包装成 Surface 交给 App(如 Skia 或 OpenGL)。App 以为自己是在往屏幕画图,实际上是在往这个 mProducer 塞数据。
  • 消费者 (mBufferItemConsumer) :留在 BLASTBufferQueue 内部。
B. 角色身份转换 (The Bridge)

这是 BLAST 架构最精妙的地方:

  • 在传统架构中,消费者通常在 SurfaceFlinger 进程。
  • 在此处,消费者就在 App 进程内部
  • 当 App 画完一帧(onFrameAvailable),BLASTBufferQueue 里的消费者会立刻"吃掉"这块 Buffer,然后不是 直接显示,而是把它打包进一个 SurfaceComposerClient::Transaction(事务)中。
C. 性能与监控初始化
  • 双缓冲管理 :通过 setMaxDequeuedBufferCount(2) 保证渲染的流畅性。
  • 追踪与调试 :通过 mQueuedBufferTrace 为 Android 系统分析工具(如 Perfetto/Systrace)提供数据埋点,让我们能看到 Buffer 是在哪个阶段卡住了。
  • 卡顿监控 :通过 addQueueStallListener 监控图形事务是否卡死(Hang),这对于分析视觉掉帧(Jank)非常重要。
  • 厂商优化:引入了高通(QTI)的扩展,说明在实际商业设备中,这块逻辑还会针对特定硬件(如 GPU/显示芯片)进行性能优化。

总结

构造函数完成后,BLASTBufferQueue 已经准备好作为一个**"中间人":向上提供 Surface 让应用绘图,向下将绘图结果转换为 Transaction 提交给系统,从而实现同步**的图形更新。

然后再看下createBufferQueue函数代码

复制代码
/**
 * 创建 BufferQueue 的生产者和消费者接口
 * @param outProducer 输出参数,用于存放创建好的生产者句柄
 * @param outConsumer 输出参数,用于存放创建好的消费者句柄
 */
void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
                                         sp<IGraphicBufferConsumer>* outConsumer) {
    // 1. 安全检查:确保传入的指针不为空,否则直接触发致命错误(Fatal Log)
    LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BLASTBufferQueue: outProducer must not be NULL");
    LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BLASTBufferQueue: outConsumer must not be NULL");

    // 2. 创建 BufferQueueCore:这是 BufferQueue 的灵魂
    // 它是一个中控机,存储了所有的缓冲区(GraphicBuffer)状态、槽位信息以及同步对象
    sp<BufferQueueCore> core(new BufferQueueCore());
    LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore");

    // 3. 创建生产者 (BBQBufferQueueProducer)
    // 注意:这里没有使用标准的 BufferQueueProducer,而是使用了 BBQ 专用的派生类
    // 并将 "this" (当前 BLASTBufferQueue) 传入,以便生产者操作时能回调控制类
    sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core, this));
    LOG_ALWAYS_FATAL_IF(producer == nullptr,
                        "BLASTBufferQueue: failed to create BBQBufferQueueProducer");

    // 4. 创建消费者 (BufferQueueConsumer)
    sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core));
    LOG_ALWAYS_FATAL_IF(consumer == nullptr,
                        "BLASTBufferQueue: failed to create BufferQueueConsumer");

    // 5. 关键配置:允许额外的 Acquire
    // 这允许消费者在尚未释放前一帧的情况下,尝试获取(acquire)新的 Buffer
    // 这在处理多缓冲和事务同步时非常重要,能防止渲染流水线卡顿
    consumer->setAllowExtraAcquire(true);

    // 6. 将创建好的组件通过指针返回给调用者
    *outProducer = producer;
    *outConsumer = consumer;
}

在这段代码中,你可以看到 Android 图形系统的标准三元组:

  1. BufferQueueCore:内部状态机,管理 Buffer 的生命周期(Free -> Dequeued -> Queued -> Acquired)。
  2. Producer (BBQBufferQueueProducer) :应用层(如 HWUI 或 Skia)持有的接口。应用调用 dequeueBuffer 申请空间绘图,绘图后调用 queueBuffer 归还缓冲区。
  3. Consumer (BufferQueueConsumer)BLASTBufferQueue 内部持有的接口。当 Producer 提交了数据,Consumer 会收到通知并将其"取走"交给 SurfaceFlinger。

然后我们根据类图梳理下当前的关系

然后继续查看BLASTBufferQueue的getSurface函数

复制代码
sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) {
    std::lock_guard _lock{mMutex};
    sp<IBinder> scHandle = nullptr;
    if (includeSurfaceControlHandle && mSurfaceControl) {
        scHandle = mSurfaceControl->getHandle();
    }
    return new BBQSurface(mProducer, true, scHandle, this);
}

BLASTBufferQueue::getSurface方法中会去创建一个BBQSurface, 这个类继承自Surface,其中保存了对应layer的handle,进一步再看看 BBQSurface的构造函数

复制代码
    BBQSurface(const sp<IGraphicBufferProducer>& igbp, bool controlledByApp,
               const sp<IBinder>& scHandle, const sp<BLASTBufferQueue>& bbq)
          : Surface(igbp, controlledByApp, scHandle), mBbq(bbq) {}

再对照一下Surface的构造函数

Surface中有: 图形缓冲生产者:mGraphicBufferProducer,这个就是BLASTBufferQueue::createBufferQueue时创建的,并一路传递过来; SurfaceControl句柄: mSurfaceCtrolHandle是 SurfaceControl::generateSurfaceLocked()中mClient->createSurface时获取的,并一路传递下来;

总结

我们初步可以看到一些概念: 一个BLASTBufferQueue(BufferQueue)对应一个Layer,一个BufferQueue中有多个Buffer,一般是2个或者3个。 一个BLASTBufferQueue(BufferQueue)有一个Producer,一个Consumer 结合前面的分析,一个Surface和一个Layer也是一一对应的,和窗口也是一一对应的。 可见,BLASTBufferQueue(BufferQueue)就是两个连接纽带,连接着Producer和Consumer。 Android 12之前的版本是在SurfaceFlinger的BufferLayer中去创建BufferQueue,而Android 12把这个逻辑从SurfaceFlinger移出来了,而是在BLASTBufferQueue中去创建 BufferQueue。BLASTBufferQueue会关联到特定的layer,并与SurfaceFlinger交互来和layer建立联系。

根据上述内容进行时序图总结

时序图

流程图

关系图

相关推荐
阿巴斯甜20 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker21 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95271 天前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android