webkit浏览器渲染影响因素分析

前言:浏览器的渲染对性能影响非常大,特别是在移动端页面,在宏观上,我们可以参考雅虎那20几条军规来操作,但在微观渲染层面,实际还没有一套相对成型的理论做为依据。

本文只是抛砖引玉,带大家进入微观的优化领域,实际在渲染优化这块上,还有很多技巧及方法需要大家去挖掘。本文写的也比较凌乱,望包涵!!

先来看个chrome timeline 工具上的一个图:

rendering1

在timeline上,我们看到有6种颜色的柱子,这6个类型的柱子构建了整个webkit浏览器的渲染过程。

简单的分类一下:蓝色表示加载,黄色表示脚本执行,紫色表示计算样式及布局,绿色表示绘制合成,白色表示空闲时间,灰色表示其他时间。这里主要看的是前面三个。

如下图:

rendering2

蓝色loading包含各种资源加载,在页面初始化加载阶段可以看到蓝色部分是耗时最长的,因为正在加载资源(加载html、css、js、img、flash、mp3等。)

Chrome上各个渲染部分的实际含义:

Parse Html:

发送一个http请求,获取请求的内容,然后解析html的过程。

Recalculate Style:

重新计算样式,它计算的是Style,和Layout做的事情完全不同。Layout计算的一个元素绝对的位置和尺寸,或者说是“Compute Layout”。

Recalculate被触发的时候做的事情就是处理JavaScript给元素设置的样式而已。Recalculate Style会计算出Render 树(渲染树),然后从根节点开始进行页面渲染,将CSS附加到DOM上的过程。

任何企图改变元素样式的操作都会触发Recalculate 。同Layout一样,它也是在JavaScript执行完成后才触发的。

Layout:

计算页面上的布局,即元素在文档中的位置及大小。如上面所说,Layout计算的是布局位置信息。任何有可能改变元素位置或大小的样式都会触发这个Layout事件,如width、height

Rasterizer:

光栅化,一般的安卓手机都会进行光栅化,光栅主要是针对图形的一个栅格化过程。低端手机在这部分耗时还蛮多的。

Paint:

页面上显示东西有任何变动都会触发Paint 。包括拖动滚动条、鼠标选中文字,等这些完全不改变样式,只改变显示结果的动作都会触发Paint。

Paint的工作就是把文档中用户可见的那一部分展现给用户。Paint是把Layout和Recalculate的计算的结果直接在浏览器窗体上绘制出来,它并不实现具体的元素计算。

Image Decode:

图片解码,将图片解析到浏览器上显示的过程。

Image Resize:

图片的大小设置,图片加载解析后,若发现图片大小并不是实际的大小(CSS改变了宽高),则需要Resize。Resize越大,耗时越久,所以尽量以图片的原始大小输出。

Composite Layers:

最后合并图层,输出页面到屏幕。浏览器在渲染过程中会将一些含义特殊样式的DOM结构绘制于其他图层,有点类似于Photoshop的图层概念。一张图片在Photoshop是由多个图层组合而成,而浏览器最终显示的页面实际也是有多个图层构成的。

有哪些因素会导致新建图层:

1、进行3D或者透视变换的CSS属性

2、使用硬件加速视频解码的<video>元素

3、具有3D(WebGL)上下文或者硬件加速的2D上下文的<canvas>元素

4、组合型插件(即Flash)

5、具有有CSS透明度动画或者使用动画式Webkit变换的元素

6、具有硬件加速的CSS滤镜的元素

在CSS里面,不同的属性会触发不同的layout或者paint,所以通过JS改变css的属性时,应该考虑到这些方面。如下图:

2

再引用另外一张图来看看CSS不同属性所触发的情况:

3

关于CSS属性的一个渲染问题,可以看下表,需翻墙查看: https://docs.google.com/spreadsheet/pub?key=0ArK1Uipy0SbDdHVLc1ozTFlja1dhb25QNGhJMXN5MXc&single=true&gid=0&output=html

如何优化渲染时间

1、为了确保页面的流程,必须保证60fps内不发生2次渲染树更新。 如下图,16ms内只发生如下几个操作则是正常及正确的 :

QQ截图20140817214845

2、页面滚动时,需要避免不必要的渲染及长时间渲染。

不必要的渲染包括:

1)position:fixed

fixed定位在滚动时会不停的进行渲染,特别是如果是页面顶部有个fiexd,页面底部有个类似返回顶部的fixed,则在滚动时会整个页面进行渲染,效率非常低。可以加 transform :  translateZ(0) ; 解决。

7

2) overflow:scroll

3) hover effects

有些:hover伪类在页面滚动时会不小心就触发到,如hover效果有阴影、圆角等比较耗时的属性时,建议页面滚动时,先取消hover效果,滚动停止后再加上hover效果。这个可以通过在外层加类名进行控制。

4) touch listeners

6

长时间渲染包括:

1)复杂的CSS

2)Image Decodes

这里特别是图片的Image Decodes及 Images Resize 这2个过程在移动端是非常耗时的,如下图:

4

3)Large empty layers(大的空图层,DIV)

