Android渲染

208 阅读6分钟

Android渲染

Android渲染整体架构

Android渲染整个过程是一个很典型的生产者消费者模式 App(生产者) <-> BufferQueue(图像缓冲区) <-> SurfaceFlinger(消费者)

producer_consumer.png

渲染流程

  1. 由app生成渲染数据。app#draw()把绘制指令通过canvas传递给framework层的RenderThread线程
  2. framework层的RenderThread线程通过surface.dequeue得到缓冲区graphic buffer,然后通过OpenGL完成真正渲染命令,再把缓冲区交还给BufferQueue队列
  3. SurfaceFlinger(Android中担当消费者角色)。负责从队列中取数据,同时和HAL完成layer的合成工作,最终交给HAL显示(HAL硬件抽象层。负责把图像数据展示到设备显示器上)

生产者

生成渲染数据主要有:MediaPlayer、CameraPreview、NDK(Skia)、OpenGL ES

  • MediaPlayer、CameraPreview:直接读取图像源生成图像数据
  • NDK(Skia)、OpenGL ES:通过自身绘制产生图像数据

渲染库

OpenGL
  • 跨平台的3D图形绘制规范接口,OpenGL ES针对嵌入式设备(手机)做了优化
  • 在OpenGL驱动中会把API验证、内存管理、多线程管理等工作帮忙完成,即使API出错,也能保证App正常运行,开发者使用简单,但牺牲了大量性能
  • 单线程渲染,资源浪费
Skia
  • 图像渲染库(Android、Fullter),支持2D、3D、CPU软硬绘制。
  • 2D图形绘制依赖自身完成。3D需依赖硬件,由OpenGL、Vulkan、Metal支持
Vulkan
  • 科纳斯组织基于AMD Mantle基础上开发,是用来替换OpenGL的(OpenGL不能充分发挥CPU多核多线程性能优势)
  • 支持2D、3D绘制,更轻量级,跨平台
  • Vulkan把API验证、内存管理、多线程管理等工作交给开发者负责,一旦API出错,APP就会crash。虽增加了API使用的复杂度和困难度,但大大提升了性能(单在驱动层去掉API验证操作,就将性能提升了9倍)
  • 引入Command Buffer概念,每个线程均可往Command Buffer提交渲染数据,可充分发挥CPU多核多线程优势
OpenGL与Vulkan
  • Vulkan的优势在于显示控制和多线程功能,可使我们在更少CPU时间内将更多命令推送至GPU,具有更精细的成本控制
  • OpenGL的优势在于其提供了更易于使用的硬件访问方式
选择
  1. 性能
  • Vulkan提升的是驱动性能,非渲染流程导致的性能问题,Vulkan不会带来巨大收益
  • Vulkan和OpenGL在GPU上的使用没有质的差距,若是GPU导致性能问题(例GPU loading过重),Vulkan也不会带来多大提升
  • 若对应用有微小的卡顿或帧率抖动很在意,使用Vulkan可以显示的控制场景渲染期间何时发生了耗时操作,比OpenGL通过推断的方式管理状态和资源更有优势
  • 若希望充分发挥CPU多核多线程能力,则选Vulkan
  • Vulkan在离屏渲染上也很有优势
  1. 复杂度
  • Vulkan复杂的API对开发者是个挑战,由开发者书写代码量大(例三角形绘制需上千行代码),OpenGL降低了代码复杂性和维护负担,提供了便于使用的API
  • Vulkan驱动层把API验证、资源管理等交给开发者,需额外考虑内存管理和线程同步,否则会crash或花屏。而OpenGL驱动层已实现,开发者无需额外处理
  • Vulkan虽支持跨平台,但因其Vulkan extension是平台相关的,所以代码耦合性会很高

CPU与GPU

CPU与GPU是除屏幕外,UI渲染的核心硬件

CPU
  • 中央处理器
  • 计算机运算与控制的核心,信息处理和程序运行的最终执行单元
GPU
  • 图形处理器
  • 专门针对图像运算的处理器,是显卡的核心部件
  • UI绘制到屏幕前会经过栅格化(Rasterization)操作,栅格化是绘制Button、Path、Bitmap等显示的基本操作,其将UI组件拆分到显示器的不同像素上显示,其非常耗时,GPU的引入是为了加快栅格化

软硬绘制

draw_soft_hard.png

从上图可以看出,硬绘制是通过底层代码实现,使用GPU图形计算功能替代CPU,由GPU完成绘制任务

硬件加速本质上是使用GPU代替CPU完成Graphic Buffer绘制工作,以提高绘制性能。Android 4.0开始支持

软绘制虽使用Skia,但不代表其不支持硬件加速,Skia在Android 8.0开始支持,Skia在Android 9.0默认硬件加速。原理:通过 copybit 模块调用 OpenGLSkia 实现

BufferQueue图像缓冲区

Android中OpenGL、Skia、Vulkan将绘制数据存放至图像缓冲区,SurfaceFlinger从中取出进行加工及合成,最终显示至屏幕

黄油计划

2012年Google在I/O大会宣布Project Butter,并在Android 4.1开始启动该机制

VSYNC信号
  • 全称Vertical Synchronization,是Project Butter的核心。
  • CPU/GPU会根据VSYNC信号同步处理数据,可使CPU/GPU有完整的16ms时间处理数据,减少jank(卡顿)
三缓冲机制
  • Android 4.0之前,采用双缓冲机制,绘制和显示器拥有各自的buffer。GPU将完成的一帧图像数据写入到Back Buffer,显示器使用Frame Buffer,屏幕刷新时Frame Buffer不变,当Back Buffer准备好后,交换数据。但在复杂界面中,CPU/GPU处理时间超过16ms就会出现jank cache.png 双缓冲机制中会在VSYNC时交换数据,但CPU与GPU绘制是不确定的。上图分析
  1. 显示器显示第0帧数据,此时CPU和GPU渲染第一帧画面,且在下一帧前完成显示
  2. 因渲染及时,显示器显示完第0帧后(即第一个VSYNC后),交换buffer数据,正常显示第一帧
  3. 接着第二帧开始处理,但第二个VSYNC即将到来时才开始处理,因此在第二个VSYNC到来时,数据未处理完,缓存未交换,显示器显示的还是第1帧内容。即发生Jank(丢帧)
  4. 第二帧准备就绪后并不会立即显示,而是要等待下一个VSYNC,然后交换缓存数据并显示
  5. 如上屏幕会多显示一次第一帧。原因:CPU、GPU计算超时,未能在VSYNC信号到来之前完成任务
  • 三缓冲机制就是在双缓冲机制的基础上增加了一个Graphic Buffer缓冲区,有效利用等待VSYNC的时间,从而减少jank。缺点是多占用了一块内存 cache.png
  1. 第一个jank不可避免,但第二个16ms时间段,CPU/GPU使用第三个Buffer完成3帧计算,虽会多显示一次1帧,但后续比较顺畅,有效避免jank
  2. 第3段中,1帧计算完成,但在第4个VSYNC来时才显示,若是双缓存,第3个VSYNC即可显示
  3. Buffer正常两个,出现jank后3个足以