接上文chrome渲染流程(一),经过前面的三个阶段:生成DOM树、样式计算、布局,得到了布局树,下一步干什么?
4 分层
页面中可能会有复杂的效果(见后文),为了更加方便地实现这些效果,渲染引擎还需要为特定节点生成专用的图层,并生成一颗对应的图层树(LayerTree)。
可在chrome开发者工具 -> 更多工具 -> 图层中查看页面的分层情况。
之前的Demo(没有设置变换、定位)的图层树:
如果一个节点没有对应的图层,那么这个节点就从属于父节点的图层。(一个节点都会直接或间接地从属于一个图层)
比如上面,#app、#first-row的节点都是从属于#document图层。
什么情况下渲染引擎才会为节点创建新的图层?
1)拥有层叠上下文的元素
我们假定用户正面向(浏览器)视窗或网页,而 HTML 元素沿着其相对于用户的一条虚构的 z 轴排开,层叠上下文就是对这些 HTML 元素的一个三维构想。众 HTML 元素基于其元素属性按照优先级顺序占据这个空间。
常见的场景:
- 文档跟元素(html)
- 定位(需要满足一些条件)
- opacity(值小于1)
- 3D变换
- CSS滤镜
更多详细内容请见层叠上下文
添加样式:
<style>
#app{
opacity: 0.8;
}
#first-row{
transform: translateY(10px);
}
#second-row{
position: fixed;
top: 200px;
}
</style>
2)需要裁剪的地方
<style>
#third-row{
height: 70px;
background-color: gold;
overflow: auto;
}
</style>
<body>
<div id="third-row">
<p>怀旧空吟闻笛赋,到乡翻似烂柯人</p>
<p>沉舟侧畔千帆过,病树前头万木春。<p>
</div>
</body>
出现这类裁剪的情况,渲染引擎会为文字部分单独创建一个图层,如果出现滚动条,滚动条也会被提升为单独的层。
5 绘制
拿到LayerTree后,需要将每个图层的绘制拆分成小的绘制指令,按照顺序组成一个绘制列表(一个图层一个列表):
可以在【图层】处查看绘制列表(我的devTools中只有详情,没有Profiler,看不到):
注意,这个步骤的结果是绘制列表,此时还未进行绘制。
6 光栅化
有了绘制列表后,需要执行渲染了,此时渲染引擎的主线程会将绘制列表提交(commit)给合成线程,合成线程需要做什么?
在这之后我们先回忆一个概念——视口(ViewPort),屏幕上页面的可见区域就叫做视口。
可能页面很大,但我们最多也只能看到视口部分的内容。考虑到这种情况,在绘制图层时,并不需要一次性绘制出所有图层内容。
基于上面的原因,合成线程会将图层划分为图块(tile),图块的大小通常是256x256或者521x512。
合成线程会优先将视口附近的图块生成为位图。
所谓光栅化(Raster)就是指将图块转为位图,图块是光栅化操作的最小单位。
渲染进程维护了一个光栅化的线程池,所有光栅化的操作均在里边执行。
通常,会使用GPU来加速光栅化过程,此时是用GPU来生成位图,位图会被保存在GPU内存中。这个过程叫做快速光栅化或者GPU光栅化。
GPU是另外的进程,此时涉及渲染进程和GPU进程的通信。
渲染进程将生成图块的指令发送给GPU,在GPU中执行图块转为位图。
这里有个疑惑,生成位图的操作在GPU进程时,合同线程的光栅线程的任务和不用GPU时的差别是什么?
7 合成与显示
- 所有图块都被光栅化后,合成线程会生成一个命令——
DrawQuad,然后将该命令提交给浏览器进程。 - 浏览器进程中有一个叫viz的组件,用来接受
DrawQuad命令,将页面内容绘制到内存中,最后显示到屏幕中。
虽然有些内容没明白,不过不影响对整体流程的了解,好像大概也够用了。