前面我们了解了 App 的绘制流程:
- dequeueBuffer :从
BLASTQueueBuffer
中dequeue
一块GraphicBuffer
- draw :通过
Skia/OpenGL
绘制 UI 到GraphicBuffer
- queueBuffer : 把绘制完成的
GraphicBuffer
插入BLASTBufferQueue
我们接下来继续分析,我们看 queuebuffer
之后的,消费者是如何工作的
rust
// frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
...
item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
...
// mCore->mQueue 在 BLastBufferQueue 已经详细介绍过
// 把一块 GraphicBuffer 插入了队列
mCore->mQueue.push_back(item);
// 拿到 mConsumerListener
frameAvailableListener = mCore->mConsumerListener;
...
// 通知消费者消费 Buffer
frameAvailableListener->onFrameAvailable(item);
}
在 BufferQueueProducer::queueBuffer
中:
- 把
GraphicBuffer
插入BufferQueueCore
的队列中 frameAvailableListener
执行onFrameAvailable
通知消费者来消费这一块Buffer
那么这个 mCore->mConsumerListener
指的是哪个对象呢?又是什么时候赋值的呢? 其实在之前的章节已经提到了,大家不熟悉的话可以回顾 BLASTBufferQueue 那一章节:juejin.cn/post/752723... ,这里我直接说结论: mCore->mConsumerListener 就是 BLASTBufferQueue 本身
接下来继续分析 BLASTBufferQueue::onFrameAvailable
scss
//frameworks/native/libs/gui/BLASTBufferQueue.cpp
status_t BLASTBufferQueue::acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) {
// 初始化一个 Transaction
SurfaceComposerClient::Transaction localTransaction;
SurfaceComposerClient::Transaction* t = &localTransaction;
BufferItem bufferItem;
// 获取 Available 的 BufferItem
status_t status =
mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
auto buffer = bufferItem.mGraphicBuffer;
...
Rect crop = computeCrop(bufferItem);
mLastBufferInfo.update(true /* hasBuffer */, bufferItem.mGraphicBuffer->getWidth(),
bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform,
bufferItem.mScalingMode, crop);
auto releaseBufferCallback = makeReleaseBufferCallbackThunk();
sp<Fence> fence =
bufferItem.mFence ? sp<Fence>::make(bufferItem.mFence->dup()) : Fence::NO_FENCE;
// 开始构建 Transaction
// 传入 buffer 等一系列参数
// 注意这里也传入了 releaseBufferCallback 的回调,当 SurfaceFlinger 合成完成之后,会回调这个方法
t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, mProducerId,
releaseBufferCallback, dequeueTime);
t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
t->addTransactionCompletedCallback(makeTransactionCallbackThunk(), nullptr);
mSurfaceControlsWithPendingCallback.push(mSurfaceControl);
if (mUpdateDestinationFrame) {
t->setDestinationFrame(mSurfaceControl, Rect(mSize));
} else {
const bool ignoreDestinationFrame =
bufferItem.mScalingMode == NATIVE_WINDOW_SCALING_MODE_FREEZE;
t->setFlags(mSurfaceControl,
ignoreDestinationFrame ? layer_state_t::eIgnoreDestinationFrame : 0,
layer_state_t::eIgnoreDestinationFrame);
}
t->setBufferCrop(mSurfaceControl, crop);
t->setTransform(mSurfaceControl, bufferItem.mTransform);
t->setTransformToDisplayInverse(mSurfaceControl, bufferItem.mTransformToDisplayInverse);
t->setAutoRefresh(mSurfaceControl, bufferItem.mAutoRefresh);
if (!bufferItem.mIsAutoTimestamp) {
t->setDesiredPresentTime(bufferItem.mTimestamp);
}
if (com_android_graphics_libgui_flags_apply_picture_profiles() &&
bufferItem.mPictureProfileHandle.has_value()) {
t->setPictureProfileHandle(mSurfaceControl, *bufferItem.mPictureProfileHandle);
// The current picture profile must be maintained in case the BBQ gets its
// SurfaceControl switched out.
mPictureProfileHandle = bufferItem.mPictureProfileHandle;
// Clear out the picture profile if the requestor has asked for it to be cleared
if (mPictureProfileHandle == PictureProfileHandle::NONE) {
mPictureProfileHandle = std::nullopt;
}
}
// Drop stale frame timeline infos
while (!mPendingFrameTimelines.empty() &&
mPendingFrameTimelines.front().first < bufferItem.mFrameNumber) {
ATRACE_FORMAT_INSTANT("dropping stale frameNumber: %" PRIu64 " vsyncId: %" PRId64,
mPendingFrameTimelines.front().first,
mPendingFrameTimelines.front().second.vsyncId);
mPendingFrameTimelines.pop();
}
if (!mPendingFrameTimelines.empty() &&
mPendingFrameTimelines.front().first == bufferItem.mFrameNumber) {
ATRACE_FORMAT_INSTANT("Transaction::setFrameTimelineInfo frameNumber: %" PRIu64
" vsyncId: %" PRId64,
bufferItem.mFrameNumber,
mPendingFrameTimelines.front().second.vsyncId);
t->setFrameTimelineInfo(mPendingFrameTimelines.front().second);
mPendingFrameTimelines.pop();
}
// 把 BLATSBufferQueue 内部 pending 的事物合并到一起,确保所有的变更都能同步提交给 SurfaceFlinger
mergePendingTransactions(t, bufferItem.mFrameNumber);
if (applyTransaction) {
// 通过 apply 提交到 SurfaceFlinger
t->setApplyToken(mApplyToken).apply(false, true);
mAppliedLastTransaction = true;
mLastAppliedFrameNumber = bufferItem.mFrameNumber;
} else {
t->setBufferHasBarrier(mSurfaceControl, mLastAppliedFrameNumber);
mAppliedLastTransaction = false;
}
return OK;
}
在 acquireNextBufferLocked
方法中:
- 通过
mBufferItemConsumer->acquireBuffer
获取一个BufferItem
对象,这个BufferItem
对象内部持有GraphicBuffer
- 初始化一个
SurfaceComposerClient::Transaction
事物,这里解释一下什么是Transaction
,Transaction
用于封装一组对Surface
的状态修改,并且一次原子性地提交 到SurfaceFlinger
进行处理,比如设置 Buffer,设置大小,透明度等。 - 对
SurfaceComposerClient::Transaction
进行一系列的设置 - 合并已经 pending 的
Transaction
,最后通过apply
一次性提交到SurfaceFlinger
这里重点说一下 mBufferItemConsumer->acquireBuffer
这个方法,从 BufferQueueCore
的 mQueue
中拿到一块 BufferItem
rust
status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
nsecs_t expectedPresent, uint64_t maxFrameNumber) {
ATRACE_CALL();
int numDroppedBuffers = 0;
sp<IProducerListener> listener;
{
std::unique_lock<std::mutex> lock(mCore->mMutex);
...
// 遍历 BufferQueueCore 的 mQueue
BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());
if (expectedPresent != 0 && !mCore->mQueue.empty()) {
while (mCore->mQueue.size() > 1 && !mCore->mQueue[0].mIsAutoTimestamp) {
const BufferItem& bufferItem(mCore->mQueue[1]);
// 丢弃一些过期的 buffer
nsecs_t desiredPresent = bufferItem.mTimestamp;
if (desiredPresent < expectedPresent - MAX_REASONABLE_NSEC ||
desiredPresent > expectedPresent) {
// This buffer is set to display in the near future, or
// desiredPresent is garbage. Either way we don't want to drop
// the previous buffer just to get this on the screen sooner.
BQ_LOGV("acquireBuffer: nodrop desire=%" PRId64 " expect=%"
PRId64 " (%" PRId64 ") now=%" PRId64,
desiredPresent, expectedPresent,
desiredPresent - expectedPresent,
systemTime(CLOCK_MONOTONIC));
break;
}
if (!front->mIsStale) {
// Front buffer is still in mSlots, so mark the slot as free
mSlots[front->mSlot].mBufferState.freeQueued();
// After leaving shared buffer mode, the shared buffer will
// still be around. Mark it as no longer shared if this
// operation causes it to be free.
if (!mCore->mSharedBufferMode &&
mSlots[front->mSlot].mBufferState.isFree()) {
mSlots[front->mSlot].mBufferState.mShared = false;
}
// Don't put the shared buffer on the free list
if (!mSlots[front->mSlot].mBufferState.isShared()) {
mCore->mActiveBuffers.erase(front->mSlot);
mCore->mFreeBuffers.push_back(front->mSlot);
}
if (mCore->mBufferReleasedCbEnabled) {
listener = mCore->mConnectedProducerListener;
}
++numDroppedBuffers;
}
mCore->mQueue.erase(front);
// 拿到队列的头部 BufferItem
front = mCore->mQueue.begin();
}
}
// 如果是共享内存
if (sharedBufferAvailable && mCore->mQueue.empty()) {
// make sure the buffer has finished allocating before acquiring it
mCore->waitWhileAllocatingLocked(lock);
slot = mCore->mSharedBufferSlot;
// Recreate the BufferItem for the shared buffer from the data that
// was cached when it was last queued.
outBuffer->mGraphicBuffer = mSlots[slot].mGraphicBuffer;
outBuffer->mFence = Fence::NO_FENCE;
outBuffer->mFenceTime = FenceTime::NO_FENCE;
outBuffer->mCrop = mCore->mSharedBufferCache.crop;
outBuffer->mTransform = mCore->mSharedBufferCache.transform &
~static_cast<uint32_t>(
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
outBuffer->mScalingMode = mCore->mSharedBufferCache.scalingMode;
outBuffer->mDataSpace = mCore->mSharedBufferCache.dataspace;
outBuffer->mFrameNumber = mCore->mFrameCounter;
outBuffer->mSlot = slot;
outBuffer->mAcquireCalled = mSlots[slot].mAcquireCalled;
outBuffer->mTransformToDisplayInverse =
(mCore->mSharedBufferCache.transform &
NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
outBuffer->mSurfaceDamage = Region::INVALID_REGION;
outBuffer->mQueuedBuffer = false;
outBuffer->mIsStale = false;
outBuffer->mAutoRefresh = mCore->mSharedBufferMode &&
mCore->mAutoRefresh;
} else if (acquireNonDroppableBuffer && front->mIsDroppable) {
BQ_LOGV("acquireBuffer: front buffer is not droppable");
return NO_BUFFER_AVAILABLE;
} else {
// 如果是非共享内存,则把 mQueue 的头部可用的 BufferItem 给 outBuffer
slot = front->mSlot;
*outBuffer = *front;
}
BQ_LOGV("acquireBuffer: acquiring { slot=%d/%" PRIu64 " buffer=%p }",
slot, outBuffer->mFrameNumber, outBuffer->mGraphicBuffer->handle);
if (!outBuffer->mIsStale) {
mSlots[slot].mAcquireCalled = true;
// Don't decrease the queue count if the BufferItem wasn't
// previously in the queue. This happens in shared buffer mode when
// the queue is empty and the BufferItem is created above.
if (mCore->mQueue.empty()) {
mSlots[slot].mBufferState.acquireNotInQueue();
} else {
// 更新 BufferItem 的状态为 ACQUIRED
mSlots[slot].mBufferState.acquire();
}
mSlots[slot].mFence = Fence::NO_FENCE;
}
// If the buffer has previously been acquired by the consumer, set
// mGraphicBuffer to NULL to avoid unnecessarily remapping this buffer
// on the consumer side
if (outBuffer->mAcquireCalled) {
outBuffer->mGraphicBuffer = nullptr;
}
mCore->mQueue.erase(front);
// We might have freed a slot while dropping old buffers, or the producer
// may be blocked waiting for the number of buffers in the queue to
// decrease.
#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL)
mCore->notifyBufferReleased();
#else
mCore->mDequeueCondition.notify_all();
#endif
ATRACE_INT(mCore->mConsumerName.c_str(), static_cast<int32_t>(mCore->mQueue.size()));
#ifndef NO_BINDER
mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());
#endif
VALIDATE_CONSISTENCY();
}
if (listener != nullptr) {
for (int i = 0; i < numDroppedBuffers; ++i) {
listener->onBufferReleased();
}
}
return NO_ERROR;
}
acquireBuffer
主要就是弹出 BufferQueueCore
的 mQueue
的头部 BufferItem
,并将 这个BufferItem
写入 outBuffer
。
最后,我们再看一下 SurfaceComposerClient::Transaction
的 apply
方法
scss
status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay) {
...
// 拿到 SurfaceFlinger 的 Binder 句柄
sp<ISurfaceComposer> sf(ComposerService::getComposerService());
// Binder Call, 跨进程调用 SurfaceFlinger 的 setTransactionState 方法
status_t binderStatus = sf->setTransactionState(std::move(mState));
mState.mId = generateId();
// Clear the current states and flags
clear();
if (synchronous && binderStatus == OK) {
syncCallback->wait();
}
if (mState.mLogCallPoints) {
ALOG(LOG_DEBUG, LOG_SURFACE_CONTROL_REGISTRY, "Transaction %" PRIu64 " applied", getId());
}
mStatus = NO_ERROR;
return binderStatus;
}
这里就是一个非常典型的 Binder Call 了,
- 拿到 Binder Server 的句柄,这里的 Binder Server 是
SurfaceFlinger
- 拿到句柄之后就可以直接调用 Binder Server 的方法
setTransactionState
, 并把数据作为参数传递过去。Binder 封装了底层的 IPC 通信细节,使得调用远程(跨进程)对象的方法像调用本地(本进程)对象的方法一样。
总结一下,到这里为止,
- App 渲染后的
GraphicBuffer
插入BLASTBufferQueue
BLASTBufferQueue
通过 Binder IPC 把 Buffer 提交给SurfaceFlinger
。
下一篇了解 SurfaceFlinger
如何处理从 BLASTBufferQueue
提交过来的 Buffer。