浏览器内核之渲染基础(下)

简介: 浏览器内核之渲染基础(下)

1.3 渲染方式


1.3.1 绘图上下文(GraphicsContext)


RenderObject 对象是用什么来绘制内容的呢?在 WebKit 中,绘图操作被定义了一个抽象层,就是绘图上下文,所有绘图的操作都是在该上下文中来进行的。


绘图上下文可以分成两种类型:一,是 2D 图形上下文(GraphicsContext),用来绘制 2D 图形的的上下文;二是 3D 绘图上下文,是用来绘制 3D 图形的上下文。


这两种上下文都是抽象基类,它们只提供接口,因为 WebKit 需要支持不同的移植。而这两个抽象基类的具体绘制则由不同的移植提供不同的实现,每个移植使用的实际绘图类非常不一样,依赖的图形率也不一样。


2D 绘图上下文的具体作用就是提供基本绘图单元的绘制接口以及设置绘图的样式。绘图接口包括画点,画线、画图片、画多边形、画文字等,绘图样式包括颜色、线宽、字号大小、渐变等。RenderObject 对象知道自己需要画什么样的点,什么样的图片,所以 RenderObject 对象调用绘图上下文的这些基本操作就是绘制实际的显示结果。关系看 图 7-8 。


微信图片_20220512122613.png


关于 3D 绘图上下文,它的主要用处是支持 CSS3D、WebGL 等。

在现有的网页中,由于 HTML5 标准引入了很多新的技术,所以同一网页中可能既需要使用 2D 绘图上下文,也需要使用 3D 绘图上下文。对于 2D 绘图上下文来说,其平台相关的实现既可以使用 CPU 来完成 2D 相关的操作,也可以使用 3D 图形接口(如 OpenGL)来完成 2D 的操作。而对于 3D 绘图上下文来说,因为性能问题,WebKit 的移植通常都是使用 3D 图形接口(如 OpenGL 或者 Direct3D 等技术)来实现。

1.3.2 渲染方式

在完成构建 DOM 树之后,WebKit 会构建渲染的内部表示并使用图形库将这些模型绘制出来。 网页的渲染方式,有三种方式,一是软件渲染,二是硬件加速渲染,三是混合模式。


每个 RenderLayer 对象可以被想象成图像中的一个层,各个层一同构成了一个图像。在渲染的过程中,浏览器也可以作同样的理解。每个层对应网页中的一个或者一些可视元素,这些元素都绘制内容到该层上,在本书中,一律把这一过程称为绘图操作。


如果绘图操作使用 CPU 来完成,称之为软件绘图。如果绘图操作由 GPU 来完成,称之为 GPU 硬件加速绘图。理想情况下,每个层都有个绘制的存储区域,这个存储区域用来保存绘图的结果。最后,需要将这些层的内容合并到同一个图像之中,本书称之为合成(Compositing),使用了合成技术的渲染称之为合成化渲染。


所以在 RenderObject 树和 RenderLayer 树之后,WebKit 的机制操作将内部模型转换成可视的结果分为两个阶段:每层的内部进行绘图工作及之后将这些绘图的结果合成一个图像。对于软件渲染机制,WebKit 需要使用 CPU 来绘制每层的内容,而软件渲染机制是没有合成阶段的,因为没有必要,在软件渲染中,通常渲染的结果就是一个位图(Bitmap),绘制每一层的时候都使用该位图,区别在于绘制的位置可能不一样,当然每一层都按照从后到前的顺序。当然,你也可以为每层分配一个位图,问题是,一个位图就已经能够解决所有的问题。


微信图片_20220512122628.png


