文章目录
前言
上接:
- 【读书笔记】【WebKit 技术内 幕(一)】浏览器架构与浏览器内核;chromium、webkit和blink的渲染过程;chromium、webkit的架构与代码结构;webkit2架构
- 【读书笔记】【WebKit技术内 幕(二)】Chromium资源加载与网络栈、DOM树、HTML解释器、影子DOM、CSS解释器和样式布局、网页层次与渲染、绘图上下文、
Something great
- parser: 解析器
- AST :(Abstract Syntax Tree,抽象语法树)
- openGL :(Open Graphics Library,开放图形库);一个大部分平台都支持的 底层图形库的 API 标准。
- DirectX:(Direct eXtension,简称DX)微软的多媒体API。
- 沙箱(sandBox):操作系统对 进程的可访问的内存地址所做的限制。
- 渲染进程被 Sandbox 隔离,网页 web 代码内容必须通过 IPC 通道才能与浏览器内核进程通信,通信过程会进行安全的检查。
- 沙箱运行:渲染器在单独的进程中运行,通过沙箱限制其对系统资源(文件、网络、显示、击键)的访问,而须通过父浏览器进程访问
- 插件:浏览器的插件用于显示网页特定内容
- 扩展:浏览器的扩展用于增加浏览器新功能的软件或压缩包
- inspector:(web inspector,调试页面))
- DOM:(Document Object Model,文档对象模型)
- XSS(cross site security)
- CSS:(cascading style sheet,级联样式表)
- OpenGL ES:(OpenGL for Embedded Systems)是 OpenGL 三维图形API的子集
- WebGL:(Web Graphics Library)是一种3D绘图协议。
- 图形上下文:所有的绘图都由图形上下文(graphics context,GC)来完成。
- 表示的是图形绘制的平台。包含绘制参数以及需要执行一系列绘图命令的设备信息。图形上下文定义了包括绘制颜色、裁剪区域、曲线宽度以及绘制模式信息、文本字体信息、一些合成选项或者是一些其他的有关绘制的基本属性。
- < canvas >:是H5中最受欢迎的元素,在页面上划出一片区域,利用JS在上面画出图形,起初由Apple公司提出,各大浏览器厂商也对其做了不同程度上的实现。
- canvas中规定了了2D context和3D context(WebGL),目前绝大部分浏览器支持2D context。WebGL的发展还比较缓慢。
- WebGL:一套基于3D图形定义的JS接口。
- Skia 图形库:2D向量图形处理函数库,包含字型、坐标转换,以及点阵图都有高效能且简洁的表现。
第8章 硬件加速机制
硬件加速基础 —— ***
- 硬件加速:
- 这里的硬件加速就是使用GPU 硬件能力帮助渲染网页。 主要用于绘制3D图形。
- 对于GPPU绘图:
- 不像软件渲染:只是计算其中更新区域。
- 当更新时,如果没有分层,引擎可能需要重新绘制所有区域。因为计算更新部分对GPU来说可能更费时间。 (因为CPU和GPU,一个是通用计算,一个是专用计算)(GPU更适合大规模并发计算)
- 当分层后,部分更新只在几个层,只需要重新绘制那几个层,并将其和之前绘制完的层合成,使用了GPU的能力,也减少了重绘开销。
- 为了减少GPU内存使用,硬件加速机制在RenderLayer 树建立之后,做三件事情完成网页渲染:
- webkit 决定哪些 RenderLayer 对象组合,形成一个有后端存储的新层,新层不久后用于合成,称其为合成层。
- 一个RenderLayer 对象没有后端存储的新层,那么就使用它父亲使用的合成层。
- 每个合成层包含的RenderLayer 内容绘制在合成层的后端存储中。
- 合成器用于将多个合成层合成起来,形成网页最终可是结果:图片。
- 合成器 是一种能将多个合成层,按照前后顺序、合成层的3D变形等设置而合成一个图像的设施。
- webkit 决定哪些 RenderLayer 对象组合,形成一个有后端存储的新层,新层不久后用于合成,称其为合成层。
- 对于GPPU绘图:
- 这里的硬件加速就是使用GPU 硬件能力帮助渲染网页。 主要用于绘制3D图形。
- WebKit硬件加速设施 :
- 一个 RenderLayer 对象需要后端存储时,创建一个RenderLayerBacking 对象,负责RenderLayer 对象的各种存储。
- 如果一个 RenderLayer 对象被webkit 创建了后端存储,那么该RenderLayer 被称为合成层。
- 每个合成层有一个 RenderLayerBacking ,负责管理RenderLayer 所需所有后端存储。
- 为何要合成层:
- 合并一些 RenderLayer 层,减少内存使用。
- 合并后,减少合并带来的重绘性能和处理上的困难。
- 单独层能显著提升性能的,不会减少性能。
- GraphicsLayer 表示 RenderLayer 前景层、背景层需要的一个后端存储。
- 由RenderLayerCompositor 管理这些合成层,其需要计算和决定哪些RenderLayer 对象是合成层,并未合成层创建GraphicsLayer对象。
- 一个 RenderLayer 对象需要后端存储时,创建一个RenderLayerBacking 对象,负责RenderLayer 对象的各种存储。
- 硬件渲染过程:
- 软件渲染中,paintLayer 函数被递归调用,从RenderLayer 根节点开始,直到所有RenderLayer 对象都被遍历为止。
- 硬件加速机制中,加入了合成层,每个RenderLayer 对象被绘制到祖先链中最近的合成层。
- 最后一个步骤:渲染引擎将所有绘制完的合成层合成起来,由webkit 的移植来做。
- 软件渲染中,paintLayer 函数被递归调用,从RenderLayer 根节点开始,直到所有RenderLayer 对象都被遍历为止。
- 3D图形上下文:
- webkit 中的 3D图形上下文 主要是提供一组抽象接口,提供类似OpenGLES 的功能。用于使用OpenGL绘制3D图形的能力。
- 这一层抽象,将webkit 各个移植不同部分隐藏,webcore 可以使用统一接口。
- webkit 中,3D图形上下文 主要用途是WebGL。
- webkit 的GraphicsContext3D 类,是个抽象类,包含的接口所处理的对象是OpenGL中所提供的能力,如 针对纹理、着色器、纹理贴图、顶点等GL操作。
- GraphicsContext3D 接口三种类型:
- 所有移植共享实现的接口
- 一些移植能够共享实现的接口,可以直接调用OpenGL等。
- 跟每个移植相关。
- GraphicsContext3D 接口三种类型:
- webkit 中的 3D图形上下文 主要是提供一组抽象接口,提供类似OpenGLES 的功能。用于使用OpenGL绘制3D图形的能力。
Chromium的硬件加速机制 —— ***
-
GraphicsLayer的支持 :
- GraphicsLayer 对象是对一个渲染后端存储中某层的抽象。,webkit 移植中需要具体实现类支持功能。
-
* 这个过程是各种映射,目的是:**将webkit 的合成层,映射到合成器中的合成层。** * Blink 中就不需要这么多层次,因为Blink 仅被chromium 使用。
-
框架:
- chromium中,GPU进程负责所有GPU硬件加速操作。
- 包括GPU硬件的绘图和合成。
- 每个网页的 renderer进程,将3D绘图和合成操作,通过IPC传递给GPU进程,由其统一调度执行。
- android 中,没有GPU进程,是Browser进程中的GPU线程。
- Browser 进程也和GPU进程通信,用于创建GPU进程、提供网页渲染最后绘制的目标存储。
- GPU 进程同样提供3D图形能力接口给Pepper 插件使用。
-
- 包括GPU硬件的绘图和合成。
- WebKit 渲染引擎对GPU的使用:
-
WebKit 定义了两种类型图形上下文。都可以使用GPU加速,加速机制最后都是调用OpenGL/OpenGLES库。
-
-
chromium中的3D、2D图形上下文对应:3D图形上下文实现、Skia画布(canvas)。
- 他们调用(GL操作)之后被转换为IPC消息,传递GPU进程,通过解释器解释后,调用GL函数指针表中函数,这些函数指针是从GL库中获取。
- windwos下3D图形库是D3D,不是OpenGL接口,chromium使用通过ANGLE使用D3D来封装成OpenGL方式的接口。
- 他们调用(GL操作)之后被转换为IPC消息,传递GPU进程,通过解释器解释后,调用GL函数指针表中函数,这些函数指针是从GL库中获取。
-
chromium 快进程的硬件加速机制工作,GPU进程和renderer进程需要同步命令。
- 因为renderer进程需要等待GPU进程做好某些操作后才继续执行。(如?)
- GPU进程处理一些命令后,会报告当前状态,renderer进程检查状态和自己的期望来确定是否满足。
- GPU进程最终绘制结果不再像 软件渲染 那样通过 共享内存 传递给Browser进程,而是直接将页面内容 绘制在浏览器的标签窗口内。
-
- chromium中,GPU进程负责所有GPU硬件加速操作。
-
命令缓冲区 :
- 命令缓冲区(command buffer)主要用于GPU进程和GPU的调用进程 传递GL操作命令。
- chromium 使用共享内存,采用环形缓冲区的方式实现,采用基于GLES编码。
- 命令被分为命令头和命令体。命令头:长度+标识、命令体:立即操作数等。
- 长命令,采用桶(bucket)机制:共享内存的分块传输,最后合并到接收端的桶中。
- chromium 使用共享内存,采用环形缓冲区的方式实现,采用基于GLES编码。
- 命令缓冲区(command buffer)主要用于GPU进程和GPU的调用进程 传递GL操作命令。
-
Chromium合成器(Chromium Compositor):
- 架构:
- 合成器:将多个合成层合成,并输出一个最终结果。即合成网页划分后的合成层。
- 输入为多个合成层,每层都有一些属性(3D变换等)。输出为一个后端存储,如一个GPU的纹理缓冲区。
- 可以合成网页、用户界面、标签页。
- 合成器采用:表示和实现分离。
- webkit 对合成层的各种设置,最后都使用Layer 树表示,每个Layer树包含3D变形、裁剪等属性。
- 但是chromium 将这些属性应用到后端存储并合成,委托LayerImpl 树来完成。
- 通过代理 协调和同步两者之间的这些操作。
- 表示部分和实现部分,不同线程中,为线程化合成。在一个线程中,就是线程内合成。
- android 中还存在合成器在Browser 进程中,用于将网页结果和浏览器用户界面结合。
- 合成器:将多个合成层合成,并输出一个最终结果。即合成网页划分后的合成层。
- 基础设施:
- 为了支持chromium 的线程化合成和线程内合成,引入很多类来支持他们
- 包括了 事件处理部分、合成层的表示和实现、合成层组成两种类型的树、合成调度器、合成器的输出结果、各种后端存储等资源、支持动画和3D变形所需的基础设施。 这些一起构成了chromium 合成器。
- 代理是个抽象类,定义了Layer 树和LayerImpl树之前完成合成所需的转接工作。
- 为了支持chromium 的线程化合成和线程内合成,引入很多类来支持他们
- 瓦片话的后端存储:
- 和分层一样,减少成本,提高性能。
- GPU硬件支持有限的纹理大小
- 瓦片话存储,一部分变化,只需要重绘存在更新的瓦片。
- 层滚动时,大小相同的后端存储容易被重复利用。
- 和分层一样,减少成本,提高性能。
- 线程化合成模式才需要调度器。
- 合成过程: 由调度器调度。
- 创建输出结果的目标对象
Surface
,也就是合成结果的存储空间。 - 计算一个新的帧,包括计算滚动和缩放大小、动画计算、重新计算网页的布局、绘制每个合成层等。
- 将Layer 树 中包含这些变动同步到LayerImpl树中。
- 合成LayerImpl树中各个层并交换前后帧缓冲区,完成一帧的绘制和显示动作。
- 步骤1 在最开始时调用,后面网页出现动画护着JS代码修改CSS样式和DOM等情况,一般只执行后三个步骤或者最后一个步骤。
- 创建输出结果的目标对象
- 架构:
-
减少重绘:
- 重绘包括:计算布局、绘图、合成。合成耗时较短。
- webkit 减少重绘:
- 使用合适的网页分层技术,减少需要重新计算的布局和绘图。(层次化canvas 渲染优化技术)
- 使用CSS 3D变化和动画技术。
- CSS 3D 变形技术,让浏览器仅使用合成器来合成所有的层就可以达到动画效果。
- 计算每一帧时, JS代码首先设置元素3D属性、设置样式信息,chrome 只需要在随后使用合成功能。
- webkit 减少重绘:
- 重绘包括:计算布局、绘图、合成。合成耗时较短。
其他硬件加速模块
-
2D图形的硬件加速机制:
- 传统很多绘图操作都是针对2D图形,如边框、文字、图片、填充
- HTML5引入2D绘图的画布功能,提供2D绘图的JS 接口,使用JS代码来绘制2D图形。
- 2D绘图本身是使用2D图形上下文,一般使用软件方式绘制,也就是光栅化。
- 2D绘图也可以使用GPU,即3D绘图来完成。称为2D图形的硬件加速机制。
- 当前2D图形的硬件加速应用于:网页基本元素的绘制、以及HTML5新元素 **canvas,**用于绘制2D图形。
- 传统很多绘图操作都是针对2D图形,如边框、文字、图片、填充
-
2D图形上下文:
- 2D图形上下文 在webkit 的chromium 移植使用Skia 图形库完成2D图形操作。
- chromium 创建一个Skia 图形库中提供的SkCanvas 对象处理 Webkit 的2D图形操作请求 。
- 使用软件绘图还是GPU绘图,取决于SkCanvas 对象的设置。
- SkCanvas 类表示一个画布,2D图形操作在这个画布上处理,绘制结果也保存在SkCanvas 对象中。
- 当软件绘制时,使用光栅扫描的方法一一计算绘制像素结果,使用的是CPU内存,保存的是一个个像素值,如RBGA格式。
- GPU硬件来绘图,需要传入SkSurface_Gpu 对象。将2D图形操作转变成对GL的操作,使用GrContext的3D图形上下文绘制,存储在GPU的内存缓冲区。
- chromium 创建一个Skia 图形库中提供的SkCanvas 对象处理 Webkit 的2D图形操作请求 。
-
canvas 2D:
-
canvas 开始时只是个2D画布对象,用JS接口在画布上绘制任意2D图形。
- 后来提出在该元素上用JS绘制3D图形,即WebGL或者canvas 3D。
-
canvas 的
getContext
指定创建上下文对象的类型为2d
,返回一个2D图形上下文,提供绘制2D图形的各种应用程序编程接口。(如圆形、文字、图形变换、图片绘制及合成等) -
-
canvas 2D可以使用软件绘图,也可以使用GPU来加速绘图。
- 网页的canvas 2D 技术同样需要借助Skia 技术。
*- DOM树构建后,webkit 检查有无变化的CSS样式时,JS代码改变了canvas 元素属性,webkit 才会更新RenderObject 树和RenderLayer 树。
- 绘图时,chromium采用缓存模式处理JS代码的2D图形操作。
- chromium 绘制新帧时,Skia 图形库才会一次性提交并绘制缓存的操作。
- canvas 2D 机制由JS代码绘制2D图形,所以这时候的canvas 所在合成层已经绘制完成,这时候webkit 不需要绘制该层,只需要改变3D图形上下文的状态。
- chromium 采用的延迟思想,合并了很多2D绘图操作,提高了绘制的性能。
- 网页的canvas 2D 技术同样需要借助Skia 技术。
-
-
WebGL:
- 3D图形上下文: webcore 表示该上下文的抽象是 GraphicsContext3D。
- WebGL的实现:
- WebGL 是一套基于3D图形定义的JS接口,基于canvas,可以使用3D图形接口绘制各种3D图形。
- WebGL 被执行的每个GL调用直接传给GPU进程,没有缓存机制。
- WebGL 是一套基于3D图形定义的JS接口,基于canvas,可以使用3D图形接口绘制各种3D图形。
-
CSS 3D变形:
- CSS 3D变形和动画是HTML5 的新特性,CSS 3D变形用于对任意DOM子树做3D变形。
- WebGL是在一个canvas 元素内部绘制3D图形,CSS 3D变形可以对任何元素进行3D变形, 是可以被元素子女继承的属性。
- webkit 对应用变形技术的DOM子树使用单独合成层、硬件加速机制。
- 当JS代码修改元素变换属性值时,最后样式是设置在该合成层上,合成过程中,webkit 通过3D变形作用到该合成层上。
- 仅在第一次绘制元素内容,之后仅设置变换属性值,然后重新合成即可。
- CSS 3D变形和动画是HTML5 的新特性,CSS 3D变形用于对任意DOM子树做3D变形。
第9章 JavaScript引擎
JavaScript & JavaScript 引擎 & 渲染引擎
- JS 是脚本语言,主要用于Web 客户端,为了控制网页客户端的逻辑,如同用户交互、异步通信等。
- 是一种解释型语言( 最然后面不一样了),函数为第一等公民,可以被当参数或返回值来传递。
- JS是一种无类型语言: (动态类型语言)
- C++、JAVA,都是静态类型语言、强类型语言,编译时能确定每个变量类型。
- 事先已经知道所存取的成员变量类型,语言解释系统只需要利用数组和位移来存取这些变量和方法的地址。
- JS在运行时计算和决定类型。 根据实际传递的对象来计算。
- 对应变量为属性名-属性 对,这些属性名也会被存储(一般是字符串),访问时需要通过属性名匹配来获取相应的属性。
- 在执行阶段可以修改对象的属性(添加或删除属性本身)
- C++、JAVA,都是静态类型语言、强类型语言,编译时能确定每个变量类型。
- JIT(just - in - time) 技术:
- 为了提高JS运行速度,解决解释性语言的性能问题。
- 让JS执行环境,不仅仅解释这些内部表示,而将其中一些字节码转成本地代码(汇编),直接被CPU执行, 而不是解释执行。
- java虚拟机、javascriptcore、v8 都有使用 JIT。
- 为了提高JS运行速度,解决解释性语言的性能问题。
- 作用域链和闭包:
- 闭包:是一个拥有许多变量和绑定了这些变量的环境的表达式(如函数),因而这些变量也是该表达式的一部分)
- js使用作用域链实现闭包,作用域链由执行环境维护,JS中所有标识符都通过作用域链查找值。
- JS代码运行时,预先创建了一个全局的域,该域包含一个全局上下文, 包含了winddows、navigatoe 等内置对象。
- 进入函数时,执行环境为其创建一个上下文,指向其父上下文, 形成一个作用域链。
- 查找变量时,函数上下文中没有包含该变量,那么执行环境会不停向上查找。
- 右侧代码使用了匿名函数:
- 因为JS 全局函数在其他.js 文件中也可见,容易导致名字冲突和模块化问题。
- 匿名函数不会污染全局空间,同时内部函数根据作用域链,只在该匿名函数内有效,不会影响其他代码,这里使用的就是闭包技术。
- JavaScript引擎:
- JS 引擎:将JS代码处理并执行的运行环境。
- C/C++ :编译器编译成本地代码,被系统加载器加载执行,这些本地代码由OS调度CPU直接执行。
- Python 等解释型脚本语言:使用脚本解释器,将脚本加载后解释执行。
- java:经过编译器生成字节码, 字节码与平台无关,java 运行环境是java虚拟机,加载字节码使用解释器执行字节码。也引入JIT,将字节码变成本地代码提高执行效率。
- JavaScript:通常啊转换为AST解释执行,也有使用JIT技术提高执行效率。
- js 编译和执行分不开,在网页的加载和渲染过程中实施,对其处理时间有很高要求。
- JS 引擎:将JS代码处理并执行的运行环境。
- JS引擎包括:
- 编译器:编译为抽象语法树或字节码。
- 解释器:解释执行字节码,依赖垃圾回收机制。
- JIT工具:字节码转换为本地代码,依赖垃圾回收机制、
- 垃圾回收器和分析工具:负责垃圾回收、手机引擎信息。
- JS引擎包括:
- JavaScript引擎和渲染引擎 :
- js引擎提供接口给渲染引擎,让其处理JS代码。
- 渲染引擎根据桥接接口让JS访问DOM。
V8引擎 —— *
-
采用直接将JS编译成本地代码的方式。Node.js 就是基于V8。
- V8,只是个C++库而已。
- 代码结构:
- 接口使用:
- V8,只是个C++库而已。
- V8提供的D8工具,通过V8接口实现一个可执行程序,可以读入JS文件输出结果,提供调试JS的基础能力。
-
工作原理:
- V8中数据表示分为:内容和句柄。
- 句柄大小固定,存着指向数据的指针。
- 因为V8需要垃圾回收, 也需要移动数据内容,修改指针更简单,且使用的句柄还没变化。
- 存整数的话,就是高31位表示。对于其他类型,用高30位存指针,低2bits 存01 标识是地址。
- 因为存放都是4字节对齐,地址的低两位是00就可。
- 句柄大小固定,存着指向数据的指针。
- V8工作过程:
- 编译+运行,且有延迟思想,让有些代码的编译在运行时才发生,减少有些代码不会运行到的时间开销。
- 缺点:直接从AST到本地代码,无法从中间代码进行优化。
- 代码编译:
- parser:生成AST。
- FullCodeGenerator:生成本地代码。
- FullCodeGenerator因为本地代码和硬件平台相关,所以使用多个后端生成实际代码。
- 代码运行:
- 延迟编译:函数调用时,查找本地代码是否生成,没生成就触发生成。减少有些代码不会运行到的时间开销。
- 优化回滚:
- 编译器认为代码稳定,变量类型不变,生成高效的本地代码。
- 发现一些变量类型变化,就熬回滚之前的一般情况。
- 隐藏类和内存缓存:
- 参考C++,为JS对象构建类型信息,将原本的 通过字符串匹配来查找属性值 ->替换为使用C++编译器类似的 偏移地址机制实现。
- 将相似属性的对象,抽象出来一个类,但是类型变化后,就要将变化的对象重新抽象一个类
- 参考C++,为JS对象构建类型信息,将原本的 通过字符串匹配来查找属性值 ->替换为使用C++编译器类似的 偏移地址机制实现。
- 内存管理:
- 包括内存划分和JS垃圾回收。
- 包括内存划分和JS垃圾回收。
- 快照:
- 将内置对象和函数加载后的内存,保存并序列化,序列化的结果很好反序列化,减少启动时间。
- V8中数据表示分为:内容和句柄。
-
绑定和扩展:
- V8提供扩展:通过V8提供的基类Extension,扩展JS能力。
- 绑定:使用IDL文件、接口文件生成绑定文件,同V8引擎代码一起编译。
JavaScriptCore引擎
-
JavaScriptCore 最开始主要基于抽象语法树的解释器,后来重新实现了编译器、字节码解释器。后来将内嵌缓存、基于正则表达式的JIT 和简单的JIS引入。
-
代码结构:
-
数据表示:
- 同样使用句柄,用64标识,除了小整数外,直接可以存浮点类型。但是句柄占用空间较大。
-
-
模块:
- JavaScriptCore 使用了字节码的中间表示。加入了多层JIT编译器帮助改善性能。
- 因为有了字节码,就不需要JS源代码了。
- JavaScriptCore 使用了字节码的中间表示。加入了多层JIT编译器帮助改善性能。
-
内存管理:
- 像V8一样,引入了分代垃圾回收机制。
-
绑定: JavaScriptCore同样提供绑定机制,渲染引擎同样通过该机制访问DOM的操作函数。
实践——高效的JavaScript代码
- 编程方式: