w4lle's Notes

人生如逆旅,我亦是行人。

w4lle's avatar w4lle

Flutter UI 渲染浅析(一)总览

Flutter UI 渲染系列文章,基于 Flutter v1.20.4

我们知道屏幕显示的基本单位是像素,每个像素点显示不同的颜色值,按一定规则排列就形成了图像。

显示器按一定频率从 GPU 获取数据,就可以完成图像的更新。现在手机屏幕一般有60HZ、90HZ、120HZ,以60HZ为例,屏幕每秒会发出 60 个 VSync 垂直同步信号。

VSync 信号用于协调显示器、CPU 和 GPU 的工作。

GPU 每秒可以绘制的帧数叫做帧速率,如果帧速率大于屏幕刷新率,屏幕上显示的内容就有可能是两个图像的不完全内容,造成图像撕裂。

VSync 垂直信号可以保证刷新频率的统一。

在 VSync 信号的调度下,UI 系统把逻辑代码变成图像显示到屏幕上,一般需要经过以下几个阶段:

  1. View,构建 View/DOM 节点
  2. Layout,计算样式(Style)、布局(Layout)
  3. ViewTree,将 View 节点按一定规则归属到不同的图层,构建或更新 ViewTree
  4. Paint,绘制,输出绘制指令到 DisplayList
  5. Rasterization,光栅化,执行 DisplayList 中的绘图指令,生成图层区域的像素数据
  6. Composite,合成,把各图层光栅化后的数据进行叠加和特性处理,输出到屏幕上

简单来说就是做了两件事情:

  1. 构建 UI 描述规则,用于构建或刷新图像,并把绘制指令保存起来用于绘制,即 1-4
  2. 光栅化和合成,对硬件绘制 API 做了统一封装,屏蔽底层绘制细节,即 5-6

Android 和 iOS 基本都是按照这个流程来构建UI的。

以 Android 为例,Android 通过 XML 来描述 UI 结构,用 DisplayList 保存绘制指令信息,通过 Skia 封装底层细节实现光栅化合成。

Flutter 作为一款跨端 UI 开发框架,它的渲染流程也是类似的。

这个系列的文章主要分析下 Flutter UI 渲染流程,涉及到的中间过程会分部解析,基于 Flutter v1.20.4 版本源码,主要分为以下几个部分:

  1. 总览
  2. VSync 注册
  3. Animate 及动画实现原理
  4. Build
  5. Layout
  6. Paint & RepaintBoundary
  7. CompositeFrame
  8. RasterThread 光栅化及合成

1、Flutter 架构

Flutter 架构分为三层:Dart Framework、C++ Engine、 Platform Embedder。

Dart Framework 提供了响应式的开发框架,使用 Dart 开发,它对渲染逻辑做了统一封装,屏蔽了底层实现,对底层 C++ Engine 提供双向通信能力,开发者只需要组合 Widgets 用于构建 App 视图即可。

  • 最底层的 Foundation 层提供一些最基础的抽象类或定义,基于此, Animation 动画、Painting 绘制、Gestures 手势等构建出通用抽象能力
  • Rendering 层,构建出渲染树 Render Tree,也即 RenderObject Tree,用于具体绘制,RenderObject 会自动随着数据改变而动态改变
  • Widgets 层,提供了一套非常丰富的 Widget 组件库,用于构建 Widgets Tree 和 Element Tree,这是响应式编程的基础实现,每一个 RenderObject 都有一个对应的 Widget 及 Element
  • Materail 层和 Cupertino 层使用 Widgets 组件库,构建 Android Materail 或者 iOS Cupertino 风格的应用视图,开发者基于这些 Widgets 即可构建出效果一致的跨端应用

C++ Engine 是 Flutter 的核心部分,大部分使用 C++ 开发,它的主要职责是光栅化合成上屏用于显示绘制内容,同时它也提供低层次的核心能力,比如Skia图形化绘制(graphics)、TextLayout、文件系统、网络 I/O、无障碍支持、插件体系、Dart运行时(DartVM)和 GC、编译链。

Engine 层对 App 层暴露 dart:ui 包,dart:ui 包是 Flutter App 的构建基础,其中的 dart 类对 C++ Engine 层中的实现类做了包装,它提供了基础能力,诸如交互系统、图形图像处理、渲染子系统等。

