Flutter 渲染流水线浅析

我在之前的文章:

易旭昕:关于 Flutter 的一些想法zhuanlan.zhihu.com图标

谈了 Flutter 是什么?它在客户端开发的技术栈中处于什么位置?跟哪些技术比较相似,跟哪些技术存在相互竞争的可能?它的优点是什么?

在 Flutter 渲染流水线浅析这篇文章里,我想谈一下自己对 Flutter 渲染流水线的理解,它和其他 UI Toolkit,和浏览器渲染流水线的相似和相异之处。


Flutter 渲染流水线概览


总的说来 Flutter 的渲染流水线,整体架构上(包括线程架构)跟 Android 5.x 之后的 Android View Rendering 架构基本一致,跟目前其他主流的 UI Toolkit,如 iOS,Qt QML 也十分类似。但是在内部实现的一些概念和细节上更接近于浏览器,特别是 WebKit/Blink,这跟 Flutter 的几位初期成员都是来自 Chrome 团队,负责 WebKit/Blink 的排版和绘制相关的工作有关。



跟 Android 5.x View Rendering Pipeline 一样,Flutter 的渲染流水线也包括两个线程 —— UI 线程和 GPU 线程。

UI 线程主要负责的是根据 UI 界面的描述生成 UI 界面的绘制指令,而 GPU 线程负责光栅化和合成。

Flutter 以图层树(Layer Tree)的方式对生成的 UI 界面绘制指令进行组织,而从 Android 4.x 开始,Android View Rendering 使用的 View DisplayList 树的方式,虽然组织的方式有所差异,但是其中的主要内容都是 UI 界面的绘制指令。而图层树这种组织方式又非常类似 WebKit/Blink。


Android View Rendering 的 GPU 线程在 3.x 和 4.x 上是 Android UI 线程,从 5.x 开始是 Android Render 线程,2.x 时并不支持 GPU 绘制。

Flutter UI 界面的绘制



上图显示了 Flutter 更新 UI 界面的过程:

  1. 运行动画,动画的结果会导致 Widget State 的改变;
  2. State Changes 触发 Flutter 生成一棵新的 Widget 树;
  3. Flutter 根据新/旧 Widget 树的差异更新 Render 树,重新排版更新界面布局;
  4. Flutter 根据新的 Render 树更新 Composited Layer(合成图层)的 Display List;
  5. 输出新的图层树;

Widget

在 Flutter 里面 Widget 的定义是 UI 界面的不可变的抽象描述,它跟其他 UI Toolkit 的 Widget 或者 View 有较大差别,相比其他 UI Toolkit 里面的基础 UI 组件,Flutter Widget 的抽象层次更高,涵盖的范围更广,单纯从抽象层次来说,倒是更类似浏览器的 DOM/CSSOM,虽然两者实际上并不相同。给我的感觉就像是混合了各种不同的对 UI 界面进行描述的方式所创造出的一个新概念,采用浏览器渲染里面抽象层次划分的方式对其他 UI Toolkit 的基础 UI 组件进行层次划分得到的一个新的抽象层。

RenderObject

Flutter 使用 RenderObject 作为 UI 界面的内部描述方式,基于 RenderObject 计算 UI 的布局和生成 UI 的绘制指令。Flutter 里面 RenderObject 和 Render 树的概念跟浏览器就基本差不多了,连命名都一样。从抽象层次和主要功能来说,RenderObject 跟其他 UI Toolkit 里面的 Widget 和 View 这些基础 UI 组件也比较接近。

Composited Layer

Flutter 使用 Composited Layer 来对 RenderObject 的绘制进行组织,通常一个 Composited Layer 对应一棵 RenderObject 子树,Composited Layer 的 Display List 记录了这棵 RenderObject 子树的绘制指令,这跟浏览器里面的 RenderObject 和 Composited Layer 基本是一样的。

不同的 UI Toolkit 对图层的处理方式并不一样,Android 没有一个完整的图层概念,只是间接地允许应用指定为特定的 View 子树生成图层,并且这个图层的概念跟 Flutter 里面的图层相比,Flutter 具备更高的抽象层次,它是绘制指令的一种组织方式,并不一定就需要分配额外的缓存做间接光栅化,Flutter 内部采用一个重复绘制次数阈值来控制是否为图层分配额外的缓存,通过这种方式来平衡内存占用和重复光栅化的性能损失。iOS 有比较完整的图层概念,跟 Flutter 更接近,并允许应用自己控制。


浏览器的部分可以参考我的文章 BlinkOn9 - Blink Rendering浏览器渲染流水线解析与网页动画性能优化


Flutter 光栅化和合成


Flutter 光栅化和合成的流水线设计对比浏览器就简单的多了,跟其他 UI Toolkit 基本一样,只需要在 GPU 线程完成全部的光栅化和合成,并且全部由 GPU 实现。Android HWUI 的光栅化和合成将来应该也会改用 Skia 的实现,所以长远来看,Android 和 Flutter,另外也包括 Chrome, 在光栅化和合成的实现上应该会是使用同样的代码。


如果读者觉得这篇文章有所帮助,请继续关注专栏和麻烦点个赞,这样可以帮助我继续创作更多更有价值的文章。

文章被以下专栏收录