概要

文档主要详述在Chrome中关于使用硬件加速图层合并的背景知识及其相关细节

引言:为什么使用硬件进行渲染图层的合并

在传统的web浏览器中,完全依赖CPU来进行web页面内容的渲染的。如今,即使使用最小的硬件设备
都会集成功能可靠的GPUs,因此关注点开始转移到这类底层硬件上来,探索如何使用他们更高效地实现更好的性能和电量节约。
使用GPU进行web页面内容的合成可以非常好的进行页面加速。

使用硬件进行图层合并的三大优势

  1. 在涉及到大量像素的页面进行绘图和图层合并操作中,使用GPU合并页面图层比起使用CPU可以带来更高的效率(在速度上以及电量使用上)。GPU就是专门为这类场景而设计的
  2. 对于已经存在GPU的内容,重复的读取数据是相当昂贵的(比如已加速的video、canvas2D、webGL)。
  3. 通过CPU与GPU的平行处理可以同时创建高效的管道实现图形数据流的绘制。

最后,需要先声明:Chrome的图形层在前面几年已经有了根本上的演变。这篇文章只关注此时最优秀的架构,而不兼顾所有的平台。关于具体的功能细节,请移步GPU架构之路。此处对不在开发中使用的不会详述。

第一部分:Blink的渲染基础

Blink渲染引擎的源码是非常复杂而且庞大的,甚至几乎没有文档。为了理解GPU如何在Chrome中加速渲染的,第一步就是要弄明白Blink如何渲染页面的基础组成模块。

DOM节点与文档树

在Blink中,web页面的内容在内部是以叫做文档树的DOM节点对象树存储的。在页面中的每个HTML元素包括在元素节点之间的文本节点都是DOM节点。文档树的最顶层节点被称作文档节点(Document Node)

从节点到渲染对象

每个在文档树中会产生视觉输出的节点都会生成一个渲染对象。渲染对象保存在与文档树平行的数据结构中,叫做渲染树。每个渲染对象通过调用图形上下文(GrapuicsContext)的绘图调用接口,可以知道如何将DOM节点绘制到显示设备平面上。图形上下文主要负责将像素映射成位图,最终呈现到显示屏幕的。在Chrome中,图形上下文包裹了Skia(2D 绘图库)。

传统的图形上下文的调用大多数都是通过直接调用如SkCanvas或者SkPlatformCanvas将内容绘制到位图点的(可以查看这篇文章,了解关于Chrome如何使用Skia在这种模型下处理的详细内容)。但是从主线程中移除绘制过程(后面会详细讲述),这些指令现在都会记录到 SkPicture。SkPicture是一个序列化的数据结构,可以捕获并且可以在随后重新触发这些指令。类似一组 展示列表

从渲染对象到渲染层

每一个渲染对象都直接或者通过祖先的渲染对象间接的与一个渲染层相关联。

分享公共坐标空间(例如被同样的CSS transform影响)的渲染对象一般属于同一个显然层。页面中按照正确顺序复合进行展示重叠内容的元素,半透明元素等等都会生成渲染层。
对于一个特定的渲染对象有很多情形都会直接出发一个新的渲染层的生成。具体通过复写一些继承类实现的,定义在RenderBoxModelObject::requiresLayer()。一些通用的情形下肯定会出发渲染层的生成。

  • 页面的根对象
  • 有明确CSS定位属性的渲染对象(relative、absolute或者transform)
  • 有透明度
  • 会溢出的、有alpha遮罩或者映射的渲染对象
  • 有CSS过滤
  • 有3D(webGL)上下文或者2D加速上下文的对应canvas元素
  • 对应的video元素

注意渲染对象与渲染层不是完全一一对应的。特定的渲染对象或者关联着一个已经为这个渲染对象创建好的渲染层,或者关联着第一级祖先已经有了的渲染层。

渲染层也会形成一个树形结构。树的根节点就对应着页面根节点以及视觉上包含在这个父渲染层的每个子孙节点。每个渲染层节点的子节点会以升序方式保存在两个排序列表中,negZorderList和posZorderList,negZorderList保存着有负值z-indices子渲染层(即层级低于当前层级的渲染层),posZorderList保存着正值z-indices的子渲染层(即层级高于当前层级的渲染层)。

从渲染层到图形层

部分(不是全部)的渲染层通过自己的背景层实现图层的复合(拥有自己背景层的渲染层被广泛的视作复合层)。每个有复合层的渲染层有自己的图形层,或者使用初始元素的图形层。这种关系与渲染对象与渲染层的关系相似。

每个图形层都有一个图形上下文,这个上下文将关联的渲染层绘制到图形层中。复合图层最后负责将图形上下文的位图输出进行整合按照复合层的顺序输出到最终的屏幕图像上去。

虽然理论上每个单独的渲染层都可以通过一个单独的复合图层进行绘制,但是在实际处理中,这将是对内存的一种浪费(尤其是显存)。目前的Blink视线中,下面的任意条件都会触发渲染层拥有自己的复合层。

  • 具有3D或者透视的css属性
  • video
  • 有3D上下文或者2D加速上下文的canvas元素
  • 被用作复合插件的渲染层
  • 使用opacity或者webkit转换的css动画
  • css过滤器
  • 后代是复合层的
  • 在一个复合层之上

层的压缩

规则都是有例外的,正如上面所提到的,复合层在内存以及其他资源上是一种消耗(例如一些重要的操作的CPU时间复杂度是与图形树的尺寸成正比的)。

如果我们把固有的创建复合层的原因(比如使用3D转换)叫做直接原因。那么为了避免当大量元素使用直接原因导致“层爆炸”,多个渲染层同一个合成层重叠时,Blink将这些层压缩到一个背后的层中。这样就可以避免重叠导致的层次爆炸。

(后续。。。。)