浏览器渲染流水线解析(一)

若干年前,我写过一篇介绍浏览器渲染流水线的文章 - How Rendering Work (in WebKit and Blink),这篇文章,一来部分内容已经过时,二来缺少一个全局视角来对流水线整体进行分析,所以打算重新写一篇新的文章,从一个更高抽象层次和高度简化的方式对浏览器的渲染流水线进行解析,能让大部分页端同学都能够看的明白,并以此作为指引来分析和优化页面的渲染/动画性能。

有些基本概念如图层,分块,光栅化基本没有发生变化,如果读者不理解的话请参考 How Rendering Work (in WebKit and Blink),本文不再过多解释。

本文基于当前版本的 Chrome 浏览器写成(60 左右),理论上部分知识可以应用于其它浏览器(当然术语会有一定差别)或者 Chrome 后续的版本,但是并不完全保证这一点。

1. 渲染流水线

上图显示了 Chrome 一个高度简化后的渲染流水线示意图:

  1. 最底层的是 Chrome 最核心的部分 Blink,负责JS的解析执行,HTML/CSS解析,DOM操作,排版,图层树的构建和更新等任务;
  2. Layer Compositor(图层合成器)接收 Blink 的输入,负责图层树的管理,图层的滚动,旋转等矩阵变幻,图层的分块,光栅化,纹理上传等任务;
  3. 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 之间的系统架构和通讯方式基本保持不变,线程架构也是同样。

  1. Blink 主要运行在 Renderer 进程的 Renderer 线程,我们通常会称之为内核主线程;
  2. Layer Compositor 主要运行在 Renderer 进程的 Compositor 线程;
  3. Display Compositor 主要运行在 Browser 进程的 UI 线程;

Display Compositor 未来应该会移到 GPU 进程的主 GPU 线程,当然对父子合成器进行调度的部分仍然是在 Browser 进程的 UI 线程。

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 节点的关键属性,同时 DisplayList 不再以图层作为单位,而是以每个排版对象作为单位)。

图层化决策一般由以下几个因素决定:

  1. 特殊元素如 Plugin,Video,Canvas(WebGL);
  2. 维护正确的层级关系来保证绘制顺序是正确的,比如 Overlap 的计算
  3. 减少图层树的结构变更,减少图层内容的变更(目前 Blink 网页内容的变更是以图层为原子单位的,如果以一个元素为根节点生成图层,该元素的某些 CSS 属性如 Transform 的变更不会引起所属图层内容的变更);

第三点是可以被页端所直接控制来优化图层结构及 Main Frame 性能,像传统的 translate3d hack 和新的 CSS 属性 will-change。

1.2.2 Compositor Frame

Layer Compositor 接收 Blink 生成的 Main Frame,并转换成合成器内部的图层树结构(因为图层化决策仍然由 Blink 负责,所以这里的转换基本上可以认为是生成一棵同样的树,再对逐个图层的进行拷贝)。

Layer Compositor 需要为每个图层进行分块,为每个分块分配 Resource(Texture 的封装),然后安排光栅化任务。

当 Layer Compositor 接收到来自 Browser 的绘制请求时,它会为当前可见区域的每个图层的每个分块生成一个 Draw Quad 的绘制指令(矩形绘制,指令实际上指定了坐标,大小,变换矩阵等属性),所有的 Draw Quad 指令和对应的 Resource 的集合就构成了 Compositor Frame。Compositor Frame 被发送往 Browser,并最终到达 Display Compositor(未来也可以直接发给 Display Compositor)。

1.2.3 GL Frame

Display Compositor 将 Compositor Frame 的每个 Draw Quad 绘制指令转换一个 GL 多边形绘图指令,使用对应 Resource 封装的 Texture 对目标窗口进行贴图,这些 GL 绘图指令的集合就构成了一个 GL Frame,最终由 GPU 执行这些 GL 指令完成网页在窗口上占据的可见区域的绘制。

1.3 调度

Chrome 渲染流水线的调度是基于请求和状态机响应,调度的最上级中枢运行在 Browser UI 线程,它按显示器的 VSync(垂直同步)周期向 Layer Compositor 发出输出下一帧的请求,而 Layer Compositor 根据自身状态机的状态决定是否需要 Blink 输出下一帧。

Display Compositor 则比较简单,它持有一个 Compositor Frame 的队列不断的进行取出和绘制,输出的频率唯二地取决于 Compositor Frame 的输入频率和自身绘制 GL Frame 的耗时。基本上可以认为 Layer Compositor 和 Display Compositor 是生产者和消费者的关系。

时间: 2024-10-25 05:25:09

浏览器渲染流水线解析(一)的相关文章

浏览器渲染流水线解析与网页动画性能优化

若干年前,我写过一篇介绍浏览器渲染流水线的文章 - How Rendering Work (in WebKit and Blink),这篇文章,一来部分内容已经过时,二来缺少一个全局视角来对流水线整体进行分析,所以打算重新写一篇新的文章,从一个更高抽象层次和高度简化的方式对浏览器的渲染流水线进行解析,能让大部分页端同学都能够看的明白,并以此作为指引来分析和优化页面的渲染/动画性能. 有些基本概念如图层,分块,光栅化基本没有发生变化,如果读者不理解的话请参考 How Rendering Work

