前端性能-合成层

Posted natsu-cc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了前端性能-合成层相关的知识,希望对你有一定的参考价值。

前言:

页面性能是前端开发特别需要关注的重点,评判前端 Web 页面性能的指标有很多,页面的流畅度是其中比较重要一点,有时候我们发现我们的页面在使用了某些属性或者进行某种操作之后,浏览器的渲染的流畅性出现问题,一般情况是渲染线程出现了问题,出现大量的reflow和repaint,页面的渲染过程这里就不过多描述了,有兴趣可以翻看之前的恩张。这里就来跟大家探讨出现的原因和如何避免这种情况。

Composite:

Composite(渲染层合并):对页面中 DOM 元素的绘制是在多个层上进行的。在每个层上完成绘制过程之后,浏览器会将所有层按照合理的顺序合并成一个图层,然后显示在屏幕上。对于有位置重叠的元素的页面,这个过程尤其重要,因为一旦图层的合并顺序出错,将会导致元素显示异常。

注意:这里讨论的是 WebKit,描述的是 Chrome 的实现细节,而并非是 web 平台的功能,所以只是举个案例。

  • Chrome 拥有两套不同的渲染路径(rendering path):硬件加速路径和旧软件路径(older software path);
  • Chrome 中有不同类型的层: RenderLayer(负责 DOM 子树)和GraphicsLayer(负责 RenderLayer的子树),只有 GraphicsLayer 是作为纹理(texture)上传给GPU的。
  • 纹理——可以把它想象成一个从主存储器(例如 RAM)移动到图像存储器(例如 GPU 中的 VRAM)的位图图像(bitmapimage);
  • Chrome 使用纹理来从 GPU上获得大块的页面内容。通过将纹理应用到一个非常简单的矩形网格就能很容易匹配不同的位置(position)和变形(transformation)。这也就是3DCSS 的工作原理,它对于快速滚动也十分有效。

Chrome的层类型:

  • RenderLayers 渲染层,这是负责对应 DOM 子树。
  • GraphicsLayers 图形层,这是负责对应 RenderLayers子树。
渲染层合成:

在 DOM 树中每个节点都会对应一个渲染对象(RenderObject),当它们的渲染对象处于相同的坐标空间(z 轴空间)时,就会形成一个 RenderLayers,也就是渲染层。渲染层将保证页面元素以正确的顺序堆叠,这时候就会出现层合成(composite),从而正确处理透明元素和重叠元素的显示。

从浏览器的渲染过程中我们知道,页面 html 会被解析成 DOM 树,每个 HTML 元素对应了树结构上的一个 node 节点。而从 DOM 树转化到一个个的渲染层,并最终执行合并、绘制的过程,中间其实还存在一些过渡的数据结构,它们记录了 DOM 树到屏幕图形的转化原理,其本质也就是树结构到层结构的演化。

渲染对象(RenderObject)

一个 DOM 节点对应了一个渲染对象,渲染对象依然维持着 DOM 树的树形结构。一个渲染对象知道如何绘制一个 DOM 节点的内容,它通过向一个绘图上下文(GraphicsContext)发出必要的绘制调用来绘制 DOM 节点。

渲染层(RenderLayer)

这是浏览器渲染期间构建的第一个层模型,处于相同坐标空间(z 轴空间)的渲染对象,都将归并到同一个渲染层中,因此根据层叠上下文,不同坐标空间的的渲染对象将形成多个渲染层,以体现它们的层叠关系。所以,对于满足形成层叠上下文条件的渲染对象,浏览器会自动为其创建新的渲染层。能够导致浏览器为其创建新的渲染层的。

几类常见的情况:

  • 根元素 document;
  • 有明确的定位属性(relative、fixed、sticky、absolute);
  • opacity < 1;
  • 有 CSS fliter 属性;
  • 有 CSS mask 属性;
  • 有 CSS mix-blend-mode 属性且值不为 normal;
  • 有 CSS transform 属性且值不为 none;
  • backface-visibility 属性为 hidden;
  • 有 CSS reflection 属性;
  • 有 CSS column-count 属性且值不为 auto 或者有 CSS column-width 属性且值不为 auto;
  • 当前有对于 opacity、transform、fliter、backdrop-filter 应用动画;
  • overflow 不为 visible;

