实现硬件混合渲染器 HAL

Hardware Composer (HWC) HAL 复合从 SurfaceFlinger 接收的层,减少了OpenGL ES (GLES)和 GPU 执行的复合量。

HWC 将覆盖层和 2D 位块传输等对象抽象为复合表面,并与专用窗口复合硬件通信以复合窗口。使用 HWC 来合成窗口,而不是让 SurfaceFlinger 与 GPU 进行合成。大多数 GPU 并未针对合成进行优化,当 GPU 从 SurfaceFlinger 合成图层时,应用程序无法使用 GPU 进行自己的渲染。

HWC 实现应支持:

  • 至少有四个叠加层:
    • 状态栏
    • 系统栏
    • 应用程序
    • 壁纸/背景
  • 大于显示屏的图层(例如壁纸)
  • 同时预乘每像素 alpha 混合和每平面 alpha 混合
  • 受保护视频播放的硬件路径
  • RGBA 打包顺序、YUV 格式以及平铺、混合和步幅属性

实施 HWC:

  1. 实施非操作性 HWC 并将所有组合工作发送到 GLES。
  2. 实现一种算法,以增量方式将组合委托给 HWC。例如,仅将前三个或四个表面委托给 HWC 的覆盖硬件。
  3. 优化 HWC。这可能包括:
    • 选择能够最大程度地减轻 GPU 负载并将其发送到 HWC 的表面。
    • 检测屏幕是否正在更新。如果不是,请将合成委托给 GLES 而不是 HWC 以节省电量。当屏幕再次更新时,继续将合成卸载到 HWC。
    • 为常见用例做好准备,例如:
      • 主屏幕,包括状态栏、系统栏、应用程序窗口和动态壁纸
      • 纵向和横向模式下的全屏游戏
      • 带隐藏式字幕和播放控制的全屏视频
      • 受保护的视频播放
      • 分屏多窗口

HWC原语

HWC 提供了两个基元:图层显示,来表示合成工作及其与显示硬件的交互。 HWC 还提供对VSYNC 的控制以及对 SurfaceFlinger 的回调,以在发生 VSYNC 事件时通知它。

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 分辨率)和颜色模式(例如本机颜色或真正的 sRGB)之间切换;以及打开、关闭显示器或进入低功耗模式(如果支持)。

函数指针

如果厂商直接实现Composer HAL,SurfaceFlinger通过HIDL IPC调用其函数。例如,要创建图层,SurfaceFlinger 会在 Composer HAL 上调用createLayer()

如果供应商实现了hwcomposer2.h接口,Composer HAL 会调用hwcomposer2.h函数指针。在hwcomposer2.h注释中,HWC 接口函数由接口中不存在的小驼峰名称引用为命名字段。几乎每个函数都是通过使用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. 锁存新的图形缓冲区(如果存在)。
  3. 如果步骤 1 或 2 导致显示内容发生更改,则执行新的合成。

为了执行新的合成,SurfaceFlinger 将创建和销毁图层或修改图层状态(如果适用)。它还使用setLayerBuffersetLayerColor等调用来更新图层的当前内容。更新所有层后,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 缓冲区而不是屏幕。 Hardware Composer (HWC) 将输出写入缓冲区,提供完成栅栏,并将缓冲区发送给使用者(例如视频编码器、GPU、CPU 等)。如果显示管道写入内存,则虚拟显示器可以使用 2D/位块传输或覆盖。

模式

SurfaceFlinger 调用validateDisplay() HWC 方法后,每个帧都处于三种模式之一:

  • GLES — GPU 合成所有层,直接写入输出缓冲区。 HWC 不参与组合。
  • MIXED — GPU 将某些层合成到帧缓冲区,HWC 将帧缓冲区和其余层合成,直接写入输出缓冲区。
  • HWC — HWC 合成所有层并直接写入输出缓冲区。

输出格式

虚拟显示缓冲区输出格式取决于其模式:

  • GLES 模式- EGL 驱动程序在dequeueBuffer()中设置输出缓冲区格式,通常为RGBA_8888 。消费者必须能够接受驱动程序设置的输出格式,否则无法读取缓冲区。
  • MIXED 和 HWC 模式— 如果消费者需要 CPU 访问,则消费者设置格式。否则,格式为IMPLEMENTATION_DEFINED ,并且 Gralloc 根据使用标志设置最佳格式。例如,如果消费者是视频编码器并且HWC可以有效地写入格式,则Gralloc设置YCbCr格式。

同步围栏

同步(sync)栅栏是Android图形系统的一个重要方面。栅栏让 CPU 工作独立于并发 GPU 工作进行,仅在存在真正的依赖关系时才会阻塞。

例如,当应用程序提交在 GPU 上生成的缓冲区时,它还会提交同步栅栏对象。当 GPU 完成写入缓冲区时,此栅栏会发出信号。

HWC 要求 GPU 在显示缓冲区之前完成写入缓冲区。同步栅栏与缓冲区一起通过图形管道,并在写入缓冲区时发出信号。在显示缓冲区之前,HWC 检查同步栅栏是否已发出信号,如果有,则显示缓冲区。

有关同步栅栏的更多信息,请参阅Hardware Composer 集成