Android 渲染机制——Display List

Display List


Android 需要把 XML 布局文件转换成 GPU 能够识别并绘制的对象。这个操作是在 DisplayList 的帮助下完成的。DisplayList 持有所有将要交给 GPU 绘制到屏幕上的数据信息。

Display List 是什么?

Display List 是一个缓存绘制命令的 Buffer,Display List 的本质是一个缓冲区,它里面记录了即将要执行的绘制命令序列。

Display List 是视图的基本绘制元素,包含元素原始属性(位置、尺寸、角度、透明度等),对应 Canvas 的 drawXxx()方法。

视图信息传递流程:Canvas(Java API) —> OpenGL(C/C++ Lib) —> 驱动程序 —> GPU。

在硬件加速渲染环境中,Android 应用程序窗口的 UI 渲染是分两步进行的:
  1. 第一步是构建 Display List,发生在应用程序进程的 Main Thread 中。
  2. 第二步是渲染 Display List,发生在应用程序进程的 Render Thread 中。增加 Render Thread 线程,也是为了避免 UI 线程任务过重,用于提高渲染性能。

这些绘制命令最终会转化为 Open GL 命令由 GPU 执行。这意味着我们在调用 Canvas API 绘制 UI 时,实际上只是将 Canvas API 调用及其参数记录在 Display List 中,然后等到下一个 VSYNC 信号到来时,记录在 Display List 里面的绘制命令才会转化为 Open GL 命令由 GPU 执行。

Display List 的构建

Display List 是以视图为单位进行构建的,因此每一个视图都对应有一个 Display List。

Android 应用程序窗口视图是树形结构的,因此它们的 Display List 是从根视图开始构建的,并且子视图的 Display List 包含在父视图的 Display List 中。这意味着根视图的 Display List 包含了 Android 应用程序窗口 UI 所有的绘制命令,因此最后我们只需要对根视图的 Display List 进行渲染即可得到 Android 应用程序窗口的 UI。

Android 应用程序窗口的根视图是虚拟的,抽象为一个 Root Render Node。此外,一个视图如果设置有 Background,那么这个 Background 也会抽象为一个 Background Render Node。Root Render Node、Background Render Node 和其它真实的子视图,除了 TextureView 和软件渲染的子视图之外,都具有 Display List,并且是通过一个称为 Display List Renderer 的对象进行构建的。最后,Root Render Node 的 Display List 被一个称为 Open GL Renderer 的对象进行渲染,就得到 Android 应用程序窗口的UI了。

TextureView 不具有 Display List,它们是通过一个称为 Layer Renderer 的对象以 Open GL 纹理的形式来绘制的,不过这个纹理也不是直接就进行渲染的,而是先记录在父视图的 Display List 中以后再进行渲染的。同样,软件渲染的子视图也不具有 Display List,它们先绘制在一个 Bitmap 上,然后这个 Bitmap 再记录在父视图的 Display List 中以后再进行渲染的。

Display List 的使用和优势

在某个 View 第一次需要被渲染时,Display List 会因此被创建,当这个 View 要显示到屏幕上时,我们会执行 GPU 的绘制指令来进行渲染。

如果 View 的属性发生了改变(例如移动位置),我们就仅仅需要 Execute Display List 就够了。

在这里插入图片描述

但是,如果我们修改了 View 中的某些可见组件的内容,那么之前的 DisplayList 就无法继续使用了,我们需要重新创建一个 DisplayList 并重新执行渲染指令更新到屏幕上。

在这里插入图片描述

注意:任何时候 View 中的绘制内容发生变化时,都会需要重新创建 DisplayList,渲染 DisplayList,更新到屏幕上等一系列操作。这个流程的表现性能取决于你的 View 的复杂程度,View 的状态变化以及渲染管道的执行性能。举个例子,假设某个 Button 的大小需要增大到目前的两倍,在增大 Button 大小之前,需要通过父 View 重新计算并摆放其他子 View 的位置。修改 View 的大小会触发整个视图树的重新计算大小的操作。如果是修改 View 的位置则会触发视图树重新计算其他 View 的位置。如果布局很复杂,这就会很容易导致严重的性能问题。

完整过程如下:

在这里插入图片描述

这里总结下,使用 DisplayList 的优势:

  1. 第一个好处是在绘制窗口的下一帧时,若某一个视图的 UI 没有发生变化,那么就不必执行与它相关的 Canvas API,即不用执行它的成员函数 onDraw,而是直接复用上次构建的 Display List 即可。
  2. 第二个好处是在绘制窗口的下一帧时,若某一个视图的 UI 发生了变化,但是只是一些简单属性发生了变化,例如位置和透明度等简单属性,那么也不必重建它的 Display List,而是直接修改上次构建的 Display List 的相关属性即可,这样也可以省去执行它的成员函数 onDraw。

硬件加速条件下,CPU 用于控制复杂绘制逻辑、构建或更新 DisplayList;GPU 用于完成图形计算、渲染 DisplayList。在硬件加速条件下,刷新界面尤其是播放动画时,CPU 只重建或更新必要的 DisplayList,进一步提高渲染效率。实现同样效果,应尽量使用更简单的 DisplayList,从而达到更好的性能(例如:Shape 代替 Bitmap 等)。

总结


  1. Android 需要把 XML 布局文件转换成 GPU 能够识别并绘制的对象。这个操作是在 DisplayList 的帮助下完成的。DisplayList 持有所有将要交给 GPU 绘制到屏幕上的数据信息。

  2. Display List 是一个缓存绘制命令的 Buffer,Display List 的本质是一个缓冲区,它里面记录了即将要执行的绘制命令序列。

  3. Display List 是视图的基本绘制元素,包含元素原始属性(位置、尺寸、角度、透明度等),对应 Canvas 的 drawXxx()方法。

  4. 渲染 Display List,发生在应用程序进程的 Render Thread 中。增加 Render Thread 线程,也是为了避免 UI 线程任务过重,用于提高渲染性能。

  5. Display List 是以视图为单位进行构建的,因此每一个视图都对应有一个 Display List。

  6. 在某个 View 第一次需要被渲染时,Display List 会因此被创建,当这个 View 要显示到屏幕上时,我们会执行 GPU 的绘制指令来进行渲染。

  7. 在绘制窗口的下一帧时,若某一个视图的 UI 没有发生变化,那么就不必执行与它相关的 Canvas API,即不用执行它的成员函数 onDraw,而是直接复用上次构建的 Display List 即可。

  8. 在绘制窗口的下一帧时,若某一个视图的 UI 发生了变化,但是只是一些简单属性发生了变化,例如位置和透明度等简单属性,那么也不必重建它的 Display List,而是直接修改上次构建的 Display List 的相关属性即可,这样也可以省去执行它的成员函数 onDraw。

  9. 硬件加速条件下,CPU 用于控制复杂绘制逻辑、构建或更新 DisplayList;GPU 用于完成图形计算、渲染 DisplayList。


**PS:更多精彩内容,请查看 --> 《Android 性能优化》
**PS:更多精彩内容,请查看 --> 《Android 性能优化》
**PS:更多精彩内容,请查看 --> 《Android 性能优化》

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卜大爷

觉得不错的可以给我加油哦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值