多种刷新率

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)时,将使用默认刷新率。

Frame Rate API

应用可通过 Frame Rate API 将其预期帧速率告知 Android 平台,该 API 可在以 Android 11 为目标平台的应用中使用。如需详细了解帧速率 API,请查看 developer.android.com 上的开发者文档。

开发者选项

菜单中新增了一个开发者选项,它可以使用当前刷新率在屏幕上切换叠加层。新选项位于设置 > 系统 > 开发者选项 > 显示刷新频率下。