给Android应用和游戏开发工程师的最新参考。
长时间以来,手机显示屏幕的刷新率一直是固定的60Hz. 从2019年开始,由于游戏等应用对响应速度的需求越来越高,越来越多的手机开始在高刷新率(90/120Hz)的屏幕硬件上进行尝试,其中典型厂商的有小米、三星、摩托罗拉等。国内的天马、BOE等屏幕生产商也开始有能力供货高刷新率屏幕面板。
当手机屏幕都是60Hz刷新率的时候,应用和游戏开发者只需要认定每一帧的处理时间16.6毫秒,并据此来优化自己的代码,显示的一切就都可以掌控。但以后就不是这样了。新的旗舰手机甚至中低档手机,都开始装配高刷新率的显示屏幕来提供更流畅动画,更低的延迟,更优秀的整体用户体验。有些手机更是同时支持多种刷新率,比如Pixel 4,Motorola edge,都同时支持60Hz 和 90Hz. 作者本次就是拿Pixel 4和Motorola edge作为参考机来了解高刷新率feature的。
运行在60Hz刷新率模式的屏幕,每16.6毫秒刷新一次显示内容。这意味着一副画面显示的时间长度都是16.6毫秒的整数倍(16.6毫秒,33.3毫秒,50毫秒,等等)。但对于支持多刷新率的屏幕,想要保证显示刷新的速度和流畅性,应用开发工程师则需要考虑更多。比如,游戏开发工程师通常简单地假设手机一直以60Hz刷新,当在游戏运行中发现不能维持60fps流畅帧率的时候,就把游戏画面的帧率降低到30fps,从而保证用户仍然不会感觉到卡顿(因为此时屏幕每16.6毫秒更新一帧,那么更低的帧率就是每33.3毫秒更新一帧)。但一款游戏不能只适合运行在60Hz的设备上,当它被安装到90Hz刷新的手机上时,为了维持流畅的刷新,应用开发工程师需要把帧率降低到45fps(因为此时屏幕每11.1毫秒更新一帧,那么更低的帧率就是每22.2毫秒更新一帧)。在90hz的手机上,相同的游戏可以给用户提供更顺畅的显示体验(90/45fps比60/30fpx感觉更平滑)。一款同时支持90Hz和120Hz刷新率的手机,它在120、90、60(120/2)、45(90/2)、40(120/3)、30(90/3)、24(120/5)fps的帧率下,都能提供平滑的显示。
Rendering at high rates Render的速度越快,越难以维持该render速度,这是因为更高的render速度意味着同样的工作但更短的处理时间。在90Hz的刷新率下,应用只有11.1毫秒来处理完一帧数据。而在60Hz的刷新率下,应用就有16.6毫秒来完成相同的工作。
为了更直观的说明问题,我们来看一下Android UI的render流水线,把应用的帧显示过程拆分成5个阶段:
- 应用的UI thread 收到 input events,调用应用的callbacks并更新 the View hierarchy’s list of recorded drawing commands;
- 应用的RenderThread发送 the recorded commands到GPU;
- GPU进行绘图工作;
- 负责各应用窗口显示工作的系统服务SurfaceFlinger,对各应用的显示内容进行合成并把合成后的frame发送给HAL(有些硬件合成的过程就是往屏幕发送数据的过程);
- frame开始扫描并更新到屏幕上(比如通过MIPI接口)。
整个流水线是由Android Choreographer控制的. Choreographer的运行又基于屏幕的vertical synchronization (vsync) events。vsync event代表显示硬件开始扫描输出图像到屏幕上(一行一行的显示). 基于vsync events,应用和SurfaceFlinger有不同的唤醒时间偏移量。下图显示了Pixel4在60Hz刷新率模式下的帧处理流水线。 应用在vsync event 发生后2毫秒被唤醒,而SurfaceFlinger是在vsync event发生后的6毫秒被唤醒. 这样应用就有20毫秒(阶段2+阶段3)的时间去处理一帧数据,而SurfaceFlinger也有10毫秒的时间去合成显示数据(阶段4)。这个处理时间对于一般的硬件平台都是可以胜任的。
当手机运行在90Hz刷新率模式下的时候,应用仍是是在vsync event发生后2毫秒被唤醒,而SurfaceFlinger是在vsync event发生后的1毫秒被唤醒,这样为了给Surface留下足够的10毫秒时间去合成数据。但是,如果仍然要求数据在第2帧就往屏幕上发送的话,应用就只有10毫秒的时间去画图了,如下图。 这是一个非常短的时间,稍微复杂一些的显示工作,对于大部分平台的AP和GPU性能,都难以这么短的时间内完成画图工作。
为了减轻画图的负担,Android的UI系统(hwui) 使用一种 “render ahead”机制 (SurfaceFlinger合成时间推迟一帧,但仍然在vsync event发生后1毫秒开始) 把显示时间延迟一个vsync period来拉长一帧数据的流水线时长. 这样应用就有21ms的时间去处理数据(画图,包括AP render和GPU渲染),而仍然能保持90Hz的帧率。
有些应用,包括大部分游戏,会根据他们的画图的工作量来定制自己的render流水线,增加或减少render阶段数量。一般来说,因为画图工作量太大,会增加render流水线的长度,即在阶段2和阶段3增加并行处理阶段来提升计算效率。然而,这样的折中方案又会引入更大的帧延时 (the latency will be number_of_pipeline_stages x longest_pipeline_stage),需要综合权衡考量。