Hardware Composer (HWC) HAL 会合成从 SurfaceFlinger 收到的图层,从而减少 OpenGL ES (GLES) 合成量和 GPU 执行量。
HWC 会抽象化对象(例如,叠加层和 2D Blitter)以合成界面,并与专用窗口合成硬件通信以合成窗口。请使用 HWC 合成窗口,而不是让 SurfaceFlinger 使用 GPU 合成。大多数 GPU 都未针对合成进行优化,并且当 GPU 合成来自 SurfaceFlinger 的图层时,应用无法将 GPU 用于其自身的渲染。
HWC 实现应支持:
- 至少四个叠加层
- 状态栏
- 系统栏
- 应用
- 壁纸/背景
- 大于显示屏的图层(例如,壁纸)
- 同步预乘每像素 Alpha 混合和每平面 Alpha 混合
- 受保护视频播放的硬件路径
- RGBA 封装顺序、YUV 格式以及平铺、像素重排和步幅属性
要实现 HWC,请执行以下操作:
- 实现非操作 HWC 并将所有合成工作发送到 GLES。
- 实现一种算法,以将合成工作逐步委托给 HWC。例如,仅将前三到四个界面委托给 HWC 的叠加层硬件。
- 优化 HWC。可能包括:
- 选择使 GPU 负载最大化的界面,并将它们发送到 HWC。
- 检测屏幕是否正在更新。如果未更新,则将合成委托给 GLES 而不是 HWC,以节省电量。当屏幕再次更新时,继续将合成卸载到 HWC。
- 为常见用例做好准备,例如:
- 主屏幕,其中包括状态栏、系统栏、应用窗口和动态壁纸
- 纵向和横向模式下的全屏游戏
- 带有隐藏式字幕和播放控件的全屏视频
- 受保护的视频播放
- 分屏多窗口
HWC 图元
HWC 提供两个图元:图层和显示屏,用于表示合成工作及其与显示屏硬件的互动。HWC 还提供对 VSYNC 的控制以及 SurfaceFlinger 的回调,以便在 VSYNC 事件发生时通知 SurfaceFlinger。
HIDL 接口
Android 8.0 及更高版本使用名为 Composer HAL 的 HIDL 接口,用于 HWC 和 SurfaceFlinger 之间的绑定器化 IPC。Composer HAL 取代了旧版 hwcomposer2.h
接口。如果供应商提供 HWC 的 Composer HAL 实现,则 Composer HAL 会直接接受来自 SurfaceFlinger 的 HIDL 调用。如果供应商提供 HWC 的旧版实现,则 Composer HAL 会从 hwcomposer2.h
加载函数指针,并将 HIDL 调用转发到函数指针调用中。
HWC 提供一些函数来确定给定显示屏的属性;在不同的显示屏配置(例如 4k 或 1080p 分辨率)和颜色模式(例如原生颜色或 true sRGB)之间切换;以及在支持的情况下,开启、关闭显示屏或使显示屏进入低功耗模式。
函数指针
如果供应商直接实现 Composer HAL,则 SurfaceFlinger 会通过 HIDL IPC 调用其函数。例如,要创建图层,SurfaceFlinger 会在 Composer 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 函数直通函数,请参阅 composer
。如需详细了解 HWC 函数指针,请参阅 hwcomposer2.h
。
图层和显示屏句柄
层和显示器由 HWC 生成的句柄进行操作。这些句柄对于 SurfaceFlinger 是不透明的。
当 SurfaceFlinger 创建新层时,它会调用 createLayer
,对于直接实现返回 Layer
类型,对于直通实现返回 hwc2_layer_t
类型。当 SurfaceFlinger 修改该层的属性时,SurfaceFlinger 会将 hwc2_layer_t
值以及进行修改所需的任何其他信息传递到相应的修改函数中。hwc2_layer_t
类型足够大,可以容纳指针或索引。
物理显示器通过热插拔创建。当物理显示器热插拔时,HWC 会创建一个句柄,并通过热插拔回调将句柄传递给 SurfaceFlinger。虚拟显示器由 SurfaceFlinger 调用 createVirtualDisplay()
来请求显示器而创建。如果 HWC 支持虚拟显示器合成,它会返回一个句柄。然后,SurfaceFlinger 将显示器的合成委托给 HWC。如果 HWC 不支持虚拟显示器合成,SurfaceFlinger 会创建句柄并合成显示器。
显示器合成操作
每个 VSYNC 周期,如果 SurfaceFlinger 有新的内容要合成,它就会唤醒。这个新内容可以是来自应用的新图像缓冲区,或者是对一个或多个图层属性的更改。当 SurfaceFlinger 唤醒时,它会执行以下操作:
- 处理事务(如果存在)。
- 锁定新的图形缓冲区(如果存在)。
- 执行新的合成(如果步骤 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 完成合成过程并显示最终结果。
多个显示器
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>)
虚拟显示器合成
虚拟显示器合成类似于外部显示器合成。虚拟显示器合成和物理显示器合成之间的区别在于,虚拟显示器将输出发送到 Gralloc 缓冲区,而不是发送到屏幕。硬件合成器 (HWC) 将输出写入缓冲区,提供完成栅栏,并将缓冲区发送给使用者(例如视频编码器、GPU、CPU 等)。如果显示管线写入内存,虚拟显示器可以使用 2D/blitter 或叠加层。
模式
在 SurfaceFlinger 调用 validateDisplay()
HWC 方法后,每个帧都处于以下三种模式之一
- GLES — GPU 合成所有图层,直接写入输出缓冲区。HWC 不参与合成。
- 混合 (MIXED) — GPU 将某些图层合成到帧缓冲区,而 HWC 合成帧缓冲区和其余图层,直接写入输出缓冲区。
- HWC — HWC 合成所有图层,并直接写入输出缓冲区。
输出格式
虚拟显示器缓冲区输出格式取决于其模式
- GLES 模式 — EGL 驱动程序在
dequeueBuffer()
中设置输出缓冲区格式,通常为RGBA_8888
。使用者必须能够接受驱动程序设置的输出格式,否则无法读取缓冲区。 - 混合和 HWC 模式 — 如果使用者需要 CPU 访问权限,则使用者设置格式。否则,格式为
IMPLEMENTATION_DEFINED
,Gralloc 会根据使用标志设置最佳格式。例如,如果使用者是视频编码器并且 HWC 可以高效地写入该格式,则 Gralloc 会设置 YCbCr 格式。
同步栅栏
同步 (sync) 栅栏是 Android 图形系统的关键方面。栅栏允许 CPU 工作独立于并发 GPU 工作进行,仅在存在真正的依赖关系时才阻塞。
例如,当应用提交正在 GPU 上生成的缓冲区时,它还会提交一个同步栅栏对象。此栅栏指示 GPU 何时完成写入缓冲区。
HWC 要求 GPU 在显示缓冲区之前完成缓冲区写入。同步栅栏通过图形管线与缓冲区一起传递,并指示缓冲区何时写入。在显示缓冲区之前,HWC 会检查同步栅栏是否已发出信号,如果已发出信号,则会显示缓冲区。
有关同步栅栏的更多信息,请参阅硬件合成器集成。