从浏览器的一次请求到渲染的完成

从用户在浏览器地址栏输入网址,到看到整个页面,中间发生了哪些事情?

从HTTP请求页面分析(请与HTTPS区别)

起稿时间并没有考虑是具体发送请求到那个服务器

开始-浏览器发送HTTP请求

DNS

一般情况下,用户一般输入的都是URL,而不是IP地址,所以这里需要经过一次的DNS解析。

DNS是互联网中用于解析域名和IP地址对应关系的系统。当我们在浏览器中输入一个URL时,DNS就会进行解析,将域名转换为对应的IP地址,以便浏览器可以访问相应的网站。

DNS解析过程:

浏览器首先会检查本地缓存中是否已经有该域名对应的IP地址。如果有,则直接使用缓存中的IP地址,跳过后续的解析过程。

如果本地缓存中没有该域名对应的IP地址,浏览器会向本地DNS服务器发送一个DNS查询请求,请求解析该域名对应的IP地址。

如果本地DNS服务器缓存中有该域名对应的IP地址,就直接返回IP地址给浏览器,完成解析过程。

如果本地DNS服务器缓存中没有该域名对应的IP地址,则会向根域名服务器发送一个DNS查询请求,请求解析该域名对应的IP地址。

根域名服务器返回一个包含顶级域名服务器的IP地址的响应给本地DNS服务器。

本地DNS服务器再向顶级域名服务器发送一个DNS查询请求,请求解析该域名对应的IP地址。

顶级域名服务器返回一个包含二级域名服务器的IP地址的响应给本地DNS服务器。

本地DNS服务器再向二级域名服务器发送一个DNS查询请求,请求解析该域名对应的IP地址。

二级域名服务器返回一个包含IP地址的响应给本地DNS服务器。

本地DNS服务器将该IP地址缓存起来,并将IP地址返回给浏览器,完成解析过程。

在经过DNS解析后,我们便知道了我们请求的发送目的地,就只需要通过IP寻址即可。

但是并不是只是知道的IP的地址便万事大吉,这其中可能会有丢包,服务器未响应,或者拒绝连接等等情况,还需要知道服务器与连接线路的情况。

TCP

我们发送的是HTTP 协议,是基于TCP协议的应用层传输协议,也就是说需要与服务器建立TCP连接,这时会有“三次握手”的现象:

第一次握手:客户端将TCP报文标志位SYN置为1,随机产生一个序号值seq=J,保存在TCP首部的序列号字段里,指明客户端打算连接的服务器的端口,并将该数据包发送给服务器端,发送完毕后,客户端进入SYN_SENT状态,等待服务器端确认。

第二次握手:服务器端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务器端将TCP报文标志位SYN和ACK都置为1,ack=J+1,随机产生一个序号值seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进入SYN_RCVD状态。

第三次握手:

客户端收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给服务器端,服务器端检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。

经过三次握手,我们终于可以发送我们正式的页面请求了。

这里我们需要假设网络不拥堵,不会产生丢包或超时现象(不然服务器可能根本接收不到请求)。

HTTP请求

一个HTTP请求报文由四个部分组成:请求行、请求头部、空行、请求数据。

请求行

请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。比如 GET /data/info.html HTTP/1.1

方法字段就是HTTP使用的请求方法,比如常见的GET/POST。

请求头部

请求头部包括若干个键值对,每个键值对占一行。键表示请求头部字段的名称,如Host、User-Agent、Accept等;值表示请求头部字段的值,这里的请求头一般帮助服务器详细了解连接状态,等待时长,客户端返回何种数据,何种结构等等

空行

它的作用是通过一个空行,告诉服务器请求头部到此为止。

请求数据

如果是HTTP页面请求,这里一般置空,但是如果是数据请求的话,可以填写需要的数据,或者发送服务器返回数据所必须接收的数据。(比如发送用户的账号与密码来获取)内容填写只需要与前端解析显示进行约定即可。

一个HTTP请求的报文:

GET /index.html HTTP/1.1

Host: www.baidu.com

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

Accept-Language: cn

Accept-Encoding: gzip, deflate, br

Connection: keep-alive

Referer: http://www.baidu.com/

(此处为空行)

这是一个GET请求报文,请求的资源路径为/index.html,使用的HTTP协议版本为HTTP/1.1。请求头部包含了若干个键值对:

