Chrome 渲染优化 - 层模型 已翻译 100%

crossgate9 投递于 2013/04/30 23:03 (共 13 段, 翻译完成于 07-10)
阅读 7323
收藏 85
11
加载中

引言

对于大多数Web开发者而言,Web页面的基本模型就是DOM模型。页面的渲染过程常常并不为人所知,大家只是知道它是一个将页面的DOM表示方式转化为在屏幕上显示的一个图片的过程。近几年,现代浏览器利用图形卡对页面渲染方式进行了改进:这种改进一般都笼统地称为“硬件加速”。当谈及普通Web页面(也即:非Canvas2D或者WebGL页面)时,硬件加速这个词到底意味着什么?本文将对作为Chrome中硬件加速下Web页面渲染基础的基本模型进行说明。

冗长的注意事项

我们在这里讨论的是WebKit,更具体地讲,我们讨论的是Chromium下的WebKit。 本文所涉及的是Chrome的实现细节,而不是Web平台的特性。Web平台及其标准并没有对这个层次的实现细节做出详细的规定,所以我们并不能保证本文中的内容可以适用于其它的浏览器,但是对浏览器内部工作机理的了解定会有益于对应用进行高水平的错误调试和性能调优。

fbm
fbm
翻译于 2013/07/09 17:15
1

另外还要主意,整个这篇文章讨论的都是Chrome渲染架构中的一个核心部分,但这个结构仍在迅速变化之中。本文试图仅仅涉及那些不太可能发生变化的部分,但要是在6个月后再看这篇文章,那可就不一定是什么情况了。

很重要的是要明白Chrome当下有段时间还会有两个不同的渲染途径:硬件加速式的渲染和早期软件式的渲染。直到写这篇文章之时为止,在Windows、ChromeOS和Android下的Chrome中,所有的页面都走的是硬件加速途径。在Mac和Linux下, 只有那些部分内容需要组合的页面才会走硬件加速途径(更多关于什么才需要组合的说明请参见下文),但用不了多久,所有页面将都会走硬件加速途径。

最后要说的是,我们在这里的内容只是对Chrome的渲染引擎的管窥之见,所看到的只是其对性能影响比较大的一些特性。当你想要提高你的网站的性能时,对层模型有所了解会非常有帮助作用,但这也容易造成搬起石头砸自己脚的情况:层这种结构非常有用,但是创建过多的层会造成整个图形栈开销的增加。到时候可别说我没有提前警告过你哦!

fbm
fbm
翻译于 2013/07/09 17:50
1

从DOM到屏幕

层的引入

页面一旦在装入并解析完成后,就会表示为许多Web开发者所熟悉的结构:DOM。然而,在页面的渲染过程中,浏览器还具有一系列并不直接暴露给开发者的页面中间表示方式。这些表示方式中最重要的结构就是层。

在Chrome中实际上有几种不同类型的层:掌管DOM子树的渲染层(RenderLayer)以及掌管渲染层子树的图形层(GraphicsLayer)。 我们这里最关心的是后者,因为作为纹理上传到GPU之中的就是图形层。本文自此就只用“层”来指代图形层了。

fbm
fbm
翻译于 2013/07/09 18:07
1

稍稍离题说一下同GPU有关的几个术语:什么是纹理?纹理可以看作是位图图像,从主存储器(也就是RAM)传递到视频存储器(也就是GPU之上的VRAM)之中的就是这个图像。一旦传递到GPU之中后,你就能够将纹理映射到一个网格几何结构之上 —— 在视频游戏或者CAD程序中,这种技术用来给框架式的3D模型添加“皮肤”。Chrome采用纹理把页面中的内容分块发送给GPU。纹理能够以很低的代价映射到不同的位置,而且还能够以很低的代价通过把它们应用到一个非常简单的矩形网格中进行变形。这就是3D CSS的实现原理,而且这么做对页面在屏幕的快速滚动也非常好 —— 现在先说这些,这两方面更详细的探讨情况下文。

下面让我们用几个例子来说明层的概念。

fbm
fbm
翻译于 2013/07/09 18:23
1

在Chome中研究层时有一个非常有用的工具就是Chrome的开发者工具里设置(也就是那个小齿轮图标)中“渲染(rendering)”小标题下的“显示层的组合边界(show composited layer borders)”开关。让我们把这个开关打开。本文中所有的截屏和例子都来自最新版的Chrome Canary,在写这篇文章的时候是Chrome 27。

图1: 只有一层的页面 (将在新窗口中打开)

<html>
  <body>
    <div>I am a strange root.</div>
  </body>
</html>

译者注:这里缺了一个图,原文中的图就看不到,可能是需要翻墙?
在页面的基本层中组合层的渲染边界屏幕截图

这个页面只有一层。蓝色的栅格表示的是分块,这些分块可以看作是比层更低一级的单位,Chrome以这些分块为单位,一次向GPU上传一个分块的内容。这里它们并不怎么重要。

fbm
fbm
翻译于 2013/07/09 18:49
1

图2:有自己的层的元素 (open stand-alone)

<html>
  <body>
    <div style="-webkit-transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
      I am a strange root.
    </div>
  </body>
</html>


译者注:这里缺了一个图,原文中的图就看不到,可能是需要翻墙?
旋转后层的渲染边界的截屏

在<div>上加上让它旋转一个角度的3D CSS属性后,我们就能够看到一个元素有自己的层是个什么样子:请注意其中的橘色边界,这个边界给出了这个视图中层的轮廓。

