16、SurfaceFlinger与显示优化:BufferQueue深度调优、HWC合成优化、显示刷新率匹配、Triple Buffer机制

说到Android显示系统,SurfaceFlinger绝对是绕不开的核心。我早期做Camera HAL时,总觉得显示优化是上层的事,直到有一次预览画面卡顿到被客户投诉……嗯,从那以后我才真正重视起这条链路。

说白了,SurfaceFlinger就是Android的“画面合成器”。它把各个App的图层(比如相机预览、UI按钮、状态栏)合成一帧,再交给屏幕显示。如果这步没优化好,你HAL层处理得再快也没用——画面根本送不出去。

核心观点:显示优化的本质是“减少等待、减少拷贝、减少合成次数”。

16.1 BufferQueue深度调优

BufferQueue是SurfaceFlinger和生产者(比如Camera HAL)之间的缓冲区队列。我见过太多项目,一卡顿就怀疑HAL,结果问题出在BufferQueue的深度配置上。

BufferQueue的深度,说白了就是队列里能放多少个Buffer。默认一般是4个,但不同场景需要不同配置:

场景 推荐深度 原因
相机预览 3~4 保证流畅度,同时控制延迟
视频播放 6~8 容忍网络抖动,预解码更多帧
游戏/VR 2~3 降低延迟,避免画面滞后

我在项目中遇到过一个问题:相机预览偶尔会跳帧。查了半天,发现是BufferQueue深度设成了6,导致生产者(HAL)和消费者(SurfaceFlinger)之间延迟过大。改成3之后,问题立刻消失。

调优建议:不要盲目增加BufferQueue深度。深度越大,延迟越高。对于实时性要求高的场景(相机、触控),建议控制在2~4之间。

16.2 HWC合成优化

HWC(Hardware Composer)是硬件合成器。它的作用是把多个图层“合并”成一个,减少GPU的负担。你想想看,如果每个图层都让GPU去合成,功耗和性能都会爆炸。

HWC的工作模式有两种:

  • 客户端合成(Client Composition):由GPU合成,灵活性高,但功耗大。
  • 设备端合成(Device Composition):由HWC硬件合成,功耗低,但有限制(比如图层数量、格式)。

优化的核心思路是:尽量让HWC做合成,而不是GPU。我建议你在开发时,通过dumpsys SurfaceFlinger查看当前合成方式:

adb shell dumpsys SurfaceFlinger | grep "Composition"
// 输出示例:
// Composition: Device (5 layers)
// 如果看到 "Client",说明走了GPU合成,需要优化

我曾经在一个项目中,发现相机预览图层总是走GPU合成。排查后发现是图层格式不匹配——HWC不支持NV12格式,只能让GPU去处理。改成HWC支持的格式后,功耗直接降了15%。

避坑指南:HWC对图层数量有限制。如果图层超过硬件支持的上限(通常是4~6层),多余的图层会自动回退到GPU合成。所以,尽量减少不必要的图层。

16.3 显示刷新率匹配

显示刷新率匹配,说白了就是让SurfaceFlinger的合成频率和屏幕刷新率保持一致。如果两者不匹配,就会出现“撕裂”或“卡顿”。

Android支持动态刷新率(比如60Hz/90Hz/120Hz切换)。我建议你在Camera HAL中,根据预览帧率动态调整刷新率:

  • 如果预览帧率是30fps,没必要让屏幕跑在120Hz——浪费功耗。
  • 如果预览帧率是60fps,屏幕跑在60Hz就够。
  • 如果预览帧率是90fps或更高,才需要切换到高刷新率。

我记得有一次,客户反馈相机预览时手机发热严重。查了一圈,发现屏幕一直跑在120Hz,但预览只有30fps。通过SurfaceFlinger.setDesiredActiveMode()动态调整刷新率后,温度降了3度。

关键API:SurfaceFlinger.setDesiredActiveMode()DisplayManager.setRefreshRate()

16.4 Triple Buffer机制

Triple Buffer(三缓冲)是解决“卡顿”的经典方案。它的原理很简单:用三个Buffer轮流工作,避免生产者等待消费者。

为什么需要三缓冲?我给你画个图就明白了:

Triple Buffer 工作机制 Buffer A 生产者写入中 Buffer B 消费者读取中 Buffer C 空闲等待中 生产者(HAL) 消费者(SurfaceFlinger) 备用Buffer 当Buffer A写满后,生产者立刻切换到Buffer C继续写入 消费者同时读取Buffer B,互不阻塞

双缓冲的问题在于:如果生产者速度比消费者快,生产者就得等消费者读完才能写。三缓冲多了一个备用Buffer,生产者永远有地方写。

我建议你在Camera HAL中,如果预览帧率超过60fps,一定要启用三缓冲。否则很容易出现“生产者等消费者”的情况,导致帧率下降。

实现方式:在SurfaceFlinger中,通过setBufferCount(3)设置三缓冲。注意,不是所有硬件都支持三缓冲,需要确认GPU和显示控制器是否兼容。

16.5 实战:综合调优流程

说了这么多,咱们来一个实际案例。假设你正在优化一个120fps的相机预览项目:

  1. 第一步:检查BufferQueue深度。设成3,减少延迟。
  2. 第二步:确认HWC合成方式。如果走了GPU,检查图层格式和数量。
  3. 第三步:动态匹配刷新率。预览120fps时,屏幕切到120Hz;预览30fps时,切回60Hz。
  4. 第四步:启用三缓冲。确保生产者不会因为等待消费者而卡住。

我曾经按这个流程优化过一个项目,预览延迟从45ms降到了18ms,功耗还低了10%。效果非常明显。

注意:不要盲目套用。每个项目的硬件平台、显示芯片、屏幕规格都不一样。建议先通过dumpsyssystrace定位瓶颈,再针对性优化。

好了,这一章的内容就到这里。显示优化是个系统工程,BufferQueue、HWC、刷新率、三缓冲,每个环节都可能成为瓶颈。我的经验是:先测量,再优化,不要凭感觉改参数。


公众号:蓝海资料掘金营,微信deep3321