4. Layer与Surface管理:Layer创建与销毁、BufferQueue工作机制、Surface状态管理、Layer合成策略
好,我们进入第四章。这一章可以说是整个显示系统的「交通枢纽」。你想想看,应用层画了图,怎么送到屏幕上去?中间就是靠 Layer 和 Surface 这套机制来调度。
我个人习惯把 Layer 理解为「显示图层」,Surface 则是「画布」。两者配合,才能完成一次完整的画面输出。咱们一个一个拆开讲。
4.1 Layer的创建与销毁
Layer 在 SurfaceFlinger 里是个核心对象。每个窗口、每个状态栏、每个壁纸,背后都是一个 Layer。
创建时机:当应用通过 SurfaceFlinger::createLayer() 请求时,SurfaceFlinger 会分配一个 Layer 对象。我记得早期版本里,Layer 的创建是同步的,后来改成了异步,避免阻塞应用端。
// 简化版创建流程
sp<Layer> SurfaceFlinger::createLayer(
const String8& name,
uint32_t w, uint32_t h,
uint32_t flags) {
// 分配 Layer 对象
sp<Layer> layer = new Layer(this, name, w, h, flags);
// 添加到 Layer 列表
mCurrentState.layersSortedByZ.add(layer);
// 触发重绘
setTransactionFlags(eTransactionNeeded);
return layer;
}
关键点:Layer 的 Z-order 决定了叠放顺序。数值越大,越靠上。
销毁时机:当应用退出或窗口关闭时,会调用 destroyLayer()。这里有个坑——我曾经遇到过应用崩溃后 Layer 没及时销毁,导致内存泄漏。后来加了个 watchdog 机制,定期清理僵尸 Layer。
注意:Layer 销毁时,要确保所有 Buffer 都归还给 BufferQueue。否则会造成 buffer 泄漏。
4.2 BufferQueue工作机制
BufferQueue 是 SurfaceFlinger 和生产者(应用)之间的「快递通道」。说白了,就是应用画好一帧,丢进队列,SurfaceFlinger 取走合成。
它的核心结构很简单:
- 生产者:应用端,通过 dequeueBuffer() 获取空闲 buffer
- 消费者:SurfaceFlinger,通过 acquireBuffer() 获取已填充 buffer
- 队列:最多 3 个 buffer(早期是 2 个,后来改成了 3 个减少 jank)
// 生产者流程
status_t BufferQueueProducer::dequeueBuffer(
int* outSlot, sp<Fence>* outFence) {
// 找空闲 buffer
int slot = findAvailableSlot();
// 返回给应用
*outSlot = slot;
return NO_ERROR;
}
// 消费者流程
status_t BufferQueueConsumer::acquireBuffer(
BufferItem* item, nsecs_t presentWhen) {
// 取最新的一帧
*item = mSlots[mFront];
return NO_ERROR;
}
经验之谈:我建议你在调试时关注 dequeueBuffer 的耗时。如果超过 16ms,说明 buffer 不够用,应用会卡顿。这时候可以适当增加 buffer 数量。
BufferQueue 还有个重要机制——Buffer 重用。应用画完一帧后,buffer 不会立即销毁,而是回到空闲池。这样下次画图时可以直接用,省去了重新分配的开销。
4.3 Surface状态管理
Surface 的状态变化,直接决定了 Layer 的显示行为。我把它归纳为三种核心状态:
| 状态 | 含义 | 触发条件 |
|---|---|---|
| FREE | 空闲,可被 dequeue | 消费者释放后 |
| DEQUEUED | 应用正在绘制 | 应用调用 dequeueBuffer |
| QUEUED | 已提交,等待合成 | 应用调用 queueBuffer |
嗯,这里要注意:状态切换是原子的。我曾经在项目中遇到过状态竞争的问题——两个线程同时操作同一个 Surface,导致状态混乱。后来加了个 mutex 锁才解决。
Surface 还有一个「隐藏状态」——损坏状态。当应用 crash 或者 buffer 内容异常时,Surface 会标记为 damaged。SurfaceFlinger 看到这个标记,会跳过该 Layer 的合成,避免显示脏数据。
实战建议:在 logcat 中搜索 Surface::damage,可以快速定位哪些 Layer 出了问题。
4.4 Layer合成策略
合成策略,说白了就是决定「哪些 Layer 用硬件合成,哪些用软件合成」。这是性能优化的关键。
SurfaceFlinger 的合成策略分三步:
- 检查 Layer 属性:是否透明、是否有旋转、是否有缩放
- 判断硬件能力:HWC 支持多少层、是否支持混合
- 分配合成方式:能走硬件的走硬件,剩下的走 GPU
// 合成策略伪代码
void SurfaceFlinger::chooseCompositionType() {
for (auto& layer : mLayers) {
if (layer->isOpaque() && !layer->hasTransform()) {
// 不透明且无变换,走 HWC
layer->setCompositionType(HWC);
} else if (layer->hasAlpha() && hwcSupportsBlend()) {
// 透明但硬件支持混合,也走 HWC
layer->setCompositionType(HWC);
} else {
// 剩下的走 GPU
layer->setCompositionType(GPU);
}
}
}
我的经验:曾经有个项目,GPU 合成一直很慢。后来发现是 Layer 数量太多(超过 10 层),GPU 扛不住。我建议把静态 Layer(比如壁纸)合并成一个,减少层数。效果立竿见影。
还有一个容易被忽略的点——Layer 的裁剪区域。如果某个 Layer 只有一小块区域变化,没必要全屏重绘。SurfaceFlinger 会计算 dirty region,只合成变化的部分。这个优化在低端设备上特别明显。
避坑指南:我曾经遇到过裁剪区域计算错误,导致画面撕裂。排查了半天,发现是某个 Layer 的边界坐标没对齐。记住,所有坐标必须是 2 的整数倍,否则硬件会报错。
最后总结一下:Layer 和 Surface 的管理,核心就是「创建-使用-销毁」的生命周期,加上 BufferQueue 的流转,再加上合成策略的决策。这三者配合好了,显示系统才能流畅运行。
下一章我们会深入 HWC 的硬件合成细节,到时候再聊。