总结
- 分层:将页面分成多个图层,每个图层可以独立栅格化。
- 绘制:为每个图层生成对应绘制操作列表,每个绘制操作都代表一个绘图的步骤。
- 分块:将图层分成若干图块,交给 GPU 进程栅格化。
- 栅格化:栅格器由线程池,中的线程根据图块的绘制操作列表,将图块转化成位图数据。
- 合成和显示:合成多个图层到内存中,然后显示在屏幕上。
分层(layer)
一个图层是页面的一部分,它可以独立于其他层被变形和栅格化。每个节点都属于一个图层,没有特别指定层级的节点属于父节点的所在的图层。
单独提升为一个图层的条件
-
拥有堆叠上下文属性的元素
-
文档的根元素(
<html>
); -
position 属性的值为 absolute(绝对定位)或 relative(相对定位)且 z-index 属性的值不为 auto;
-
position 值为 fixed(固定定位)或 sticky(粘滞定位)的元素(沾滞定位适配所有移动设备上的浏览器,但老的桌面浏览器不支持);
-
flex (flexbox) 容器的子元素,且 z-index 值不为 auto;
-
grid (grid) 容器的子元素,且 z-index 值不为 auto;
-
opacity 属性值小于 1 的元素;
-
mix-blend-mode 属性值不为 normal 的元素;
-
以下任意属性值不为 none 的元素:
-
- transform
- filter
- perspective
- clip-path
- mask / mask-image / mask-border
- isolation 属性值为 isolate 的元素;
- -webkit-overflow-scrolling 属性值为 touch 的元素;
- will-change 值设定了任一属性而该属性在 non-initial 值时会创建层叠上下文的元素;
- contain 属性值为 layout、paint 或包含它们其中之一的合成值(比如 contain: strict、contain: content)的元素。
-
-
需要被裁切的元素
当一个元素的内容尺寸超过了元素本身的尺寸,并且指定了 overflow 属性,这时候就产生了裁剪。渲染引擎会把内容的一部分显示在元素区域内。
出现这种裁剪情况的时候,渲染引擎会为内容元素单独创建一个层,如果出现滚动条,滚动条也会被提升为单独的层。
绘制(paint)
绘制过程中为每个图层构建一个绘制操作(paint ops)列表。绘制的顺序是元素的堆叠顺序而不是 DOM 顺序,绘制会分成好几个阶段(backgrounds->floats->foregrounds->outlines->…),每个阶段都是对堆叠上下文的单独遍历。
栅格化(raster)
栅格化将绘制阶段构建的绘制操作列表转换成位图。
分块(tilling)
绘制操作列表准备好之后,主线程会将图层的绘制操作列表提交给合成器线程。
图层可能会很大,这样的话栅格化整个图层耗费的代价是十分昂贵的,而且对那些图层中不可见的部分栅格化是完全没必要的。所以合成器线程会将图层分成若干个图块(tiles:瓷砖,瓦片)。
栅格化是多线程的
图块是栅格化的单元,被分块的若干图块会被一个栅格器线程池中的线程渲染。越靠近视口的图块越优先被栅格化。因为渲染进程是运行在沙盒中,所以无法直接执行操作系统调用。经过绘制后得到的数据被送到 GPU 进程栅格化,所以栅格器是运行在 GPU 线程中的。
栅格化之后生成的位图被存储在内存中,通常是由 OpenGL 纹理对象引用的 GPU 内存。GPU 还可以运行生成位图的命令(“加速栅格化”)。
合成与显示
图块被栅格化之后,合成器线程会生成一个 DrawQuads 命令,这个命令代表在屏幕上一个特定位置画出图块。这个命令还没有真正地转化成屏幕上的像素,而是对应在内存中的表示。最后再将内存中的数据显示在屏幕上(显示这个步骤很模糊,课程中也是一笔带过)。