图像显示框架八——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建立联系。

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

时序图

流程图

关系图

相关推荐
WK100%3 小时前
二叉树经典OJ题
c语言·数据结构·经验分享·笔记·链表
1登峰造极4 小时前
uniapp 运行安卓报错reportJSException >>>> exception function:createInstanceContext, exception:white screen
android·java·uni-app
木易 士心4 小时前
Android Handler 机制原理详解
android
kkk_皮蛋4 小时前
作为一个学生,如何用免费 AI 工具手搓了一款 Android AI 日记 App
android·人工智能
金山毒霸电脑医生4 小时前
植物大战僵尸杂交版最新v0.2版下载安装|2025图文解析教程
android·游戏·ios·植物大战僵尸·软件下载安装
羑悻的小杀马特4 小时前
Docker-Android 容器化 + cpolar 穿透,完善异地调试
android·运维·docker·容器·cpolar
恋猫de小郭4 小时前
Android Gradle Plugin 9.0 发布,为什么这会是个史诗级大坑版本
android·flutter·ios·开源
宫瑾5 小时前
【C语言】嵌入式C加强学习
java·c语言·学习