前言
BufferQueueProducer跟图形实际内容生产方连接,从BBQ队列向生产方提供空闲Graphic Buffer,并在生产方填充好数据后再返回给Buffer Queue。在整个工作过程中,BufferQueueProducer主要负责以下几个操作:
-
allocateBuffers:预分配Buffer;
-
dequeueBuffer:向图形内容生产方提供空闲Buffer,确切地说是BBQ队列中空闲Buffer的索引;
-
requesetBuffer:图形内容生产方和BBQ队列中的Buffer进行映射;
-
queueBuffer:将图形内容生产方填充好内容的Buffer及其所有权返回给BBQ队列;
-
cancelBuffer:当图形内容生产方不再需要填充Buffer时,通过该操作将Buffer所有权返回给BBQ队列;
-
attachBuffer:将图形内容生产方指定的Buffer关联到BBQ中对应的BufferSlot上,并返回BufferSlot索引值;
-
detachBuffer:将指定slot对应的BufferSlot重置回到未绑定GraphicBuffer的FREE状态。
本篇文章中,对以上操作中的四类主要操作进行分析总结。
一、allocateBuffers操作
allocateBuffers()
方法用于为BBQ队列批量申请分配GraphicBuffer。
cpp
// frameworks/native/include/gui/BufferQueueProducer.h
virtual void allocateBuffers(uint32_t width, uint32_t height,
PixelFormat format, uint64_t usage) override;
BBQ中可在两个操作时分配Buffer:
dequeueBuffer
时分配:当返回BufferSlot索引对应的BufferSlot对象未绑定GraphicBuffer、或者由于参数发生变化需要重新分配GraphicBuffer时,会申请分配;- 应用渲染线程预分配:通过
allocateBuffers
在创建完成BBQ后预先根据队列配置参数批量分配,减少dequeueBuffer
操作的耗时。
在初始化GraphicBuffer对象时,会通过Gralloc
图形内存分配器完成分配。
应用进程在ViewRootImpl中创建完成BBQ后,进行预分配GraphicBuffer的操作如下:
cpp
// frameworks/base/core/java/android/view/ViewRootImpl.java
private void performTraversals() {
......
// 预分配Buffer
mAttachInfo.mThreadedRenderer.allocateBuffers();
}
经过一系列调用,在BBQSurface中,开启子线程并调用BufferQueueProducer::allocateBuffers()
方法进行预分配:
cpp
// frameworks/native/libs/gui/BLASTBufferQueue.cpp
class BBQSurface : public Surface {
......
void allocateBuffers() override {
uint32_t reqWidth = mReqWidth ? mReqWidth : mUserWidth;
uint32_t reqHeight = mReqHeight ? mReqHeight : mUserHeight;
auto gbp = getIGraphicBufferProducer();
// 开启子线程,通过gbp申请分配Graphic Buffer
std::thread ([reqWidth, reqHeight, gbp=getIGraphicBufferProducer(),
reqFormat=mReqFormat, reqUsage=mReqUsage] () {
gbp->allocateBuffers(reqWidth, reqHeight,
reqFormat, reqUsage);
}).detach();
}
BufferQueueProducer::allocateBuffers()
方法如下:
cpp
// frameworks/native/libs/gui/BufferQueueProducer.cpp
void BufferQueueProducer::allocateBuffers(uint32_t width, uint32_t height,
PixelFormat format, uint64_t usage) {
// width、height未指定时,使用默认size
const bool useDefaultSize = !width && !height;
// 每次申请一个,循环申请,直到mCore->mFreeSlots为空(达到最大buffer数)
while (true) {
size_t newBufferCount = 0;
......
{ // Autolock scope
std::unique_lock<std::mutex> lock(mCore->mMutex);
// 如果当前mCore->mIsAllocating为true,说明处于正在分配Buffer流程中,
// 此时进入等待状态,直到上次申请流程结束后将mCore->mIsAllocating置为false时,
// 通过notify进行唤醒继续
// mCore->mIsAllocatingCondition锁用于分配Buffer过程和其他流程间的控制
mCore->waitWhileAllocatingLocked(lock);
// mCore->mAllowAllocation为false表示不允许为该队列分配Buffer
if (!mCore->mAllowAllocation) {
BQ_LOGE("allocateBuffers: allocation is not allowed for this "
"BufferQueue");
return;
}
// 如果mCore->mFreeSlots为空,说明已经申请分配完成了最大Buffer数量的GraphicBuffer,
// 此时无法申请新的Buffer.
// mCore->mFreeSlots保存还没有绑定GraphicBuffer的空闲BufferSlot在mSlots列表中
// 的索引值, 非空说明还有BufferSlot未和GraphicBuffer绑定
newBufferCount = mCore->mFreeSlots.empty() ? 0 : 1;
if (newBufferCount == 0) {
return;
}
......
// 表示正在申请分配GraphicBuffer中,该方法结尾重置为false
mCore->mIsAllocating = true;
} // Autolock scope
// 正式进行GraphicBuffer申请
Vector<sp<GraphicBuffer>> buffers;
// newBufferCount最大为1,该循环只执行1次
for (size_t i = 0; i < newBufferCount; ++i) {
// 申请分配GraphicBuffer
sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
allocWidth, allocHeight, allocFormat, BQ_LAYER_COUNT,
allocUsage, allocName);
status_t result = graphicBuffer->initCheck();
......
// 申请的GraphicBuffer保存到列表中
buffers.push_back(graphicBuffer);
}
{ // Autolock scope
......
for (size_t i = 0; i < newBufferCount; ++i) {
.......
// 取出mCore->mFreeSlots第一个索引元素, 将mSlots中对应BufferSlot绑定该Buffer
auto slot = mCore->mFreeSlots.begin();
// 清除mSlots[*slot]上的旧数据
mCore->clearBufferSlotLocked(*slot); // Clean up the slot first
// 将GraphicBuffer赋给mSlots[*slot].mGraphicBuffer
mSlots[*slot].mGraphicBuffer = buffers[i];
// 初始化一个无效Fence对象
mSlots[*slot].mFence = Fence::NO_FENCE;
// 将索引值存储在mCore->mFreeBuffers中,
// mCore->mFreeBuffers用于存放已申请完成Buffer但未被使用的BufferSlot数组索引值
// 或者说用于存放处于空闲且已经绑定Buffer的BufferSlot数组索引值
mCore->mFreeBuffers.push_front(*slot);
// 将索引值从mCore->mFreeSlots中移除
// 一旦mSlot[slot]绑定了Buffer,则需要将该slot从mCore->mFreeSlots移除,
// 保存到mCore->mFreeBuffers中
mCore->mFreeSlots.erase(slot);
}
// 分配过程结束,唤醒等待线程(如有)
mCore->mIsAllocating = false;
mCore->mIsAllocatingCondition.notify_all();
// mDequeueWaitingForAllocation为true表示dequeueBuffer操作在等待申请过程的完成
// dequeueBuffer继续后,会唤醒mDequeueWaitingForAllocationCondition阻塞线程
while (mDequeueWaitingForAllocation) {
mDequeueWaitingForAllocationCondition.wait(lock);
}
} // Autolock scope
} // while true
}
这里再对以上方法中使用到的两个条件锁和两个容器进行下说明。
1.1、mCore->mIsAllocatingCondition锁
mCore->mIsAllocatingCondition
锁用于申请GraphicBuffer流程和其他操作的同步,当mCore->mIsAllocating
为true时,表示正在进行申请分配Buffer操作,此时会通过该锁进入等待状态,直到上次申请分配流程完成后,mCore->mIsAllocating
会重置为false,然后唤醒此次等待流程继续执行。
1.2、mDequeueWaitingForAllocationCondition锁
mDequeueWaitingForAllocationCondition
锁用于allocateBuffers
和dequeueBuffer
操作的同步。
如果RenderThread线程在执行dequeueBuffer
操作时,线程A正在进行allocateBuffers
操作,此时在dequeueBuffer
中,会首先将mDequeueWaitingForAllocation
设置为true,然后执行mIsAllocatingCondition.wait()
进入阻塞状态;
allocateBuffers
中每完成一个Buffer分配后,会先执行mIsAllocatingCondition.notify_all()
唤醒被Block线程,然后根据mDequeueWaitingForAllocation
是否为true决定是否通过mDequeueWaitingForAllocationCondition
Block当前线程。
通过这两个锁,即保证了数据的同步,而且还降低了dequeueBuffer
的延时。
1.3、mCore->mFreeSlots列表
mCore->mFreeSlots
数组用于存放当前未绑定GraphicBuffer的BufferSlot对象在mCore->mSlots
数组中的索引值。
在初始化BufferQueueCore时就会对mCore->mFreeSlots
进行填充值,allocateBuffers
操作时,根据该数组是否为空确定是否申请分配新的Buffer,为空时说明已全部绑定了GraphicBuffer,将不会申请新Buffer。
allocateBuffers
时,每申请一个Buffer,就会从mCore->mFreeSlots
中按照顺序取出一个slot,并将该slot从mCore->mFreeSlots
中移除。
1.4、mCore->mFreeBuffers列表
mCore->mFreeBuffers
列表用于存放已完成GraphicBuffer绑定、但未被使用(处于FREE状态)的BufferSlot对象在mCore->mSlots
数组中的索引值(BufferSlot::mGraphicBuffer != nullptr)。
allocateBuffers
时,从mCore->mFreeSlots
中按顺序取出索引slot
,然后将新分配的GraphicBuffer对象赋值给mSlots[slot].mGraphicBuffer
,并将slot
保存在mCore->mFreeBuffers
中,意味着当前BBQ中已经有FREE且可用的GraphicBuffer。
allocateBuffer
操作过程中BBQ内部变化表示如下:
- 刚创建完成BBQ时,BufferQueueCore中
mCore->mSlots
数组中的BufferSlot都没有和GraphicBuffer关联; - 进行
allocateBuffers
后,每成功分配一个GraphicBuffer,mCore->mFreeSlots
数组元素逐次减一,mCore->mFreeBuffers
列表逐次增一,直到mCore->mFreeSlots
为空。
目前BBQ默认最大Buffer数量4个,因此在初始化时,会申请4个GraphicBuffer。
二、dequeueBuffer操作
dequeueBuffer
操作为图形内容生产方获取一个Graphic Buffer的所有权。
在BufferQueueProducer中,会将mCore->mSlots
数组中空闲BufferSlot对象的索引值通过slot
参数返回。
返回slot
所在BufferSlot对象此时可能没有跟具体的GraphicBuffer绑定,这种情况时,客户端需要执行requestBuffer
操作来重新分配新的GraphicBuffer完成绑定。
cpp
// frameworks/native/include/gui/IGraphicBufferProducer.h
virtual status_t dequeueBuffer(int* slot, sp<Fence>* fence, uint32_t w, uint32_t h,
PixelFormat format, uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) = 0;
参数
-
slot
:返回给图形内容生产方的GraphicBuffer在Buffer队列中的索引值; -
fence
:跟GraphicBuffer关联的Fence对象,作为acquire Fence监听GPU是否完成绘制,Fence::NO_FENCE表示初始无效Fence对象,会由图形内容生产方创建; -
w/h
:获取GraphicBuffer的宽和高,为0时使用BBQ中的默认值; -
format
:获取Graphic Buffer的格式,为0时使用BBQ中的默认值; -
usage
:指定Graphic Buffer使用方式,GRALLOC_USAGE_HW_RENDER
表示GB可以被当做OpenGL ES的渲染目标; -
outBufferAge
:自完成上一次queueBuffer后经过的帧数; -
outTimestamps
:用于帧数据统计。
返回值
dequeueBuffer
返回值是一个可以包含不同标记的int值,当成功获得空闲Buffer时,返回值为非负数:
NO_ERROR
:默认返回值;BUFFER_NEEDS_REALLOCATION
:表示图形生产方需要立即执行requestBuffer
重新分配GraphicBuffer;
当dequeueBuffer
失败时,返回负数:
NO_INIT
:表示该BBQ已经废弃,与Consumer断开连接;BAD_VALUE
:参数错误、或给Shared Buffer Slot重新分配Buffer时返回;INVALID_OPERATION
:处于DEQUEUED
状态的Buffer数量大于等于最大dequeue buffer数量时返回(mCore->mMaxDequeuedBufferCount
);WOULD_BLOCK
:当没有空闲Buffer时,默认进入阻塞状态(同步模式)直到有空闲Buffer时才返回。如果是异步模式,则会返回WOULD_BLOCK;TIMED_OUT
:阻塞时间超过dequeue timeout还未获得空闲Buffer时返回。
该方法如下:
cpp
// frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
uint32_t width, uint32_t height, PixelFormat format,
uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) {
......
{ // Autolock scope
// 如果mCore->mFreeBuffers中没有可用Buffer,且正在进行申请Buffer流程,则在这里进行等待;
// 申请Buffer完成后唤醒,同时通过mDequeueWaitingForAllocation通知allocateBuffer
// dequeueBuffer不再等待
if (mCore->mFreeBuffers.empty() && mCore->mIsAllocating) {
mDequeueWaitingForAllocation = true;
mCore->waitWhileAllocatingLocked(lock);
mDequeueWaitingForAllocation = false;
mDequeueWaitingForAllocationCondition.notify_all();
}
......
// while循环中获取空闲BufferSlot索引
int found = BufferItem::INVALID_BUFFER_SLOT;
while (found == BufferItem::INVALID_BUFFER_SLOT) {
// waitForFreeSlotThenRelock中获取空闲BufferSlot索引
status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
if (status != NO_ERROR) {
return status;
}
......
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
// Buffer参数变化后的重新分配操作
// mCore->mAllowAllocation为false时,waitForFreeSlotThenRelock返回的Slot对应
// BufferSlot肯定已绑定GraphicBuffer,这时需要判断Buffer参数是否发生变化
// 若发生变化,清空该BufferSlot,并重新执行waitForFreeSlotThenRelock
if (!mCore->mAllowAllocation) {
if (buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {
......
mCore->mFreeSlots.insert(found);
mCore->clearBufferSlotLocked(found);
found = BufferItem::INVALID_BUFFER_SLOT;
continue;
}
}
}
// 获取对应索引位置GraphicBuffer
const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
//Shard Buffer mode的Buffer无法重新分配
if (mCore->mSharedBufferSlot == found &&
buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage)) {
return BAD_VALUE;
}
// 标记为ActiveBuffer
if (mCore->mSharedBufferSlot != found) {
mCore->mActiveBuffers.insert(found);
}
// 将索引赋值给outSlot
*outSlot = found;
// BufferSlot.mNeedsReallocation值为true时,说明该BufferSlot重新绑定了新GraphicBuffer,
// 这时需要添加标记BUFFER_NEEDS_REALLOCATION通知Surface,防止使用过时GraphicBuffer
attachedByConsumer = mSlots[found].mNeedsReallocation;
// 重置mSlots[found].mNeedsReallocation
mSlots[found].mNeedsReallocation = false;
// 将BufferSlot.mBufferState状态设置为DEQUEUED状态
mSlots[found].mBufferState.dequeue();
// 如果found来自mCore->mFreeSlots,则Buffer为空,或Buffer需要reallocate时,
// 重置BufferSlot对象中所有属性
if ((buffer == nullptr) ||
buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
{
mSlots[found].mAcquireCalled = false;
mSlots[found].mGraphicBuffer = nullptr;
mSlots[found].mRequestBufferCalled = false;
mSlots[found].mEglDisplay = EGL_NO_DISPLAY;
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE;
mCore->mBufferAge = 0;
mCore->mIsAllocating = true;
// 添加BUFFER_NEEDS_REALLOCATION标记
returnFlags |= BUFFER_NEEDS_REALLOCATION;
} else {
// 用于记录最近一次dequeueBuffer返回Buffer的年龄,即自上次queueBuffer后经过的帧数,总帧数 - 该Buffer帧数
mCore->mBufferAge = mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber;
}
eglDisplay = mSlots[found].mEglDisplay;
eglFence = mSlots[found].mEglFence;
// 设置acquire Fence
*outFence = (mCore->mSharedBufferMode &&
mCore->mSharedBufferSlot == found) ?
Fence::NO_FENCE : mSlots[found].mFence;
// 重置BufferSlot对象mEglFence和mFence
mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
mSlots[found].mFence = Fence::NO_FENCE;
......
// 发起mConsumerListener->onFrameDequeued回调
if (!(returnFlags & BUFFER_NEEDS_REALLOCATION)) {
if (mCore->mConsumerListener != nullptr) {
mCore->mConsumerListener->onFrameDequeued(mSlots[*outSlot].mGraphicBuffer->getId());
}
}
}
// 执行reallocate Buffer操作
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
// 通过new GraphicBuffer申请Buffer
sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
width, height, format, BQ_LAYER_COUNT, usage,
{mConsumerName.string(), mConsumerName.size()});
status_t error = graphicBuffer->initCheck();
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
if (error == NO_ERROR && !mCore->mIsAbandoned) {
graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
// 将新GraphicBuffer跟BufferSlot绑定
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
if (mCore->mConsumerListener != nullptr) {
mCore->mConsumerListener->onFrameDequeued(
mSlots[*outSlot].mGraphicBuffer->getId());
}
}
// 分配Buffer结束,唤醒mIsAllocatingCondition
mCore->mIsAllocating = false;
mCore->mIsAllocatingCondition.notify_all();
......
} // Autolock scope
}
// 设置BUFFER_NEEDS_REALLOCATION标记
if (attachedByConsumer) {
returnFlags |= BUFFER_NEEDS_REALLOCATION;
}
......
return returnFlags;
}
以上方法中,主要有以下几个较为关键的内容:
- 通过
waitForFreeSlotThenRelock()
方法获得空闲BufferSlot索引; - 当Buffer参数等发生变化需要重新分配时,进行reallocateBuffer的操作;
- 将获得索引值添加到
mCore->mActiveBuffers
列表中,同时通过mSlots[found].mBufferState.dequeue()
将该BufferSlot状态设置为DEQUEUED
状态。
2.1、waitForFreeSlotThenRelock()获取空闲BufferSlot索引
在dequeueBuffer
操作中,实际在waitForFreeSlotThenRelock()
方法中获取空闲BufferSlot索引,默认场景下(同步模式),当获取不到空闲BufferSlot索引时,将阻塞当前线程,直到获得空闲BufferSlot索引:
cpp
// frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
std::unique_lock<std::mutex>& lock, int* found) const {
bool tryAgain = true;
while (tryAgain) {
// 如果BufferQueue已废弃,直接返回NO_INIT,disconnect()后将废弃
if (mCore->mIsAbandoned) {
return NO_INIT;
}
// 统计DEUEUED和ACQUIRED的Buffer个数
int dequeuedCount = 0;
int acquiredCount = 0;
for (int s : mCore->mActiveBuffers) {
if (mSlots[s].mBufferState.isDequeued()) {
++dequeuedCount;
}
if (mSlots[s].mBufferState.isAcquired()) {
++acquiredCount;
}
}
// 获取BBQ最大Buffer数,默认为4个
const int maxBufferCount = mCore->getMaxBufferCountLocked();
// 当producer出现disconnect又重新connect场景时,Buffer最大数量如果发生调整,
// 且mCore->mQueue > maxBufferCount时,会先等待mCore->mQueue中的完成使用和释放,
// 避免出现溢出
bool tooManyBuffers = mCore->mQueue.size()
> static_cast<size_t>(maxBufferCount);
if (tooManyBuffers) {
BQ_LOGV("%s: queue size is %zu, waiting", callerString,
mCore->mQueue.size());
} else {
// 如果有shared buffer存在,则直接返回Shared buffer slot
if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot !=
BufferQueueCore::INVALID_BUFFER_SLOT) {
*found = mCore->mSharedBufferSlot;
} else {
if (caller == FreeSlotCaller::Dequeue) {
// 优先从mCore->mFreeBuffers列表中获取BufferSlot索引,
// 如果mCore->mFreeBuffers为空,则再从mCore->mFreeSlots中获取索引
// mCore->mFreeSlots中获得的Slot没有绑定Buffer, 需要重新分配
int slot = getFreeBufferLocked();
if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
*found = slot;
} else if (mCore->mAllowAllocation) {
*found = getFreeSlotLocked();
}
} else {
// attachBuffer操作
}
}
}
// 没有获得空闲BufferSlot索引、或QUEUED状态Buffer数大于BBQ最大Buffer数时,
// 在同步模式下会进入等待,直到获得slot
tryAgain = (*found == BufferQueueCore::INVALID_BUFFER_SLOT) ||
tooManyBuffers;
if (tryAgain) {
// 非阻塞模式(异步模式)时直接返回WOULD_BLOCK
if ((mCore->mDequeueBufferCannotBlock || mCore->mAsyncMode) &&
(acquiredCount <= mCore->mMaxAcquiredBufferCount)) {
return WOULD_BLOCK;
}
// 设置了dequeue timeout时,到达耗时时间后返回TIMED_OUT
// 默认为-1, BBQ初始化时会设置为最大int值
if (mDequeueTimeout >= 0) {
std::cv_status result = mCore->mDequeueCondition.wait_for(lock,
std::chrono::nanoseconds(mDequeueTimeout));
if (result == std::cv_status::timeout) {
return TIMED_OUT;
}
} else {
// 进入wait状态,直到有Buffer空出时唤醒
mCore->mDequeueCondition.wait(lock);
}
}
} // while (tryAgain)
return NO_ERROR;
}
这个方法中,会从mCore->mFreeBuffers
或mCore->mFreeSlots
中获取空闲BufferSlot索引,并且优先从mCore->mFreeBuffers
列表中获取:
- 如果
mCore->mFreeBuffers
列表不为空,则从该列表中获取第一个Slot索引返回; - 如果
mCore->mFreeBuffers
列表已经为空,则从mCore->mFreeSlots
数组中获取一个未绑定GraphicBuffer的slot,并在dequeueBuffer
后续操作执行分配Buffer操作后绑定GB到对应BufferSlot上。
从以上两列表获取slot方法如下:
cpp
// frameworks/native/libs/gui/BufferQueueProducer.cpp
// 返回mCore->mFreeBuffers的头元素
int BufferQueueProducer::getFreeBufferLocked() const {
if (mCore->mFreeBuffers.empty()) {
return BufferQueueCore::INVALID_BUFFER_SLOT;
}
int slot = mCore->mFreeBuffers.front();
mCore->mFreeBuffers.pop_front();
return slot;
}
// 返回mCore->mFreeSlots的头元素
int BufferQueueProducer::getFreeSlotLocked() const {
if (mCore->mFreeSlots.empty()) {
return BufferQueueCore::INVALID_BUFFER_SLOT;
}
int slot = *(mCore->mFreeSlots.begin());
mCore->mFreeSlots.erase(slot);
return slot;
}
接下来便根据BBQ模式(默认同步模式)、mDequeueTimeout值和获取结果决定是否要进入等待状态。如果当前已经超过最大Buffer个数或没有空闲BufferSlot时:
- 同步模式下:通过
mCore->mDequeueCondition
进入等待状态,直到有空闲BufferSlot或到mDequeueTimeout超时时间后唤醒; - 异步模式下:直接返回
WOULD_BLOCK
。
2.2、reallocateBuffer操作
dequeueBuffer
中会根据获得的BufferSlot和Surface中传递Buffer参数决定是否进行重新分配Buffer的操作。以下两种情形下会重新分配Buffer:
- 获得的BufferSlot未绑定GraphicBuffer时;
- 获得的BufferSlot已绑定GraphicBuffer,但请求Buffer参数发生变化时。
这两种场景时,会将BufferSlot参数重置,并重新初始化一个GraphicBuffer对象绑定到BufferSlot上。
此外,会在dequeueBuffer()
返回值上添加BUFFER_NEEDS_REALLOCATION
标记,通知Surface执行主动执行requestBuffer()
操作。
2.3、BufferSlot状态更新
成功获得空闲BufferSlot后,索引值会从两个空闲列表中移除,并添加到mCore->mActiveBuffers
中,同时BufferSlot状态将由FREE
变为DEQUEUED
:
cpp
// frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
uint32_t width, uint32_t height, PixelFormat format,
uint64_t usage, uint64_t* outBufferAge,
FrameEventHistoryDelta* outTimestamps) {
......
if (mCore->mSharedBufferSlot != found) {
mCore->mActiveBuffers.insert(found);
}
*outSlot = found;
mSlots[found].mBufferState.dequeue();
......
}
mCore->mActiveBuffers
列表用于保存所有处于非空闲状态的BufferSlot对象在mCore->mSlots
数组中的索引值,包括处于DEQUEUED
、QUEUED
、ACQUIRED
状态的BufferSlot索引。
dequeueBuffer
操作过程中BBQ变化过程如下所示:
三、requesetBuffer操作
dequeueBuffer
向图形内容生产方返回的是Buffer队列中空闲Buffer的索引,并非Buffer对象本身,这样做传递成本太大,因此在BBQ中每初始化一个GraphicBuffer对象,都必须映射给对应的图形内容生产方。这个映射操作通过requesetBuffer
进行。
在Surface中也定义了一个同BBQ大小的mSlots
数组,当dequeueBuffer
第一次返回一个索引时,Surface::mSlots
中对应BufferSlot未绑定GraphicBuffer,这种情况下会调用requestBuffer
操作,将mCore->mSlots[slot].mGraphicBuffer
赋值给Surface::mSlots[slot].mGraphicBuffer
,完成映射。
BufferQueueProducer::requestBuffer()
方法如下:
cpp
// frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
std::lock_guard<std::mutex> lock(mCore->mMutex);
.......
mSlots[slot].mRequestBufferCalled = true;
// 将mSlots[slot].mGraphicBuffer赋值给参数buf
*buf = mSlots[slot].mGraphicBuffer;
return NO_ERROR;
}
还有一种情况是:如果BBQ中由于Buffer参数发生变化,重新分配Buffer后,通过返回值携带BUFFER_NEEDS_REALLOCATION
标记告知图形内容生产方重新映射GraphicBuffer,在Surface::dequeueBuffer()
中:
cpp
// frameworks/native/libs/gui/Surface.cpp
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
......
// 执行dequeueBuffer操作
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, dqInput.width,
dqInput.height, dqInput.format,
dqInput.usage, &mBufferAge,
dqInput.getTimestamps ?
&frameTimestamps : nullptr);
......
// 根据返回索引获取GraphicBuffer对象
sp<GraphicBuffer>& gbuf(mSlots[buf].buffer);
......
// 如果GraphicBuffer为空或者返回值携带BUFFER_NEEDS_REALLOCATION标记时,
// 主动进行requestBuffer操作
if ((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
}
......
return OK;
}
四、queueBuffer操作
当图形内容生产方向Buffer中完成数据填充后,通过queueBuffer
操作将Buffer所有权重新返回给BBQ队列。
cpp
// frameworks/native/include/gui/IGraphicBufferProducer.h
virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
QueueBufferOutput* output) = 0;
参数
slot
:对应BufferSlot在mSlots
数组中的索引值;QueueBufferInput& input
:客户端中给GraphicBuffer填充的所有属性,如scalingMode、dataspace、transform等;QueueBufferOutput* output
:执行完成queueBuffer
后向客户端返回的信息参数;
返回值
queueBuffer
操作执行成功时,返回NO_ERROR
;
执行失败时,返回一个负数:
NO_INIT
:表示该BufferQueue已经被废弃,Producer已经断开连接;BAD_VALUE
:参数错误时返回,如fence为空、scaling mode值异常、slot索引越界等。
BufferQueueProducer::queueBuffer()
方法如下:
cpp
// frameworks/native/libs/gui/BufferQueueProducer.cpp
status_t BufferQueueProducer::queueBuffer(int slot,
const QueueBufferInput &input, QueueBufferOutput *output) {
......
// 反序列化QueueBufferInput获取客户端写入的参数
input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
&crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
&getFrameTimestamps);
const Region& surfaceDamage = input.getSurfaceDamage();
const HdrMetadata& hdrMetadata = input.getHdrMetadata();
// fence、scaling mode等属性校验
......
// 初始化BufferItem,BufferItem封装了所有来自客户端输入的图形数据
BufferItem item;
{ // Autolock scope
std::lock_guard<std::mutex> lock(mCore->mMutex);
// 异常情况校验和处理,略
......
// 设置acquireFence
mSlots[slot].mFence = acquireFence;
// 将对应Buffer状态由DEQUEUED状态转变为QUEUED状态
mSlots[slot].mBufferState.queue();
// 帧数统计+1
++mCore->mFrameCounter;
// 当前帧序列号
currentFrameNumber = mCore->mFrameCounter;
mSlots[slot].mFrameNumber = currentFrameNumber;
// 开始填充BufferItem
// 该BufferSlot是否已经执行过acquireBuffer的操作
item.mAcquireCalled = mSlots[slot].mAcquireCalled;
// 获取BufferSlot中的GraphicBuffer
item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
// GraphicBuffer的裁剪区域
item.mCrop = crop;
// GraphicBuffer区域的转换标记,如横纵向翻转、旋转等
item.mTransform = transform &
~static_cast<uint32_t>(NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
// GraphicBuffer区域的转换方式标记,表示是否需要屏幕逆矩阵进行转置
item.mTransformToDisplayInverse =
(transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
// GraphicBuffer的缩放模式
item.mScalingMode = static_cast<uint32_t>(scalingMode);
// 客户端进行queueBuffer的当前时间戳,单调递增
item.mTimestamp = requestedPresentTimestamp;
// 是否自动生成时间戳
item.mIsAutoTimestamp = isAutoTimestamp;
// GraphicBuffer的dataspace
item.mDataSpace = dataSpace;
// BufferSlot对应的HDR元数据
item.mHdrMetadata = hdrMetadata;
// BufferSlot进行queueBuffer的次数
item.mFrameNumber = currentFrameNumber;
// BufferSlot索引
item.mSlot = slot;
// GraphicBuffer对应的acquireFence
item.mFence = acquireFence;
// acquireFence Signal时间
item.mFenceTime = acquireFenceTime;
// 该Buffer是否可丢弃,异步模式时会设置为true
item.mIsDroppable = mCore->mAsyncMode ||
(mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) ||
(mCore->mLegacyBufferDrop && mCore->mQueueBufferCanDrop) ||
(mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
// 相比上一帧的赃区域
item.mSurfaceDamage = surfaceDamage;
// 表示BufferSlot已执行queueBuffer操作
item.mQueuedBuffer = true;
// 表示Consumer是否可以在不用等待Buffer可用时就可以获取下一帧,仅用于Share buffer mode
item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh;
// BBQ连接的Producer API
item.mApi = mCore->mConnectedApi;
mStickyTransform = stickyTransform;
// 共享模式时,缓存相关数据
if (mCore->mSharedBufferMode) {
mCore->mSharedBufferCache.crop = crop;
mCore->mSharedBufferCache.transform = transform;
mCore->mSharedBufferCache.scalingMode = static_cast<uint32_t>(
scalingMode);
mCore->mSharedBufferCache.dataspace = dataSpace;
}
output->bufferReplaced = false;
if (mCore->mQueue.empty()) {
// 当队列为空时,直接将BufferItem放入队列即可
mCore->mQueue.push_back(item);
// 初始化frameAvailableListener接口
frameAvailableListener = mCore->mConsumerListener;
} else {
// 队列不为空时,需要判断队列中的最后一个BufferItem是否可丢弃来选择替换旧Item还是插入
const BufferItem& last = mCore->mQueue.itemAt(
mCore->mQueue.size() - 1);
// last BufferItem是否是可丢弃的
if (last.mIsDroppable) {
// last BufferItem是否已过时,如果还没有过时,这里会进行强制FREE操作,仅针对非共享的Buffer
if (!last.mIsStale) {
// 将对应BufferSlot退出QUEUED状态
mSlots[last.mSlot].mBufferState.freeQueued();
// 非共享模式时,重置给BufferSlot.mShared属性
if (!mCore->mSharedBufferMode &&
mSlots[last.mSlot].mBufferState.isFree()) {
mSlots[last.mSlot].mBufferState.mShared = false;
}
// 从mCore->mActiveBuffers中移除,添加到mCore->mFreeBuffers中,
// Buffer状态变为空闲(FREE)
if (!mSlots[last.mSlot].mBufferState.isShared()) {
mCore->mActiveBuffers.erase(last.mSlot);
mCore->mFreeBuffers.push_back(last.mSlot);
// 表示BufferSlot发生了替换
output->bufferReplaced = true;
}
}
......
// 将队列中最后一个BufferItem替换
mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
frameReplacedListener = mCore->mConsumerListener;
} else {
// 说明last bufferItem是不可丢弃的,这时直接插入新的BufferItem
mCore->mQueue.push_back(item);
frameAvailableListener = mCore->mConsumerListener;
}
}
// 表示BufferItem已经完成了入对
mCore->mBufferHasBeenQueued = true;
// 唤醒dequeueBuffer流程
mCore->mDequeueCondition.notify_all();
mCore->mLastQueuedSlot = slot;
// 填充给客户端同步的QueueBufferOutput对象
output->width = mCore->mDefaultWidth;
output->height = mCore->mDefaultHeight;
output->transformHint = mCore->mTransformHintInUse = mCore->mTransformHint;
output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
output->nextFrameNumber = mCore->mFrameCounter + 1;
......
} // Autolock scope
//
if (!mConsumerIsSurfaceFlinger) {
item.mGraphicBuffer.clear();
}
......
{ // scope for the lock
// mCallbackCondition是为了保证onFrame*方法在下一次queueBuffer到来之前可以可以有序执行,
std::unique_lock<std::mutex> lock(mCallbackMutex);
while (callbackTicket != mCurrentCallbackTicket) {
mCallbackCondition.wait(lock);
}
// 执行回调,通知Consumer
if (frameAvailableListener != nullptr) {
frameAvailableListener->onFrameAvailable(item);
} else if (frameReplacedListener != nullptr) {
frameReplacedListener->onFrameReplaced(item);
}
connectedApi = mCore->mConnectedApi;
lastQueuedFence = std::move(mLastQueueBufferFence);
// 更新mLastQueueBufferFence
mLastQueueBufferFence = std::move(acquireFence);
++mCurrentCallbackTicket;
mCallbackCondition.notify_all();
}
// 等待上一帧Buffer对应acquireFence是否Signal
if (connectedApi == NATIVE_WINDOW_API_EGL) {
lastQueuedFence->waitForever("Throttling EGL Production");
}
return NO_ERROR;
}
在这个方法中,会根据Slot索引,找到对应BufferSlot的GraphicBuffer,并将其他填充的参数封装到BufferItem中,然后将BufferItem存储到mCore->mQueue
中。
4.1、BufferSlot状态更新
对QueueBufferInput参数进行反序列化获取来自客户端传入的数据后, 首先是对mSlots[slot]
中对应的BufferSlot对象进行状态更新:
ini
// frameworks/native/libs/gui/BufferQueueProducer.cpp
// 更新acquireFence,用于检测GPU是否完成绘制
mSlots[slot].mFence = acquireFence;
// BufferSlot状态由DEQUEUED变为QUEUED
mSlots[slot].mBufferState.queue();
// 更新帧序列号
mSlots[slot].mFrameNumber = currentFrameNumber;
其中,在执行完成mSlots[slot].mBufferState.queue()
后,BufferSlot状态将由DEQUEUED
状态转换为QUEUED
状态。
4.2、填充BufferItem对象
BufferItem封装了所有来自客户端输入的图形数据,传递给SurfaceFlinger的GraphicBuffer属性基本也全都是来自于BufferItem中。
在queueBuffer
操作中,会完成BufferItem的填充,并在填充完毕后,将BufferItem放入mCore->mQueue
列表,进行入队操作。
在BufferItem入队时,如果是异步模式或Share Buffer模式,可能会对mCore->mQueue
队列中最后一个BufferItem元素进行替换,其他情况时直接将BufferItem放入队尾。
4.3、执行frameAvailableListener接口回调
在该步操作的最后部分,将mCore->mConsumerListener
赋给了frameAvailableListener
,并执行frameAvailableListener->onFrameAvailable(item)
方法,BLASTBufferQueue类实现了mCore->mConsumerListener
接口,通过这个方法将开始BBQ中的消费者组件进行消费的操作。
还有一点要说明的是,在queueBuffer
最后,会检查lastQueuedFence
是否已经Signal,如果还未Signal,则会进入等待状态阻塞当前进程。lastQueuedFence
是上一帧Buffer对应的acquireFence对象,所以这步操作,主要目的是限制QUEUED
状态Buffer的个数最多不能超过2个,当某一帧Buffer的上一帧还没有绘制完成时,其下一帧将无法进入QUEUED
状态。
queueBuffer
操作过程中BBQ变化过程如下所示: