Android 11 增加了对具有多种刷新率的设备的支持。此功能包含三个主要组成部分:
android.hardware.graphics.composer@2.4
中引入的新 HAL API。- 平台代码,用于解析不同刷新率的设备配置并设置所需的刷新率
- 新增的 SDK 和 NDK API,使应用可以设置所需的帧速率
实现
我们在 android.hardware.graphics.composer@2.4 HAL
中添加了对刷新率切换的专属支持。我们强烈建议使用此版本,因为早期版本的 Composer HAL 对刷新率切换的支持有限。
配置群组
IComposerClient::Attribute
中添加了新属性 CONFIG_GROUP
,您可以使用 getDisplayAttribute_2_4
API 对其进行查询。通过此属性,供应商可以将屏幕配置组合在一起。在大多数情况下,同一组中的配置允许无缝切换这些配置。平台使用配置群组来区分可以相互切换的不同配置,用以切换刷新率而不是其他配置属性。
下面的示例演示了在支持 4 种屏幕配置的设备上使用群组的好处:
- 1080p 60Hz
- 1080p 90Hz
- 1080i 72Hz
- 1080i 48Hz
尽管设备支持 48Hz、60Hz、72Hz 和 90Hz 的刷新率,但屏幕会在不同模式下运行,并且从 60Hz 切换到 72Hz 时,会导致屏幕配置从 1080p 更改为 1080i,而这可能并不是想要的行为。这一点可以通过配置群组来解决。将 60Hz 和 90Hz 放到一个配置群组中,而将 48Hz 和 72Hz 放入另一个配置群组中。平台知道,它可以在 60Hz 到 90Hz 之间切换,还可以在 48Hz 和 72Hz 之间切换,但不能在 60Hz 和 72Hz 之间切换,因为这会导致配置更改,而不仅仅是简单地变换刷新率。
Composer API 更新
- getDisplayVsyncPeriod
- 为了在变换刷新率时更好地进行控制并提高可预测性,我们添加了
getDisplayVsyncPeriod
。getDisplayVsyncPeriod
会返回屏幕的当前刷新率(以 Vsync 周期为单位)。当在刷新率之间进行转换,而平台需要获取当前的刷新率以决定何时启动下一帧时,这尤其有用。 - setActiveConfigWithConstraints
setActiveConfigWithConstraints
方法是现有setActiveConfig
方法的一个新扩展,可提供有关配置更改的更多信息。限制条件以vsyncPeriodChangeConstraints
参数的一部分提供,并包含以下参数。- desiredTimeNanos
CLOCK_MONOTONIC
中的时间,在该时间过后,vsync 周期可以发生变化(即,vsync 周期在此时间之前不得发生变化)。当平台需要提前计划刷新率更改,但它在队列中已有一些要呈现的缓冲区时,这会非常有用。平台会考虑这些缓冲区并相应地设置此时间,并确保刷新率转换尽可能顺利。- seamlessRequired
- 如果为 true,则要求 vsync 周期无缝地发生变化,而不能产生任何明显的视觉痕迹。当因内容发生变化而需要变换刷新率时(例如,设备处于空闲状态,此时动画开始播放),平台则会使用此标志。这使供应商可以避免进行可能引起明显的视觉痕迹的某些配置更改。如果配置无法无缝更改,并且
seamlessRequired
设置为true
,则实现应返回SEAMLESS_NOT_POSSIBLE
作为返回代码,并在能够无缝完成相同的配置更改时调用新的onSeamlessPossible
回调。 实现成功后,系统将返回一个
VsyncPeriodChangeTimeline
,告知平台预计何时会变换刷新率。当新屏幕要开始以新的 vsync 周期刷新时,newVsyncAppliedTimeNanos
参数需要设置为CLOCK_MONOTONIC
中的时间。此标志与desiredTimeNanos
结合使用时,平台可以预先规划刷新率开关,并提前使应用进入切换到新刷新率的倒计时。这样可以实现刷新频率的无缝过渡。某些实现要求在发送刷新频率之前发送刷新帧。为此,HAL 采用了两个参数:一个是
refreshRequired
,用于指示需要某个刷新帧;另一个是refreshTimeNanos
,用于指示需要在其后发送刷新帧的第一个 vsync。- onVsyncPeriodTimingChanged [callback]
- 可由 HAL 调用的新回调,用于告知平台时间轴的某些参数发生了更改,平台需要调整其时间轴。如果由于某种原因,旧的时间轴因 HAL 的处理时间过长或者刷新帧延迟而错过,则应调用此回调。
平台如何决定变换刷新频率?
刷新率选择在以下两个系统服务中发生:
- DisplayManager
DisplayManager
用于设置与刷新率有关的高层级政策。它会设置默认的屏幕配置,此配置与混合渲染器 HAL 配置相同。此外,它还会设置一个由最大值和最小值界定的范围,供SurfaceFlinger
从中选出刷新率。- SurfaceFlinger
- 设置一个与默认配置位于同一配置群组且刷新率介于最小值/最大值范围内的配置,从而确定刷新率。
屏幕管理器会执行以下步骤来确定政策:
- 从
SurfaceFlinger
查询活跃配置,找出默认配置 ID - 通过遍历系统条件来限制这个通过最小值和最大值界定的范围
- 默认刷新率设置:默认刷新率值在
R.integer.config_defaultRefreshRate
配置叠加层中设置。该值用于确定动画和轻触互动的标准设备刷新率。 - 峰值刷新率设置:峰值刷新率值是从
Settings.System.PEAK_REFRESH_RATE
读取的。该值在运行时更改,以反映当前设备设置(例如,从菜单选项更改)。默认值在R.integer.config_defaultPeakRefreshRate
配置叠加层中设置。 - 最小刷新频率设置:最小刷新频率值是从
Settings.System.MIN_REFRESH_RATE
读取的。该值可以在运行时更改,以反映当前设备设置(例如,从菜单选项更改)。默认值为 0,因此不存在默认的最小值。 - 应用要求的 ModeId:应用可以设置
WindowManager.LayoutParams.preferredDisplayModeId
以反映屏幕应运行的首选配置。在大多数情况下,DisplayManager
会相应地设置默认配置 ID,并设置最小和最大刷新频率以匹配配置的刷新频率。 - 省电模式:当设备处于低功耗模式(通过
Settings.Global.LOW_POWER_MODE.
指示)时,刷新率上限为 60Hz。
- 默认刷新率设置:默认刷新率值在
DisplayManager
设置政策后,SurfaceFlinger
会根据活跃层(将帧更新排入队列的层)设置刷新率。如果该层的所有者设置了帧速率,则 SurfaceFlinger 会尝试将刷新率设置为该帧速率的倍数。
例如,如果两个活跃层的帧速率设置为 24 和 60,则 SurfaceFlinger 会选择 120Hz(如果可用)。如果 SurfaceFlinger 无法使用此类刷新率,则会尝试选择对帧速率来说误差最小的刷新率。如需了解详情,请参阅 developer.android.com 上的开发者文档
SurfaceFlinger
维护以下标志来控制刷新率的确定方式:
ro.surface_flinger.use_content_detection_for_refresh_rate:
如果已设置,系统会根据活跃层确定刷新率,即使未设置帧速率。SurfaceFlinger 持续运用启发法,通过关注连接到缓冲区的呈现时间戳,找出该层发布缓冲区的平均 fps。ro.surface_flinger.set_touch_timer_ms
:如果大于 0,则在配置的超时时间内有用户轻触屏幕时,将使用默认刷新率。此启发法完成时,便会对动画采用默认刷新率。ro.surface_flinger.set_idle_timer_ms
:如果大于 0,则在配置的超时时间内没有屏幕更新时,将使用最小刷新频率。ro.surface_flinger.set_display_power_timer_ms
:如果大于 0,则在配置的超时时间内开启屏幕(或退出 AOD)时,将使用默认刷新率。
帧速率 API
帧速率 API 会让应用告知 Android 平台其预期帧速率,并在以 Android 11 为目标平台的应用上可用。如需详细了解帧速率 API,请查看 developer.android.com 上的开发者文档。
开发者选项
菜单中新增了一个开发者选项,它可以使用当前刷新率在屏幕上切换叠加层。新选项位于设置 > 系统 > 开发者选项 > 显示刷新频率下。