EN

Android Systrace 基础知识 - Vsync 解读

字数统计: 3.9k阅读时长: 15 min
2019/12/01
loading

本文是 Systrace 系列文章的第七篇,主要是是介绍 Android 中的 Vsync 机制。文章会从 Systrace 的角度来看 Android 系统如何基于 Vsync 每一帧的展示。Vsync 是 Systrace 中一个非常关键的机制,虽然我们在操作手机的时候看不见,摸不着,但是在 Systrace 中我们可以看到,Android 系统在 Vsync 信号的指引下,有条不紊地进行者每一帧的渲染、合成操作,使我们可以享受稳定帧率的画面。

本系列的目的是通过 Systrace 这个工具,从另外一个角度来看待 Android 系统整体的运行,同时也从另外一个角度来对 Framework 进行学习。也许你看了很多讲 Framework 的文章,但是总是记不住代码,或者不清楚其运行的流程,也许从 Systrace 这个图形化的角度,你可以理解的更深入一些

本文目录

系列文章目录

  1. Systrace 简介
  2. Systrace 基础知识 - Systrace 预备知识
  3. Systrace 基础知识 - Why 60 fps ?
  4. Systrace 基础知识 - SystemServer 解读
  5. Systrace 基础知识 - SurfaceFlinger 解读
  6. Systrace 基础知识 - Input 解读
  7. Systrace 基础知识 - Vsync 解读
  8. Systrace 基础知识 - Vsync-App :基于 Choreographer 的渲染机制详解
  9. Systrace 基础知识 - MainThread 和 RenderThread 解读
  10. Systrace 基础知识 - Binder 和锁竞争解读
  11. Systrace 基础知识 - Triple Buffer 解读
  12. Systrace 基础知识 - CPU Info 解读
  13. Systrace 流畅性实战 1 :了解卡顿原理
  14. Systrace 流畅性实战 2 :案例分析: MIUI 桌面滑动卡顿分析
  15. Systrace 流畅性实战 3 :卡顿分析过程中的一些疑问
  16. Systrace 响应速度实战 1 :了解响应速度原理
  17. Systrace 响应速度实战 2 :响应速度实战分析-以启动速度为例
  18. Systrace 响应速度实战 3 :响应速度延伸知识
  19. Systrace 线程 CPU 运行状态分析技巧 - Runnable 篇
  20. Systrace 线程 CPU 运行状态分析技巧 - Running 篇
  21. Systrace 线程 CPU 运行状态分析技巧 - Sleep 和 Uninterruptible Sleep 篇

正文

版本说明(2026-10-13 核查):原文与截图来自 2019 年,完整设备版本未记录。DispSync、phase dump 和旧函数名按 AOSP android-9.0.0_r1 / android-10.0.0_r1 理解;Android 16 的 Scheduler 已使用不同的预测与回调调度实现。文中的 60 Hz、0 / 4 ms Offset 是案例条件,不是所有设备的固定配置。

阅读新 Trace,可以配合 Perfetto 8:深入理解 Vsync 机制与性能分析,沿 App、RenderThread、SurfaceFlinger、GPU 和 Display/HWC 查同一帧。Android 12(API 31)起,采集到相应数据时可用 FrameTimeline 对照预期和实际时序;轨道缺失不代表耗时为零,也不能仅靠相邻片段认定帧归属。

硬件时序与软件调度是配合关系。 HWC 向 SurfaceFlinger 上报显示相关的 Vsync 时间戳;旧版 DispSync 据此建立周期模型,通过软件定时按不同相位触发 App/SF 回调。模型可在硬件回调暂时关闭时继续工作。因此“现在基本都由硬件产生”不能用来描述每一次 App 回调的来源。

这张旧图中的 retire fence 是显示反馈,不是每个 App 的 GPU 渲染完成通知。不同年代的 retire/present fence 名称、语义和硬件偏差需要结合实现,不能把几个时间戳直接当同一个时刻。

在 Choreographer 渲染机制 中,App 按需请求下一次帧回调,再在 Looper 中执行 doFrame。没有更新任务时,不需要每个显示周期都重画。常见 60 / 90 / 120 Hz 间隔约为 16.67 / 11.11 / 8.33 ms;App 帧率、显示刷新率和回调频率不必完全相等。高刷背景可看 Perfetto 6:为什么是 120Hz?。