从上图可能看到,软件渲染中网页使用的一个位图,实际上就是一块 CPU 使用的内存空间。而图中的第二和第三种方式,都是使用了合成化的渲染技术,也就是使用 GPU 硬件来加速合成这些网页层,合成的工作都是由 GPU 来做,称为硬件加速合成(Accelerated Compositing)。但是,对于每个层,这两种方式有不同的选择,其中某些层,第二种方式使用 CPU 来绘图,另外一些层使用 GPU 来绘图。对于使用 CPU 来绘图的层,该层的结果首先当然保存在 CPU 内存中,之后被传输到 GPU 的内存中,这主要是为了后面的合成工作。第三种渲染方式使用使用 GPU 来绘制所有合成层。第二和第三种方式其实都属于硬件加速渲染方式。前面的这些描述,是把 RenderLayer 对象和实际的存储空间对应,现实中不是这样的,这只是理想的情况。


渲染的基本知识:


首先,对于常见的 2D 绘图操作,使用 GPU 来绘图不一定比使用 CPU 绘图在性能上有优势,例如绘制文字、点、线等,原因是 CPU 的使用缓存机制有效减少了重复绘制的开销而且不需要 GPU 并行性。


其次,GPU 的内存资源相对 CPU 的内存资源来说比较紧张,而且网页的分层使得 GPU 的内存使用相对较多。


所以就目前的情况来看,三者的存在是有其合理性的。


1.4 WebKit 软件渲染技术


1.4.1 软件渲染过程

在很多情况下,也就是没有那些需要硬件加速内容的时候,WebKit 可以使用软件渲染技术来完成页面的绘制工作(除非读者强行打开硬件加速机制),目前用户浏览的很多门户网站、论坛网站、社交网站等所设计的网页,都是采用这项技术来完成页面的渲染。

而软件渲染过程需要关注两个方面,一是 RenderLayer 树,二是每个 RenderLayer 所包含的 RenderObject 树。WebKit 遍历 RenderLayer 树来绘制各个层。

对于每个 RenderObject 对象,需要三个阶段绘制自己。


一是绘制该层中所有块的背景和边框

二是绘制浮动内容

三是前景(Foreground),也就是内容部分、轮廓等部分。当然,每个阶段还可能会有一些子阶段。


值得指出的是,内嵌元素的背景、边框、前景等都是在第三阶段中被绘制的。

图 7-10 描述了一个 RenderLayer 层是如何绘制自己和子女的,这过程是一个递归过程。


且是一个大致的过程。


微信图片_20220512122721.png


微信图片_20220512122736.png


最开始的时候,也就是 WebKit 第一次绘制网页的时候,WebKit 绘制的区域等同于可视区域大小。而这在之后,WebKit 只是首先计算需要更新的区域,然后绘制同这些区域有交集的 RenderObject 节点。也就是说,如果更新区域跟某个 RenderLayer 节点有交集,WebKit 会断续查找 RenderLayer 树中包含的 RenderObject 子树中的特定一个或一些节点,而不是绘制整个 RenderLayer 对应的 RenderObject 子树。图 7-12 描述了在软件渲染过程中 WebKit 实际更新的区域,也就是之前描述软件渲染过程的生成结果。


微信图片_20220512122754.png


1.4.2 Chromium 的多进程软件渲染技术

Chromium 的设计与实现中,因为引入了多进程模型,所以 Chromium 需要将渲染结果从 Renderer 进程传递到 Browser 进程。

先是 Renderer 进程。


WebKit 的 Chromium 移植的接口类是 RenderViewImpl,该类包含一个用于表示一个网页的渲染结果的 WebViewImpl 类。其实 RenderViewImpl 类还有一个作用就是同 Browser 进程通信,所以它继承自 RenderWidget 类。RenderWidget 类不仅负责页面渲染和页面更新到实际的 WebViewImpl 类等操作,而且它负责同 Browser 进程的通信。


另外一个重要的设施是 PlatformCanvas 类,也就是 SkiaCanvas(Skia j是一个 2D 图形库),RenderObject 树的实际绘制操作和绘制结果都由该类来完成,它类似于 2D 绘图上下文和后端存储的结合体。

