若干年前,我写过一篇介绍浏览器渲染流水线的文章 - How Rendering Work (in WebKit and Blink),这篇文章,一来部分内容已经过时,二来缺少一个全局视角来对流水线整体进行分析,所以打算重新写一篇新的文章,从一个更高抽象层次和高度简化的方式对浏览器的渲染流水线进行解析,能让大部分页端同学都能够看的明白,并以此作为指引来分析和优化页面的渲染/动画性能。
有些基本概念如图层,分块,光栅化基本没有发生变化,如果读者不理解的话请参考 How Rendering Work (in WebKit and Blink),本文不再过多解释。
写这篇文章是始于年初对页端开发高性能(交互/动画) Mobile WebApp 的一些思考,实际开始写已经是年中… 陆陆续续写了几个月才终于写完。感觉最难的还是开始的部分,要能够先把基础的部分讲解清楚,然后再循序渐进去讲解更复杂的概念。思考了很久,最终决定从帧的概念入手,然后把动画定义成一个连续的帧序列的组合这种方式来对动画进行解析,最终成文的效果还是比较让自己满意的。
文章是在作业部落上写的的,然后再把 Markdown 贴到其它网站,无法保证每个网站最终呈现的排版效果,如果读者觉得排版欠佳,可以访问在作业部落上发布的原网址。
本文基于当前版本的 Chrome 浏览器写成(60 左右),理论上部分知识可以应用于其它浏览器(当然术语会有一定差别)或者 Chrome 后续的版本,但是并不完全保证这一点。
1. 渲染流水线
上图显示了 Chrome 一个高度简化后的渲染流水线示意图:
- 最底层的是 Chrome 最核心的部分 Blink,负责JS的解析执行,HTML/CSS解析,DOM操作,排版,图层树的构建和更新等任务;
- Layer Compositor(图层合成器)接收 Blink 的输入,负责图层树的管理,图层的滚动,旋转等矩阵变幻,图层的分块,光栅化,纹理上传等任务;
- Display Compositor 接收 Layer Compositor 的输入,负责输出最终的 OpenGL 绘制指令,将网页内容通过 GL 贴图操作绘制到目标窗口上,如果忽略掉操作系统本身的窗口合成器,也可以简单认为是绘制在显示屏上;
当我们说 Compositor,在没有加修饰语的情况下,一般都是指 Layer Compositor。另外术语 Child Compositor(子合成器)也是指 Layer Compositor,相对于作为 Parent 的 Display Compositor 而言。
1.1 进程与线程
一个 Chrome 浏览器一般会有一个 Browser 进程,一个 GPU 进程,和多个 Renderer 进程,通常每个 Renderer 进程对应一个页面。在特殊架构(Android WebView)或者特定配置下,Browser 进程可以兼作 GPU 进程或者 Renderer 进程(意味着没有独立的 GPU 或者 Renderer 进程),但是 Browser 跟 Renderer,Browser 跟 GPU,Renderer 跟 GPU 之间的系统架构和通讯方式基本保持不变,线程架构也是同样。
- Blink 主要运行在 Renderer 进程的 Renderer 线程,我们通常会称之为内核主线程;
- Layer Compositor 主要运行在 Renderer 进程的 Compositor 线程;
- Display Compositor 主要运行在 Browser 进程的 UI 线程;
Display Compositor 后面应该会移到 GPU 进程的主 GPU 线程,当然对父子合成器进行调度的部分仍然是在 Browser 进程的 UI 线程,不太确定各个不不同平台的状况,Android WebView 平台是已经实现了。
1.2 帧
所有的渲染流水线都会有帧的概念,帧这个概念抽象描述了渲染流水线下级模块往上级模块输出的绘制内容相关数据的封装。我们可以看到 Blink 输出 Main Frame 给 Layer Compositor,Layer Compositor 输出 Compositor Frame 给 Display Compositor,Display Compositor 输出 GL Frame 给 Window。我们觉得一个动画是否流畅,最终取决于 GL Frame 的帧率(也就是目标窗口的绘制更新频率),而觉得一个触屏操作是否响应即时,取决于从 Blink 处理事件到 Window 更新的整个过程的耗时(理论上应该还要加上事件从 Browser 发送给 Compositor,再发送给 Blink 的这个过程的耗时)。
1.1.1 Main Frame
Main Frame 包含了对网页内容的描述,主要以绘图指令的形式,或者可以简单理解为某个时间点对整个网页的一个矢量图快照(可以局部更新)。当前版本的 Chrome,图层化的决策仍然由 Blink 来负责,Blink 需要决定如何根据网页的 DOM 树来生成一颗图层树,并以 DisplayList 的形式记录每个图层的内容(未来图层化决策应该会转移到 Layer Compositor,Blink 只输出 DisplayList 树和 DisplayList 节点的关键属性,同时 Displ