SurfaceFlinger 则按调度和图层状态处理事务、选择 Buffer 并组织合成,不是每次信号都把所有 Surface 无条件重画。下面是旧 Systrace 的相关轨道:

VSYNC-app、VSYNC-sf 的 0/1 每一次跳变用来标记事件,不是只有上升沿才算一帧,也不是高电平宽度等于工作耗时。软件回调、App 收到事件、线程实际获得 CPU 是不同时间;图层队列计数也不直接证明 GPU 已完成。

Android 图形数据流向

下面先用普通硬件加速 View 的旧 GL 路径说明数据如何到达屏幕。游戏、软件绘制和其他图形后端的函数名或步骤会不同。

  1. App 的 doFrame 按需处理输入、动画和 Traversal,进行必要的 Measure、Layout、Draw。DisplayList 是绘制操作记录,不是原样保存的 OpenGL 命令列表。
  2. RenderThread 同步绘制状态并驱动图形后端,可能准备资源、编码和提交命令。flush drawing commands 不能简单解释为“把 CPU 数据上传 GPU”;共享内存也不免除缓存一致性、资源生命周期和同步要求。
  3. GPU 工作通常异步执行,CPU 线程可以继续,但在 Buffer、驱动队列或 fence 等位置仍可能等待。不能保证所有真机提交都立即返回、不阻塞。
  4. GL/EGL 路径通过 eglSwapBuffersWithDamageKHR 等把 Buffer 及同步信息交给后续队列。queueBuffer / swap 返回不等于 GPU 完成,更不等于已显示;Vulkan 路径也不必出现这些 EGL 函数名。
  5. SurfaceFlinger 根据图层状态和 HWC 协商结果安排 client/device composition。Client composition 可由 RenderEngine 使用 GPU;device composition 可使用显示硬件。Fence 约束何时能消费内容,等待可能由 GPU/显示硬件执行,不是每层都先由 SF 主线程阻塞等 GPU 再统一用 GPU 合成。旧 onMessageReceived / invalidate / refresh 标记也不是新版通用接口。参见 Hardware Composer。
  6. 显示管线在合适的时刻呈现/扫描输出结果。一个 Buffer 提交、present fence 信号和屏幕所有像素完成更新不是同一个事件;不同硬件与合成方式也不要求先生成一张统一的“最终 Frame Buffer”。

下面这张历史架构图同时画出了经过 SF GPU 合成和由显示控制器处理的路径,正好说明“合成全靠 GPU”并不准确。

这些图适合认识生产者、Buffer、消费者和显示的关系,不代表 Android 16 的全部进程和队列布局,例如现代 View 路径还会涉及 BLASTBufferQueue。

Systrace 中的图像数据流

把上面的抽象流程放到这张旧 Trace 中,可以依次观察 App、SurfaceFlinger 和 composer 服务:

  1. 这次 App/SF 信号标记接近同相位;不意味着两个线程实际在同一纳秒运行。
  2. SF 处理当次合成时,选择的是之前已可用于该次合成的 Buffer。图中把它标作 App 的 N−1 帧。
  3. App 处理第 N 帧,经过主线程和 RenderThread 提交新的 Buffer,队列计数随之变化;图中的“渲染结束”只是提交过程的简写,GPU 完成还需另查。
  4. 后续 SF 调度处理第 N 帧,同时 App 可以准备下一帧。这里消费的是第 3 步提交的 Buffer,原文写成“第 2 步渲染”有误。

这是稳定流水线的一个样本。旧 Buffer 可以继续显示,新帧也可能被延迟、替换或丢弃;正式关联要用 Buffer/帧标识及 fence、FrameTimeline 等证据,不能把所有 Trace 都机械套成 N−1 / N。

Vsync Offset

旧 DispSync 使用硬件时间戳和显示反馈维护模型,再按 App/SF 相位安排软件回调,基本关系如下:

disp_sync_arch

phase-app、phase-sf 都是相对参考显示时序的偏移。在下面的简图中,App 较早开始,SF 在稍后的相位处理工作。

本文为了比较,把 Δ = phase-sf − phase-app 称为两者的 Offset;它不等于完整的 Vsync 配置。相位可以为负、跨越周期,系统还可能切换 early/late 等配置,不能统一限制为 0–16.6 ms。也没有证据说明“大部分厂商都设为 0”。相位相同只是计划时序相同,不保证线程同时收到消息或获得 CPU。

