一.背景
前一篇我们已经讲解了createSurface的流程,也在SurfaceFlinger中创建了图层,但是没有看到BufferQueue,其实在android 12中已经将BufferQueue的流程移除了SurfaceFlinger中,然后在看看我们之前的应用代码,看下BufferQueue如何引入的,以及如何工作的
二.创建BufferQueue
我们继续从创建的demo应用入手,相关内容参考:https://gonglipeng.blog.csdn.net/article/details/154405481?spm=1001.2014.3001.5502
https://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 架构将图层管理与数据渲染对接,具体步骤如下:
- 筛选属性标识 (
mCreateFlags):mCreateFlags表示createSurface的一些属性标识,这个值其实就是我们调用surfaceComposerClient->createSurface时new SurfaceControl传递下来的,然后从原始创建参数中提取不透明度(Opaque)等关键属性,确保后续创建的子图层继承必要的物理特性。 - 创建子图层 (
bbq-wrapper) :mClient,类型是sp 这个值也是我们调用surfaceComposerClient->createSurface时new SurfaceControl传递下来的,利用mClient在当前图层(如 "NativeSFDemo")下创建一个名为 "bbq-wrapper" 的子图层,并将其SurfaceControl保存在mBbqChild中,建立父子层级关系。 - 构建 BLAST 适配器 (
mBbq) :实例化一个BLASTBufferQueue对象,它负责将应用产生的缓冲区(Buffer)数据封装进 SurfaceFlinger 的事务(Transaction)中。 - 获取渲染接口 (
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 图形系统的标准三元组:
- BufferQueueCore:内部状态机,管理 Buffer 的生命周期(Free -> Dequeued -> Queued -> Acquired)。
- Producer (BBQBufferQueueProducer) :应用层(如 HWUI 或 Skia)持有的接口。应用调用
dequeueBuffer申请空间绘图,绘图后调用queueBuffer归还缓冲区。 - 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建立联系。
根据上述内容进行时序图总结
时序图

流程图

关系图