再次是 Browser 进程。


第一个设施就是 RenderWidgetHost 类,一样的必不可少,它负责同 Renderer 进程的通信。RenderWidgetHost 类的作用是传递 Browser 进程中网页操作的请求给 Renderer 进程的 RenderWidget 类,并接收自对方的请求。


第二个是 BackingStore 类,顾名思义,它就是一个后端的存储空间,它的大小通常就是网页可视区域的大小,该空间存储的数据就是页面的显示结果。


BackingStore 类的作用很明显,第一,它保存当前的可视结果,所以 Renderer 进程的绘制工作不会影响该网页结果的显示;第二,WebKit 只需要绘制网页的变动部分,因为其余的部分保存在该后端存储空间,Chromium 只需要将网页的变动更新到该后端存储中即可。


最后是两个进程传递信息和绘制内容的实现过程。


两个进程传递绘制结果是通过 TransportDIDB 类来完成,该类在 Linux 系统下其实是一个共享内存的实现。对 Renderer 进程来说,Skia Canvvas 把内容绘制到位图中,该位图的后端即是共享的 CPU 内存。当 Browser 进程接收到 Renderer 进程关于绘制完成的通知信息,Browser 进程会把共享内存的内容复制到 BackingStore 对象中,然后释放共享内存。


微信图片_20220512122808.png


根据上面的组成部分,一个多进程软件渲染过程大致如下:


RenderWidget 类接收到更新请求时,Chromium 创建一个共享内存区域。然后 Chromium 创建 Skia 的 SkCanvas 对象,并且 RenderWidget 会把实际绘制的工作派发给 RenderObject 树。具体来讲,WebKit 负责遍历 RenderObject 树,每个 RenderObject 节点根据需要来绘制自己和子女的内容并存储到目标存储空间,也就是 SkCanvas 对象所对应的共享内存的位图中。最后,RenderWidgetHost 类把位图复制到 BackingStore 对象相应区域中,并调用 ”Pint“ 函数来把结果绘制到窗口中。


两种会触发重新绘制网页某些区域的请求:


  • 前端请求: 该类型的请求从 Browser 进程发起的请求,可能是浏览器自身的一些需求,也有可能是 X 窗口系统(或者其他窗口系统)的请求。一个典型的例子就是用户因操作网页引起的变化。
  • 后端请求: 由于页面自制的逻辑而发起更新部分区域的请求,例如 HTML 元素或者样式的改变、动画等。一个典型的例子是 JavaScript 代码每隔 50ms 便会更新网页样式,这时样式更新会触发部分区域的重绘。


微信图片_20220512122832.png


  1. Renderer 进程的消息循环(Message Loop)调用处理 ”界面失效“的回调函数,该函数主要调用 RenderWidget::DoDeferredUpdate 来完成绘制请求。
  2. RenderWidget::DoDeferredUpdate 函数首先调用 Layout 函数来触发检查是否有需要重新计算的布局和更新请求。
  3. RenderWidget 类调用 TransportDIB 类来创建共享内存,内存大小为绘制区域的 高X宽X4 ,同时调用 Skia 图形库来创建一个 SkCanvas 对象。SKCanvas cf 对象的绘制目标是一个使用共享内存存储的位图。
  4. 当渲染该页面的全部或者部分时,ScrollView 类请求按照从前到后的顺序遍历并绘制所有 RenderLayer 对象的内容到目标的位图中。WebKit 绘制每个 RenderLay 对象通过以下步骤来完成:首先 WebKit 计算重绘的区域是否呼 RenderLyaer 对象有重叠,如果有,WebKit 要求绘制该层中的所在 RenderObject 对象。
  5. 绘制完成后,Renderer 进程发送 UpdateRect 的消息给 Browser 进程,Renderer 进程同时返回以完成渲染的过程。Browser 进程接收到消息后首先由 BackingStoreManagere 类来获取或者创建 BackingStoreX 对象(在Linux 平台上),BackingStoreX 对象的大小与可视区域相同,包含整个网页的坐标信息,它根据 UpdateRect 的更新区域的位置信息将共享内存的内容绘制到自己的对应存储区域中。