5

参考: https://speakerdeck.com/addyosmani/velocityconf-rendering-performance-case-studies

【译】chrome – GPU加速的图层组合操作

原文:http://www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome

译注:(专业词汇对应翻译)

Node(节点)
DOM Tree(DOM树)
RenderObject(渲染对象)
RenderTree(渲染树)
GraphicsContext(图形环境对象、图形上下文对象)
一个Graphics Context默示一个绘制目标。它包含绘制体系用于完成绘制指令的绘制参数和设备相干信息。Graphics Context定义了根蒂根基的绘制属性,如色彩、裁减区域、线条宽度和样式信息、字体信息、混淆模式等。来自 <http://www.byywee.com/page/M0/S772/772925.html>
Bitmap位图
RenderLayers渲染图层
Alpha mask透明遮罩
Css filtercss滤镜
Software Rendering path软件渲染模式。译成“软件渲染路径”、“软件渲染路线”都可以,它是webkit渲染页面时的传统模式,不依赖硬件加速。
HWND返回窗体或控件的句柄(注意 OLE容器控件不支持该属性。句柄:是由操作环境定义的一个唯一的整数值,它被程序用来标识或者切换到对象,如窗体或控件等。)来自 <http://baike.baidu.com/link?url=knrqRLDl2voMqg6N6pq97dWrZGaxwR7G7gwW4HQyHzn6WGTflGg0nnms9afq7oARZ1hDaduE-81jnuxXQaGziq>
IPCIPC(Inter-Process Communication,进程间通信) 来自 <http://baike.baidu.com/view/373.htm>

概要

本文档阐述chrome浏览器“硬件加速图层组合”的背景和实现细节。

介绍

一般情况下,web浏览器完全依赖CPU进行网页内容的渲染。随着GPU在硬件设备(即便是很小的硬设)中的集成应用,以及富媒体例如视频、3D效果等在网页体验中扮演着越来越重要的角色,人们开始注意想方设法高效利用底层硬件获取更好的性能、电源节能。诚然,利用GPU直接对网页内容进行图层组合可以带来显著的性能提升(译注:这里的性能应该是指渲染速度方面)。最明显的例子莫过于video元素,它利用硬件进行视频解码,利用WebGL为H5 canvas提供硬件3D加速图形绘制渲染,期间占用的内存区域是CPU无法快速存取的。(译注:WebGL参考百科文档)

将网页图层组合的操作扔给GPU还有别的诸多好处,在大多数情况下,GPU在涉及大量像素的图形绘制、组合操作方面比CPU快的多,因为GPU是专门设计善干这类事情的硬件。此外,充分利用GPU做图形绘制、组合这类工作的同时,CPU就有更多的可以并行地做其他方面的运算,这种并行工作的方式创建了一个高效的图形管道。

Part 1  Webkit渲染基础及软件渲染模式

Webkit渲染引擎的源码巨大且复杂,晦涩难懂,几乎没啥米注释!为了弄清楚GPU在chrome里面如何工作,我们得先了解下webkit渲染网页的基本构件流程。下面我们从webkit如何工作作为基本出发点,回头再慢慢引入GPU影响渲染的过程,最终了解GPU如何工作。

节点和DOM树

在Webkit里面,网页内容内部存储为一个节点对象的树,我们称之为DOM树。网页上的每个标签包括标签之间的文本,各自对应DOM树上的一个节点。DOM树最上面的节点即Document节点。

从节点到渲染对象