可用 adb shell dumpsys SurfaceFlinger 查看设备实际 dump,但字段随版本变化。下面保留两份历史输出,不把它们当成 Android 16 固定格式。

两相位之差为 0:App 与 SF 都是 1,000,000 ns,即各自相对参考时序偏移 1 ms,并不是两者自身的 phase 都为 0。

1
2
3
4
5
6
Sync configuration: [using: EGL_ANDROID_native_fence_sync EGL_KHR_wait_sync]
DispSync configuration:
app phase 1000000 ns, sf phase 1000000 ns
early app phase 1000000 ns, early sf phase 1000000 ns
early app gl phase 1000000 ns, early sf gl phase 1000000 ns
present offset 0 ns refresh 16666666 ns

两相位之差为 4 ms:6,000,000 − 2,000,000 = 4,000,000 ns。这里 16,666,666 ns 的周期约对应 60 Hz。

1
2
3
4
5
6
7
Sync configuration: [using: EGL_ANDROID_native_fence_sync EGL_KHR_wait_sync]

VSYNC configuration:
         app phase:   2000000 ns          SF phase:   6000000 ns
   early app phase:   2000000 ns    early SF phase:   6000000 ns
GL early app phase:   2000000 ns GL early SF phase:   6000000 ns
    present offset:         0 ns      VSYNC period:  16666666 ns

present offset 是另一项显示反馈/时序配置,不是上面两相位之差。early、early GL 等行也是候选配置,不能从静态 dump 断定某帧实际采用哪一行。

Offset 为 0

先看这次相位差为 0 的案例:

App 和 SF 的计划回调接近对齐,App 新提交的 Buffer 在下一次 SF 工作中被使用。图上约 16.6 ms 标的是两个 Vsync 之间的周期;从 Buffer 提交到被取用的等待,只是这个周期剩余的一部分,不能一律写成又等了完整 16.6 ms。

看到这里,大家可能会想:App 的 Buffer 已经准备好了,能不能让 SurfaceFlinger 早点拿去合成,少等一会儿?把 SF 的计划相位稍后安排,有机会让 App 在当次 SF 工作前提交可用的新帧。但这不是“queueBuffer 一完成就立即触发 SF 合成”的事件模式;仍要满足当次 latch/合成条件,并给 GPU、SF/HWC 留出目标显示时刻之前的时间。

Offset 不为 0

下面保留原文标作 4 ms 的相位差案例:

红框说明的是两个调度标记的间隔,不是 App 渲染耗时、GPU 完成时间或触摸到显示的总延迟。是否更早呈现,还要沿着这一帧的提交、同步和显示结果核实。

Offset 的优缺点

调整相位是在输入新鲜度、App 工作时间和合成余量之间取舍,不能仅凭“越大/越小越好”配置。

  1. Δ 太小,App 的新 Buffer 可能赶不上本次合成。旧内容通常仍可继续显示,不是该 App 从画面里消失。理想化的周期模型里,下一次 SF 时刻相对 App 开始可能是 T + Δ,但它不是完整的端到端显示延迟公式。
  2. Δ 太大,可能挤压 SF/HWC 及相关 GPU 工作的时间,导致错过目标显示时刻;也可能在某个负载下仍有收益,并非简单“失效”。

Android 16 补充:Scheduler 配合 VSyncPredictor、VSyncReactor 和 VSyncDispatchTimerQueue 等组件预测并安排回调。后者会按目标 Vsync、workDuration、readyDuration 计算唤醒时间,并处理调度更新;不能直接拿旧 DispSync 的常量或一组固定 phase 解释所有帧。参见 VSyncDispatchTimerQueue.cpp(android-16.0.0_r1)。

另外,Android 15 引入的 ARR 还需要显示硬件与 HAL 支持。ARR 可将显示 Vsync 节奏与实际刷新间隔解耦,例如官方案例用约 240 Hz 的 Vsync 步长支持最高 120 Hz 的刷新;不能看到某个 Vsync 周期就直接当作 App 帧率。见 Adaptive refresh rate。

HW_Vsync

关闭 HWC 的 Vsync 事件回调,不等于关闭面板刷新,也不意味着 GPU 停止工作。软件预测可以在不持续接收硬件回调时继续安排 App/SF 事件;重同步或显示模式变化等情况再请求硬件样本。