其中Host表示请求的服务器主机名

User-Agent表示客户端浏览器的类型和版本

Accept表示客户端可以接受的响应数据类型

Accept-Language表示客户端可以接受的语言类型

Accept-Encoding表示客户端可以接受的压缩格式

Connection表示客户端与服务器之间的连接类型

Referer表示客户端请求的来源页面。

当然这些键值对有很多都可以省略,具体需要看服务器与前端的需求。

现在发送这个HTTP请求到网络协议栈中,由其负责将这个请求发送到实际的网络中。

注:在代码编写发送数据时,是将数据发到缓存区,而不是直接发送到网络中。

数据会首先到达网卡,再由网卡将其发送,然后会经过各种网关:

本地网关,一般发送的请求的主机都不具有公网IP,而是一个内网IP地址,这个地址在外部是无法辨认/转发的,所以需要经过一次映射,获取其上一层的IP地址。

边缘网关:请求从本地网关进入广域网后,会经过边缘网关,这个网关会连接数个本地网关,其内部这些网关也是内网的一部分,所以上一次的IP映射结果还需要经过一次映射,来保证其IP地址可以在边缘网关的外层可以被正确转发。

这里使用的映射技术便是NAT。

NAT

NAT是一种网络协议,它可以将私有网络内部的IP地址和端口号转换为公共网络可用的IP地址和端口号,从而实现内部网络与外部网络之间的互联。NAT映射就是NAT协议实现的一种技术,它可以将私有IP地址和端口号映射到公共IP地址和端口号,使得内部网络可以通过公共网络访问外部网络,同时也可以通过映射关系实现外部网络访问内部网络中的主机。

NAT映射主要有三种类型:静态映射、动态映射和端口映射。静态映射是指在NAT设备上预先定义好内部网络中的主机与外部网络之间的映射关系,这种映射是固定不变的。动态映射是指NAT设备根据内部网络中主机的请求动态地分配公共IP地址和端口号,这种映射是临时的,使用完毕后就会释放。端口映射是指将内部网络中主机的端口号映射到公共网络的端口号上,这样就可以实现多个内部主机使用同一个公共IP地址访问外部网络。

NAT(Network Address Translation)是一种网络协议,它可以将私有网络内部的IP地址和端口号转换为公共网络可用的IP地址和端口号,从而实现内部网络与外部网络之间的互联。NAT的实现步骤如下:

NAT设备接收到内部网络中主机发出的数据包,将目的IP地址和端口号进行转换。如果数据包是从内部网络发往外部网络,则将数据包中的内部IP地址和端口号转换为公共IP地址和端口号;如果数据包是从外部网络发往内部网络,则将数据包中的公共IP地址和端口号转换为内部IP地址和端口号。

NAT设备根据预定义的映射规则,将数据包中的IP地址和端口号进行转换。映射规则可以是静态映射、动态映射或端口映射。在静态映射中,内部网络中的主机与外部网络之间的映射关系是固定的,NAT设备需要根据预定义的映射表进行转换;在动态映射中,NAT设备根据内部网络中主机的请求动态地分配公共IP地址和端口号;在端口映射中,NAT设备将内部主机的端口号映射到公共网络的端口号上。

NAT设备修改数据包的IP头,将源IP地址和目的IP地址进行转换。如果数据包是从内部网络发往外部网络,则将源IP地址改为NAT设备的公共IP地址,将目的IP地址改为目标主机的IP地址;如果数据包是从外部网络发往内部网络,则将源IP地址改为发送主机的IP地址,将目的IP地址改为内部网络中的主机IP地址。

NAT设备根据转换后的IP地址和端口号,选择适当的路由进行转发。如果数据包是从内部网络发往外部网络,则将数据包发送到外部网络的网关;如果数据包是从外部网络发往内部网络,则将数据包发送到内部网络中的目标主机。

NAT设备在转发数据包时需要进行多次转换和修改,其中包括IP地址和端口号的转换、IP头的修改和路由选择等步骤。这些步骤都需要依靠NAT设备的转换表和路由表进行转发和处理。

NAT映射是一种非常重要的网络技术,它可以帮助内部网络实现与外部网络的通信,同时也可以保护内部网络的安全。

网络协议栈内核与网卡

物理网卡是硬件设备,工作在OSI最底层,其硬件功能是以比特流的方式接收和发送数据

