• Home
  • Archives
  Evan的博客
  • Home
  • Archives
  • 面试
  • 原理笔记
  • 项目实践
  • 其他

CSS 硬件加速

2020/10/09 posted in  面试

什么是硬件加速(GPU 加速)

硬件加速也叫 gpu 加速,就是利用图像处理芯片(俗称显卡)提高渲染性能

浏览器渲染过程是这样的:
910706-20170208123830713-1326457716

JavaScript / CSS 修改元素之后,会开始计算 layout 布局,然后开始在各个元素所在的层进行绘制,所有层绘制完毕,将所有层合并

如果我们能绕开 layout 和 paint,也就是躲过回流和重绘,直接进入 composite 层,那渲染性能将大大提高。硬件加速就是利用 gpu 直接在 composite 层进行重组绘制,绕开 layout 和 paint,提升性能。

图形层和复合层

生成 DOM 树之后,结合 CSS 会生成渲染树,渲染树一个图形层 GraphicsLayers 负责绘制 ui 与渲染图形。

每个 GraphicsLayer 都有一个 GraphicsContext,GraphicsContext 绘图上下文的责任就是向屏幕进行像素绘制 (这个过程是先把像素级的数据写入位图中,然后由 GPU 负责位图的合成,最后再显示到显示器)。在 chrome 里,绘图上下文包裹了 Skia(chrome 自己的 2d 图形绘制库)

对于一些特殊的 DOM 元素(如 video 标签)或拥有与动画相关的 css3 属性,chrome 会将其定义为合成层 Compositing Layers,拥有单独的 GraphicsLayer。而其他不是合成层的渲染树节点,则和其第一个拥有 GraphicsLayer 的层公用一个 GraphicsLayer。

合理的利用合成层,就能利用独立的 GraphicsLayer 进行 GPU 绘制,避开回流和重绘。也就是所谓的硬件加速。

如何开启硬件加速

  • 目前下面这些因素都会引起 Chrome 创建合成层

    1. 3D 或透视变换 (perspective,transform) CSS 属性
    2. 使用加速视频解码的 video 元素
    3. 拥有 3D (WebGL) 上下文或加速的 2D 上下文的 canvas 元素
    4. 混合插件 (如 Flash)
    5. 对自己的 opacity 做 CSS 动画或使用一个动画 webkit 变换的元素
    6. 拥有加速 CSS 过滤器的元素
    7. 元素 A 有一个 z-index 比自己小的元素 B,且元素 B 是一个合成层(换句话说就是该元素在复合层上面渲染),则元素 A 会提升为合成层

    需要注意的是,创建一个新的合成层并不是免费的,它得消耗额外的内存和管理资源。实际上,在内存资源有限的设备上,合成层带来的性能改善,可能远远赶不上过多合成层开销给页面性能带来的负面影响。

  • 利用 will-change 属性也能开启硬件加速

    当我们通过某些行为(点击、移动或滚动)触发页面进行大面积绘制的时候,浏览器往往是没有准备的,只能被动使用 CPU 去计算与重绘,由于没有事先准备,应付渲染够呛,于是掉帧,于是卡顿。

    而 will-change 属性能告诉浏览器使用 gpu 进行计算。

    1. auto
      就跟 width:auto 一样,实际上没什么卵用,昨天嘛,估计就是用来重置其他比较屌的值。
    2. scroll-position
      告诉浏览器,我要开始翻滚了。
    3. contents
      告诉浏览器,内容要动画或变化了。
    4. 顾名思意,自定义的识别。非规范称呼,应该是 MDN 自己的称呼,以后可能会明确写入规范。比方说 animation 的名称,计数器 counter-reset, counter-increment 定义的名称等等。
    5. 可动画的一些特征值。比方说 left, top, margin 之类。移动端,非 transform, opacity 属性的动画性能都是低下的,所以都是建议避免使用 left/top/margin 之流进行唯一等。但是,如果你觉得自己是 margin 属性奶大的,非要使用之,试试加个 will-change:margin 说不定也会很流畅(移动端目前支持还不是很好)。

硬件加速需要注意的细节

虽然硬件加速能利用合成层进行渲染,但也是有坑的。

910706-20170407143632582-1052021913

上图的例子中,元素 B 应该在单独的合成层上,并且屏幕的最终图像应该在 GPU 上组成。B 元素在做动画,理所当然会被提升到合成层进行渲染,避免大量重绘。

但是 A 元素在 B 元素的顶部,我们没有指定提升 A 元素自身层级的东西。那么浏览器将会强制为元素 A 创建一个新的合成图层。这样,A 和 B 都被提升到单独的复合层。

可以看 一个例子

如果有一个元素,它的兄弟元素在复合层中渲染,而这个兄弟元素的 z-index 比较小,那么这个元素(不管是不是应用了硬件加速样式)也会被放到复合层中。

最可怕的是,浏览器有可能给复合层之后的所有相对或绝对定位的元素都创建一个复合层来渲染,于是就有了上面那个项目截图的那种效果。

因此,使用 GPU 加速提升动画性能时,最好给当前动画元素增加一个高一点的 z-index 属性,人为干扰复合层的排序,可以有效减少 Chrome 创建不必要的复合层,提升渲染性能。

« better-scroll 排坑指南

白屏优化与性能指标 »

Evan的博客

Evan 的博客 - 非典型码农,bug永动机
Instagram Weibo GitHub Email RSS

Categories

面试 原理笔记 项目实践 其他 JS Vue 性能优化 算法 计算机网络

Recent Posts

  • 从 HTTP 发展历程重学计算机网络
  • 应届前端的逆袭(中)
  • 应届前端的逆袭(上)
  • 应届前端的逆袭(下)
  • 前端面经复盘

Copyright © 2015 Powered by MWeb,  Theme used GitHub CSS.