一、重点API功能介绍
Google针对新的同步机制,在BBQ对象JAVA层面设计了一系列功能接口,列举功能更新较大几个接口:
- setNextTransaction(@Nullable SurfaceControl.Transaction t)
提供用于下一次缓冲区要更新的事务。 BBQ 不会立即提交此事务,通过该接口将下一帧的提交控制在调用者手中,调用者可以将其用于更高级别的同步。
- mergeWithNextTransaction(SurfaceControl.Transaction t, long frameNumber)
将传入的事务合并到 BBQ 中的下一个事务。 当具有指定帧号的下一帧可用时,将直接与含有Buffer的事务进行合并并提交。
- setTransactionCompleteCallback(long frameNumber @Nullable TransactionCompleteCallback completeCallback)
客户端可以监听Buffer的合成状态,在 SurfaceFlinger 中已应用包含带有 framenumber 的缓冲区的事务时触发回调,通知客户端合成完成。
与之前Android版本不同的是,Surface对象的创建、Buffer size与Surface size的更新也支持直接通过BBQ进行操作。
二、BBQ初始化
Android 12 Google将BufferQueue(简称BQ)组件从SF端移动到了客户端,BQ组件的初始化也放在BBQ的初始化中。通过类名可以看出BBQ更像是BQ的装饰者,在BQ本来功能特性的基础上添加了同步的功能。
通过官图大概了解,整个生产消费模型都在客户端,图形缓冲区的出队、入队、获取等操作都在客户端完成,预示着生产着模型从远程通讯变成了本地通讯, 消费者监听器也从SF端的ContentsChangedListener。带来的改变就是客户端需要通过事务Transaction来向SF端提交Buffer与图层的属性。
接下来以应用显示流程为例,梳理下BBQ的初始化流程:
流程的梳理过程所贴的代码,为了突出重点会删除部分逻辑。
-
创建BBQ对象
应用端通过方法relayoutWindow向WMS服务申请窗口布局,创建应用对应SurfaceControl,随后根据SurfaceControl创建BlastBufferQueue:
frameworks/base/core/java/android/view/ViewRootImpl.java
1958 Surface getOrCreateBLASTSurface() {
1959 if (!mSurfaceControl.isValid()) {
1960 return null;
1961 }
1962
1963 Surface ret = null;
1964 if (mBlastBufferQueue == null) {
//第一次获取会创建BBQ
1965 mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
1966 mSurfaceSize.x, mSurfaceSize.y,
1967 mWindowAttributes.format);
1968 // 通过BBQ创建Surface对象
1970 ret = mBlastBufferQueue.createSurface();
1971 } else {
//BBQ已创建,则更新Buffer与Layer的几何属性
1972 mBlastBufferQueue.update(mSurfaceControl,
1973 mSurfaceSize.x, mSurfaceSize.y,
1974 mWindowAttributes.format);
1975 }
1976
1977 return ret;
1978 }
-
通过JNI方法创建对应的Native对象
BBQ主要核心逻辑的初始化都放在了Native对象的构造函数,做了以下几件事:
- 创建图形缓冲区生产消费模型;
- 连接本地的图形缓冲区消费者;
- 设置图形缓冲区监听器。
frameworks/native/libs/gui/BLASTBufferQueue.cpp
133BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp& surface,
134 int width, int height, int32_t format)
135 : mSurfaceControl(surface),
136 mSize(width, height),
137 mRequestedSize(mSize),
138 mFormat(format),
139 mNextTransaction(nullptr) {
// 创建图形缓冲区生产消费模型
140 createBufferQueue(&mProducer, &mConsumer);
141 // 由于适配器在客户端进程中,显式设置 dequeue 超时,以便 dequeueBuffer 流程阻塞
143 mProducer->setDequeueTimeout(std::numeric_limits::max());
145 // 默认设置最大dequeue buffer 数量
146 mProducer->setMaxDequeuedBufferCount(2);
// 初始化图形缓冲区消费者
147 mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
148 GraphicBuffer::USAGE_HW_COMPOSER |
149 GraphicBuffer::USAGE_HW_TEXTURE,
150 1, false);
//设置消费者标识
156 mBufferItemConsumer->setName(String8(consumerName.c_str()));
//添加图形缓冲区入队监听
157 mBufferItemConsumer->setFrameAvailableListener(this);
//添加图形缓冲区消费者释放监听
158 mBufferItemConsumer->setBufferFreedListener(this);
//设置缓冲区Size
159 mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height);
160 mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
161 mBufferItemConsumer->setBlastBufferQueue(this);
162 //通过SF端计算并获取最大可消费缓冲区数量
163 ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
164 mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
165
166 mTransformHint = mSurfaceControl->getTransformHint();
167 mBufferItemConsumer->setTransformHint(mTransformHint);
168 SurfaceComposerClient::Transaction()
169 .setFlags(surface, layer_state_t::eEnableBackpressure,
170 layer_state_t::eEnableBackpressure)
171 .setApplyToken(mApplyToken)
172 .apply();
173 mNumAcquired = 0;
174 mNumFrameAvailable = 0;
177}
-
创建图形缓冲区生产消费模型
frameworks/native/libs/gui/BLASTBufferQueue.cpp
797void BLASTBufferQueue::createBufferQueue(sp* outProducer,
798 sp* outConsumer) {
//创建BufferQueue核心类,主要负责缓冲区的调度工作
802 sp core(new BufferQueueCore());
//创建生产者模型,等待图形生产者连接使用
805 sp producer(new BBQBufferQueueProducer(core));
//创建消费者模型,等待图形消费者连接使用
809 sp consumer(new BufferQueueConsumer(core));
810 consumer->setAllowExtraAcquire(true);
814 *outProducer = producer;
815 *outConsumer = consumer;
816}
-
连接本地的图形缓冲区消费者
BLASTBufferItemConsumer(简称BBIC)继承自ConsumerBase,创建BBIC的同时,消费者模型与消费者监听器建立起了连接:
frameworks/native/libs/gui/ConsumerBase.cpp
58ConsumerBase::ConsumerBase(const sp& bufferQueue, bool controlledByApp) :
59 mAbandoned(false),
60 mConsumer(bufferQueue),
61 mPrevFinalReleaseFence(Fence::NO_FENCE) {
62 //进行类型转换,将 ConsumerBase 转换为 ConsumerListener
69 wp listener = static_cast(this);
//嵌套一层监听器代理
70 sp proxy = new BufferQueue::ProxyConsumerListener(listener);
71 //连接消费者模型
72 status_t err = mConsumer->consumerConnect(proxy, controlledByApp);
73 if (err != NO_ERROR) {
76 } else {
77 mConsumer->setConsumerName(mName);
78 }
79}
frameworks/native/libs/gui/BufferQueueConsumer.cpp
510status_t BufferQueueConsumer::connect(
511 const sp& consumerListener, bool controlledByApp) {
...
528 //将BufferQueue::ProxyConsumerListener 连接至 BufferQueueCore.mConsumerListener
529 mCore->mConsumerListener = consumerListener;
530 mCore->mConsumerControlledByApp = controlledByApp;
531
532 return NO_ERROR;
533}
这一步也就让 BBIC 建立了对Buffer状态的监听。接下来看BBQ如何有选择性的监听Buffer的状态。
-
设置图形缓冲区监听器
BBIC 拥有监听Buffer所有状态的能力,BBQ对Buffer特定状态的监听离不开 BBIC,因此,BBQ 继承了两个抽象类 ConsumerBase 与 BufferItemConsumer,分别针对 Buffer 消费状态与生产状态进行监听。
frameworks/native/libs/gui/BufferQueueConsumer.cpp
133BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp& surface,
134 int width, int height, int32_t format)
135 : mSurfaceControl(surface),
136 mSize(width, height),
137 mRequestedSize(mSize),
138 mFormat(format),
139 mNextTransaction(nullptr) {
//设置消费者标识
156 mBufferItemConsumer->setName(String8(consumerName.c_str()));
//添加图形缓冲区可消费状态监听
157 mBufferItemConsumer->setFrameAvailableListener(this);
//添加图形缓冲区可生产状态监听
158 mBufferItemConsumer->setBufferFreedListener(this);
//设置缓冲区Size
159 mBufferItemConsumer->setDefaultBufferSize(mSize.width, mSize.height);
160 mBufferItemConsumer->setDefaultBufferFormat(convertBufferFormat(format));
177}
BBQ初始化完成,消费者模型建立完成,由于BBQ动态监听缓冲区的状态,如果有可消费的缓冲区,BBQ会触发缓冲区的事务提交:
546void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
//如果客户通过setNextTransaction函数设置了自定义事务,触发线程阻塞
550 const bool nextTransactionSet = mNextTransaction != nullptr;
551 if (nextTransactionSet) {
552 while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) {
553 BQA_LOGV("waiting in onFrameAvailable...");
554 mCallbackCV.wait(_lock);
555 }
556 }
557 // add to shadow queue
558 mNumFrameAvailable++;
//执行缓冲区的提交流程
564 processNextBufferLocked(nextTransactionSet /* useNextTransaction */);
565}
-
创建Surface对象
通过梳理BBQ的初始化,对消费者端的大概流程有了一定的认识,接下来梳理下生产者方的代表,也就是Surface。Android 显示的的内容来源于各种绘制模块,而这些绘制模块需要与BQ建立连接,获取Buffer用以绘制,这样才能将绘制的画像通过BBQ提交给SF合成。Surface作为生产者模型与绘制模块之间桥梁,相关的流程掌握显得尤为重要。
绘制模块指的是那些图像生产者,如以使用SurfaceView、GlSurfaceView、TextureView控件为代表的Video模块、Camera模块、游戏应用等,以及使用软件绘制、硬件加速绘制为代表的普通控件。
回到创建BBQ的流程,在ViewRootImpl.getOrCreateBLASTSurface方法中,创建完BBQ,紧接着会创建Surface对象,直接看Native 对象的构造函数:
66Surface::Surface(const sp& bufferProducer, bool controlledByApp,
67 const sp& surfaceControlHandle)
68 : mGraphicBufferProducer(bufferProducer),
69 mCrop(Rect::EMPTY_RECT),
70 mBufferAge(0),
71 mGenerationNumber(0),
72 mSharedBufferMode(false),
73 mAutoRefresh(false),
74 mAutoPrerotation(false),
75 mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
76 mSharedBufferHasBeenQueued(false),
77 mQueriedSupportedTimestamps(false),
78 mFrameTimestampsSupportsPresent(false),
79 mEnableFrameTimestamps(false),
80 mFrameEventHistory(std::make_unique()) {
81 // Initialize the ANativeWindow function pointers.
82 ANativeWindow::setSwapInterval = hook_setSwapInterval;
83 ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
84 ANativeWindow::cancelBuffer = hook_cancelBuffer;
85 ANativeWindow::queueBuffer = hook_queueBuffer;
86 ANativeWindow::query = hook_query;
87 ANativeWindow::perform = hook_perform;
88
89 ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
90 ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
91 ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
92 ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
116 mSurfaceControlHandle = surfaceControlHandle;
117}
首先Surface的创建会传入生产者模型 GraphicBufferProducer,这样Surface对象拥有了操作缓冲区的能力,同时在构造函数中Surface提供了一系列hook为首的函数,连接到ANativeWindow的函数指针,为的是给EGL模块提供对缓冲区操作的入口。而hook函数会直接调用内部的本地函数,以hook_queueBuffer 为例:
379int Surface::hook_dequeueBuffer(ANativeWindow* window,
380 ANativeWindowBuffer** buffer, int* fenceFd) {
381 Surface* c = getSelf(window);
382 {
383 std::shared_lock lock(c->mInterceptorMutex);
384 if (c->mDequeueInterceptor != nullptr) {
385 auto interceptor = c->mDequeueInterceptor;
386 auto data = c->mDequeueInterceptorData;
387 return interceptor(window, Surface::dequeueBufferInternal, data, buffer, fenceFd);
388 }
389 }
390 return c->dequeueBuffer(buffer, fenceFd);
391}
632int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
....
661 status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width,
662 dqInput.height, dqInput.format,
663 dqInput.usage, &mBufferAge,
664 dqInput.getTimestamps ?
665 &frameTimestamps : nullptr);
680
739 .....
740 return OK;
741}
同时软件绘制不需要通过hook函数来中转,当上层通过Surface.lockCanvas方法获取画布时会直接调用本地函数函数 Surface::dequeueBuffer。
Surface只是绘制的中介,还需要与绘制模块进行连接后,绘制模块才能获取缓冲区和绘制图像数据,关于绘制模块如何连接到Surface,这里不做记录。
三、重点API功能原理解析
结合第一节的关于BBQ 重点API功能介绍与BBQ的初始化流程,回过头看下这三个API功能是如何实现的。
-
setNextTransaction 功能原理解析
首先看 setNextTransaction 函数,调用者通过该接口可以实现将当前帧 Buffer 的提交权利控制在自己手中,同时可以加入其他图层想要的更新,然后提交,放在同一帧生效。可以思考下,如果当前帧的控制权交给了调用者,是否会导致下一帧的紊乱呢?看下这块流程:
582 void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
583 std::lock_guard _lock{mMutex};
584 mNextTransaction = t;
585}
546 void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
//如果客户通过setNextTransaction函数设置了自定义事务,触发线程阻塞
550 const bool nextTransactionSet = mNextTransaction != nullptr;
551 if (nextTransactionSet) {
552 while (mNumFrameAvailable > 0 || maxBuffersAcquired(false /* includeExtraAcquire */)) {
554 mCallbackCV.wait(_lock);
555 }
556 }
557 // add to shadow queue
558 mNumFrameAvailable++;
//执行缓冲区的提交流程
564 processNextBufferLocked(nextTransactionSet /* useNextTransaction */);
565}
339void BLASTBufferQueue::releaseBufferCallback(const ReleaseCallbackId& id,
340 const sp& releaseFence, uint32_t transformHint,
341 uint32_t currentMaxAcquiredBufferCount) {
362 ...
//唤醒线程
386 mCallbackCV.notify_all();
387}
这里BBQ做了线程阻塞的机制,当绘制模块绘制完成下一帧,并将Buffer放回了缓冲区队列,触发BBQ的 onFrameAvailable回调,如果调用者使用了setNextTransaction函数传入了自定义事务,那么就会在 onFrameAvailable函数中阻塞住线程, 暂停执行下一帧的 processNextBufferLocked。而唤醒线程的任务交给了 releaseBufferCallback 函数。
当前帧会执行绘制提交函数 processNextBufferLocked,但是不会立即提交,会将事务控制在自己手中。可以看到,releaseBufferCallback的回调函数会通过 t->setBuffer传递到SF端。
389void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
403
404 SurfaceComposerClient::Transaction localTransaction;
405 bool applyTransaction = true;
406 SurfaceComposerClient::Transaction* t = &localTransaction;
//如果客户通过setNextTransaction函数设置了自定义事务,applyTransaction 为false
407 if (mNextTransaction != nullptr && useNextTransaction) {
408 t = mNextTransaction;
409 mNextTransaction = nullptr;
410 applyTransaction = false;
411 }
412 ...
525 //存在客户端设置的自定义事务,放弃自此的缓冲区提交,将提交权给与客户端
526 if (applyTransaction) {
527 t->setApplyToken(mApplyToken).apply();
528 }
537}
也就是说当调用者主动提交事务后,SF端合成完成后会回调该通知,唤醒线程。否则会一直阻塞等待调用者提交。
大概流程如图示:
根据BBQ相关文档提示:
该机制在同步单个帧时阻塞在 UI 线程中很好,但在尝试同步多个帧时效果不佳。 它最终会减慢渲染速度。 相反,在 RenderThread 级别处理同步以允许 UI 线程继续处理帧
因此多帧同步还是有优化空间。
-
mergeWithNextTransaction 功能原理解析
将调用者传入的事务合并到 BBQ 中的下一个事务。 当具有指定帧号的下一帧可用时,将直接与含有Buffer的事务进行合并并提交。也就是说将调用者事务所包含的其他对图层属性的更新合入到BBQ的事务中,与BBQ的事务在指定帧数一同生效。这个怎么实现的呢?
697void BLASTBufferQueue::mergeWithNextTransaction(SurfaceComposerClient::Transaction* t,
698 uint64_t frameNumber) {
699 std::lock_guard _lock{mMutex};
700 if (mLastAcquiredFrameNumber >= frameNumber) {
701 // Apply the transaction since we have already acquired the desired frame.
702 t->apply();
703 } else {
704 mPendingTransactions.emplace_back(frameNumber, *t);
705 // Clear the transaction so it can't be applied elsewhere.
706 t->clear();
707 }
708}
这个函数会将调用者传入的事务都保存在 mPendingTransactions 集合中,当执行到下一帧的
processNextBufferLocked 函数时,将集合中的事务都合入到BBQ事务中,然后直接提交:
389void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
403
404 ...
479
//如果上层通过mergeWithNextTransaction方法传入了事务,会被保存在mPendingTransactions
//在下一帧提交时统一合入到 t 事务中
511 auto mergeTransaction =
512 [&t, currentFrameNumber = bufferItem.mFrameNumber](
513 std::tuple pendingTransaction) {
514 auto& [targetFrameNumber, transaction] = pendingTransaction;
515 if (currentFrameNumber < targetFrameNumber) {
516 return false;
517 }
518 t->merge(std::move(transaction));
519 return true;
520 };
521
522 mPendingTransactions.erase(std::remove_if(mPendingTransactions.begin(),
523 mPendingTransactions.end(), mergeTransaction),
524 mPendingTransactions.end());
525 //存在客户端设置的自定义事务,放弃自此的缓冲区提交,将提交权给与客户端
526 if (applyTransaction) {
527 t->setApplyToken(mApplyToken).apply();
528 }
537}
大概流程如图示:
-
setTransactionCompleteCallback 功能原理解析
客户端可以监听Buffer的合成状态,在 SurfaceFlinger 中已应用包含带有 frameNumber 的缓冲区的事务时触发回调,通知调用者合成完成。
611void BLASTBufferQueue::setTransactionCompleteCallback(
612 uint64_t frameNumber, std::function&& transactionCompleteCallback) {
613 std::lock_guard _lock{mMutex};
614 if (transactionCompleteCallback == nullptr) {
615 mTransactionCompleteCallback = nullptr;
616 }
620}
389void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) {
403
//设置缓冲区acquire Fence
475 t->setAcquireFence(mSurfaceControl,
476 bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
//设置缓冲区合成完成的回调通知
477 t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast(this));
478 mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
479
525 //存在客户端设置的自定义事务,放弃自此的缓冲区提交,将提交权给与客户端
526 if (applyTransaction) {
527 t->setApplyToken(mApplyToken).apply();
528 }
537}
通过 t->addTransactionCompletedCallback 将 transactionCallbackThunk 回调函数传给了SF,当合成完成会触发回调,并通知调用者状态。
四、总结
根据上面流程的梳理,用一张图总结下BBQ与相关模块之间的结构关系: