1

Android 图形系统框架简介

图形框架简介,没涉及到细节。

图形框架

ape_fwk_graphics.png

Android 图形系统的框架可以通过上图来表示,各模块说明如下。

  • Image Stream Producers:图形生产者,用来生成图形缓冲区,以供图形消费者使用。例如OpenGL ES、Canvas 2D 和 mediaserver 视频解码器。
  • Native Framework:Libgui库,包含了图形系统底层的基本元素,例如Surface、BufferQueue、GraphicBufferConsumer、GraphicBufferProducer、SurfaceComposer等等。
  • Window Positioning:WindowManager,用来控制 window 对象,window为一组view对象的集合。它为 SurfaceFlinger 提供缓冲区和窗口元数据,而 SurfaceFlinger 可使用这些信息将 Surface 合成到屏幕。
  • Image Stream Consumers:图形消费者,包括 SurfaceFlinger 和一些图形应用。

    • 图形流的最常见消费者是 SurfaceFlinger,该系统服务会消耗当前可见的 Surface,并使用 WindowManager 中提供的信息将它们合成到显示部分。SurfaceFlinger 使用 OpenGL 和 Hardware Composer 来合成一组 Surface。
    • 一些 OpenGL ES 应用也可以做为消费者,例如相机应用会消费相机预览图像流。非 GL 应用也可以是消费者,例如 ImageReader 类。
  • HAL:显示子系统的硬件抽象实现,包括 Hardware Composer 和 Gralloc。

    • SurfaceFlinger 可以将某些合成工作委托给 Hardware Composer,以分担 OpenGL 和 GPU 上的工作量。这时,SurfaceFlinger 只是充当另一个 OpenGL ES 客户端,Hardware Composer 则进行图形渲染的工作。Hardware Composer 必须支持事件,其中之一是 VSYNC(另一个是支持即插即用 HDMI 的热插拔)。
    • Gralloc用来分配图形生产方请求的内存。

图形系统使用生产者-消费者模式,根据框架图,可以看出基本的操作流程。

  • 图形生产者创建一个 Surface,将图形数据画到 Surface 上。Surface 使用的图形缓冲是通过 Gralloc 来分配的。
  • 图形生产者的窗口信息由 WindowManager 管理,WindowManager 将窗口元数据发送给 SurfaceFlinger 进行合成。
  • 图形消费者处理图形生产者产生的数据。当图形数据用于屏幕显示时,SurfaceFlinger 使用窗口元数据将图形缓冲合成到显示上。
  • 图形数据最终渲染到显示设备上是通过 Hardware Composer 完成的。硬件一般支持多个显示层,如果显示层可以满足 Surface 的需求,则可以将所有可见 Surface 发送给 Hardware Composer 进行合成。也可以使用 GPU 预先完成一些 Surface 数据的合成工作,之后再送给 Hardware Composer。

图形系统框架简单来说就如上所述,但涉及到具体实现时会显得非常复杂,本文不会分析。接下来将讲述图形系统的一些要点,还是以流程为主。

BufferQueue

graphics_pipeline.png

上图描述了图形管道的流程。左侧为图形生产者,右侧为图形消费者,中间通过 BufferQueues 连接。图中,主屏幕、状态栏和系统界面通过 GPU 渲染生成图形缓冲区,做为生产者传递给 BufferQueues。SurfaceFlinger 做为消费者,接收到 BufferQueues 的通知后,取出可用的图形缓冲区,送给显示端。例图中将状态栏和系统界面的图形缓冲送给 GPU 合成,生成新的图形缓冲后再通过 BufferQueues 发送到硬件混合渲染器。

在图形管道流程中,BufferQueues 做为图形组件之间的粘合剂,将图形生产者和图形消费者连接起来。一旦生产者移交其图形缓冲区,SurfaceFlinger 便会负责将所有内容合成到显示部分。BufferQueue 是将缓冲区池与队列相结合的数据结构,它使用 Binder IPC 在进程之间传递缓冲区。BufferQueue 通常用于渲染到 Surface,并且与 GL 消耗方及其他任务一起消耗内容。一个 BufferQueue 通信过程如下图所示。

