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 的合成策略分三步:

  1. 检查 Layer 属性:是否透明、是否有旋转、是否有缩放
  2. 判断硬件能力:HWC 支持多少层、是否支持混合
  3. 分配合成方式:能走硬件的走硬件,剩下的走 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 的硬件合成细节,到时候再聊。