阅读 386

Life of a Pixel 学习笔记

前言

Life of a Pixel 演讲的学习笔记。浏览器的渲染过程极为的复杂,演讲的语速也特别的快,所以如有错误请及时指出。

Content

前端的所有代码统称为Content,比如html,js,css,图片,视频等。

在Chromium代码架构上Content命名空间包含了,黄色框内的所有内容。在Chromium代码中由WebContent类表示,WebContent类中封装了渲染进程。

image.png

Chromium

Chromium是Google的开源项目,Chrome浏览器就是基于Chromium代码实现,Edge,Opera也是基于Chromium代码。

Blink 浏览器排版引擎

Blink是浏览器排版引擎,属于Chromium中的一部分。Blink属于Content中渲染过程代码的子集。

总结一下:在Chromium中WebContent类负责Content的渲染,渲染过程交由Blink实现

初次渲染阶段

parsing(解析HTML)-> style (生成样式规则模型) -> layout(生成布局对象)-> compositing update (输入合成)-> paint(绘制)-> commit(提交)-> tiling(切割)-> raster(栅格化)-> draw quads -> display(显示到屏幕上)

open gl

浏览器并不能只靠自己将网页渲染到屏幕上,浏览器的渲染需要使用底层操作系统提供的图形库,比如OpenGL API。但是OpenGL并不认识Html,Css这些内容,所以需要通过浏览器的Web Content需要将Html,Css转换成OpenGL可以识别的内容,然后通过OpenGL渲染到屏幕上。

在渲染到屏幕之后,需要监听并响应,JavaScript,用户输入,异步加载,动画,滚动,缩放,然后进行渲染更新。对于一些更新渲染,是不需要从头走完全部的渲染流程的。

parsing

从网络请求开始,最先获取的是html文件,所以浏览器渲染的起点,是HTML解析器。同时HTML中引入了CSS,JS,图片等资源,浏览器也会加载它们。

HTML文件的解析过程:

HTML文件 -stream-> HTMLDocumentParser(HTML文档解析器)-> HTMLTreeBuilder(HTML树构建器)-> DOM

DOM(Document Object Model) 文档对象模型

DOM是基于HTML的倒着的树形结构,DOM有两个作用:

  1. 作为Chrome的内部表示
  2. DOM树经过v8引擎的包装。将更新,查询的API,暴露给js。

style

CSS样式表的解析过程:

CSS样式表 -> CSSParser(CSS解析器)-> 样式规则模型(Style Rule Model)

CSS样式是如何作用与DOM的?

根据已经解析的样式规则(Style Rule),和浏览器的默认样式,计算出每一个DOM的样式。样式和属性值存储在一个巨大的ComputedStyle对象中。ComputedStyle对象是属性和属性值的映射。ComputedStyle对象中会挂载元素,应对应不同元素的不同样式。

layout

在构建完DOM并完成样式计算后,需要确定所有元素的几何形状(几何形状所占的区域以及坐标),这些布局信息称为LayoutObject对象,

布局对象(LayoutObject)保存布局树中,并与DOM关联。不同的节点,对应不同的布局类。但是不同布局类都继承自LayoutObject这个父类。

image.png

image.png

LayoutObject对象和DOM元素并不是一一对应的。比如当DOM元素的样式是display: none时,是没有LayoutObject对象的。

compositing update

输入合成阶段,我们在下面讲🍵

paint

根据布局对象(LayoutObject),绘制操作会将绘制操作记录在一个待显示项目列表(display items list)中。

什么是绘制操作?比如,根据布局对象(LayoutObject)在指定区域绘制一个红色的矩形。这就是绘制操作。

每一个布局对象(LayoutObject)对应多个待显示项目,因为可能涉及到绘制不同的部分。目前只是记录绘制操作,还没有执行绘制操作。绘制的顺序,受控于z-index属性。

commit,tiling

提交,分割阶段,我们在下面讲🍵

raster

待显示项目列表中绘制的实际操作,是由栅格化进程执行的。

栅格化会将待显示项,转换为颜色值的位图(将图像的信息,以像素为单位记录,记录每一个像素点的颜色信息rgba值)。位图信息保存在内存中(通常是显存中)。gpu也可以将待显示项目列表栅格化,我们称为gpu加速。此时位图信息还保存在显存中,没有输出到屏幕上。

对于图像信息,栅格化会对图像进行解码,获取位图信息。

栅格化的过程是通过SKIA库调用OpenGL API完成的。

SKIA

SKIA是一个开源的图形引擎,SKIA可以实现栅格化,PDF输出,GPU加速。

draw quads

draw quads阶段,我们在下面讲🍵

gpu(display阶段)

SKIA产生OpenGL调用,使用命令缓存区的形式,传输到GPU进程,GPU接受到命令缓存,产生GL调用。像素被渲染到屏幕上了。

更新渲染阶段

commit(提交)-> tiling(切割)-> raster(栅格化)-> draw quads -> display(显示到屏幕上)

渲染不是静态的,JavaScript,用户输入,异步加载,动画,滚动,缩放,都会改变渲染。从头执行渲染流程代价会很昂贵。如果每一秒低于60fps,就会变得卡顿。

优化1: Invalidation

为了避免从头执行整个渲染流程,常用的优化方法就是标记出,发生了改变的部分,复用没有改变的部分。比如一个节点需要重新计算样式,在下一帧的时候,只重新计算该节点的样式。

优化2: enter: compositing 输入合成

Chrome另外的优化手段是分层

  1. 将页面分解成多个图层,每一个图层可以单独进行栅格化。
  2. 另外单独线程进行图层的合成

image.png

Layers 分层

动画,滚动,缩放,操作都会创建图层。单独的合成线程,会对滚动输入操作,进行单独处理。


while(true) {
}
复制代码

当我们使用js阻塞主线程时,由于单独的合成线程的存在,合成线程会对用户的滚动操作进行处理,虽然主线程被阻塞但是页面依然可以滚动

Layers tree 图层树

图层的存储结构也是树的形式,图层树间接的基于布局树。

image.png

compositing update 输入合成

compositing update(输入合成)阶段发生在Layout(布局)阶段之后,Paint(绘制)阶段之前。对页面进行分层,每个图层被分别绘制。绘制阶段存在的待显示项目列表(display items list),其实不同的图层拥有不同的待显示项目列表(display items list)

image.png

commit

image.png

绘制完成后,通过同步主线程的状态,更新合成线程图层的状态,最后同步回主线程

tiling

Raster会将待显示项目列表(display items list)转换为位图,但是有时候图层会很大,Raster又是一个开销很大的步骤,合成线程会将图层分割为图块,图块是Raster的基本单位。距离视口越近,会优先创建图块优先被栅格化。

图块会在单独Raster线程中栅格化

image.png

draw

在绘制完所有图块之后,合成器线程将生成draw quadsdraw quads是绘制图块的指令,draw quads被包装在合成器框架对象中,提交给浏览器进程。

display

浏览器进程,运行显示合成器的组件,合成器组件调用OpenGL绘制draw quads,最后像素在用户的屏幕上可见。

参考