fbm
fbm
翻译于 2013/07/09 18:58
1

层的创建准则

还要其它什么元素会得到自己的层?Chrome在这方面采用的规则仍在随着时间推移逐渐发展变化,但在目前下面这些因素都会引起Chrome创建层:

  • 进行3D或者透视变换的CSS属性
  • 使用硬件加速视频解码的<video>元素
  • 具有3D(WebGL)上下文或者硬件加速的2D上下文的<canvas>元素
  • 组合型插件(即Flash)
  • 具有有CSS透明度动画或者使用动画式Webkit变换的元素
  • 具有硬件加速的CSS滤镜的元素
  • 子元素中存在具有组合层的元素的元素(换句话说,就是存在具有自己的层的子元素的元素)
  • 同级元素中有Z索引比其小的元素,而且该Z索引比较小的元素具有组合层(换句话说就是在组合层之上进行渲染的元素)

实际意义:动画

我们还可以将层在页面中到处移动,正好可用于动画。

图3: 动画层(将在新窗口中打开

<html>
<head>
    <style type="text/css">
    div {
        -webkit-animation-duration: 5s;
        -webkit-animation-name: slide;
        -webkit-animation-iteration-count: infinite;
        -webkit-animation-direction: alternate;
        width: 200px;
        height: 200px;
        margin: 100px;
        background-color: gray;
    }
    @-webkit-keyframes slide {
        from {
            -webkit-transform: rotate(0deg);
        }
        to {
            -webkit-transform: rotate(120deg);
        }
    }
    </style>
</head>
<body>
    <div>I am a strange root.</div>
</body>
</html>
fbm
fbm
翻译于 2013/07/09 22:18
1

正如前文所述,层在将Web页面中的静态内容随处移动方面真的很有用。在最基本的情况下,Chrome将层中的内容绘制到软件位图中,然后再将该位图作为纹理上载到GPU之中。如果层中的内容将来不会发生变化,那就不需要对其进行重绘了。这是个好事:重绘将会占用本可以用于干其它事情,比如运行JavaScript的时间,而且如果绘制过程太长,动画还会出现卡顿现象。

例如,可以在Chrome的开发工具中看一下这个页面的时间线:但该层在来回旋转的时候,并没有出现绘制操作。

动画过程中的开发者工具时间线屏幕截图

fbm
fbm
翻译于 2013/07/09 23:05
2

无效!重绘

但是如果层中的内容发生了改变,它就需要重绘了。

图4:层的重绘 (将在新窗口打开)

<html>
<head>
  <style type="text/css">
  div {
      -webkit-animation-duration: 5s;
      -webkit-animation-name: slide;
      -webkit-animation-iteration-count: infinite;
      -webkit-animation-direction: alternate;
      width: 200px;
      height: 200px;
      margin: 100px;
      background-color: gray;
  }
  @-webkit-keyframes slide {
      from {
          -webkit-transform: rotate(0deg);
      }
      to {
          -webkit-transform: rotate(120deg);
      }
  }
  </style>
</head>
<body>
  <div id="foo">I am a strange root.</div>
  <input id="paint" type="button" onclick="" value=”repaint”>
  <script>
    var w = 200;
    document.getElementById('paint').onclick = function() {
      document.getElementById('foo').style.width = (w++) + 'px';
    }
  </script>
</body>
</html>

每次点击按钮后,旋转中的元素的宽度就会增加1px。这将导致页面重新布局,整个元素都需要重绘,在这个例子中需要重绘的是整个层。

查看Chrome重绘了哪些元素的一个很好的办法是使用开发者工具中的“显示绘制矩形”开关,这个开关同样也在开发者工具设置中的“渲染”标题下。打开该开关后,在点击按钮的时候请注意动画中的元素和按钮周围都会有一个红色的矩形闪现。

Screenshot of show paint rects checkbox显示绘制矩形检查框的屏幕截图
fbm
fbm
翻译于 2013/07/10 01:14
2

同时绘制时间也出现在开发者工具里的时间线中了。明眼的读者可能还注意到这里有两个绘制事件:一个是层的,另外一个是按钮的。按钮会在它的状态变成按下状态和从按下状态变为非按下状态时需要进行重绘。

Screenshot of Dev Tools Timeline repainting a layer开发者工具时间线中层的重绘的屏幕截图

请注意Chrome并不总是需要重绘整个层,它会尽量地以聪明的方式只绘制DOM中发生变化的那部分内容。在我们的这个例子中,我们所改变的DOM元素是整个层的尺寸。但是在很多情况下,在一层中会有大量的DOM元素。

fbm
fbm
翻译于 2013/07/10 01:24
2
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(8)

damon708
damon708

引用来自“damon708”的评论

译者注:这里缺了一个图,原文中的图就看不到,可能是需要翻墙?

我这有原图,可以发给你
https://my.oschina.net/u/3261932/blog/835418
damon708
damon708
译者注:这里缺了一个图,原文中的图就看不到,可能是需要翻墙?

我这有原图,可以发给你
马知常
马知常
+1
MrCong
MrCong
学习了。
vingzhang
vingzhang
不懂前端的我看不懂
刘德洪Sofire
刘德洪Sofire
原图是放到Google墙后面了。
asdton
asdton
不错!有收获!!!
贾珣
贾珣
+1
返回顶部
顶部