GPU渲染性能优化:HWUI渲染管线、过度绘制检测、图层合成优化
咱们做Android系统优化的,迟早要跟GPU渲染打交道。说实话,很多性能问题看起来像CPU瓶颈,一抓trace发现全是渲染卡在了GPU上。这一章我就把这三块核心内容——HWUI渲染管线、过度绘制检测、图层合成优化,给你掰开揉碎了讲清楚。
1. HWUI渲染管线:别再当黑盒
HWUI是Android从4.0开始推的硬件加速渲染引擎。它接管了原来Skia的CPU绘制,把绘制指令交给GPU去并行处理。我最早做SystemUI优化时,发现下拉通知栏掉帧严重,翻源码才发现是HWUI管线里一个动画回调触发了全屏重绘。
HWUI渲染管线大致分三步:
- UI线程记录绘制指令——每帧你的View.onDraw()会被RecordViewCanvas记录成DisplayList。
- RenderThread提交——RenderThread把这些DisplayList变成GPU指令,并上传纹理。
- GPU执行——GPU完成绘制,交换缓冲区到屏幕。
嗯,这里要注意:RenderThread的负载高往往是罪魁祸首。我见过一个案例,某个App的列表Item里用了大量Layer.draw(Canvas),导致RenderThread纹理上传一秒40次,直接撑爆GPU带宽。
关键指标:在Systrace中关注"HWUI"标记下的线程,如果RenderThread的CPU占用超过5ms/frame,或GPU Completion时间超过16ms,就需要深入排查了。
2. 过度绘制检测:一眼看穿冗余渲染
过度绘制说白了就是同一像素被多次绘制。你想想看,屏幕的像素就那么多,每个像素每帧只能跑一次,结果你画了七八层,GPU不累才怪。
2.1 怎么检测?
最简单的是设备开发者选项里的“调试GPU过度绘制”。打开后屏幕会变成彩虹色:
- 蓝色:只覆盖1次(理想)
- 绿色:2次(ok)
- 淡红:3次(需要关注)
- 深红:4次以上(严重)
我习惯在CTS验证前跑一遍所有页面的过度绘制测试。有一次发现Launcher的壁纸层和背景层重叠了,一看代码是某个自定义View的背景色没走主题,硬画了两层。
2.2 优化手段
- 使用
canvas.clipRect()裁剪不可见区域——我曾在拨号盘的拨号按键动画里用clipRect减掉了40%的过度绘制。 - 尽量用
<merge>和<include>合并布局层级,少用RelativeLayout嵌套。 - 自定义View的
onDraw()里只画可见区域,别在一张画布上把整个背景重新画一遍。
我曾经踩过的坑:给Dialog设了全屏半透明背景,然后里面又画了圆角背景,结果过度绘制直接飙到深红。正确做法是让Dialog的背景透明,只画需要的部分。
3. 图层合成优化:SurfaceFlinger不是甩手掌柜
所有App绘制的图层最终要交给SurfaceFlinger合成到屏幕上。这里容易出两个问题:CPU合成和HWC(硬件合成器)效率低。
3.1 合成流程
SurfaceFlinger会检查每个图层的属性(比如是否透明、有无旋转),然后决定是由GPU合成(OpenGL ES),还是交给显示硬件的Overlay通道。HWC能直接合成多个图层为一张图,几乎不耗GPU,但Overlay通道是有限的——通常只有3~4个。
3.2 优化要点
| 问题 | 原因 | 优化措施 |
|---|---|---|
| 图层数过多 | 状态栏、导航栏、多个Window叠加 | 减少FloatWindow、Toast弹出数量;合并固定图层到SurfaceFlinger的Client Layer |
| 图层频繁更新 | 动画或视频帧率高导致SurfaceFlinger反复合成 | 使用android:hardwareAccelerated="false"仅对不需要的View禁用硬件加速 |
| Overlay不足 | 3个以上不透明图层走GPU合成 | 将不透明背景设为Opaque=True,让HWC认出它占满屏幕,从而跳过合成 |
记得有一次做车载系统,中控屏上有四五个应用图层同时显示,SurfaceFlinger的dumpsys数据显示全是GPU合成,帧率直接掉到20fps。我们把导航应用的背景设为不透明(它本来就是全屏的),HWC自动把Overlay打开,帧率瞬间回到60fps。
4. 三者的关系与实操思路
用一句话总结:HWUI管“怎么画得快”,过度绘制管“别画多余”,图层合成管“最后怎么拼接”。优化时我会按这个顺序查:
- 先开过度绘制工具,把所有红色区域干掉——这一步收益最大。
- 再抓Systrace看HWUI RenderThread和GPU Completion——看是上传纹理慢还是GPU渲染慢。
- 最后dumpsys SurfaceFlinger看图层合成模式——如果有大量GPU合成,就调整图层属性和合并图层。
提示:用adb shell dumpsys gfxinfo可以快速拿到窗口的渲染统计,里面就有过度绘制计数和渲染管线时间。
这张图基本把咱们刚讲的三块内容串起来了。从左到右,从应用层渲染到最终显示,每个环节都有可优化的点。你如果手头有性能优化的项目,不妨照着这个顺序过一遍,通常能找到一两个惊喜。