浏览器渲染流水线解析(二)

2. 网页动画 动画可以看做是一个连续的帧序列的组合.我们把网页的动画分成两大类 -- 一类是合成器动画,一类是非合成器动画(UC 内部也将其称为内核动画,虽然这不是 Chrome 官方的术语). 合成器动画顾名思义,动画的每一帧都是由 Layer Compositor 生成并输出的,合成器自身驱动着整个动画的运行,在动画的过程中,不需要新的 Main Frame 输入: 内核动画,每一帧都是由 Blink 生成,都需要产生一个新的 Main Frame: 2.1 合成器动画 合成器动画又可以分

浏览器渲染流水线解析(四)

5 非合成器动画性能分析和优化指南 前面已经我们已经把非合成器动画区分为 Blink 触发,无法由合成器运行的动画和由 Timer/RAF 驱动的 JS 动画两类,因为前者可以认为是后者的一个简化版本,所以这一章主要讨论 Timer/RAF 驱动的 JS 动画. 5.1 动画流水线 从上图可以看出非合成器动画的流水线比合成器动画更长更复杂,并且非合成器动画的后半段跟合成器动画是一致的. JavaScipt 部分是页端实现的逻辑,可能包含了计算的部分,和调用浏览器提供的 API 的部分(修改 DO

Chrome 渲染流水线演化的未来

前段时间我写了一篇文章浏览器渲染流水线解析与网页动画性能优化,对目前 60 左右版本的 Chrome 的渲染流水线进行解析,文末也讨论了当前渲染流水线的一些不足和未来演化的方向. 当前的渲染流水线过于复杂和冗长,特别是对于非合成器动画来说,过多的线程/进程间交互增加了不少额外开销,异步光栅化的机制也是有利于合成器动画而不利于非合成器动画.而未来的演化理应需要简化渲染流水线,减少线程/进程间交互,避免非必要的额外开销,光栅化和合成不再像现在一样泾渭分明,渲染流水线可以支持更灵活和动态自适应的图层化

浏览器渲染过程与性能优化

大家都知道万维网的应用层使用了 HTTP 协议,并且用浏览器作为入口访问网络上的资源.用户在使用浏览器访问一个网站时需要先通过 HTTP 协议向服务器发送请求,之后服务器返回 HTML 文件与响应信息.这时,浏览器会根据 HTML 文件来进行解析与渲染(该阶段还包括向服务器请求非内联的 CSS 文件与 JavaScript 文件或者其他资源),最终再将页面呈现在用户面前. 现在知道了网页的渲染都是由浏览器完成的,那么如果一个网站的页面加载速度太慢会导致用户体验不够友好,本文通过详解浏览器渲染页面

【Web动画】CSS3 3D 行星运转 && 浏览器渲染原理

承接上一篇:[CSS3进阶]酷炫的3D旋转透视 . 最近入坑 Web 动画,所以把自己的学习过程记录一下分享给大家. CSS3 3D 行星运转 demo 页面请戳:Demo.(建议使用Chrome打开) 本文完整的代码,以及更多的 CSS3 效果,在我 Github 上可以看到,也希望大家可以点个 star. 嗯,可能有些人打不开 demo 或者页面乱了,贴几张效果图:(图片有点大,耐心等待一会) CSS3 3D 行星运转效果图 随机再截屏了一张: 强烈建议你点进 Demo页感受一下 CSS3

css的效率和浏览器渲染的速度

浏览器如何读取你的CSS选择器有一个很重要的原则,那就是它们从右到左读取.这意味这像 ul > li a[title="home"] 这样的选择器,a[title="home"] 将是最先被读取的. 我承认我并不经常想这个问题......我们写的css的效率是怎么样的呢,浏览器渲染的速度又如何呢? 这是应该是浏览器开发者应该关心的(页面加载更快,用户就会更愉快).Mozilla有一篇文章: about best practices . Google 当然也很关

统一模式的WebKit浏览器渲染引擎的利弊

Opera宣布转向使用开源的WebKit引擎 在本周前几天,欧朋浏览器(Opera)宣布正在逐步关闭其独立浏览器渲染引擎(brower rendering engine)的相关开发工作,继而转向使用开源的WebKit引擎,该消息很快引起了不小的轰动. WebKit引擎支持谷歌安卓系统和苹果IOS系统的内置浏览器,在移动领域,WebKit引擎实际上已经成为了移动浏览器内核开发的标准,而且它也非常可能成为桌面浏览器的内核标准.在全球范围内Chrome浏览器已经遥遥领先以Trident排版引擎为内核的

浏览器加载、渲染和解析过程黑箱简析_javascript技巧

用 Fiddler 监控,在 IE6 下,资源下载顺序为: 很明显,下载顺序从上到下,文档流中先出现的资源先下载.在 IE8, Safari, Chrome 等浏览器下也类似. Firefox 对下载顺序做了优化:Firefox 会将 js, css 提前下载,而将图片等资源延迟到后面下载. 对于渲染,利用 Fiddler 将网速调慢,可以看到 css 下载后会马上渲染到页面,渲染和下载同步进行.js 的解析和运行,也类似. 对于 js 运行,以及页面加载相关事件的触发,特别做了测试.在 Fir