其中最重要的一个类是包下的 Window,它向上提供了最核心的一些服务,比如任务Scheduler API、绘制 API、输入事件响应等等。

Platform Embedder 是平台嵌入层,把 Flutte 代码打包嵌入到具体的实现平台,提供运行入口,并对上层提供最基础的能力,比如提供渲染画布、插件系统、无障碍、交互管理、消息循环管理等。

Flutter 分层架构使得平台相关性大大降低。

Dart Framework 对上提供统一的基于 dart 的响应式 UI 描述框架。

C++ Engine 向下对 Skia 绘制引擎对下统一封装,屏蔽了平台实现。

ReactNative / Weex 作为跨端框架 ,虽然走的也是是原生渲染,但是需要通过 js 来组织和描述 UI,中间需要做一层转换,才可以变成原生的 UI 描述结构,进而原生渲染。

中间多了一层转换过程。

而 Flutter 自建渲染引擎,不依赖平台实现,并且 Dart 可以直接被编译成机器码,从架构上来说,性能相比 ReactNative / Weex 会更好。

2、Flutter 渲染管线

Flutter 渲染管线的设计是类似的,流程参考下图

首先看下用到的线程:

UIThread 是 Platform 创建的子线程,DartVM Root Isolate 所有的 dart 代码都运行在该线程。

阻塞UIThread 会直接导致 Flutter 应用卡顿掉帧、

RasterThread 原本叫做 GPUThread,也是 Platform 创建的子线程,由于很多人误认为运行在 GPU 上,但其实它是运行在 CPU 用于处理数据提交给 GPU,所以 Flutter 团队将其名字改为 Raster,表明它的作用是光栅化。

C++ Engine 中的光栅化和合成过程运行在该线程。

整个流程会经过以下几个过程:

  • C++ Engine 触发 Platform 注册 VSync 垂直信号回调,通过 Platform -> C++ Engine -> Dart Framework 触发整个绘制流程
  • Dart Framework 构建出四棵树,Widget Tree、Element Tree、RenderObject Tree、Layer Tree,布局、记录绘制区域及绘制指令信息生成 flutter::LayerTree,并保存在 Scene 对象用以光栅化,这个过程运行在 UIThread
  • 通过 Flutter 自建引擎 Skia 进行光栅化和合成操作, 将 flutter::LayerTree 转换为 GPU 指令,并发送给 GPU 完成光栅化合成上屏显示操作,这个过程执行在 RasterThread

整个调度过程是生产者消费者模型,它的实现在 Engine 的 LayerTreePipeline

UIThread 负责生产 flutter::Layer Tree,RasterThread 负责消费 flutter::Layer Tree

这种调度机制可以确保 RasterThread 不至于过载(2个任务),同时也可以避免 UIThread 不必要的资源消耗。

所以不论在 UIThread 还是在 RasterThread 耗时太久,都可能会导致 Flutter 应用卡顿,因为会导致延迟接受 VSync 信号,导致掉帧。

这个调度过程会在下一篇文章中详细分析。

3、Flutter UI 绘制管线

其中在 UIThread 生成 Layer Tree 的过程,我们将其称为 Rendering Pipeline 绘制管线。

主要过程为:

  • Animate,触发动画更新下一帧的值
  • Build,触发构建或刷新 Widget Tree、Element Tree、RenderObject Tree
  • Layout,触发布局操作,确定布局大小和位置信息
  • CompositeBits,更新需要合成的 Layer 层标记
  • Paint,触发 RenderObject Tree 的绘制操作,构建 Layer Tree
  • Composite,触发 Layer Tree 发送到 Engine,生成 Engine LayerTree

在 UIThread 构建出四棵树,并在 Engine 生成 Scene,最后提交给 RasterThread,对 LayerTree 做光栅化合成上屏。

下一篇文章分析下 VSync 注册。

参考

渲染流水线中的光栅化(一)

从架构到源码:一文了解Flutter渲染机制

Flutter渲染机制—UI线程

本文链接: http://w4lle.com/2020/11/09/flutter-ui-overview/

版权声明:本文为 w4lle 原创文章,可以随意转载,但必须在明确位置注明出处!
本文链接: http://w4lle.com/2020/11/09/flutter-ui-overview/