渲染侧掉帧:从 doFrame 到合成逐段定位
2026/8/9大约 3 分钟渲染管线Android 14掉帧渲染doFrame
渲染侧掉帧:从 doFrame 到合成逐段定位
上一章看了输出接缝。这一章回答:渲染掉帧到底发生在哪一段,如何从 doFrame 到 合成逐段拆解并定位?
掉帧要先定位迟到发生在哪一段
| 常见误解 | 正确理解 |
|---|---|
| 掉帧就是 onDraw 慢 | 可能在主线程记录、RenderThread 执行、GPU 或合成 |
| gfxinfo 显示慢就是应用问题 | 也可能来自合成或系统负载 |
| 掉帧都一样处理 | 偶发与持续、记录与执行要分开定位 |
核心结论
- 定位顺序:doFrame(主线程)→ RenderThread → GPU → 合成。
- 主线程慢:测量/布局/记录绘制复杂。
- RenderThread 慢:DisplayList 执行或资源上传。
- GPU 慢:着色器、纹理、带宽。
- 合成慢:层数、特效、合成方式。
四段定位主链
逐步解释:
- 主线程:看 doFrame 内的测量/布局/记录耗时。
- RenderThread:看执行耗时。
- GPU:看帧缓冲与绘制提交。
- 合成:看 SurfaceFlinger 侧。
如何用工具拆段
Perfetto 的 gfx 轨道能同时显示主线程 doFrame、RenderThread 与 SurfaceFlinger; gfxinfo 的帧统计给出各阶段耗时汇总。两套数据对照,先定段再定责。
源码证据
阅读目标:确认帧轨迹的数据来源。
正文指针:Choreographer.java doFrame();libs/hwui/ 的帧统计; SurfaceFlinger 的合成时间线。
// Choreographer.java(伪代码,行号以 r75 为准)
void doFrame(long frameTimeNanos, int frame) {
// 主线程帧工作开始
}这段代码证明: 主线程帧从 doFrame 开始;后续 RenderThread 与合成的耗时由各层 记录,Perfetto 帧轨道把它们放在同一时间线。
验证与排障
环境:开发设备或模拟器;以下命令不需要 root。
adb shell dumpsys gfxinfo <包名> framestats
adb shell dumpsys gfxinfo <包名> | grep -E "Janky|50th|95th"
adb shell perfetto -o /data/misc/perfetto-traces/trace.perfetto-trace \
-t 10s sched freq idle atrace gfx view am wm先看 gfxinfo 汇总确认掉帧比例,再用 Perfetto 帧轨道拆段。主线程段超时查测量/布局/ 记录;RenderThread 段超时查 DisplayList;合成段超时回 SurfaceFlinger。
常见误区
- “掉帧都是 onDraw”:先拆段。
- “gfxinfo 慢就是应用慢”:合成与系统负载也在。
- “掉帧处理没有区别”:偶发看调度/GC,持续看复杂度。
- “只看一个工具就够”:gfxinfo + Perfetto 对照。
延伸问题
- 如何区分 RenderThread 与 GPU 的耗时?
- 合成特效(模糊、圆角)如何影响掉帧?
- 掉帧与调度抢占如何区分?
- 多窗口下合成负载如何影响应用?
源码入口
| 文件 | 关键位置 | 作用 |
|---|---|---|
Choreographer.java | doFrame() | 主线程帧 |
libs/hwui/ | 帧统计 | RenderThread |
SurfaceFlinger | 合成时间线 | 合成段 |
dumpsys gfxinfo | framestats | 段级汇总 |
公共路径:frameworks/base/core/java/android/view/、 frameworks/base/libs/hwui/ 与 frameworks/native/services/surfaceflinger/。 行号以 r75 检索为准。
把各阶段转换为可执行检查链,见 渲染问题排查。