DOM 节点和渲染对象是一一对应的,满足以上条件的渲染对象就能拥有独立的渲染层。

图形层(GraphicsLayer)

GraphicsLayer 其实是一个负责生成最终准备呈现的内容图形的层模型,它拥有一个图形上下文(GraphicsContext),每个GraphicsLayer(合成层单独拥有的图层) 都有一个 GraphicsContext,GraphicsContext 会负责输出该层的位图,位图是存储在共享内存中,作为纹理上传到 GPU 中,最后由 GPU 将多个位图进行合成,然后显示到屏幕上。

合成层(CompositingLayer)

RenderLayers 来保证页面元素以正确的顺序合成,这时候就会出现层合成(composite),从而正确处理透明元素和重叠元素的显示。满足某些特殊条件的渲染层,会被浏览器自动提升为合成层。合成层拥有单独的 GraphicsLayer,而其他不是合成层的渲染层,则和其第一个拥有 GraphicsLayer 的父层共用一个。

合成层创建标准:

  • 3D 或透视变换(perspective transform) CSS 属性;
  • 使用加速视频解码的 元素 拥有 3D;
  • (WebGL) 上下文或加速的 2D 上下文的 元素;
  • 混合插件(如 Flash);
  • 对自己的 opacity 做 CSS动画或使用一个动画变换的元素;
  • 拥有加速 CSS 过滤器的元素;
  • 元素有一个包含复合层的后代节点(换句话说,就是一个元素拥有一个子元素,该子元素在自己的层里);
  • 元素有一个z-index较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染);

合成层的优点:

renderLayer提升为合成层就会有自己的绘图上下文,并且会开启硬件加速,有利于性能提升。

  • 合成层的位图,会交由 GPU 合成,比 CPU 处理要快;
    提升到合成层后合成层的位图会交GPU处理仅仅只是合成的处理(把绘图上下文的位图输出进行组合)需要用到GPU,生成合成层的位图处理(绘图上下文的工作)是需要CPU。
  • 当需要 repaint 时,只需要 repaint 本身,不会影响到其他的层;
    repaint的时候可以只repaint本身,不影响其他层,但是paint之前还有style, layout,那就意味着即使合成层只是repaint了自己,但style和layout本身就很占用时间。
  • 对于 transform 和 opacity 效果,不会触发 layout 和 paint;

总结合成层的优势:一个元素开启硬件加速后会变成合成层,可以独立于普通文档流中,改动后可以避免整个页面重绘,有利于性能的提升。

性能优化方案:

  • 减少动画元素对其他元素的影响,从而减少paint,我们需要把动画效果中的元素提升为合成层。
    较好的方式will-change 设置为opacity、transform、top、left、bottom、right 可以将元素提升为合成层。
  • 使用 transform 或者 opacity 来实现动画效果, 这样只需要做合成层的合并。
  • 减少绘制区域 对于不需要重新绘制的区域应尽量避免绘制,以减少绘制区域。
注意:
  • 合成层占用内存的问题(每个合成层都占用额外的内存)。
  • 层爆炸
    由于某些原因可能导致产生大量不在预期内的合成层,虽然有浏览器的层压缩机制,但是也有很多无法进行压缩的情况,这就可能出现层爆炸的现象。(我们经常用translateZ(0)等属性来进行所谓的硬件加速,以提升性能,达到优化页面动态效果的目的。)
    最佳方案是打破 overlap 的条件,也就是说让其他元素不要和合成层元素重叠。简单直接的方式:使用3D硬件加速提升动画性能时,最好给元素增加一个z-index属性,人为干扰合成的排序,可以有效减少chrome创建不必要的合成层,提升渲染性能,移动端优化效果尤为明显。
  • 绘制的图层必须传输到 GPU,层的数量和大小达到一定量级后,可能会导致传输非常慢。

总结:

  • 动画使用 transform和opacity 实现
  • 适当使用合成层
  • 减少隐式合成
  • 减小合成层的尺寸

以上是关于前端性能-合成层的主要内容,如果未能解决你的问题,请参考以下文章

浏览器极致性能调优之——浏览器合成与渲染层优化

浏览器层合成与页面性能优化

性能优化-合成层

[JSOI2008]魔兽地图

优化web前端性能的几个方法

前端能实现的性能优化