层和屏幕是两个基元,用于表示合成工作以及与屏幕硬件的交互。
层
层是合成的最重要单元。层是 Surface 和 SurfaceControl 实例的组合。每个层都有一组属性,用于定义它与其他层的交互方式。层属性如下表所述。
属性 | 说明 |
---|---|
定位 | 定义层在其屏幕上的显示位置。包括层边缘的位置及其相对于其他层的 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
捕获启用显示设备的操作,然后逐帧播放。