层和屏幕

层和屏幕是两个基元,用于表示合成工作以及与屏幕硬件的交互。

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

属性 说明
定位 定义层在其屏幕上的显示位置。包括层边缘的位置及其相对于其他层的 Z 顺序(指示该层在其他层之前还是之后)等信息。
内容 定义应如何在定位属性定义的边界内呈现层上显示的内容。包括诸如剪裁(用来扩展内容的一部分以填充层的边界)和转换(用来显示旋转或翻转的内容)等信息。
合成 定义层应如何与其他层合成。包括混合模式和用于 Alpha 合成的全层 Alpha 值等信息。
优化 提供对于正确合成层并非绝对必要但可由硬件混合渲染器 (HWC) 设备用来优化合成执行方式的信息。包括层的可见区域以及层的哪个部分自上一帧以来已经更新等信息。

屏幕

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

虚拟屏幕

SurfaceFlinger 支持一个内部屏幕(内置于手机或平板电脑中的屏幕)、一个外部屏幕(如通过 HDMI 连接的电视)以及一个或多个令合成的输出在系统中可用的虚拟屏幕。虚拟屏幕可用于记录屏幕信息或通过网络发送屏幕信息。为虚拟屏幕生成的帧会写入 BufferQueue。

虚拟屏幕可以与主屏幕共享相同的一组层(层堆叠),也可拥有自己的一组层。虚拟屏幕没有 VSYNC,因此内部屏幕的 VSYNC 可为所有屏幕触发合成。

在支持虚拟屏幕的 HWC 实现中,虚拟屏幕可以与 OpenGL ES (GLES)、HWC 或者 GLES 及 HWC 合成在一起。在不支持虚拟屏幕的实现中,虚拟屏幕始终使用 GLES 进行合成。

案例研究:screenrecord

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 捕获启用显示设备的操作,然后逐帧播放。