物理网卡需网卡驱动程序向内核注册后方可工作,注册后一般会显示对应的网卡接口,网卡接口名称是给用户看的,内核不以接口名称来识别网卡

为网卡接口做的配置属于内核而非网卡

内核在和某网卡交互时,会基于内核中属于该网卡的配置属性做出决策和对应的处理

物理网卡一端是内核(网络协议栈),另一端是外界网络

物理网卡收发数据(先不考虑DMA方式):

发:本机通过某物理网卡发送数据时,内核将网络协议栈中的数据写入该网卡,该网卡将其发送出去

收:网卡收到数据后,中断通知内核,内核从网卡读取数据并放入网络协议栈

但网卡具有DMA功能,所以网卡和网络协议栈之间的数据传输主要由网卡负责,DMA传输完毕后DMA控制器中断通知内核已传输完毕

返回-服务器接收HTTP请求并处理

服务器接收到请求,需要解析请求(也就是请求行与请求头部)并查看服务器是否支持接收到请求,在解析完后,服务器发现可以处理,便会去响应(如果无法解析或者不支持接收到的请求头部,也会响应,但是只是一种通知,通知浏览器,服务器拒绝请求)

现在,数据经过网卡,进入网络协议栈,再到达服务器程序,通过了重重协议的解析(DNS,NAT,TCP)我们终于连接到服务器,并将HTTP请求发送到了服务器。

服务器通过TCP/IP的方式接收到数据,再将其通过HTTP的方式进行解析:

解析请求行:HTTP请求的第一行为请求行,包含了请求方法、请求URL和HTTP协议版本号等信息。服务器需要解析请求行,确定请求的类型和目标资源。

解析请求头:HTTP请求的第二行到倒数第二行为请求头,包含了客户端发送的附加信息,如请求头中的Accept、User-Agent、Cookie等。服务器需要解析请求头,了解客户端的请求参数和特性。

解析请求体:如果HTTP请求为POST请求,则请求头之后会有一个请求体,包含了客户端提交的数据。服务器需要解析请求体,获取客户端提交的数据。

处理请求:根据请求的类型和目标资源,服务器进行相应的处理,如返回HTML页面、执行CGI程序、查询数据库等。

生成响应:HTTP服务器根据处理结果,生成HTTP响应,包括响应状态行、响应头和响应体等内容。

发送响应:HTTP服务器通过TCP/IP协议将HTTP响应发送回客户端。

响应报文的在网络上的流动/解析过程与其发送请求中的过程一致,只是发送方变成了服务器,目标地变成了发送方。

接收-浏览器接收返回的HTTP的Request

HTTP响应

一个HTTP响应的报文(与HTTP请求对应):

HTTP/1.1 200 OK

Server: Apache/2.4.46 (Win64) OpenSSL/1.1.1h PHP/7.4.13

Content-Type: text/html; charset=UTF-8

Content-Length: 1270

Date: Mon, 23 Nov 2020 08:20:10 GMT

(此处为空行)

<!DOCTYPE html>

<html>

<head>

<title>Example Website</title>

<meta charset="UTF-8">

<meta name="viewport" content="width=device-width, initial-scale=1.0">

</head>

<body>

<h1>Welcome to Example Website</h1>

<p>This is an example website.</p>

<p>Thank you for visiting.</p>

</body>

</html>

报文由三部分组成:响应状态行、响应头和响应体。

响应状态行:包含了HTTP协议版本号、状态码和状态描述。例如,HTTP/1.1 200 OK表示HTTP协议版本为1.1,状态码为200,状态描述为OK。

响应头:包含了服务器返回的附加信息,如Content-Type、Content-Length、Server等。这些信息用于告诉客户端如何处理响应数据,以及服务器的类型和版本等信息。

响应体:包含了服务器返回的实际数据,可以是HTML页面、文本文件、图片等。响应体的内容长度由Content-Length响应头指定。

HTTP响应报文的解析过程如下:

接收响应:HTTP客户端通过TCP/IP协议接收HTTP服务器返回的响应报文。

解析响应状态行:HTTP响应的第一行为状态行,包含了HTTP协议版本号、状态码和状态描述。客户端需要解析状态行,了解服务器返回的状态和信息。

解析响应头:HTTP响应的第二行到倒数第二行为响应头,包含了服务器返回的附加信息。客户端需要解析响应头,了解服务器返回的数据类型、长度和服务器信息等。

解析响应体:HTTP响应的最后一行为响应体,包含了服务器返回的实际数据。客户端需要解析响应体,获取服务器返回的数据。

处理响应:根据服务器返回的状态码和状态描述,客户端进行相应的处理,如显示HTML页面、保存文件等。

执行-浏览器接收到HTTP页面数据,准备执行

现在我们已经拥有一个完整的HTTP页面,可以准备渲染出来了。

在前面,我们需要了解一下现在浏览器的结构:

浏览器主进程:负责浏览器的用户界面和进程间的协调。它管理所有的标签页和插件进程,并控制浏览器的行为,如地址栏、书签、前进和后退等。

渲染器进程:用来控制显示标签页内的所有内容,包括HTML、CSS和JavaScript等。渲染器进程一般为每个标签页创建一个渲染进程,以保证不同标签页之间的互不干扰。

GPU进程:用来处理浏览器界面的渲染任务。GPU进程将渲染任务分配给GPU硬件加速器来执行,以提高页面的渲染性能。

网络进程:负责发送和接收网络请求和响应。网络进程通过HTTP协议与服务器进行通信,从而获取网页的数据。

插件进程:控制浏览器的拓展接口和插件。插件进程负责加载和运行插件,以支持浏览器的扩展功能。

缓存进程:负责数据的缓存,以减少网络传输和提高页面的加载速度。缓存进程会将经常访问的数据缓存到本地,以便下次访问时可以更快地加载。

现代浏览器采用多进程的方式来实现各种功能,以保证浏览器的稳定性和安全性。每个进程都负责不同的任务,通过相互协作来提供完整的浏览器功能。

渲染流程

大致的流程:

渲染引擎从网络层获取请求文档的内容(内容的大小一般限制在 8000 个块以内)。

渲染引擎开始解析 HTML 文档,先转换为 token,后将 token 逐个转化成“内容树”上的 DOM 节点。

同时也会解析外部 CSS 文件以及样式元素中的样式数据。

HTML 中这些带有视觉指令的样式信息将用于创建另一个树结构: layout 树。layout 树包含多个带有视觉属性(如颜色和尺寸)的矩形。这些矩形的排列顺序就是它们将在屏幕上显示的顺序。

layout 树构建完毕之后,进入布局处理阶段,也就是为每个节点分配一个应出现在屏幕上的确切坐标。

下一个阶段是绘制,呈现引擎会遍历 layout 树,由用户界面后端层将每个节点绘制出来。

DOM

首先需要将HTML解析为一个DOM树,具体流程如下:

分词:将HTML文本中的字符流转换为标记流,每个标记对应一个标记类型和一组属性值。分词过程一般采用状态机来实现,遇到不同的字符流就转换到不同的状态中,根据状态的转换生成标记。

构建节点对象:遍历标记流,对每个标记创建相应的节点对象。起始标记生成一个元素节点,结束标记生成一个结束标签节点,属性生成属性节点,文本生成文本节点等。

构建DOM树:将节点对象按照标记之间的层次关系组织起来,构建成一棵DOM树。每个节点对象都有一个指向其父节点、子节点和同级节点的指针,用于组织节点对象之间的关系。

解析样式和脚本:将HTML文本中的样式和脚本解析出来,并将它们应用到DOM树上。样式通常采用CSS语言来描述,脚本通常采用JavaScript语言来编写。

渲染页面:将DOM树和样式表合并起来,生成一棵渲染树,并根据渲染树生成页面布局和绘制页面。渲染树包含了DOM树和样式表的所有信息,用于描述页面元素的样式和布局。

STYLE

主线程在获取DOM树后还不知道页面长什么样子,所以需要进行CSS解析,读取CSS样式表,来形成CSSOM规则解析树:

分析CSS样式表:浏览器首先需要对CSS样式表进行分析,了解样式表中定义的所有规则和属性,并将它们转换成一组规则集合。

构建CSSOM规则解析树:根据样式表中定义的规则集合,浏览器构建一棵CSSOM规则解析树。这棵树的节点包括规则和规则的属性,每个规则对应一个节点,节点的属性对应规则的属性。

