网站构建的开发费用,深圳做网站开发,网站流量分析表,滨河网站建设目录
一、引言
二、概览
三、实现HWC
3.1 为什么是HWC#xff1f;
3.2 HWC的支持需求
3.3 HWC的实现思路
3.4 HWC的基元
3.5 HIDL接口
3.6 函数指针
3.7 图层和屏幕句柄
3.8 屏幕合成操作
3.9 多个屏幕
3.10 虚拟屏幕合成
3.10.1 模式
3.10.2 输出格式
3.11 同…目录
一、引言
二、概览
三、实现HWC
3.1 为什么是HWC
3.2 HWC的支持需求
3.3 HWC的实现思路
3.4 HWC的基元
3.5 HIDL接口
3.6 函数指针
3.7 图层和屏幕句柄
3.8 屏幕合成操作
3.9 多个屏幕
3.10 虚拟屏幕合成
3.10.1 模式
3.10.2 输出格式
3.11 同步fence
3.12 热插拔处理
3.12.1 概念理解
3.12.2 更新显示功能
3.12.3 处理显示功能的更改
3.12.4 常见的连接场景处理
3.12.5 使用顺序配置 ID 来防止竞争条件
3.13 客户端帧缓冲区管理
3.13.1 分辨率切换期间的帧缓冲区管理
四、Layers and displays
4.1 Displays
4.2 虚拟屏幕
五、VSYNC
5.1 VSYNC 偏移
5.2 DispSync
5.3 VSYNC/退出偏移
5.4 VSYNC 和 SF_VSYNC 偏移
六、帧同步
6.1 每秒帧数 (FPS) 节流干预
6.1.1 GameManagerService
6.1.2 SurfaceFlinger
七、多种刷新率
7.1 实现
7.2 配置群组
7.3 Composer API 更新
7.3.1 getDisplayVsyncPeriod
7.3.2 setActiveConfigWithConstraints desiredTimeNanos seamlessRequired
7.3.3 onVsyncPeriodTimingChanged [callback]
7.4 平台如何决定变换刷新频率
7.5 Frame Rate API
7.6 开发者选项 一、引言
Hardware Composer HAL是什么怎么实现的呢Composer HAL涉及到哪些概念呢
二、概览
Hardware Composer HAL 用于确定铜鼓可用硬件来合成缓冲区的最有效方法。
Hardware Composer HAL 作为HAL是基于特定设备的。通常是由显示硬件OEM厂商完成。
一部普通Android手机其屏幕方向为纵向状态栏在顶部导航栏在底部其他区域显示应用的内容。这3块单独的内容对应的是3个图形缓冲区那么可以考虑如下面2种方式来处理合成 第一种将应用内容渲染到暂存buffer然后在其上渲染状态栏其下渲染导航栏最后将这个buffer传送给显示硬件。 第二种将三个缓冲区全部传送到显示硬件并指示它从不同的缓冲区读取屏幕不同部分的数据。 这第一种就是我们采用GLES通过GPU来渲染合成的。第二种就是HWC硬件方式合成的。硬件合成的方式就可以显著提高效率。
由于显示处理器功能差异很大。Overlay的数量以及对定位和重叠的限制很难通过API来表达。为了完成这些工作HWC执行如下动作 1Surfaceflinger向HWC提供一个所有层的列表询问HWC哪些是可以处理的 2HWC的响应是将每个层标记DeviceHWC或者ClientGPU合成。 3Surfaceflinger会先把所有标记成Client的图层通过GPU来完成合成合成后的结果缓存给到HWC让HWC将此缓存以及标记成Device的图层一起处理完成。 一般情况下怎么处理才能实现最佳性能呢 当屏幕的内容没有变化时Overlay planes 的效率可能会低于 GL 合成。当叠加层内容具有透明像素且叠加层混合在一起时尤其如此。 这种情况HWC就可以请求GLES来合成一部分或者全部然后保留合成后的缓冲区。 如果Surfaceflinger请求合成同一组buffersHWC就可以直接给先前合成好的buffer这样就可以减少设备空闲耗电从而提升续航 Android设备典型的时支持4个Overlay planes如果需要合成多于4个Overlay planes的层数时系统就需要对其中一些layer使用GLES来合成。这也意味着应用使用的层数会对能耗和性能产生重大的影响。
小结大概介绍了下Android系统屏幕的内容合成的可行方式通过2种可行的处理方式引出HWC要成处理需要的流程的3个步骤。同时介绍了省电提升续航的思路。
三、实现HWC
3.1 为什么是HWC
HWC HAL是用于合成从Surfaceflinger接收到的图层而分担GLES和GPU执行合成的压力提升整体性能和效率 HWC可以抽象出 overlays 和 2D blitter 对象来合成Surface或者与特定窗口硬件通信来合成窗口。 使用HWC来合成该窗口而不是用surfaceflinger通过GPU来合成原因 1大部分GPU没有为合成优化 2当使用Surfaceflinger通过GPU来合成layers 时应用是无法用GPU来做渲染的工作的
3.2 HWC的支持需求
HWC的实现需要能支持下面几个方面的需求 1至少能支持4个overlays(statusbar systembar app wallpaper/background) 2大于屏幕的Layers如壁纸 3同时预乘的每像素 Alpha 混合和每平面 Alpha 混合 4播放受保护视频的硬件路径 5支持RGBA的顺序打包YUV的格式tillingswizzlingstride 属性平铺、重排和步幅属性
3.3 HWC的实现思路
要实现HWC可以按照下面的思路展开 1实现一个非运行的HWC并将所有的合成工作发送到GLES 2实现一种算法以逐步将合成委托给HWC。例如仅将前3个或者前4个Surface委托给HWC的 overlay 硬件合成的方式就可以显著提高效率。 3优化HWC。包括 a 选择最大限度提高从GPU移除的负载的Surface并将其发送给HWC。 b 检测屏幕是否正在更新。如果不是则将合成委托给GLES而不用HWC这样可以省电。当屏幕更新时继续将合成委托给到HWC。 c 为常见用例做准备例如 主屏幕包括状态栏系统蓝应用窗口动态壁纸 纵向和横向模式下的全屏游戏 带有字幕和播放空间的全屏视频 受保护的视频播放 分屏多窗口 注意目标是提升性能降低功耗所以用例要尽可能针对常规可预测的用例而非边缘用例。
3.4 HWC的基元
HWC提供两个基元Layer 和 display表示合成工作及其与屏幕硬件的交互。 HWC还提供VSYNC的控制以及对Surfaceflinger的回调。回调是用来通知Surfaceflinger有VSYNC事件发生。
3.5 HIDL接口
Android 8.0 及更高版本使用Composer HAL 的 HIDL 接口用于在 HWC 和 SurfaceFlinger 之间绑定 IPC。该HIDLjiekou 取代了旧版 hwcomposer2.h 接口。如果供应商提供 HWC 的Composer HAL 新版实现则Composer HAL 将直接接受来自 SurfaceFlinger 的 HIDL 调用。如果供应商提供 HWC 的旧版实现Composer HAL 将从 hwcomposer2.h 加载函数指针从而将 HIDL 调用转发到函数指针调用中。 HWC 提供相应函数包括 a 确定给定屏幕的属性 b 在不同的屏幕配置例如 4k 或 1080p 分辨率和颜色模式例如原生颜色或真正的sRGB之间切换 c 打开、关闭屏幕或将其切换到低功率模式如果支持。
3.6 函数指针
如果厂商直接实现了 Composer HAL新版实现就是实现了HWC的 HIDL接口那么Surfaceflinger就可以通过HIDL IPC的方式调用它的函数。 例如要创建图层SurfaceFlinger 会在混合渲染器 HAL 上调用 createLayer()。 如果厂商实现的是 hwcomposer2.h 接口旧版实现Composer HAL会调用 hwcomposer2.h 函数指针。 在 hwcomposer2.h 注释中HWC 接口函数由 lowerCamelCase 名称引用这些名称并未作为命名的字段存在于接口中。几乎每个函数都是通过使用 hwc2_device_t 提供的 getFunction 请求函数指针来进行加载。 例如函数 createLayer 是一个 HWC2_PFN_CREATE_LAYER 类型的函数指针当枚举值 HWC2_FUNCTION_CREATE_LAYER 传递到 getFunction 中时便会返回该指针。 有关Composer HAL 函数和 HWC 直通式函数的详细文档请参阅 https://android.googlesource.com/platform/hardware/interfaces//master/graphics/composer 有关 HWC 函数指针的详细文档请参阅头文件 https://android.googlesource.com/platform/hardware/libhardware//master/include/hardware/hwcomposer2.h
3.7 图层和屏幕句柄
Layer and display 是通过由HWC生成的句柄控制。这些句柄对 SurfaceFlinger 而言是不透明的。当 SurfaceFlinger 要创建新图层时它会调用 createLayer该函数会针对直接实现返回 Layer 类型或者针对直通式实现 返回 hwc2_layer_t 类型。 当 SurfaceFlinger 要修改该图层的属性时它会将该 hwc2_layer_t 值以及进行修改所需的任何其他信息传递给相应的修改函数。hwc2_layer_t 类型的大小足以容纳一个指针或一个索引。
物理屏幕是通过热插拔创建的。在热插拔物理屏幕时HWC 会创建一个句柄并通过热插拔回调将该句柄传递给 SurfaceFlinger。 虚拟屏幕是由通过调用 createVirtualDisplay() 来请求屏幕的 SurfaceFlinger 创建的。如果 HWC 支持虚拟屏幕合成则会返回一个句柄。然后SurfaceFlinger 将屏幕的合成工作委托给 HWC。如果 HWC 不支持虚拟屏幕合成则 SurfaceFlinger 会创建句柄并合成屏幕。
3.8 屏幕合成操作
如果 SurfaceFlinger 具有可合成的新内容则会唤醒且每个 VSYNC 唤醒一次。该新内容可以是来自应用的新图像缓冲区也可以是一个或多个图层的属性更改。
当 SurfaceFlinger 唤醒时它将执行以下步骤 1处理事务如果存在。 2如果存在新的图形缓冲区则将其锁定。 3如果步骤 1 或 2 导致显示内容更改则执行新的合成。 为了执行新的合成SurfaceFlinger 会创建和销毁图层或修改图层状态如适用。 它还会使用 setLayerBuffer 或 setLayerColor 等调用用图层的当前内容来更新图层。 更新所有图层之后SurfaceFlinger 会调用 validateDisplay以告诉 HWC 检查各个图层的状态并确定如何进行合成。 在默认情况下SurfaceFlinger 会尝试配置每个图层以使相应图层由 HWC 进行合成。 在某些情况下SurfaceFlinger 会通过 GPU 回退来合成图层。
在调用 validateDisplay 之后SurfaceFlinger 会调用 getChangedCompositionTypes以查看 HWC 是否需要在执行合成之前更改任何图层的合成类型。如果有更改SurfaceFlinger 就需要调用 acceptDisplayChanges来处理这些更改
如果已将任何图层标记为进行 SurfaceFlinger 合成SurfaceFlinger 会将这些图层合成到目标缓冲区中。然后SurfaceFlinger 通过调用 setClientTarget 将该缓冲区提供给屏幕以便缓冲区可以在屏幕上显示或者进一步与未标记为进行 SurfaceFlinger 合成的图层合成。如果没有将任何图层标记为进行 SurfaceFlinger 合成则 SurfaceFlinger 会跳过合成步骤。
最后SurfaceFlinger 会调用 presentDisplay以告知 HWC 完成合成过程并显示最终结果。
3.9 多个屏幕
Android 10 支持多个物理屏幕。在设计要在 Android 7.0 及更高版本上使用的 HWC 实现时还存在一些 HWC 定义中并不存在的限制假设只有一个内部屏幕。内部屏幕是初始热插拔在启动期间报告的屏幕。内部屏幕在热插拔后无法断开连接。 在设备的正常操作期间除了内部屏幕之外还可以热插拔任意数量的外部屏幕。 该框架假设在热插拔第一个内部屏幕之后所有热插拔操作都是在外部屏幕上进行因此如果添加了更多内部屏幕它们将被错误地归类为 Display.TYPE_HDMI而不是 Display.TYPE_BUILT_IN。 尽管上述 SurfaceFlinger 操作按屏幕执行但会对所有活动的屏幕依序执行这些操作即使只更新了一个屏幕的内容也不例外。 例如如果更新了外部屏幕则顺序为
// In Android 9 and lower:// Update state for internal display
// Update state for external display
validateDisplay(internal display)
validateDisplay(external display)
presentDisplay(internal display)
presentDisplay(external display)
// In Android 10 and higher:// Update state for internal display
// Update state for external display
validateInternal(internal display)
presentInternal(internal display)
validateExternal(external display)
presentExternal(external display)
3.10 虚拟屏幕合成
虚拟屏幕合成与外部屏幕合成类似。虚拟屏幕合成与物理屏幕合成之间的区别在于虚拟屏幕将输出发送到 Gralloc 缓冲区而不是发送到屏幕。硬件混合渲染器 (HWC) 将输出内容写入缓冲区提供完成栅栏信号并将缓冲区发送给使用方例如视频编码器、GPU、CPU 等。如果显示通道写入内存虚拟屏幕便可使用 2D/位块传送器或叠加层。
3.10.1 模式
在 SurfaceFlinger 调用HWC 的 validateDisplay() 方法之后每个帧处于以下三种模式之一 1GLES - GPU 合成所有图层直接写入输出缓冲区。HWC 不参与合成。 2MIXED - GPU 将一些图层合成到帧缓冲区由 HWC 合成帧缓冲区和剩余的图层直接写入输出缓冲区。 3HWC - HWC 合成所有图层并直接写入输出缓冲区。
3.10.2 输出格式
虚拟屏幕的缓冲区输出格式取决于其模式 1GLES 模式 - EGL 驱动程序在 dequeueBuffer() 中设置输出缓冲区格式通常为RGBA_8888。使用方必须能够接受该驱动程序设置的输出格式否则便无法读取缓冲区。 2MIXED 和 HWC 模式 - 如果使用方需要 CPU 访问权限则由使用方设置格式。否则格式将为 IMPLEMENTATION_DEFINEDGralloc 会根据使用标记设置最佳格式。例如如果使用方是视频编码器则 Gralloc 会设置 YCbCr 格式而 HWC 可以高效地写入该格式。
注意Android 10 取消了有关 eglSwapBuffers() 在渲染开始后将缓冲区移出队列的要求。缓冲区可能会立即退出队列。
3.11 同步fence
同步fence是Android图形系统的关键部分。fence允许 CPU 工作与并行的 GPU 工作相互独立进行仅在存在真正的依赖关系时才会阻塞。例如当应用提交在 GPU 上生成的缓冲区时它还将提交一个同步fence对象。当 GPU 完成写入缓冲区的操作时该fence会变为有信号状态。
HWC 要求 GPU 在显示缓冲区之前完成写入缓冲区的操作。同步fence通过包含缓冲区的图形管道传递并在缓冲区写入完成时发出信号。在显示缓冲区之前HWC 会检查同步fence是否已发出信号如果有则显示缓冲区。
3.12 热插拔处理
3.12.1 概念理解
显示功能指的是如显示模式和支持的 HDR 类型等显示相关的功能 外部连接指的是如使用 HDMI 或 DisplayPort 连接等
例如这样的一个场景与设备Android 电视机顶盒 (STB) 和 机顶盒 (OTT) 设备当用户从一个显示器切换到另一个显示器在没有连接显示器的情况下启动设备 Android 12 及更高版本框架可以 处理热插拔 和 动态显示功能。那么
显示热插拔是怎么处理的 显示热插拔时Composer HAL 实现中显示功能是怎么更改的
显示热插拔情况下管理关联的帧缓冲区并防止竞争条件
这些问题会接下来进行说明。
注意 Android API 和框架中使用的术语显示模式等同于 Composer HAL 中使用的术语显示配置。
3.12.2 更新显示功能
Android 框架如何处理由 Composer HAL 发起的显示功能更改 在 Android 可以正确处理显示功能的更改之前OEM 必须实现 Composer HAL以便它使用onHotplug(display, connectionCONNECTED)通知框架显示功能的任何更改。 实现Composer HAL的onHotplug(display, connectionCONNECTED)后Android 会按如下方式
3.12.3 处理显示功能的更改
1框架接收通知在检测到显示功能发生变化时框架会收到一个onHotplug(display, connectionCONNECTED)通知。 2框架丢弃旧的显示状态创建新的显示状态收到通知后框架会丢弃其显示状态并使用getActiveConfig 、 getDisplayConfigs 、 getDisplayAttribute 、 getColorModes 、 getHdrCapabilities和getDisplayCapabilities方法使用 HAL 的新功能重新创建它。 3框架通知应用程序框架重新创建新的显示状态后它会将onDisplayChanged回调发送到正在侦听此类事件的应用程序。 注意确保getDisplayConfigs准确报告所有支持的显示刷新率和分辨率。
3.12.4 常见的连接场景处理 3.12.5 使用顺序配置 ID 来防止竞争条件
如果 Composer HAL 在框架调用setActiveConfig或setActiveConfigWithConstraints的同时更新支持的显示配置则可能会出现竞争条件。 解决方案实现 Composer HAL 以使用顺序 ID 并防止此问题。
竞争条件可能如何发生
考虑以下事件序列当新的顺序 ID 未分配给新的显示配置时会导致竞争条件 1支持的显示配置 ID 为 id1 , 1080x1920 60 赫兹 id2 , 1080x1920 50赫兹 2框架调用setActiveConfig(display, config1) 3同时Composer HAL 处理显示配置的更改并将其内部状态更新为一组新的显示配置如下所示 id1 , 2160x3840 60赫兹 id2 , 2160x3840 50赫兹 id3 , 1080x1920 60 赫兹 id4 , 1080x1920 50赫兹 4Composer HAL 向框架发送一个onHotplug事件以通知支持的模式集已更改。 5Composer HAL 接收setActiveConfig(display, config1) 来自第 2 步 6HAL 解释框架已请求将配置更改为2160x3840 60 Hz 尽管实际上需要1080x1920 60 Hz 。
使用非顺序 ID 分配的过程在这里以对所需配置更改的误解结束。
配置 Composer HAL 以使用顺序 ID 为避免此类竞争条件OEM 必须按如下方式实现 Composer HAL 1当 Composer HAL 更新支持的显示配置时它会为新的显示配置分配新的顺序 ID。 2当框架使用无效的配置 ID 调用setActiveConfig或setActiveConfigWithConstraints时Composer HAL 会忽略该调用。 这些步骤用于防止竞态条件如下面的讨论所示。
重新以上述事件序列为例当新的顺序 ID 分配给新的显示配置时 1支持的显示配置 ID 为 id1 , 1080x1920 60 赫兹 id2 , 1080x1920 50赫兹 2框架调用setActiveConfig(display, config1) 3同时Composer HAL 处理显示配置的更改并将其内部状态更新为一组新的显示配置如下所示 id3 , 2160x3840 60赫兹 id4 , 2160x3840 50赫兹 id5 , 1080x1920 60 赫兹 id6 , 1080x1920 50赫兹 4Composer HAL 向框架发送一个onHotplug事件以通知支持的模式集已更改。 5Composer HAL 接收setActiveConfig(display, config1) 来自第 2 步 6Composer HAL 忽略调用因为 ID 不再有效。 7该框架接收并处理步骤 4 中的onHotplug事件。它使用函数getDisplayConfigs和getDisplayAttribute调用 Composer HAL。通过这些功能框架为所需的分辨率和刷新率 1080x1920 和 60 Hz 识别新 ID (5)。 8框架发送另一个更新后的 ID 为 5 的setActiveConfig事件。 9Composer HAL 从第 5 步接收setActiveConfig(display, config5) 。 10HAL 正确解释框架已请求将配置更改为 1080x1920 60 Hz。 如上例所示使用顺序 ID 分配的过程可确保防止竞争条件并更新正确的显示配置更改。
适用于Hardware Composer HAL 的 AIDL 从 Android TAOSP 实验版起我们开始使用 AIDL 定义混合渲染器 (HWC) HAL还废弃了 HIDL 版本 android.hardware.graphics.composer2.1 至 android.hardware.graphics.composer2.4。 本页将介绍适用于 HWC 的 AIDL HAL 与 HIDL HAL 之间的区别以及 AIDL HAL 的实现和测试。 AIDL HAL 和 HIDL HAL 之间的区别 新的 AIDL Composer HAL 名为 android.hardware.graphics.composer3在 IComposer.aidl 中定义 它公开提供了一个类似于 HIDL HAL android.hardware.graphics.composer2.4 的 API但做出了以下更改 1移除了快速消息队列 (FMQ)改为使用 Parcelable 命令 2移除了 IComposerClient.getClientTargetSupport因为此方法没有活跃的客户端。 3用浮点数而非字节表示颜色以便更好地与 Android 中的上层图形堆栈如 ASurfaceTransaction_setColor 中所定义保持一致。 4添加了用于控制 HDR 内容的新字段。 5在 Composition.aidl 中新增了用于装饰屏幕的新合成类型 DISPLAY_DECORATION。 6为 DisplayCommand.aidl 添加了新的 expectedPresentTime 字段 7添加了用于控制启动显示配置的新 API 8新增了用于控制显示空闲定时器的新 API
3.13 客户端帧缓冲区管理
从 Android 13 开始每当屏幕分辨率发生变化时系统都会分配在客户端合成期间使用的新的帧缓冲区。分辨率变化后SurfaceFlinger 会对下一个“失效”周期执行此分配。
3.13.1 分辨率切换期间的帧缓冲区管理
分辨率会由于以下两种情况之一而发生变化 1由硬件混合渲染器 (HWC) 发起的热插拔事件从一个外部屏幕切换到具有不同默认分辨率的另一个外部屏幕时发生。 在热插拔事件期间旧的屏幕数据取消分配后系统会释放旧的帧缓冲区的句柄。 2由 SurfaceFlinger 发起的显示模式切换用户使用用户设置更改分辨率时或应用使用 preferredDisplayModeId 更改分辨率时发生。 在显示模式切换期间SurfaceFlinger 会在调用 setActiveConfig 或 setActiveConfigWithConstraints 之前释放现有客户端帧缓冲区的句柄。
为了避免出现灾难性问题如内存碎片化在没有为新旧帧缓冲区预留足够内存的设备上HWC 必须停止使用旧帧缓冲区并释放这些帧缓冲区的所有句柄如下所示 1对于热插拔事件调用 onHotplug 之前立即执行上述操作。 2对于模式切换调用 setActiveConfig 或 setActiveConfigWithConstraints 后立即执行上述操作。
释放句柄会导致帧缓冲区内存会被完全取消分配SurfaceFlinger 无法在下一个“失效”周期内执行新帧缓冲区分配。
四、Layers and displays
层和屏幕是两个基元用于表示合成工作以及与屏幕硬件的交互。 层是合成的最重要单元。层是 Surface 和 SurfaceControl 实例的组合。 每个层都有一组属性用于定义它与其他层的交互方式。层属性如下表所述。 定位定义层在其屏幕上的显示位置。包括层边缘的位置及其相对于其他层的 Z 顺序指示该层在其他层之前还是之后等信息。
4.1 Displays
屏幕是合成的另一个重要单元。系统可以具有多个屏幕并且在正常系统操作期间可以添加或移除屏幕。屏幕应 HWC 或框架的请求添加/移除。 在外部屏幕与设备连接或断开连接时称为热插拔HWC 设备请求添加或移除屏幕。客户端请求虚拟屏幕其内容会渲染到离屏缓冲区而不是实体屏幕。
4.2 虚拟屏幕
SurfaceFlinger 支持一个内部屏幕内置于手机或平板电脑中的屏幕、一个外部屏幕如通过 HDMI 连接的电视以及一个或多个令合成的输出在系统中可用的虚拟屏幕。
虚拟屏幕可用于记录屏幕信息或通过网络发送屏幕信息。为虚拟屏幕生成的帧会写入 BufferQueue。
虚拟屏幕可以与主屏幕共享相同的一组层层堆叠也可拥有自己的一组层。
虚拟屏幕没有 VSYNC因此内部屏幕的 VSYNC 可为所有屏幕触发合成。
在支持虚拟屏幕的 HWC 实现中虚拟屏幕可以与 OpenGL ES (GLES)、HWC 或者 GLES 及 HWC 合成在一起。
在不支持虚拟屏幕的实现中虚拟屏幕始终使用 GLES 进行合成。
五、VSYNC
VSYNC 信号可同步显示流水线。
Android的显示流水线一般有3部分组成
1应用渲染
2SurfaceFlinger 合成
3用于在屏幕上显示图像的硬件混合渲染器 (HWC) 。
VSYNC 可同步应用唤醒以开始渲染的时间、SurfaceFlinger 唤醒以合成屏幕的时间以及屏幕刷新周期。这种同步可以消除卡顿并提升图形的视觉表现。
HWC 可生成 VSYNC 事件并通过回调将事件发送到 SurfaceFlinger
typedef void (*HWC2_PFN_VSYNC)(hwc2_callback_data_t callbackData,hwc2_display_t display, int64_t timestamp);
注意HWC 从 HAL_PRIORITY_URGENT_DISPLAY 的线程触发 hwc2_callback_data_t延迟时间尽可能短通常小于 0.5 毫秒。
SurfaceFlinger 通过调用 setVsyncEnabled 来控制 HWC 是否生成 VSYNC 事件。 SurfaceFlinger 使 setVsyncEnabled 能够生成 VSYNC 事件因此它可以与屏幕的刷新周期同步。 当 SurfaceFlinger 同步到屏幕刷新周期时SurfaceFlinger 会停用 setVsyncEnabled 以阻止 HWC 生成 VSYNC 事件。 如果 SurfaceFlinger 检测到实际 VSYNC 与它先前建立的 VSYNC 之间存在差异则 SurfaceFlinger 会重新启动 VSYNC 事件生成过程。
5.1 VSYNC 偏移
同步 应用和SurfaceFlinger 渲染循环 应同步到硬件 VSYNC。 在 VSYNC 事件中屏幕开始显示帧 N而 SurfaceFlinger 开始为帧 N1 合成窗口。应用处理等待的输入并生成帧 N2。
与 VSYNC 同步会实现一致的延迟时间。它可以减少 应用和 SurfaceFlinger 中的错误并最大限度减小相位内外屏幕之间的偏移。这要假定 应用和 SurfaceFlinger 的每帧时间没有很大变化。延迟至少为两帧。
为了解决此问题可以通过使 应用和合成信号 与 硬件 VSYNC 相关从而利用 VSYNC 偏移减少输入设备到屏幕的延迟。这是有可能的因为应用加合成通常需要不到 33 毫秒的时间。
VSYNC 偏移的结果是具有相同周期和偏移相位的三个信号 1HW_VSYNC_0 - 屏幕开始显示下一帧。 2VSYNC - 应用读取输入内容并生成下一帧。 3SF_VSYNC - SurfaceFlinger 开始为下一帧进行合成。 通过 VSYNC 偏移SurfaceFlinger 接收缓冲区并合成帧而应用同时处理输入内容并渲染帧。 注意VSYNC 偏移会缩短可用于应用和合成的时间因此增加了出错几率。
5.2 DispSync
DispSync 维护屏幕基于硬件的周期性 VSYNC 事件的模型并使用该模型在硬件 VSYNC 事件的特定相位偏移处执行回调。
DispSync 是一个软件锁相回路 (PLL)它可以生成由 Choreographer 和 SurfaceFlinger 使用的 VSYNC 和 SF_VSYNC 信号即使没有来自硬件 VSYNC 的偏移也是如此。
图 上图 为DispSync 流程
DispSync 具有以下特点 参考 - HW_VSYNC_0。 输出 - VSYNC 和 SF_VSYNC。 反馈 - 自硬件混合渲染器的退出栅栏有信号状态时间戳。
5.3 VSYNC/退出偏移
退出fences 的有信号状态时间戳必须 与 HW VSYNC 相符即使在不使用偏移相位的设备上也是如此。否则实际造成的错误会更加严重。智能面板通常有一个增量退出fences 是对显示内存进行直接内存访问 (DMA) 的终点但是实际的显示切换和 HW VSYNC 会晚一段时间。
PRESENT_TIME_OFFSET_FROM_VSYNC_NS 在设备的 BoardConfig.mk makefile 中设置。它取决于屏幕控制器和面板特性。从退出fence 时间戳到 HW VSYNC 信号的时间以纳秒为单位进行测量。
5.4 VSYNC 和 SF_VSYNC 偏移
VSYNC_EVENT_PHASE_OFFSET_NS 和 SF_VSYNC_EVENT_PHASE_OFFSET_NS 根据高负载使用情形进行了保守设置。
例如在窗口过渡期间进行部分 GPU 合成或 Chrome 滚动显示包含动画的网页。这些偏移允许较长的应用渲染时间和较长的 GPU 合成时间。
超过一两毫秒的延迟时间是非常明显的。为了最大限度地缩短延迟时间而不显著增加错误计数请集成彻底的自动化错误测试。XTS测试
注意VSYNC 和 SF_VSYNC 偏移同样在设备的 BoardConfig.mk 文件中配置。两个设置都是 HW_VSYNC_0 之后以纳秒为单位的偏移默认值为零如未设置的话也可以为负值。
六、帧同步
Android Frame Pacing 库也称为 Swappy是 Android Game SDK 的一部分。它可帮助 OpenGL 和 Vulkan 游戏在 Android 上实现流畅的渲染和正确的帧同步。 帧同步是指游戏的逻辑和渲染循环 与 操作系统的显示子系统和底层显示硬件 之间的同步。 Android 显示子系统旨在避免某些视觉假象例如画面撕裂。
显示子系统可通过执行以下操作避免画面撕裂 1在内部缓冲之前的帧 2检测延迟帧的提交情况 3当检测到延迟帧时继续显示当前帧
帧展示时间不一致是因为 游戏渲染循环的运行速率 与 本机显示硬件支持的速率不同。如果底层显示硬件的游戏渲染循环运行速度过慢会产生问题导致展示时间不一致。例如当运行速度为 30 FPS 的游戏尝试在原生支持 60 FPS 的设备上渲染时游戏的渲染循环会导致同一帧在屏幕上又重复显示 16 毫秒。这种类型的中断会导致帧时间严重不一致例如帧时间可能为 33 ms、16 ms、49 ms 等。过于复杂的场景会让该问题更复杂因为它们会导致丢帧。
Frame Pacing 库可执行以下任务 1补偿由于游戏帧较短而出现的卡顿现象。 添加呈现时间戳以便按时呈现帧而不是提前呈现。 使用呈现时间戳扩展 EGL_ANDROID_presentation_time 和 VK_GOOGLE_display_timing。 2对导致卡顿和延迟的长帧使用同步fence。 将 wait 注入应用让显示管道能够跟上进度而不会积累背压。 使用同步fenceEGL_KHR_fence_sync 和 VkFence。 3如果设备支持多个刷新频率会选择其中一个以便提供灵活流畅的呈现效果。 4基于帧统计信息 提供用于调试和性能分析的统计信息。 如需了解如何根据需求将库配置为以不同模式运行请参阅 支持的操作模式https://developer.android.com/games/sdk/frame-pacing/?hlzh-cn#supported_operating_modes
如需使用 OpenGL 渲染程序或 Vulkan 渲染程序实现请参阅以下文章 将 Android Frame Pacing 集成至 OpenGL 渲染程序https://developer.android.com/games/sdk/frame-pacing/opengl/?hlzh-cn 将 Android Frame Pacing 集成至 Vulkan 渲染程序https://developer.android.com/games/sdk/frame-pacing/vulkan/?hlzh-cn 如需了解详情请参阅实现适当的帧同步https://developer.android.com/games/sdk/frame-pacing/?hlzh-cn
6.1 每秒帧数 (FPS) 节流干预
通过 FPS 节流干预游戏只需使用平台端更改即可以保持适当的 FPS 速度而无需开发者执行任何操作。
FPS 节流干预的实现使用以下组件
6.1.1 GameManagerService
GameManagerService 组件会保留每位用户和每个游戏的游戏模式和游戏干预信息。FPS 信息存储在 GameManagerService 中并在每个用户个人资料的 PACKAGE_NAME, Interventions 映射中存储其他干预信息例如分辨率缩放系数。 当游戏模式更改或干预更新时可以访问 FPS 信息。UID 对于每个 PACKAGE_NAME 和用户都是唯一的可以进一步转换为 UID, Frame Rate 对以发送到 SurfaceFlinger。
6.1.2 SurfaceFlinger
SurfaceFlinger 组件现已支持对应用的 FPS 进行节流但前提是帧速率必须是显示刷新率的除数。 在发生 vsync 的情况下SurfaceFlinger 会验证 vsync 时间戳是否与应用的帧速率处于同一相位以检查受到节流的应用的 vsync 有效性。如果帧速率未与 vsync 处于同一相位则 SurfaceFlinger 会保持该帧直到帧速率和 vsync 处于同相位。
下图介绍了 GameManagerService 与 SurfaceFlinger 之间的交互 SurfaceFinger 会维护一个 UID, Frame Rate 对映射以设置新的帧速率节流优先级。UID 在用户和游戏之间是唯一的因此同一台设备上的每位用户都可以对同一游戏采用不同的帧速率设置。为了对游戏的帧速率进行节流GameServiceManager 会调用 SurfaceFlinger 来替换 UID 的帧速率。借助此机制SurfaceFlinger 会在游戏模式更改或干预更新时更新映射。SurfaceFlinger 通过相应地锁定缓冲区来处理 FPS 更改。
七、多种刷新率
Android 11 增加了对具有多种刷新率的设备的支持。此功能包含三个主要组成部分 1android.hardware.graphics.composer2.4 中引入的新 HAL API。 2平台代码用于解析不同刷新率的设备配置并设置所需的刷新率 3新增的 SDK 和 NDK API使应用可以设置所需的帧速率
7.1 实现
我们在 android.hardware.graphics.composer2.4 HAL 中添加了对刷新率切换的专属支持。早期版本的 Composer HAL 对刷新率切换的支持有限。
7.2 配置群组
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 之间切换因为这会导致配置更改而不仅仅是简单地变换刷新率。 7.3 Composer API 更新
7.3.1 getDisplayVsyncPeriod
为了在变换刷新率时更好地进行控制并提高可预测性我们添加了 getDisplayVsyncPeriod 。 getDisplayVsyncPeriod 会返回屏幕的当前刷新率以 Vsync 周期为单位。 当在刷新率之间进行转换而平台需要获取当前的刷新率以决定何时启动下一帧时这尤其有用。
7.3.2 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。
7.3.3 onVsyncPeriodTimingChanged [callback]
可由 HAL 调用的新回调用于告知平台时间轴的某些参数发生了更改平台需要调整其时间轴。 如果由于某种原因旧的时间轴因 HAL 的处理时间过长或者刷新帧延迟而错过则应调用此回调。
7.4 平台如何决定变换刷新频率
刷新率选择在以下两个系统服务中发生 DisplayManager DisplayManager 用于设置与刷新率有关的上层策略。它会设置默认的屏幕配置此配置与混合渲染器 HAL 配置相同。此外它还会设置一个由最大值和最小值界定的范围供 SurfaceFlinger 从中选出刷新率。 SurfaceFlinger 设置一个与默认配置位于同一配置群组且刷新率介于最小值/最大值范围内的配置从而确定刷新率。 屏幕管理器会执行以下步骤来确定策略 1从 SurfaceFlinger 查询活跃配置找出默认配置 ID 2通过遍历系统条件来限制这个通过最小值和最大值界定的范围 a) 默认刷新率设置默认刷新率值在 R.integer.config_defaultRefreshRate 配置叠加层中设置。该值用于确定动画和轻触互动的标准设备刷新率。 b) 峰值刷新率设置峰值刷新率值是从 Settings.System.PEAK_REFRESH_RATE 读取的。该值在运行时更改以反映当前设备设置例如从菜单选项更改。默认值在 R.integer.config_defaultPeakRefreshRate 配置叠加层中设置。 c) 最小刷新频率设置最小刷新频率值是从 Settings.System.MIN_REFRESH_RATE 读取的。该值可以在运行时更改以反映当前设备设置例如从菜单选项更改。默认值为 0因此不存在默认的最小值。 d) 应用要求的 ModeId应用可以设置 WindowManager.LayoutParams.preferredDisplayModeId 以反映屏幕应运行的首选配置。在大多数情况下DisplayManager 会相应地设置默认配置 ID并设置最小和最大刷新频率以匹配配置的刷新频率。 e) 省电模式当设备处于低功耗模式通过 Settings.Global.LOW_POWER_MODE. 指示时刷新率上限为 60Hz。
DisplayManager 设置策略后SurfaceFlinger 会根据活跃层将帧更新排入队列的层设置刷新率。如果该层的所有者设置了帧速率则 SurfaceFlinger 会尝试将刷新率设置为该帧速率的倍数。 例如如果两个活跃层的帧速率设置为 24 和 60则 SurfaceFlinger 会选择 120Hz如果可用。如果 SurfaceFlinger 无法使用此类刷新率则会尝试选择对帧速率来说误差最小的刷新率。 如需了解详情请参阅 developer.android.com 上的开发者文档:https://developer.android.com/media/optimize/performance/frame-rate
SurfaceFlinger 维护以下标志来控制刷新率的确定方式 1)ro.surface_flinger.use_content_detection_for_refresh_rate: 如果已设置系统会根据活跃层确定刷新率即使未设置帧速率。SurfaceFlinger 持续运用启发法通过关注连接到缓冲区的呈现时间戳找出该层发布缓冲区的平均 fps。 2)ro.surface_flinger.set_touch_timer_ms如果大于 0则在配置的超时时间内有用户轻触屏幕时将使用默认刷新率。此启发法完成时便会对动画采用默认刷新率。 3)ro.surface_flinger.set_idle_timer_ms如果大于 0则在配置的超时时间内没有屏幕更新时将使用最小刷新频率。 4)ro.surface_flinger.set_display_power_timer_ms如果大于 0则在配置的超时时间内开启屏幕或退出 AOD时将使用默认刷新率。
7.5 Frame Rate API 应用可通过 Frame Rate API 将其预期帧速率告知 Android 平台该 API 可在以 Android 11 为目标平台的应用中使用。 如需详细了解帧速率 API请查看 developer.android.com 上的开发者文档:https://developer.android.com/media/optimize/performance/frame-rate
7.6 开发者选项
菜单中新增了一个开发者选项它可以使用当前刷新率在屏幕上切换叠加层。 新选项位于设置 系统 开发者选项 显示刷新频率下。