DOM树里面的每个视觉可见的节点,均有一个相对应的渲染对象。在webkit内,渲染对象被存储成树形数据结构,和DOM树并行,我们称之为渲染树。渲染对象知道如何在显示介质(译注:例如显示器)上呈现(绘制)对应DOM树节点的内容,具体的方式是调用图形环境对象的绘制指令。而图形环境对象负责将像素写入到位图中去,最终被显示器显示出来。在chrome里面,图形环境对象封装了一个名为Skia的2D绘图库,于是大部分绘制指令被委托给SkCanvas或者SkPlatformCanvas执行。(关于chrome如何使用Skia库,可以参考这篇文档

在软件渲染模式里,整个页面只有一个图形环境对象,所有的渲染对象被绘制到这个共享的图形环境对象中。

从渲染对象到渲染图层

每个渲染对象通过其父级渲染对象直接或间接地关联一个渲染图层。

共享相同坐标空间的渲染对象(如被相同css变形影响的哪些对象)一般情况下属于同一个渲染图层。渲染图层的存在,使得页面的元素可以按正确的顺序进行组合,使得重叠的元素、半透明的元素等等得以恰当的渲染显示。触发webkit为渲染对象创建新的渲染图层的条件有很多,具体可见源码,在方法RenderBoxModelObject::requiresLayer()或者该方法在其他类的重载方法里可以窥其一二。总的来说,对于渲染对象,只要满足下面情形之一,均会致使新的渲染图层的创建。

  1. 它是页面的根对象。(译注:应该就是document节点对应的渲染对象)
  2. 它有显式的css 位置属性。(relative,absolute,或transform)
  3. 它是透明的
  4. 它有overflow,透明遮罩或阴影(反光)
  5. 它有css滤镜
  6. 它对应的节点是3D环境或2D加速环境的canvas元素
  7. 它对应的节点是video元素

值得注意的是,渲染对象和渲染图层并不是1对1的关系。一个渲染对象所关联的渲染图层,要么是为它自身创建的渲染图层,要么是它父级对象所关联的渲染图层(如果有多个有渲染图层的父对象,取最近的那个)。(译注:同一个树路径上的多个渲染对象可能会关联同一个渲染图层)。

渲染对象是树形结构,显然渲染图层也会是树形的。渲染图层树的根节点对应页面的根元素,每个图层节点的下行图层节点(子孙节点)对应该图层视觉包含的图层(译注:视觉包含不一定是dom树上的父子、祖孙节点)。每个图层节点的子图层节点(译注:是子节点不是子孙节点)被分类存储于两个升序排序的队列(negZOrderList和posZOrderList)中,队列negZOrderList包含z轴次序比当前图层低的图层(因而这类图层在渲染图层树的位置在当前图层下面),posZOrderList反之亦然。

凑到一起:我们有很多的树

总结下前面的内容,从概念上来看我们一共有3棵并行的树结构,各自在渲染过程中的作用不尽相同:

  1. DOM树(Dom Tree),是我们最基本的模型
  2.  渲染对象树 (RenderObject Tree),其节点和DOM树的可见节点是一对一的关系。渲染过程中,渲染对象知道如果绘制其对应的Dom节点。
  3.  渲染图层树(RenderLayer Tree),由渲染图层构成。其节点和渲染对象树的节点是1对多的关系,因为一个渲染对象关联的渲染图层要么是它自身的,要么是它父级对象的。渲染图层树体现了图层之间的z轴顺序。

软件渲染模式

从根本上来说,webkit渲染页面的时候,是从根图层节点开始,进而逐级递归遍历渲染图层树。对于渲染页面的过程,Webkit的代码库里有两种迥异的编码实现过程,一个是软件渲染过程,另一个是硬件加速的渲染过程,前者是传统的渲染模式。

在软件渲染模式中,网页渲染的时候被按照渲染图层树从上而下进行,(译注:图层树体现了图层之间的z轴顺序,所以从上而下实则为从里到外,z轴越小越先被渲染。另外这里的z轴顺序和css里面的z-index有关系但并没有对应关系的,css里面的z-index是元素的,这里指的是渲染图层的。)图层被渲染的复杂过程可以参考下源码里面的RenderLayer::paintLayer()方法,为简明起见下面从这个方法里面简化提取了几个基本的步骤:

  1. 如果图层有效性校验失败,直接退出该方法。【译注:原文是图层与已坏矩形区域(the damage rect,没上下文不知道怎么翻译这玩意)发生交错,则及早退出该方法】
  2. 递归调用该图层的negZOrderList队列中的子图层的paintLayer()方法
  3. 请求该图层关联的渲染对象去绘制自身。【译注:别忘了前面提到的,渲染对象知道如何在显示介质上绘制其对应的dom节点的内容】
  4. 这个请求渲染对象绘制自身的过程,是从创建了该渲染图层的那个渲染对象开始,依据渲染对象树,对其子节点进行递归遍历绘制处理。一旦发现某个渲染对象所关联的渲染图层不是当前被渲染的图层,则停止递归遍历绘制处理。
  5. 递归调用该图层的posZOrderList队列中的子图层的paintLayer()方法

在这个模式中,渲染对象调用一个共享的图形环境对象的绘制方法,将自身绘制到目标位图中,最终被显示器显示出来。【译注:可回头看看前面的“从节点到渲染对象”小节】

要注意的是,图形环境对象本身并没有图层的概念,为了正确绘制半透明效果的图层,需要做些特别的处理:半透明效果的渲染图层在请求其关联的渲染对象绘制自身之前,会先调用图形环境对象的beginTransparencyLayer()方法。在Skia图形库实现的图形环境对象中,调用beginTransparencyLayer()方法,到导致接下来相关的渲染对象绘制自身至新的位图中,当这些渲染对象绘制完毕后,会调用一个对应的endTransparencyLayer()方法。最后,beginTransparencyLayer方法内创建的位图对象,会和paintLayer()方法创建的位图对象组合在一起,最终被显示器显示出来。

【译注:半透明效果的渲染图层,其渲染绘制的过程大概是这样的:paintLayer() => beginTransparencyLayer() => 调用关联的渲染对象的绘制方法 => endTransparenceLayer(),paintLayer和beginTransparencyLayer都会导致在内存中创建位图对象,最后两个位图对象组合在一起最终显示出来。】

从Webkit到显示器

当所有的渲染图层被绘制到一个共享位图之后,该位图需要在显示器上被显示出来。在chrome里面,位图存储于共享内存中,经由IPC(进程间通讯)传输至浏览器进程。浏览器进程负责调用操作系统的视窗API(例如windows操作系统中的HWND),将位图绘制到网页对应的标签页或窗口中去。