原文的 500 ms 需要改正。下面是 Android 9 SurfaceFlinger 中的完整方法:

1
2
3
4
5
6
7
8
9
10
11
void SurfaceFlinger::resyncWithRateLimit() {
static constexpr nsecs_t kIgnoreDelay = ms2ns(500);

// No explicit locking is needed here since EventThread holds a lock while calling this method
static nsecs_t sLastResyncAttempted = 0;
const nsecs_t now = systemTime();
if (now - sLastResyncAttempted > kIgnoreDelay) {
resyncToHardwareVsync(false);
}
sLastResyncAttempted = now;
}

它比较的是这次调用与上次调用这个限流方法之间的时间,且每次都会更新 sLastResyncAttempted;不是“距离上次合成超过 500 ms”。这也不是开启硬件回调的唯一入口。Android 10 Scheduler 有类似 500 ms 条件,而 Android 16 Scheduler::resync() 的对应限流值为 750 ms,均不能当作公开 API 保证。

图中 HW_VSYNC_0 的跳变与 HW_VSYNC_ON_0 的使能区间要区分;后者不是面板开关。轨道没有跳变也可能与回调关闭或采集内容有关,不能直接判定显示停止。

不使用HW_VSYNC

这张旧图表示已建立的软件模型继续分发事件,不需要每次 App 请求都等待一个新的 HWC 回调。

使用 HW_VSYNC

这张图增加了请求重同步、接收硬件时间戳并更新模型的路径。图中直连 App 的箭头是简化表示:旧 EventThread 通过 Connection / BitTube 分发显示事件,App 的 native 接收与 Looper/Choreographer 再处理,并不是服务端直接调用 Java onVsync。

关于样本数量,以 Android 9 DispSync.h 和 DispSync.cpp 为准:

  • 重同步时间戳最多保留 32 个,updateModelLocked() 在至少 6 个时重新估计模型。6 个时间戳形成 5 个间隔,去掉最大、最小间隔后还剩 3 个参与周期平均;不能写成“最低只需 3 个硬件 Vsync”。
  • mPeriod 是周期,mPhase 才是模型相位,还要结合参考时间和每个 listener 的 phase。初始化会先设置显示周期,首个重同步样本也会更新参考,因此不是必须等 6 个样本后才可能产生任何软件回调。
  • 误差来自有效 present fence 时间戳与模型的比较,mError 是均方误差,单位为 ns²;该版本阈值 160000000000 表示 (400 µs)²。重新采样是否继续还结合模型有效性及半阈值回差;忽略 present fence 时走另一分支。没有有效样本、或源码中误差被重置为 0,也不等于实测零误差。

这些是特定版本的算法细节,不是 Android 永久不变的“6/32”规则。Android 16 的 VSyncPredictor 使用自己的样本参数与预测逻辑,应对照对应版本。原 SW-VSYNC 的生成与传递 可作旧版流程补充阅读。

本文其他地址

本站的后续同主题文章:Perfetto 8:深入理解 Vsync 机制与性能分析。

参考

  1. VSYNC
  2. https://juejin.im/post/5b6948086fb9a04fb87771fb
  3. http://gityuan.com/2017/02/05/graphic_arch/
  4. SW-VSYNC 的生成与传递
  5. http://echuang54.blogspot.com/2015/01/dispsync.html

关于我 && 博客

下面是个人的介绍和相关的链接,期望与同行的各位多多交流,三人行,则必有我师!

  1. 博主个人介绍 :里面有个人的微信和微信群链接。
  2. 本博客内容导航 :个人博客内容的一个导航。
  3. 个人整理和搜集的优秀博客文章 - Android 性能优化必知必会 :欢迎大家自荐和推荐 (微信私聊即可)
  4. Android性能优化知识星球:付费社群已于 2026 年停止运营,不再招收新成员;此处保留历史介绍。

一个人可以走的更快 , 一群人可以走的更远

微信扫一扫

CATALOG
  1. 1. 本文目录
  • 系列文章目录
  • 正文
  • Android 图形数据流向
  • Systrace 中的图像数据流
  • Vsync Offset
    1. 1. Offset 为 0
    2. 2. Offset 不为 0
    3. 3. Offset 的优缺点
  • HW_Vsync
    1. 1. 不使用HW_VSYNC
    2. 2. 使用 HW_VSYNC
  • 本文其他地址
  • 参考
  • 关于我 && 博客