图层和显示

图层和显示是代表合成工作以及与显示硬件交互的两个基本要素。

图层

图层是合成的最重要单元。SurfaceSurfaceControl 实例的组合。每个图层都有一组属性,用于定义其与其他图层的交互方式。下图描述了图层属性。

属性 说明
位置 定义图层在其显示屏上的显示位置。包括图层边缘的位置及其相对于其他图层的 Z 顺序(应位于其他图层的前面还是后面)等信息。
内容 定义应如何在位置属性定义的边界内呈现图层上显示的内容。包括裁剪(用于展开内容的一部分以填充图层边界)和变换(用于显示旋转或翻转的内容)等信息。
合成 定义应如何将图层与其他图层合成。包括混合模式和一个图层范围的 alpha 值(用于 alpha 合成)。
优化 提供并非严格必要但可供硬件合成器 (HWC) 设备用于优化其执行合成的方式的信息。包括图层的可见区域以及自上一帧以来图层的哪个部分已更新等信息。

显示屏

显示屏是合成的另一个重要单元。一个系统可以有多个显示屏,并且可以在正常系统操作期间添加或移除显示屏。显示屏的添加/移除操作由 HWC 请求或框架请求发起。当外部显示屏连接到设备或从设备断开连接时(称为热插拔),HWC 设备会请求添加或移除显示屏。客户端请求虚拟显示屏,其内容渲染到屏幕外缓冲区中,而不是渲染到物理显示屏。

虚拟显示屏

SurfaceFlinger 支持内部显示器(内置于手机或平板电脑)、外部显示器(例如通过 HDMI 连接的电视)以及一个或多个虚拟显示器,这些虚拟显示器使合成输出在系统内可用。虚拟显示器可用于录制屏幕或通过网络发送屏幕。为虚拟显示器生成的帧被写入 BufferQueue。

虚拟显示器可以与主显示器(图层堆栈)共享同一组图层,也可以拥有自己的图层组。虚拟显示器没有 VSYNC,因此内部显示器的 VSYNC 会触发所有显示器的合成。

在支持 HWC 的实现中,虚拟显示器可以使用 OpenGL ES (GLES)、HWC 或 GLES 和 HWC 两者进行合成。在不支持的实现中,虚拟显示器始终使用 GLES 进行合成。

案例研究:screenrecord

The screenrecord 命令允许用户将屏幕上显示的所有内容录制为磁盘上的 .mp4 文件。为了实现这一点,系统从 SurfaceFlinger 接收合成帧,将它们写入视频编码器,然后将编码后的视频数据写入文件。视频编解码器由单独的进程 (mediaserver) 管理,因此大型图形缓冲区必须在系统内移动。更具挑战性的是,目标是以全分辨率录制 60 fps 的视频。使这项工作高效完成的关键是 BufferQueue。

MediaCodec 类允许应用程序以缓冲区中的原始字节或通过 surface 提供数据。当 screenrecord 请求访问视频编码器时,mediaserver 进程会创建一个 BufferQueue,将其自身连接到消费者端,然后将生产者端作为 surface 传递回 screenrecord

然后,screenrecord 实用程序要求 SurfaceFlinger 创建一个镜像主显示器的虚拟显示器(即,它具有所有相同的图层),并指示它将输出发送到来自 mediaserver 进程的 surface。在这种情况下,SurfaceFlinger 是缓冲区的生产者,而不是消费者。

配置完成后,当编码数据出现时,screenrecord 会触发。当应用程序绘制时,它们的缓冲区会传输到 SurfaceFlinger,SurfaceFlinger 将它们合成为单个缓冲区,该缓冲区直接发送到 mediaserver 进程中的视频编码器。screenrecord 进程永远不会看到完整的帧。在内部,mediaserver 进程有其自己移动缓冲区的方式,该方式也通过句柄传递数据,从而最大限度地减少开销。

案例研究:模拟辅助显示器

WindowManager 可以要求 SurfaceFlinger 创建一个可见图层,SurfaceFlinger 在其中充当 BufferQueue 消费者。也可以要求 SurfaceFlinger 创建一个虚拟显示器,SurfaceFlinger 在其中充当 BufferQueue 生产者。

如果将虚拟显示器连接到可见图层,则会创建一个闭环,其中合成屏幕会出现在窗口中。该窗口现在是合成输出的一部分,因此在下次刷新时,窗口内的合成图像也会显示窗口内容。要查看实际效果,请在设置中启用开发者选项,选择模拟辅助显示器,然后启用一个窗口。要查看辅助显示器的实际效果,请使用 screenrecord 捕获启用显示器的操作,然后逐帧回放。