BufferQueue
BufferQueue要解决的是生产者和消费者的同步问题,应用程序产生画面,SurfaceFlinger 消费画面;SurfaceFlinger 生成画面而HWC service 消费画面;用来存储这些画面的区域我们称为缓冲区,为此需要按照下面需求设计:
- 需要有缓冲区供生产者消费者使用
- 生产者生产完成需要能够通知到消费者
- 生产者一般为app,消费者是 sf,因此需要拷贝跨进程通信的问题
下面我们以应用程序为生产者,SurfaceFlinger 为消费者为例,了解一下BufferQueue 的内部设计
BufferState 的切换
在BufferQueue 的设计中,Buffer 存在下面几种状态:
FREE:表示该Buffer 是空闲的,可以给到应用程序,由应用程序来绘图
DEQUEUED:表示Buffer 的控制权已经交给了应用程序侧,这个状态下应用程序可以在上面绘图了
QUEUED:表示该Buffer已经由应用程序绘图完成,Buffer 的控制权已经回到SurfaceFlinger 了
ACQUIRED:表示该Buffer 已经交给 HWC service去合成了,这个时候控制权已经给到HWC service了
Buffer 的初始状态是Free
- 当生产者通过dequeueBuffer 来申请Buffer 成功时,buffer 的状态变为 QEQUEUED
- 应用程序绘制完成后通过queueBuffer 把BufferState 状态改为 QUEUED 状态
- SurfaceFlinger 通过 acquiredBuffer 把 Buffer 拿去给 HWC service合成,这时的Buffer 变为 ACQUIRED 状态
-
合成完成后通过 release buffer 把 Buffer 状态改为FREE状态,
状态切换图如下图所示:
从时间轴上一个Buffer 的变化过程是:
FREE->DEQUEUED->QUEUED->ACQUIRED->FREE
BufferSlot
每一个应用程序图层在SurfaceFlinger 里称为一个Layer,每个Layer都有一个独特的BufferQueue,每个 BufferQueue都会有多个Buffer,目前Android 系统上单图层最多支持 64个Buffer
BufferSlot 的定义如下:
framework/native/libs/gui/include/gui/BufferQueueDefs.h
namespace android {
class BufferQueueCore;
namespace BufferQueueDefs {
typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
} // namespace BufferQueueDefs
} // namespace android
SlotsType 被 typedef 定义成了数组类型,数组元素是BufferSlot,数组的大小为NUM_BUFFER_SLOTS(64)
BufferSlot 是对GraphicBuffer 的封装,重要成员如下:
struct BufferSlot {
.....
BufferState mBufferState; //当前的 Buffer 状态,FREE/DEQUEUED/QUEUED/ACQUIRED
sp<GraphicBuffer> mGraphicBuffer; // 代表了Buffer 存储空间
uint64_t mFrameNumber; //表示这个slot被queued的编号,在应用调dequeueBuffer申请slot时会参考该值
sp<Fence> mFence;
}
64 个 BufferSlot 可以分为两部分,usedslot(使用中的) 和 unusedlot(未使用中的)
usedslot = active slot + unactive slot
而usedslot又可以分为 active slot 和 unactive slot,处在 DEQUEUED,QUEUED,ACQUIRED状态的被称为active slot,剩下FREE状态的称为unactive Slots;
所有activeSlots都是有人正在使用中的 slot,使用者可以是生产者也可以是消费者
unactive slot = Free state slot = mFreeBuffers + mFreeSlots
而Free 状态的slot 根据是否为其分配内存可以分为mFreeBuffers 和 mFreeSlots
- mFreeBuffers:已经分配过内存的
- mFreeSlots:没有分配过内存的
如果从代码中看到mFreeSlots中拿出一个 Bufferslot 那说明这个Bufferslot 还没有配置过GraphicBuffer的,这个slot 可能第一次用到
Buffer 的分配流程
framework/native/libs/gui/Surface.cpp
- 应用第一次dequeueBuffer前会通过connect接口和SurfaceFlinger 连接
int Surface::connect(
int api, const sp<IProducerListener>& listener, bool reportBufferRemoval) {
ATRACE_CALL();
...
int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output);
}
- 调用 dequeueBuffer时会先调用requestBuffer
result 标志第一次会带 BUFFER_NEEDS_REALLOCATION 标志
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
ATRACE_CALL();//这里可以在systrace中看到
......
//这里尝试去dequeueBuffer,因为这时SurfaceFlinger对应Layer的slot还没有分配buffer,这时SurfaceFlinger会回复的flag会有BUFFER_NEEDS_REALLOCATION
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth, reqHeight,
reqFormat, reqUsage, &mBufferAge,
enableFrameTimestamps?&frameTimestamps:nullptr);
......
if((result & IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION) || gbuf == nullptr) {
......
//这里检查到dequeueBuffer返回的结果里带有BUFFER_NEEDS_REALLOCATION标志就会发出一次requestBuffer
result = mGraphicBufferProducer->requestBuffer(buf, &gbuf);
......
}
......
}
- 因为应用侧 Surface 对象中 包含的是 GraphicBufferProducer 的 binder client端对象,所以实际的调用是在
Surfaceflinger 进程的 server 端 BufferQueueProducer.cpp,注意dequeueBuffer 实际返回的是一个空间 slot
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 ((buffer == NULL) ||
buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))//检查是否已分配了GraphicBuffer
{
......
returnFlags |= BUFFER_NEEDS_REALLOCATION;//发现需要分配buffer,置个标记
}
......
if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
......
//新创建一个新的GraphicBuffer给到对应的slot
sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
width, height, format, BQ_LAYER_COUNT, usage,
{mConsumerName.string(), mConsumerName.size()});
......
mSlots[*outSlot].mGraphicBuffer = graphicBuffer;//把GraphicBuffer给到对应的slot
......
}
......
return returnFlags;//注意在应用第一次请求buffer, dequeueBuffer返回时对应的GraphicBuffer已经创建完成并给到了对应的slot上,但返回给应用的flags里还是带有BUFFER_NEEDS_REALLOCATION标记的
}
根据 client 端的调用,会继续调用到requestBuffer
status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
ATRACE_CALL();
mSlots[slot].mRequestBufferCalled = true;
*buf = mSlots[slot].mGraphicBuffer;
return NO_ERROR;
}
tips: 为什么不在 dequeueBuffer时,直接返回 GraphicBuffer 对象,而是返回一个空闲的 slot,再调用requestBuffer去获得一个 GraphicBuffer?
因为这个接口调用太频繁了,比如在90FPS的设备上,一秒钟该接口要执行90次,太频繁了,而且这个信息只需要传递一次就可以了,如果每次这个接口都要带上GraphicBuffer的信息,传输了很多冗余数据,所以不如加入一个新的api(requestBuffer)来完成GraphicBuffer传递的事情
GraphicBuffer 继承了 Parcel 对象,可以通过Binder跨进程传递