浏览器工作原理——渲染流程(下)

2021年10月22日

总结

  1. 分层:将页面分成多个图层,每个图层可以独立栅格化。
  2. 绘制:为每个图层生成对应绘制操作列表,每个绘制操作都代表一个绘图的步骤。
  3. 分块:将图层分成若干图块,交给 GPU 进程栅格化。
  4. 栅格化:栅格器由线程池,中的线程根据图块的绘制操作列表,将图块转化成位图数据。
  5. 合成和显示:合成多个图层到内存中,然后显示在屏幕上。

分层(layer)

一个图层是页面的一部分,它可以独立于其他层被变形和栅格化。每个节点都属于一个图层,没有特别指定层级的节点属于父节点的所在的图层。

单独提升为一个图层的条件

  1. 拥有堆叠上下文属性的元素

    • 文档的根元素(<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)的元素。
  2. 需要被裁切的元素

    当一个元素的内容尺寸超过了元素本身的尺寸,并且指定了 overflow 属性,这时候就产生了裁剪。渲染引擎会把内容的一部分显示在元素区域内。

    出现这种裁剪情况的时候,渲染引擎会为内容元素单独创建一个层,如果出现滚动条,滚动条也会被提升为单独的层。

绘制(paint)

绘制过程中为每个图层构建一个绘制操作(paint ops)列表。绘制的顺序是元素的堆叠顺序而不是 DOM 顺序,绘制会分成好几个阶段(backgrounds->floats->foregrounds->outlines->…),每个阶段都是对堆叠上下文的单独遍历。

绘制操作列表

绘制例子

栅格化(raster)

栅格化将绘制阶段构建的绘制操作列表转换成位图。

分块(tilling)

绘制操作列表准备好之后,主线程会将图层的绘制操作列表提交给合成器线程。

图层可能会很大,这样的话栅格化整个图层耗费的代价是十分昂贵的,而且对那些图层中不可见的部分栅格化是完全没必要的。所以合成器线程会将图层分成若干个图块(tiles:瓷砖,瓦片)。

栅格化是多线程的

图块是栅格化的单元,被分块的若干图块会被一个栅格器线程池中的线程渲染。越靠近视口的图块越优先被栅格化。因为渲染进程是运行在沙盒中,所以无法直接执行操作系统调用。经过绘制后得到的数据被送到 GPU 进程栅格化,所以栅格器是运行在 GPU 线程中的

分块和栅格器线程池

栅格化之后生成的位图被存储在内存中,通常是由 OpenGL 纹理对象引用的 GPU 内存。GPU 还可以运行生成位图的命令(“加速栅格化”)。

合成与显示

图块被栅格化之后,合成器线程会生成一个 DrawQuads 命令,这个命令代表在屏幕上一个特定位置画出图块。这个命令还没有真正地转化成屏幕上的像素,而是对应在内存中的表示。最后再将内存中的数据显示在屏幕上(显示这个步骤很模糊,课程中也是一笔带过)。

回顾

从 HTML 到像素的总流程图

相关阅读


© 2022, 分享知识和生活,记录成长与感动。