硬件加速

前言

谈起浏览器的硬件加速,想必你们都知道的一个技巧就是在用CSS3作动画时,给元素添加transform: translateZ(0)或者transform: translate3d(0, 0, 0)就会开启GPU的硬件加速,将原本应该是浏览器处理的动画效果转交给GPU处理,从而使得动画看起来更加顺畅,在移动端体验更好。本文将进一步探索其中的奥秘,例如哪些条件能够触发GPU硬件加速?硬件加速背后的工做原理是什么?是否是开启GPU硬件加速的动画应该越多越好?
首先让咱们来看一个动画效果,经过CSS3的animation属性来实现让一个小球从坐到右移动200px的距离。如今有两种实现方式:javascript

第一种方法经过改变该元素的top属性来实现:css

.ball1{
  width:100px;
  height:100px;
  border-radius: 100px;
  background:red;
}
.ball1{
  animation:mymove 3s infinite linear;
  -moz-animation:mymove 3s infinite linear;
  -webkit-animation:mymove 3s infinite linear;
  -o-animation:mymove 3s infinite linear;
}

@keyframes mymove{
   from {top:0px;}
   to {top:200px;}
}

第二种方法经过translate来实现:html

.ball1{
   animation:translateMove 3s infinite linear;
   -moz-animation:translateMove 3s infinite linear;
   -webkit-animation:translateMove 3s infinite linear;
   -o-animation:translateMove 3s infinite linear;
}
@keyframes translateMove {
   from {transform: translate3d(0,0,0);}
   to {transform: translate3d(0,200px,0)}
}

两种方式都能达到一样的效果,但浏览器在内部渲染的过程却大不相同。哪一种实现方式更优?在回答这个问题以前,咱们先来了解一下浏览器的渲染过程。java

浏览器渲染过程

已知JS是单线程工做的,可是浏览器能够开启多个线程,渲染一个网页须要两个重要的线程来共同完成:Main Thread 主线程 Compositor Thread 合成器线程css3

主线程作的工做:

运行JS
计算 HTML 元素的 CSS 样式
布局页面
将元素绘制到一个或多个位图中
把这些位图交给 Compositor Thread 来处理

合成器线程作的工做:

经过 GPU 将位图绘制到屏幕上
通知主线程去更新页面中可见或即将可见的部分的位图
计算出页面中那些部分是可见的
计算出在滚动页面时候,页面中哪些部分是即将可见的
滚动页面时将相应位置的元素移动到可视区

在了解了这两个线程各自负责的部分以后,咱们再来看浏览器的渲染过程。大致流程以下:web

clipboard.png


当咱们经过某种方法引发浏览器的reflow时,须要从新经历style和layout阶段,致使浏览器从新计算页面中每一个dom元素的尺寸及从新布局,伴随着从新进行repaint,这个过程是很是耗时的。为了把代价降到最低,固然最好只留下composite这一个步骤最好。假设当咱们改变一个容器的样式时,影响的只是它本身,而且还无需重绘,直接经过在GPU中改变纹理的属性来改变样式,岂不是更好?chrome

clipboard.png

如何能使元素达到这个效果?就是让元素拥有本身的层(layer)。有了层的概念,让咱们从层的概念再来看浏览器的渲染过程:canvas

  1. 获取 DOM 并将其分割为多个层(RenderLayer)
  2. 将每一个层栅格化,并独立的绘制进位图中
  3. 将这些位图做为纹理上传至 GPU
  4. 复合多个层来生成最终的屏幕图像(终极layer)

能够将这个过程理解为设计师的Photoshop文件。在ps源文件里,一个图像是由若干个图层相互叠加而展现出来的。分红多个图层的好处就是每一个图层相对独立,修改方便,对单个图层的修改不会影响到页面上的其余图层。所以层(layer)存在的意义在于:用最小的代价来改变某个页面元素。能够将某个css动画或某个js交互效果抽离到一个单独的渲染层,来达到加速渲染的目的。浏览器

那么如何才能建立一个层呢?

  • 3d transform属性
  • backface-visibility为hidden的元素
  • 使用加速视频解码的 <video> 元素
  • 拥有 3D (WebGL) 上下文或加速的 2D 上下文的 <canvas> 元素
  • 混合插件(如 Flash)
  • 对 opacity、transform、fliter、backdrop-filter 应用了 animation 或者 transition(须要是 active 的 animation 或者 transition,当 animation 或者 transition 效果未开始或结束后,合成层也会失效)
  • will-change 设置为 opacity、transform、top、left、bottom、right(其中 top、left 等须要设置明确的定位属性,如 relative 等)
  • 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在本身的层里)
  • 元素有一个 z-index 较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)

在webkit内核的浏览器中,若是有上述状况,就会建立一个独立的层(layer)。咱们能够借助chrome浏览器开发者工具中的layers和rendering结合来查看页面中有哪些独立的层。性能优化

性能分析

如今回到文章开始的那个动画效果,让咱们经过chrome的performance工具来看看具体的执行过程。(注:本次性能分析是将CPU性能将至原来的六分之一 模拟移动端的效果进行分析的。具体操做可将performance工具中的CPU选择6*slowdown)
经过改变top属性:

clipboard.png

clipboard.png

经过改变transform属性:

clipboard.png

clipboard.png

从上图能够看出,运动的元素如何没有独立的层,每一帧的绘制都须要通过不停的rendering和painting过程。

但硬件加速是把双刃剑,过渡的使用硬件加速会拔苗助长。其影响表如今:

  1. 内存。建立一个新的渲染层,须要消耗额外的内存和管理资源,若是渲染层的个数过多,很容易引发内存问题,这一点在移动端浏览器上尤其明显,能够引发电池耗电量的上升,下降电池的寿命。因此,必定要牢记不要让页面的每一个元素都使用硬件加速,当且仅当须要的时候才为元素建立渲染层。
  2. 使用GPU渲染会影响字体的抗锯齿效果。文本在动画期间有可能会显示的模糊。

参考文档

  1. 无线性能优化:composite
  2. CSS动画及硬件加速
  3. Javascript高性能动画与页面渲染
  4. chrome渲染优化-层模型
相关文章
相关标签/搜索