消费者创建并拥有 BufferQueue 数据结构,并且可以运行在与生产者不同的进程中。当生产者需要缓冲区时,它会通过调用 dequeueBuffer() 从 BufferQueue 请求一个可用的缓冲区,并指定缓冲区的宽度、高度、像素格式和使用标记。然后,生产者填充缓冲区并通过调用 queueBuffer() 将缓冲区返回到队列。接下来,消费者通过 acquireBuffer() 获取该缓冲区并使用该缓冲区的内容。当使用方操作完成后,它会通过调用 releaseBuffer() 将该缓冲区返回到队列。同步框架可控制缓冲区在 Android 图形管道中移动的方式。

BufferQueue 的一些特性(例如可以容纳的最大缓冲区数)由生产者和消费者联合决定。但是,BufferQueue 会根据需要分配缓冲区。除非特性发生变化,否则将会保留缓冲区。例如,如果生产者请求具有不同大小的缓冲区,则系统会释放旧的缓冲区,并根据需要分配新的缓冲区。缓冲区通过句柄进行传递,BufferQueue 永远不会复制缓冲区内容,因为移动如此多的数据是非常低效的操作。

Surface 和 SurfaceFlinger

官方文档对 Surface 的定义为:Surface 是一个接口,供生产者与消费者交换缓冲区。在架构中,Surface 通常被描述为生产者缓冲区的承载着。无论开发者使用什么渲染 API,一切内容都会渲染到“Surface”。图形数据传输过程中,Surface 代表缓冲队列中的生产方。Surface 通过 SurfaceConrol 构建和管理。SurfaceControl 还会提供屏幕框架的元数据,用来缓冲区的显示属性,例如大小、帧率、Alpha、ZOrder等等。

SurfaceFlinger 做为图形消费者接受缓冲区,对它们进行合成,然后发送到屏幕。SurfaceFlinger 根据WindowManager 窗口元数据对 Surface 进行合成。SurfaceFlinger 中与 Surface 相对应的元素是 Layer,Layer是 surface(包含 BufferQueue)和 SurfaceControl(包含屏幕框架等层元数据)的组合。当应用进入前台时,它会从 WindowManager 请求缓冲区。WindowManager 会从 SurfaceFlinger 请求 Layer,SurfaceFlinger 创建 Layer 并将其发送至 WindowManager。然后,WindowManager 将 Surface 发送至应用,但会保留 SurfaceControl 来操控应用在屏幕上的外观。

Surface 与 SurfaceFlinger 之间的大致关系如下图所示。

surface.jpg

HWC

HWC(Hardware Composer) HAL 用于通过可用硬件来确定合成缓冲区的最有效方法。作为 HAL,其实现是特定于设备的,而且通常由屏幕硬件原始设备制造商 (OEM) 完成。

假设有一部普通 Android 手机,其屏幕方向为纵向,状态栏在顶部,导航栏在底部,其他区域显示应用内容。每个层的内容都在单独的缓冲区中,通常有两种合成方式。

hwc.png

  • 离线合成:将应用内容渲染到暂存缓冲区中,然后在其上渲染状态栏,再在其上渲染导航栏,最后将暂存缓冲区传送到显示硬件。
  • 在线合成:将三个缓冲区全部传送到显示硬件,并指示它从不同的缓冲区读取屏幕不同部分的数据。

在线合成使用 HWC,合成效率显著提高。HWC 的实现上需要硬件支持多个显示图层,一个软件 Layer 对应一个硬件显示图层,渲染合成由硬件完成。硬件显示图层是有限的,Android 设备通常支持 4 个。如果软件 Layers 对于硬件显示图层时,多余的 Layers 还是需要使用 GLES 进行合成,这意味着应用使用的 Layers 数量会对能耗和性能产生重大影响。