最后 Browser 进程将 UpdataRect 的回复消息发送到 Renderer 进程,这是因为 Renderer 进程知道 Browser 进程已经使用完该共享内存,可能进行回收利用等操作,就样就完成了整个过程。


总结


  • 一个 RenderObject 对象保存了为绘制 DOM 节点所需要的各种信息
  • RenderObject 树是基于 DOM 树建立起来的一棵新树,是为了布局计算和渲染等机制而构建的一种新的内部表示。RenderObject 树节点和 DOM 节点不是一一对应关系
  • WebKit 在创建 DOM 树的同时也创建 RenderObject 对象。如果 DOM 树被动态加入了新节点,WebKit 也可能创建相应的 RenderObject 对象。
  • 网页是有层次结构的,可以分层的,RenderLayer 树是基于 RenderObject 树建立起来的一棵新树。
  • RenderObject 对象是用绘图上下文来绘制内容的,所有绘图的操作都是在该上下文中来进行的。
  • Chromium 需要将渲染结果从 Renderer 进程传递到 Browser 进程
相关实践学习
基于阿里云DeepGPU实例,用AI画唯美国风少女
本实验基于阿里云DeepGPU实例,使用aiacctorch加速stable-diffusion-webui,用AI画唯美国风少女,可提升性能至高至原性能的2.6倍。
相关文章
|
2月前
|
缓存 JavaScript 前端开发
浏览器渲染:理解页面加载的幕后工作
浏览器渲染:理解页面加载的幕后工作
|
25天前
|
移动开发 前端开发 JavaScript
浏览器端图表渲染技术SVG, VML HTML Canvas
浏览器端图表渲染技术SVG, VML HTML Canvas
14 0
|
2月前
|
前端开发 JavaScript 数据可视化
探索浏览器的内心世界:渲染机制的奥秘
探索浏览器的内心世界:渲染机制的奥秘
探索浏览器的内心世界:渲染机制的奥秘
|
2月前
|
消息中间件 JavaScript 前端开发
前端秘法进阶篇----这还是我们熟悉的浏览器吗?(浏览器的渲染原理)
前端秘法进阶篇----这还是我们熟悉的浏览器吗?(浏览器的渲染原理)
|
2月前
|
消息中间件 前端开发 Java
【面试题】前端必修-浏览器的渲染原理
【面试题】前端必修-浏览器的渲染原理
|
10月前
|
Web App开发 存储 监控
浏览器基础原理-安全: 渲染进程-安全沙盒
浏览器基础原理-安全: 渲染进程-安全沙盒
45 0
|
12月前
|
开发框架 安全 .NET
您是否也有想在浏览器中实时的编辑代码并且渲染的想法?
您是否也有想在浏览器中实时的编辑代码并且渲染的想法?
61 0
|
存储 缓存 前端开发
|
12月前
|
Web App开发 自然语言处理 JavaScript
JS进阶(五)同步异步编程及浏览器的底层渲染机制
浏览器渲染机制 浏览器底层渲染机制 一个页面从服务器访问,拿到页面源代码之后做的事情是什么? 生成Dom树(DOM Tree) => 对HTML文件的处理 基于HTML获取的是流文件 (进制编码) 把进制编码编译为具体的字符 按照令牌TOKEN进行解析 (分词/断词) 生成具体的节点 (元素标签/文本节点....) 按照相互的依赖关系生成一个DOM树 (节点树)
143 0
|
Web App开发 JavaScript 算法
深入解析 EventLoop 和浏览器渲染、帧动画、空闲回调的关系
关于 Event Loop 的文章很多,但是有很多只是在讲「宏任务」、「微任务」