解析继承和优先级:在构建CSSOM规则解析树的过程中,浏览器还需要解析CSS中的继承和优先级规则,以便正确地应用样式。继承规则指的是某些属性会被其父元素继承,而优先级规则指的是某些属性的样式优先级高于其他属性。

应用样式:最后,浏览器根据DOM树和CSSOM规则解析树中的规则,将样式应用到每个网页元素上,生成一棵渲染树。渲染树包含了所有网页元素的样式信息,用于描述页面的布局和绘制。

Layout

在 DOM 和 Style 计算好后开始进入布局 Layout 阶段,比如将 DIV 解析为一个块级的LayoutRect 区域,用x+y+width+height来表示,布局就是为了计算x,y,width,height这些数据。

默认情况下文档按照顺序排列下去形成了文档流。

文字和内联元素则是左右浮动的,而且内联元素会被行尾打断(自动换行)。当然也有从右到左的语言,比如阿拉伯语和希伯来语。

布局也包括字体的排列,因为布局需要考虑文本在哪里进行换行,Layout 使用名为 HarfBuzz 的开源文本库来计算每个字形的大小和位置,这决定了文本的总宽度。字体成型必须考虑到排版特征,如字距调整letter-spacing和连字。

布局可以计算单个元素的多种边界矩形。例如,当存在溢出时,Layout 将同时计算边界框和布局溢出。如果节点的溢出是可滚动的,Layout 还会计算滚动边界并为滚动条预留空间。最常见的可滚动 DOM 节点是文档本身。

表格元素或display:table的样式需要更复杂的布局,这些元素或样式指定将内容分成多列,或浮动对象漂浮在一边,内容在其周围流动,或者东亚语言的文本垂直排列,而不是水平排列。

通过遍历 DOM 树创建渲染树LayoutTree,节点一一对应。布局树中的节点实现布局算法。根据所需的布局行为,LayoutObject有不同的子类。比如LayoutBlockFlow就是块级Flow的文档节点。样式更新阶段也构建布局树。

在样式解析最后结束时需要构建布局树LayoutTree,布局阶段遍历布局树,对布局树每个节点LayoutObject执行布局,计算几何数据、换行符,滚动条等。

注:DOM节点与Layout节点不是一一对应的

一般情况下一个DOM节点会有一个LayoutObject,但是有时候LayoutObject是没有DOM节点与之对应的。 比如上图,span标签外部没有div标签嵌套,但是LayoutTree会自动创建LayoutBlock的匿名节点与之对应,再比如样式有display:none的样式,那么也不会创建对应的LayoutTree。

最后,如果是shadowTree的话,其LayoutObject节点可能会在不同的TreeScope里。

Paint

绘制过程是将LayoutObject转化为绘制指令paint op,每个LayoutObject会对应多个绘制指令paint ops,比如背景,前景,轮廓等。样式可以控制绘制的顺序。绘制有自己的顺序,如背景色在前,其次是浮动元素,前景色,轮廓 outline。

绘制 paint 阶段创建绘制指令列表paint ops list。

绘制指令paint ops可以理解为在某些坐标用什么颜色画一个矩形类似的意思。每个布局对象LayoutObejct可以有多个显示项目,对应于其视觉外观的不同部分,如背景、前景、轮廓等。

每个绘制阶段paint phase都需单独遍历堆叠上下文staking context。

一个元素甚至可能部分位于另一个元素的前面,部分位于另一个元素的后面。这是因为绘制在多个阶段中运行,每个绘制阶段都对自己的子树进行遍历。

Raster

中文说的栅格化或者光栅化,取自 PS 图层右键的栅格化为译文。熟悉 PS 的会知道矢量图形栅格化后,放大图形会“糊”。但是不做栅格化处理直接放大矢量图形则不会。原因就是栅格化后只记录了单像素点的 rgba 值,放大后本来一个点数据,要填满N个点,图像就糊了。

把绘制列表里的绘制操作执行过程,称为任务,或称栅格化。比如 PS 里的图层任务,主要区别就是本来矢量的图,栅格化后会变成位图 bitmap,后面再缩放就会模糊。

生成的位图 bitmap 中的每个单元格都包含对单个像素的颜色和透明度进行编码的位。这里用十六进制表示一个点的 rgba 值。

栅格化还可以对页面中嵌入的图像资源进行解码。绘制指令引用压缩数据(JPEG、PNG等),栅格化会调用适当的解码器将其解压缩。

