Graphics
移动端渲染
移动App的整体UI框架大致分成下面4个层次:
1)UI库
跟Android、iOS原生开发类似,Flutter用dart语言实现一整套UI控件。Flutter先将控件树转成渲染树,然后交由skia库绘制界面。
2)图形库
Skia图形库跟iOS平台的CoreAnimation等库功能类似,不仅提供了图形渲染功能,还提供文字绘制和图片显示功能。高级图形图像库将需要绘制的图形转成点、线、三角形等图元,再调用底层图形接口实现绘制。
3)低级图形接口
OpenGL是使用最广的低级图形接口,兼容性最好,基本上支持市面上的所有GPU。Vulkan是最近几年新推出的图形API,除了iPhone的GPU,其他厂家的GPU基本都支持。Metal是苹果新推出的图形API,只支持自家GPU。
4)硬件设备层
目前的移动设备出于性能考虑,大部分图形都是通过GPU渲染,少数情况也会使用CPU渲染,后文会介绍skia使用CPU和GPU渲染的具体场景。
iPhone 在A11芯片以前使用power vr系列GPU,之后采用自研GPU。安卓手机大部分采用高通Adreno GPU或ARM mail GPU。GPU渲染完一帧图像后送FrameBuffer,最后在合适的时机展示在屏幕上。
Skia应用广泛并且跨平台,不仅用于Flutter和Android操作系统,还用于Google Chrome浏览器,同时支持windows、Mac、iOS操作系统。Skia由C++编写,代码开源,通过研究skia有助于理解图形图像的绘制原理,为UI性能优化提供思路。
图形
Android 框架提供了各种用于 2D 和 3D 图形渲染的 API,可与制造商的图形驱动程序实现代码交互,因此,务必更好地了解这些 API 的工作原理。本页介绍了在其上构建这些驱动程序的 图形硬件抽象层 (HAL)。
应用开发者可通过三种方式将图像绘制到屏幕上:使用canvas、OpenGL ES 或 Vulkan(UE4支持)。
Android 图形组件
无论开发者使用什么渲染 API,一切内容都会渲染到 Surface 上。Surface 表示缓冲区队列中的生产方,而缓冲区队列通常会被 SurfaceFlinger 消耗。在 Android 平台上创建的每个窗口都由 Surface 提供支持。所有被渲染的可见 Surface 都被 SurfaceFlinger 合成到屏幕。
下图显示了关键组件如何协同工作:
图 1. Surface 如何被渲染
主要组件如下所述:
图像流生产方 IMAGE STREAM PRODUCERS
图像流生产方可以是生成图形缓冲区以供消耗的任何内容。例如 OpenGL ES、Canvas 2D 和 mediaserver 视频解码器。
图像流消耗方 IMAGE STREAM CONSUMERS
图像流的最常见消耗方是 SurfaceFlinger,该系统服务会消耗当前可见的 Surface,并使用窗口管理器中提供的信息将它们合成到屏幕。SurfaceFlinger 是可以修改所显示部分内容的唯一服务。SurfaceFlinger 使用 OpenGL 和 Hardware Composer 来合成一组 Surface。
其他 OpenGL ES 应用也可以消耗图像流,例如相机应用会消耗相机预览图像流。非 GL 应用也可以是使用方,例如 ImageReader 类。
硬件作曲家 Hardware Composer (简称HWC)
显示子系统的硬件抽象实现。SurfaceFlinger 可以将某些合成工作委托给Hardware Composer,以分担 OpenGL 和 GPU 上的工作量。SurfaceFlinger 只是充当另一个 OpenGL ES 客户端。因此,在 SurfaceFlinger 将一个或两个缓冲区合成到第三个缓冲区中的过程中,它会使用 OpenGL ES。这会让合成的功耗比通过 GPU 执行所有计算时更低。
硬件作曲家 Hardware Composer HAL 则进行另一半的工作,是所有 Android 图形渲染的中心点。Hardware Composer 必须支持事件,其中之一是 VSYNC(另一个是支持即插即用 HDMI 的热插拔)。
Gralloc
需要使用图形内存分配器 (Gralloc) 来分配图像生产方请求的内存。有关详情,请参阅 Gralloc HAL。
数据流
有关 Android 图形管道的描述,请参见下图:
图 2. 流经 Android 的图形数据流
左侧的对象是生成图形缓冲区的渲染器,如主屏幕、状态栏和系统界面。SurfaceFlinger 是合成器,而硬件混合渲染器是制作器。
BufferQueue
BufferQueues 是 Android 图形组件之间的粘合剂。它们是一对队列,可以调解缓冲区从生产方到消耗方的固定周期。一旦生产方移交其缓冲区,SurfaceFlinger 便会负责将所有内容合成到显示部分。
有关 BufferQueue 通信过程,请参见下图。
图 3. BufferQueue 通信过程
BufferQueue 包含将图像流生产方与图像流消耗方结合在一起的逻辑。图像生产方的一些示例包括由相机 HAL 或 OpenGL ES 游戏生成的相机预览。图像消耗方的一些示例包括 SurfaceFlinger 或显示 OpenGL ES 流的另一个应用,如显示相机取景器的相机应用。
BufferQueue 是将缓冲区池与队列相结合的数据结构,它使用 Binder IPC 在进程之间传递缓冲区。生产方接口,或者您传递给想要生成图形缓冲区的某个人的内容,即是 IGraphicBufferProducer(SurfaceTexture 的一部分)。BufferQueue 通常用于渲染到 Surface,并且与 GL 消耗方及其他任务一起消耗内容。
BufferQueue 可以在三种不同的模式下运行:
类同步模式 - 默认情况下,BufferQueue 在类同步模式下运行,在该模式下,从生产方进入的每个缓冲区都在消耗方那退出。在此模式下不会舍弃任何缓冲区。如果生产方速度太快,创建缓冲区的速度比消耗缓冲区的速度更快,它将阻塞并等待可用的缓冲区。
非阻塞模式 - BufferQueue 还可以在非阻塞模式下运行,在此类情况下,它会生成错误,而不是等待缓冲区。在此模式下也不会舍弃缓冲区。这有助于避免可能不了解图形框架的复杂依赖项的应用软件出现潜在死锁现象。
舍弃模式 - 最后,BufferQueue 可以配置为丢弃旧缓冲区,而不是生成错误或进行等待。例如,如果对纹理视图执行 GL 渲染并尽快绘制,则必须丢弃缓冲区。
为了执行这项工作的大部分环节,SurfaceFlinger 就像另一个 OpenGL ES 客户端一样工作。例如,当 SurfaceFlinger 正在积极地将一个缓冲区或两个缓冲区合成到第三个缓冲区中时,它使用的是 OpenGL ES。
Hardware Composer HAL 执行另一半工作。该 HAL 充当所有 Android 图形渲染的中心点。