多刷新率

Android 11 为支持多刷新率的设备增加了支持。此功能主要包含以下三个组件

  • android.hardware.graphics.composer@2.4 中引入了新的 HAL API。
  • 用于解析不同刷新率的设备配置并设置所需刷新率的平台代码
  • 允许应用设置其所需帧率的新的 SDK 和 NDK API

实现

已向 android.hardware.graphics.composer@2.4 HAL 添加了对刷新率切换的专用支持。我们强烈建议使用此版本,因为之前的 composer HAL 版本对刷新率切换的支持有限。

配置组

新的属性 CONFIG_GROUP 已添加到 IComposerClient::Attribute,可以使用 getDisplayAttribute_2_4 API 进行查询。此属性允许供应商将显示配置分组在一起。在大多数情况下,同一组中的配置允许在它们之间无缝切换。平台使用配置组来区分哪些配置可以在它们之间切换,以便切换刷新率,而不是配置的其他属性。

考虑以下示例,该示例演示了在支持四种显示配置的设备上使用配置组的优势

  • 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 返回显示器当前运行的刷新率(以垂直同步周期为单位)。这在刷新率转换期间尤其有用,平台需要知道当前的刷新率,以决定何时开始下一帧。
setActiveConfigWithConstraints
setActiveConfigWithConstraints 方法是对现有 setActiveConfig 方法的新扩展,并提供有关配置更改的更多信息。约束条件作为 vsyncPeriodChangeConstraints 参数的一部分给出,并包含以下参数。
    desiredTimeNanos
    垂直同步周期可能更改之后的时间(以 CLOCK_MONOTONIC 为单位)(即,垂直同步周期不得在此时间之前更改)。当平台想要提前计划刷新率更改,但队列中已经有一些缓冲区要呈现时,这很有用。平台会相应地设置此时间,以考虑这些缓冲区并确保刷新率转换尽可能平滑。
    seamlessRequired
    如果为 true,则要求垂直同步周期更改必须无缝发生,且没有明显的视觉伪影。当由于内容更改而需要刷新率更改时(例如,设备处于空闲状态并且动画开始),平台会使用此标志。这为供应商提供了机会,以便在某些配置更改可能导致明显的视觉伪影时不允许这些更改。如果无法无缝更改配置,并且 seamlessRequired 设置为 true,则实现应返回 SEAMLESS_NOT_POSSIBLE 作为返回代码,并在可以无缝完成相同的配置更改时调用新的 onSeamlessPossible 回调。

成功后,实现会返回一个 VsyncPeriodChangeTimeline,告知平台何时预期会发生刷新率更改。newVsyncAppliedTimeNanos 参数需要设置为 CLOCK_MONOTONIC 中的时间,在该时间,新显示器将开始以新的垂直同步周期刷新。这与 desiredTimeNanos 一起,允许平台提前计划刷新率切换,并提前开始为新的刷新率计时应用。这允许刷新率的无缝转换。

某些实现需要先发送刷新帧,然后才能发送刷新率。为此,HAL 具有 refreshRequired 参数来指示需要刷新帧,并具有 refreshTimeNanos 来指示需要发送刷新帧的第一个垂直同步。

onVsyncPeriodTimingChanged [回调]
HAL 可以调用新的回调来向平台指示时间线的某些参数已更改,并且平台需要调整其时间线。如果由于 HAL 上的处理时间过长或刷新帧延迟而错过了旧时间线,则预计会调用此回调。

平台如何决定更改刷新率?

刷新率选择发生在以下两个系统服务中

DisplayManager
DisplayManager 设置围绕刷新率的高级策略。它设置默认显示配置,这与 composer HAL 配置相同。此外,它还为 SurfaceFlinger 设置了最小和最大值的范围,以选择作为刷新率。
SurfaceFlinger
通过设置与默认配置在同一配置组中且刷新率在最小/最大范围内的配置来确定刷新率。

Display Manager 运行以下步骤来确定策略

  • 通过从 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,并将最小和最大刷新率设置为与配置的刷新率匹配。
    • 省电模式:当设备处于低功耗模式时,刷新率限制为 60Hz 或更低,这通过 Settings.Global.LOW_POWER_MODE. 指示。

一旦 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 的开发者文档。

开发者选项

菜单中添加了一个新的开发者选项,用于切换显示器上具有当前刷新率的叠加层。新选项位于设置 > 系统 > 开发者选项 > 显示刷新率下。