显示处理器功能差异很大,硬件显示图层的数量(无论层是否可以旋转或混合)以及对定位和叠加的限制很难通过 API 表达。为了适应这些选项,HWC 会执行以下计算:

  1. SurfaceFlinger 向 HWC 提供一个完整的层列表,并询问“您希望如何处理这些层?”
  2. HWC 的响应方式是将每个层标记为设备或客户端合成。
  3. SurfaceFlinger 会处理所有客户端,将输出缓冲区传送到 HWC,并让 HWC 处理其余部分。

由于硬件供应商可以定制决策代码,因此可以在每台设备上实现最佳性能。

当屏幕上的内容没有变化时,HWC 合成的效率可能会低于 GL 合成。当叠加层内容具有透明像素且叠加层混合在一起时,尤其如此。在此类情况下,HWC 可以为部分或全部层请求 GLES 合成,并保留合成的缓冲区。如果 SurfaceFlinger 要求合成同一组缓冲区,HWC 可以显示先前合成的暂存缓冲区。这可以延长闲置设备的电池续航时间。

VSYNC

VSYNC 信号可同步显示管道。显示管道由应用渲染、SurfaceFlinger 合成以及用于在屏幕上显示图像的硬件混合渲染器 (HWC) 组成。VYSNC 可同步应用唤醒以开始渲染的时间、SurfaceFlinger 唤醒以合成屏幕的时间以及屏幕刷新周期。这种同步可以消除卡顿,并提升图形的视觉表现。

Android 的显示管道默认使用 Triple Buffer 的机制,解决了双缓冲机制中 CPU/GPU 利用率不高和卡顿问题。一个基本的显示流程如下,

tripple_buffer.png

  • 第一个 VSync 时:显示 A Buffer,CPU 开始处理 B Buffer,之后交给 GPU合成。
  • 第二个 VSync 时:依然显示 A Buffer,CPU 开始处理 C Buffer。因为这时可能存在 GPU 没有处理完 B Buffer 的情况,所以延迟显示。这种情况下如果是双缓冲,CPU 就将处于空闲状态。
  • 第三个 VSync 时:显示 B Buffer,CPU 处理完成显示的 A Buffer,之后交给 GPU。
  • 之后按先前顺序处理 Buffer,可以流畅显示。但是如果采用双缓冲,在CPU/GPU 效率较低的情况下,很可能下一个 VSync 到来时,GPU 还没处理完下一帧。那么只能继续显示当前帧,这样就会表现为卡顿。

从上面可以看到,Display、CPU 和 GPU 并不需要在同一时刻开始处理,保持与 VSync 一定的延迟可以减少应用和 SurfaceFlinger 中的错误,并最大限度减小相位内外屏幕之间的偏移。这样就产生了三个具有相同周期和偏移相位的信号:

  • HW_VSYNC_0 - 屏幕开始显示下一帧。
  • VSYNC - 应用读取输入内容并生成下一帧。
  • SF_VSYNC - SurfaceFlinger 开始为下一帧进行合成。

Android 中使用 DispSync 来维护这三个信号。DispSync 是一个软件锁相回路 (PLL),它可以生成由 Choreographer 和 SurfaceFlinger 使用的 VSYNC 和 SF_VSYNC 信号,即使没有来自硬件 VSYNC 的偏移也是如此。

dispsync.png

DispSync 具有以下特点:

  • 参考 - HW_VSYNC_0。
  • 输出 - VSYNC 和 SF_VSYNC。
  • 反馈 - 来自 HWC 的 retire fence 时间戳。

VSync 事件并不是完全由硬件产生的,SurfaceFlinger 通过调用 setVsyncEnabled 来控制 HWC 是否生成 VSync 事件。SurfaceFlinger 会根据硬件 VSync 进行时钟校准,当同步到屏幕刷新周期时,SurfaceFlinger 就会调用 setVsyncEnabled 阻止 HWC 生成 VSync 事件。如果 SurfaceFlinger 检测到实际 VSync与它先前建立的 VSync 之间存在差异,则 SurfaceFlinger 会重新启动 VSync 事件生成过程。

参考文档:
Android Source Graphics
Android图形系统(五)-Surface图形系统概览


戈壁老王
143 声望60 粉丝

做为一个不称职的老年码农,一直疏忽整理笔记,开博记录一下,用来丰富老年生活,