GPU 可以运行生成位图的命令(“加速栅格化”)。请注意,这些像素还没有出现在屏幕上! raster 产生的位图数据存储在 GPU 内存中,通常是 OpenGL 纹理对象引用的 GPU 内存。

过去通常是存在内存里再传给 GPU,但是现代 GPU 可以直接运行着色器 shader 并在 GPU 上生成像素,这种情况称为“加速栅格化”。但是两个结果都是一致的,最终内存(主存或者GPU内存)里拥有位图 bitmap。

Raster运行在GPU进程中:

渲染器进程是一个沙箱环境,因此它不能直接进行系统调用。绘制操作被运送到GPU进程进行任务处理。GPU进程可以发出实际的GL调用。除了独立于渲染器沙箱之外,在GPU进程中隔离图形化操作还可以保护我们免受不稳定或不安全的图形驱动程序的影响。比如GPU进程崩了,浏览器可以重启GPU进程。

渲染流水线

渲染主线程获取Web内容,构建DOM树,解析样式,更新布局,layer分层后合成,生成属性树,创建绘制指令列表,再到渲染进程合成线程收到渲染主线程commit过来的带有绘制指令和属性树的layer,将layer分块为图块,使用Skia对图块进行栅格化,拷贝pending tree到active tree,生成draw quads命令,将quad发送给GPU的Viz线程,最后像素显示到屏幕上。

大多数阶段是在渲染器进程中执行的,但是raster和display则在GPU进程中执行。

核心渲染阶段DOM,style,layout,paint是在渲染进程主线程里的Blink进行的,但是滚动和缩放等交互事件在渲染主线程繁忙时可以在渲染进程合成线程里执行

渲染进程主线程

DOM: 解析HTML生成DOM树。

style: 解析styleSheet生成ComputedStyle。

layout: 生成 layout tree,跟 DOM 树基本对应,但是display:none的节点不显示,内联元素会创建 LayoutBlock 匿名节点包裹

layer分层后合成:某些样式属性会单独形成层,如transform会形成单独的层方便进行图形变换,滚动元素会多出scrollbar的4层。合成任务在渲染进程的合成线程中执行,与渲染主线程隔离互不影响。

prepaint: 为了将属性与层解耦引入prepaint阶段,prepaint阶段需要遍历并构建属性树,属性树即存储如变换矩阵,裁剪,滚动偏移,透明度等数据的地方,方便后面paint阶段拿属性树数据处理。

paint: 绘制过程是将LayoutObject转化为绘制指令paint op,每个LayoutObject会对应多个绘制指令paint ops,比如背景,前景,轮廓等。样式可以控制绘制的顺序。绘制有自己的顺序,如背景色在前,其次是浮动元素,前景色,轮廓 outline

渲染进程合成线程

页面的滚动等交互会进入渲染进程合成线程compositor thread里处理,这也是渲染进程主线程繁忙时交互也不卡的原因。

commit: 渲染进程合成线程将层从渲染主线程拷贝出两份层和属性树副本。

tiling: 栅格化整个图层成本大,渲染进程合成线程将layer分块后选择视口相近的图块tiles再进行栅格化成本小很多。

activate: 合成线程具有两个树的副本,pending tree负责将新commit的layer转到栅格化线程池里的栅格化线程处理好后同步到active tree

draw: 栅格化所有变换后的图块之后,生成draw quads命令,包含多个DrawQuad的CompositorFrame,这是渲染进程最后的输出,此时屏幕还没有像素出现。

GPU进程

raster: 栅格化是将绘制指令paint op转化为位图bitmap的过程,转化后每个像素点的rgba都确定。栅格化还处理图片解码,通过调用不同解码器解压缩图片,GPU可以加速栅格化,通过调用Skia对图块进行栅格化。

Skia: 封装OpenGL调用,提供异步显示列表,最后传递到GPU主线程处理,GPU主线程的Skia后台发起真正的GL调用。

display: GPU Viz线程里的显示合成器display compositor合并多个进程的CompositorFrame输出,并通过Skia发起图形调用,像素呈现在屏幕上。

参考文章:

https://juejin.cn/post/7050371465375383589

https://juejin.cn/post/6847902222349500430

https://juejin.cn/post/7068097802676469773#heading-49

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nameless_233

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值