东莞网站建设企慕,百度注册页面,上海网站开发培训,广州哪些做网站的公司4 WebKit软件渲染技术
4.1 软件渲染过程 在很多情况下#xff0c;也就是没有那些需要硬件加速内容的时候#xff08;包括但不限于CSS3 3D变形、CSS3 03D变换、WebGL和视频#xff09;#xff0c;WebKit可以使用软件渲染技术来完成页面的绘制工作#xff08;除非读者强行…4 WebKit软件渲染技术
4.1 软件渲染过程 在很多情况下也就是没有那些需要硬件加速内容的时候包括但不限于CSS3 3D变形、CSS3 03D变换、WebGL和视频WebKit可以使用软件渲染技术来完成页面的绘制工作除非读者强行打开硬件加速机制目前用户浏览的很多门户网站、论坛网站、社交网站等所设计的网页都是采用这项技术来完成页面的渲染。 要分析软件渲染过程需要关注两个方面其一是RenderLayer树其二是每个RenderLayer所包含的RenderObject子树。首先来看WebKit如何遍历RenderLayer树来绘制各个层。 对于每个RenderObject对象需要三个阶段绘制自己第一阶段是绘制该层中所有块的背景和边框第二阶段是绘制浮动内容第三阶段是前景Foreground也就是内容部分、轮廓它是CSS标准属性绘制于元素周围的一条线位于边框边缘的外围等部分。当然每个阶段还可能会有一些子阶段。值得指出的是内嵌元素的背景、边框、前景等都是在第三阶段中被绘制的这是不同之处。 下图描述了一个RenderLayer层是如何绘制自己和子女的这一过程是一个递归过程。图中的函数名未来可能会发生变化所以读者更多关注它们的含义。图中的调用顺序可以作如下理解这里主要节选了一些重要步骤事实上这一绘制过程还可能包含其他一些相对较小的步骤。图中有些步骤的操作并不是总是发生。这里是一个大致的过程下面是详细分析。
图 绘制RenderLayer和它的子女的调用过程
对于当前的RenderLayer对象而言WebKit首先绘制反射层Reflectionlayer这是由CSS定义的。然后WebKit开始绘制RenderLayer对象对应的RenderObject节点的背景层PaintBackground-ForFragments也就是调用“PaintPhaseBlockBackground”函数读者记住这里仅是绘制该对象的背景层而不包括RenderObject的子女。其中“Fragments”的含义是可能绘制的几个区域因为网页需要更新的区域可能不是连续的而是多个小块所以WebKit绘制的时候需要更新这些非连续的区域即可下面也是一样的道理。图中的“paintList”z坐标为负数的子女层阶段负责绘制很多Z坐标为负数的子女层。这是一个递归过程。Z坐标为负数的层在当前RenderLayer对象层的后面所以WebKit先绘制后面的层然后当前RenderLayer对象层可能覆盖它们。图中“PaintForegroundForFragments()”这个步骤比较复杂包括以下四个子阶段首先进入“PaintPhaseChildBlockBackground”阶段WebKit绘制RenderLayer节点对应的RenderObject节点的所有后代节点的背景如果某个被选中的话WebKit改为绘制选中区域背景网页内容选中的时候可能是另外的颜色其次进入“PaintPhaseFloat”绘制阶段WebKit绘制浮动的元素再次进入“PaintPhaseForeground”阶段WebKit绘制RenderObject节点的内容和后代节点的内容如文字等最后进入“PaintPhaseChildOutlines”绘制阶段WebKit的目的是绘制所有后代节点的轮廓。进入“PaintOutlineForFragments”步骤。WebKit在该步骤中绘制RenderLayer对象对应的RenderObject节点的轮廓PaintPhaseOutline。进入绘制RenderLayer对象的子女步骤。WebKit首先绘制溢出Overflow的RenderLayer节点之后依次绘制Z坐标为正数的RenderLayer节点。进入该RenderObject节点的滤镜步骤。这是CSS标准定义在元素之上的最后一步。 上面是从RenderLayer节点和它所包含的RenderObject子树来解释软件绘图这一过程那么对于RenderLayer树包含的每个RenderObject而言它们是如何被处理的呢 因为RenderObject类有很多子类每个子类都不一样不过很多子类的绘制其实比较简单所以为了能比较清楚地说明RenderObject绘制的过程这里以典型的RenderBlock类为例来说明因为它是以框模型为基础的类下图给出了绘制RenderBlock类的过程。 图RenderBlock类的绘制过程 在上图中“paint”是RenderObject基类的绘图函数用来绘制该对象的入口函数在RenderBlock类中它被重新实现了。一个RenderObject类的“paint”函数在绘制时可能会被多次调用因为不同的绘制阶段前面的一张图提到的都需要调用它来绘制不同的部分所以读者会发现上图右侧标记了在哪些阶段才会调用该绘制函数或者是哪些阶段该函数不会被调用至于这些阶段的顺序则是由RenderLayer对象中的调用过程来控制的。 图中的“paintContents”函数主要用来遍历和绘制它的子女在某些情况下WebKit其实并不需要该函数例如RenderLayer对象仅需要绘制对应的RenderObject子树的根节点的时候。 对于其他类型的节点绘制过程大致是这一过程的一个子集。例如RenderText类没有子女也不需要绘制框模型的边框等所以WebKit仅需要绘制自己的内容。 在上面这一过程中Webkit所使用的绘图上下文都是2D的因为没有GPU加速所以3D的绘图上下文没有办法工作。这意味着每一层上的RenderObject子树中不能包含使用3D绘图的节点例如Canvas 3DWebGL节点等。同时每个RenderLayer层上使用的CSS 3D变形等操作也没有办法得到支持。 最开始的时候也就是WebKit第一次绘制网页的时候WebKit绘制的区域等同于可视区域大小。而这在之后WebKit只是首先计算需要更新的区域然后绘制同这些区域有交集的RenderObject节点。这也就是说如果更新区域跟某个RenderLayer节点有交集WebKit会继续查找RenderLayer树中包含的RenderObject子树中的特定一个或一些节点而不是绘制整个RenderLayer对应的RenderObject子树。下图描述了在软件渲染过程中WebKit实际更新的区域也就是之前描述软件渲染过程的生成结果。 图WebKit绘制网页的更新区域 WebKit软件渲染结果的储存方式在不同的平台上可能不一样但是基本上都是CPU内存的一块区域多数情况下是一个位图Bitmap。至于这个位图如何处理如何跟之前绘制的结果合并如何显示出来都跟WebKit的不同移植相关。下面介绍一下Chromium是如何处理的。
4.2 Chromium的多进程软件渲染技术 在Chromium的设计和实现中因为设计者引入了多进程模型所以Chromium需要将渲染结果从Renderer进程传递到Browser进程。 先来看看Renderer进程。前面介绍了WebKit的Chromium移植的接口类是RenderViewImpl该类包含一个用于表示一个网页的渲染结果的WebViewImpl类。其实RenderViewImpl类还有一个作用就是同Browser进程通信所以它继承自RenderWidget类。RenderWidget类不仅负责调度页面渲染和页面更新到实际的WebViewImpl类等操作而且它负责同Browser进程的通信。另一个重要的设施是PlatformCanvas类也就是SkiaCanvasSkia是一个2D图形库RenderObject树的实际绘制操作和绘制结果都由该类来完成它类似于2D绘图上下文和后端存储的结合体。 再来看看Browser进程。第一个设施就是RenderWidgetHost类一样的必不可少它负责同Renderer进程的通信。RenderWidgetHost类的作用是传递Browser进程中网页操作的请求给Renderer进程的RenderWidget类并接收来自对方的请求。第二个是BackingStore类顾名思义它就是一个后端的存储空间它的大小通常就是网页可视区域的大小该空间存储的数据就是页面的显示结果。BackingStore类的作用很明显第一它保存当前的可视结果所以Renderer进程的绘制工作不会影响该网页结果的显示第二WebKit只需要绘制网页的变动部分因为其余的部分保存在该后端存储空间Chromium只需要将网页的变动更新到该后端存储中即可。 最后来看看这两个进程是如何传递信息和绘制内容的。两个进程传递绘制结果是通过TransportDIB类来完成该类在Linux系统下其实是一个共享内存的实现。对Renderer进程来说Skia Canvas把内容绘制到位图中该位图的后端即是共享的CPU内存。当Browser进程接收到Renderer进程关于绘制完成的通知消息Browser进程会把共享内存的内容复制到BackingStore对象中然后释放共享内存。 Browser进程中的后端存储最后会被绘制在显示窗口中用户就能够看到网页的结果。下图显示的是软件渲染的架构图其思想主要来源于Chromium的官方网站但这里做了一些扩充。 图Chromium的多进程软件渲染结构图 根据上面的组成部分一个多进程软件渲染过程大致是这样的RenderWidget类接收到更新请求时Chromium创建一个共享内存区域。然后Chromium创建Skia的SkCanvas对象并且RenderWidget会把实际绘制的工作派发给RenderObject树。具体来讲WebKit负责遍历RenderObject树每个RenderObject节点根据需要来绘制自己和子女的内容并存储到目标存储空间也就是SkCanvas对象所对应的共享内存的位图中。最后RenderWidgetHost类把位图复制到BackingStore对象的相应区域中并调用“Paint”函数来把结果绘制到窗口中。 后面我们会介绍在哪些时候请求绘制网页内容这里先了解两种会触发重新绘制网页中某些区域的请求如下面所示。
前端请求 该类型的请求从Browser进程发起的请求可能是浏览器自身的一些需求也有可能是X窗口系统或者其他窗口系统的请求。一个典型的例子就是用户因操作网页引起的变化。后端请求 由于页面自身的逻辑而发起更新部分区域的请求例如HTML元素或者样式的改变、动画等。一个典型的例子是JavaScript代码每隔50ms便会更新网页样式这时样式更新会触发部分区域的重绘。 下面逼仄来解释一下当有绘制或者更新某个区域的请求时Chromium和WebKit是如何来处理这些请求的。具体过程下图所示下面是其中主要的步骤。 图Chromium的软件渲染过程
Renderer进程的消息循环Message Loop调用处理“界面失效”的回调函数该函数主要调用RenderWidget::DoDeferredUpdate来完成绘制请求。RenderWidget::DoDeferredUpdate函数首先调用Layout函数来触发检查是否有需要重新计算的布局和更新请求。RenderWidget类调用TransportDIB类来创建共享内存内存大小为绘制区域的高×宽×4同时调用Skia图形库来创建一个SkCanvas对象。SKCanvas对象的绘制目标是一个使用共享内存存储的位图。当渲染该页面的全部或者部分时ScrollView类请求按照从前到后的顺序遍历并绘制所有RenderLayer对象的内容到目标的位图中。Webkit绘制每个RenderLayer对象通过以下步骤来完成首先Webkit计算重绘的区域是否和RenderLayer对象有重叠如果有Webkit要求绘制该层中的所有RenderObject对象。图7-14中省略了该部分的具体内容详情请参考代码。绘制完成后Renderer进程发送UpdateRect的消息给Browser进程Renderer进程同时返回以完成渲染的过程。Browser进程接收到消息后首先由BackingStoreManager类来获取或者创建BackingStoreX对象在Linux平台上BackingStoreX对象的大小与可视区域相同包含整个网页的坐标信息它根据UpdateRect的更新区域的位置信息将共享内存的内容绘制到自己的对应存储区域中。 最后Browser进程将UpdateRect的回复消息发送到Renderer进程这是因为Renderer进程知道Browser进程已经使用完该共享内存可以进行回收利用等操作这样就完成了整个过程。 细心的读者其实可以发现这一过程需要一些内存方面的拷贝这是因为网页的渲染和网页的显示是在两个不同的进程而这些拷贝在下一章介绍的硬件加速渲染机制中可以避免。当然硬件加速渲染机制也引入一些其他方面的问题。
4.3 实践软件渲染过程 为了直接理解Chromium的多进程软件渲染过程本节中笔者使用Chromium项目提供的“about:tracing”工具来分析该工具可以收集Chromium内部函数调用的时间分布等信息。具体步骤如下。
使用Chrome浏览器打开标签页输入“chrome://flags”找到选项“对所有网页执行GPU合成Mac, Windows, Linux”选择“停用”这样确保使用了软件渲染机制。打开网页http://www.chromium.org/developers/design-documents并打开一个新的标签页输入“chrome://tracing”。在标签页“chrome://tracing”中单击“record”按钮并切换到第二步打开的网页“Design Document”重新加载该网页之后再切换到“chrome://tracing”标签页中单击“stop tracing”按钮这样数据收集完毕读者会发现有很多如下图中下面的图层所示的信息它们表示的是浏览器的各个进程和线程的信息。 图浏览器“chrome://tracing”结果和任务管理器
单击浏览器地址栏最右侧的“设置”按钮选择“工具-任务管理器”读者会发现三个任务如果读者的浏览器没有安装其他Chrome扩展或者启动插件等这三个任务分别是网页“Design Documents”Renderer进程1、标签页“chrome://tracing”Renderer进程2和浏览器Browser进程。下图中显示的任务管理器读者看到三个任务的进程ID同“chrome://tracing”中一一对应。下面首先分析进程2312。在进程2312中选择线程“CrRendererMain”通过放大数据图读者可以看到下图所示的信息这是Chromium的多进程模型绘制网页使用的一些函数和它们消耗的时间读者可以将这些函数同图7-14中的Renderer进程中的调用过程作对比。 图“Design Documents”网页对应的Renderer进程
在进程3660中选择线程“CrBrowserMain”在Renderer进程完成图7-16中的操作之后通过放大数据图读者可以看到如图7-17所示的信息这是Chromium更新共享内存的数据并把数据绘制到BackingStore对象中最后绘制到窗口。读者同样可以将这些函数同下图中的Browser进程的调用过程作对比。 图“Design Documents”网页对应的Renderer进程 至此WebKit的基础部分已介绍完毕。通过前面的分析和介绍读者应该可以对网页的基本知识和基本的渲染过程有了一些了解。同时结合Chromium的实现读者应该理解浏览器是如何使用WebKit和扩展浏览器能力的。当然WebKit的能力远不止这些在高级篇中会介绍更多有关WebKit的高级技术。