文档地址:
GPU Accelerated Compositing in Chrome
浏览器页面渲染之composite_浏览器渲染主线程的 composite-CSDN博客
Render树、RenderObject与RenderLayer - hdu胡恩超 - 博客园
简介:为什么要进行硬件合成?
传统上,网络浏览器完全依赖 CPU 来呈现网页内容。如今,即使是最小的设备也离不开强大的 GPU,因此人们开始关注如何更有效地利用这种底层硬件来实现更好的性能和节能。使用 GPU 来合成网页内容可以显著提高速度。
硬件合成的好处有以下三个方面:
- 在涉及大量像素的绘制和合成操作中,在 GPU 上合成页面层可以实现比 CPU 更好的效率(无论是速度还是功耗)。硬件是专门为这些类型的工作负载而设计的。
- 对于已经在 GPU 上的内容(例如加速视频、Canvas2D 或 WebGL),不需要昂贵的读回。
- CPU 和 GPU 之间的并行性,可以同时运行,以创建高效的图形管道。
第 1 部分:Blink 渲染基础
Blink 渲染引擎的源代码庞大、复杂,而且文档很少。为了理解 GPU 加速在 Chrome 中的工作原理,首先要了解 Blink 渲染页面的基本构成要素。
节点和 DOM 树
在 Blink 中,网页内容在内部存储为 Node 对象树,称为 DOM 树。页面上的每个 HTML 元素以及元素之间出现的文本都与一个 Node 相关联。DOM 树的顶级节点始终是文档节点。
从节点到渲染对象
DOM 树中产生视觉输出的每个节点都有相应的 RenderObject。RenderObject 存储在称为 Render Tree 的并行树结构中。RenderObject 知道如何在显示表面上绘制 Node 的内容。它通过向 GraphicsContext 发出必要的绘制调用来实现这一点。GraphicsContext 负责将像素写入位图,最终显示在屏幕上。在 Chrome 中,GraphicsContext 包装了我们的 2D 绘图库 Skia。
传统上,大多数 GraphicsContext 调用都变成了对 SkCanvas 或 SkPlatformCanvas 的调用,即立即绘制到软件位图中(有关 Chrome 如何使用 Skia 的旧模型的更多详细信息,请参阅此文档)。但为了将绘制从主线程移出(本文档后面将详细介绍),这些命令现在被记录到SkPicture中。SkPicture 是一种可序列化的数据结构,可以捕获命令,然后稍后重放命令,类似于显示列表。
从 RenderObjects 到 RenderLayers
每个 RenderObject 都通过祖先 RenderObject 直接或间接与一个 RenderLayer 相关联。
共享相同坐标空间(例如,受相同 CSS 变换影响)的 RenderObject 通常属于同一个 RenderLayer。RenderLayers 的存在是为了使页面元素以正确的顺序组合,从而正确显示重叠内容、半透明元素等。有多种条件会触发为特定 RenderObject 创建新的 RenderLayer,如RenderBoxModelObject::requiresLayer()中所定义,并为某些派生类所覆盖。需要创建 RenderLayer 的 RenderObject 的常见情况:
- 它是页面的根对象
- 它具有明确的 CSS 位置属性(相对、绝对或变换)
- 它是透明的
- 有溢出、alpha 蒙版或反射
- 具有 CSS 过滤器
- 对应于具有 3D (WebGL) 上下文或加速 2D 上下文的 <canvas> 元素
- 对应于 <video> 元素
请注意,RenderObject 和 RenderLayers 之间并不是一一对应的。特定的 RenderObject 要么与为其创建的 RenderLayer 相关联(如果有),要么与具有 RenderLayer 的第一个祖先的 RenderLayer 相关联。
RenderLayers 也形成树状层次结构。根节点是与页面中的根元素相对应的 RenderLayer,每个节点的后代都是在视觉上包含在父层内的层。每个 RenderLayer 的子层都保存在两个按升序排序的排序列表中,negZOrderList 包含具有负 z 索引的子层(因此位于当前层下方的层),posZOrderList 包含具有正 z 索引的子层(位于当前层上方的层)。
从 RenderLayers 到 GraphicsLayers
为了使用合成器,部分(但不是全部)RenderLayer 会获得自己的支持表面(具有自己的支持表面的层通常称为合成层)。每个 RenderLayer 要么有自己的 GraphicsLayer(如果它是合成层),要么使用其第一个祖先的 GraphicsLayer。这类似于 RenderObject 与 RenderLayers 的关系。
每个 GraphicsLayer 都有一个 GraphicsContext,供关联的 RenderLayers 进行绘制。合成器最终负责在后续的合成过程中将 GraphicsContext 的位图输出组合成最终的屏幕图像。
虽然理论上每个 RenderLayer 都可以将自己绘制到单独的背景表面,但实际上这可能会浪费大量内存(尤其是 VRAM)。在当前的 Blink 实现中,RenderLayer 必须满足以下条件之一才能获得自己的合成层(请参阅CompositingReasons.h)
- 图层具有 3D 或透视变换 CSS 属性
- 图层由使用加速视频解码的 <video> 元素使用
- 图层由具有 3D 上下文或加速 2D 上下文的 <canvas> 元素使用
- 图层用于合成插件
- 图层使用 CSS 动画来实现不透明度或使用动画 webkit
- 变换
- 图层使用加速 CSS 滤镜
- 图层具有合成层的后代
- 图层具有 z 索引较低的兄弟,该兄弟具有合成层(换句话说,该图层与合成层重叠,应在其上方渲染)
层压缩
任何规则都离不开例外。如上所述,GraphicsLayers 在内存和其他资源方面可能非常昂贵(例如,某些关键操作的 CPU 时间复杂度与 GraphicsLayer 树的大小成正比)。可以为 RenderLayers 创建许多附加层,这些附加层将 RenderLayer 与其自己的背景表面重叠,这可能会非常昂贵。
我们将内在的合成原因(例如,具有 3D 变换的图层)称为"直接"合成原因。为了防止由于直接合成原因而导致许多元素位于图层之上时出现"图层爆炸",Blink 将多个 RenderLayers 与直接合成原因 RenderLayer 重叠,并将它们"挤压"到单个后备存储中。这可以防止重叠导致的图层爆炸。在此演示文稿中可以找到有关图层挤压的更多详细信息,在此演示文稿中可以找到有关 RenderLayers 和合成图层之间的代码的更多详细信息;尽管此代码在 2014 年经历了重大变化,但这两个代码都是截至 2014 年 1 月左右的最新代码。
从 GraphicsLayers 到 WebLayers 再到 CC Layers
在我们了解 Chrome 的合成器实现之前,只需再了解几个抽象层!GraphicsLayers 可以通过一个或多个 Web*Layers 表示其内容。这些是 WebKit 端口需要实现的接口;请参阅 Blink 的public/platform目录中的接口,例如 WebContentsLayer.h 或 WebScrollbarLayer.h。Chrome 的实现位于src/webkit/renderer/compositor_bindings中,它使用 Chrome 合成器层类型实现抽象的 Web*Layer 接口。
组合起来:合成森林
总而言之,概念上存在四种并行的树结构,它们的渲染目的略有不同:
- DOM 树,这是我们的基本保留模型
- RenderObject 树,它与 DOM 树的可见节点有 1:1 的映射。RenderObject 知道如何绘制其相应的 DOM节点。
- RenderLayer 树,由映射到 RenderObject 树上的RenderObject 的 RenderLayer 组成。映射是多对一的,因为每个RenderObject 要么与其自己的 RenderLayer 相关联,要么与其第一个祖先的具有 RenderLayer 的 RenderLayer 相关联。RenderLayer 树保留了各层之间的 z 顺序。
- GraphicsLayer 树,将 GraphicsLayers 一对多地映射到RenderLayers。
每个 GraphicsLayer 都有在 Chrome 中实现的 Web*Layers,以及 Chrome 合成器层。合成器知道如何操作这些最终的"cc 层"(cc = Chrome 合成器)。
从本文档中开始,"层"将指通用 cc 层,因为对于硬件合成来说,这些是最有趣的 - 但不要被愚弄,在其他地方当有人说"层"时,他们可能指的是上述任何构造!

合成森林
现在我们已经(简要地)介绍了 Blink 中将 DOM 链接到合成器的数据结构,我们已经准备好认真探索合成器本身了。
第 2 部分:合成器
Chrome 的合成器是一个用于管理 GraphicsLayer 树和协调帧生命周期的软件库。它的代码位于 Blink 之外的src/cc目录中。
合成器简介
回想一下,渲染分为两个阶段:首先绘制,然后合成。这允许合成器在每个合成层上执行额外的工作。例如,合成器负责在合成每个合成层的位图之前对其应用必要的转换(由层的 CSS 变换属性指定)。此外,由于层的绘制与合成是分离的,因此使其中一个层无效只会导致重新绘制该层的内容并重新合成。
每次浏览器需要创建新的帧时,合成器都会进行绘制。请注意这个(令人困惑的)术语区别:绘制是合成器将图层组合成最终的屏幕图像;而绘画是图层背景的填充(采用软件光栅化的位图;采用硬件光栅化的纹理)。
GPU将何去何从?
那么 GPU 是如何发挥作用的呢?合成器可以使用 GPU 来执行其绘制步骤。这与旧的软件渲染模型有很大不同,在旧的软件渲染模型中,渲染器进程将包含页面内容的位图(通过 IPC 和共享内存)传递给浏览器进程进行显示(有关其工作原理的更多信息,请参阅"旧版软件渲染路径"附录)。
在硬件加速架构中,合成通过调用特定于平台的 3D API(Windows 上为 D3D;其他地方为 GL)在 GPU 上进行。渲染器的合成器本质上是使用 GPU 将页面的矩形区域(即所有合成层,根据层树的变换层次结构相对于视口定位)绘制成单个位图,即最终的页面图像。
架构插曲:GPU 流程
在我们进一步探索合成器生成的 GPU 命令之前,了解渲染器进程如何向 GPU 发出命令非常重要。在 Chrome 的多进程模型中,我们有一个专门用于此任务的进程:GPU 进程。GPU 进程的存在主要是为了安全。请注意,Android 是一个例外,Chrome 使用进程内 GPU 实现,该实现作为浏览器进程中的线程运行。Android 上的 GPU 线程的行为与其他平台上的 GPU 进程相同。
受其沙箱的限制,渲染器进程(包含 Blink 和 cc 的实例)无法直接调用操作系统提供的 3D API(GL / D3D)。因此,我们使用单独的进程来访问设备。我们将此进程称为 GPU 进程。GPU 进程专门设计用于从渲染器沙箱或更严格的Native Client "监狱"内提供对系统 3D API 的访问。它通过客户端-服务器模型工作,如下所示:

GPU 进程
命令缓冲区
GPU 进程接受的命令与 GL ES 2.0 API 非常相似(例如,有一个命令对应于 glClear,一个命令对应于 glDrawArrays,等等)。由于大多数 GL 调用没有返回值,因此客户端和服务器可以大部分异步工作,从而将性能开销保持在相当低的水平。客户端和服务器之间任何必要的同步(例如客户端通知服务器还有其他工作要做)都是通过 IPC 机制处理的。
从客户端的角度来看,应用程序可以选择将命令直接写入命令缓冲区,也可以通过我们提供的客户端库使用 GL ES 2.0 API,该库在后台处理序列化。为了方便起见,合成器和 WebGL 目前都使用 GL ES 客户端库。在服务器端,通过命令缓冲区收到的命令将通过ANGLE转换为对桌面 OpenGL 或 Direct3D 的调用。
资源共享与同步
除了为命令缓冲区提供存储空间外,Chrome 还使用共享内存在客户端和服务器之间传递较大的资源,例如纹理位图、顶点数组等。有关命令格式和数据传输的更多信息,请参阅命令缓冲区文档。
另一个构造称为邮箱,它提供了一种在命令缓冲区之间共享纹理并管理其生命周期的方法。邮箱是一个简单的字符串标识符,可以将其附加(使用)到任何命令缓冲区的本地纹理 ID,然后通过该纹理 ID 别名进行访问。以这种方式附加的每个纹理 ID 都持有对底层真实纹理的引用,一旦通过删除本地纹理 ID 释放所有引用,真实纹理也会被销毁。
同步点用于在想要通过邮箱共享纹理的命令缓冲区之间提供非阻塞同步。在命令缓冲区 A 上插入同步点,然后在命令缓冲区 B 上"等待"同步点,可确保您随后在 B 上插入的命令不会在同步点之前在 A 上插入的命令之前运行。
命令缓冲区复用
目前,Chrome 每个浏览器实例使用一个 GPU 进程,处理来自所有渲染器进程和任何插件进程的请求。GPU 进程可以在多个命令缓冲区之间进行多路复用,每个命令缓冲区都与其自己的渲染上下文相关联。
每个渲染器可以有多个 GL 源,例如 WebGL Canvas 元素直接创建 GL 命令流。直接在 GPU 上创建内容的图层的合成工作方式如下:它们不是直接渲染到后缓冲区,而是渲染到纹理中(使用帧缓冲区对象),合成器上下文在渲染该 GraphicsLayer 时会抓取并使用该纹理。需要注意的是,为了让合成器的 GL 上下文能够访问由屏幕外 GL 上下文(即用于其他 GraphicsLayers 的 FBO 的 GL 上下文)生成的纹理,GPU 进程使用的所有 GL 上下文都应创建为共享资源。
最终的架构如下所示:

处理多个上下文
概括
GPU 流程架构具有多种优势,包括
- 安全性:大部分渲染逻辑仍保留在沙盒渲染器进程中,平台 3D API 的访问仅限于 GPU 进程。
- 稳健性:GPU 进程崩溃(例如由于驱动程序故障)不会导致浏览器崩溃。
- 统一性:无论平台如何,都将 OpenGL ES 2.0 标准化为浏览器的渲染 API,这样可以在 Chrome 的所有操作系统端口上使用单一、更易于维护的代码库。
- 并行性:渲染器可以快速将命令发送到命令缓冲区并返回到 CPU 密集型渲染活动,让 GPU 进程来处理它们。借助此管道,我们可以同时充分利用多核机器上的两个进程以及 GPU 和 CPU。
简介:为什么要进行硬件合成?
传统上,网络浏览器完全依赖 CPU 来呈现网页内容。如今,即使是最小的设备也离不开强大的 GPU,因此人们开始关注如何更有效地利用这种底层硬件来实现更好的性能和节能。使用 GPU 来合成网页内容可以显著提高速度。
硬件合成的好处有以下三个方面:
- 在涉及大量像素的绘制和合成操作中,在 GPU 上合成页面层可以实现比 CPU 更好的效率(无论是速度还是功耗)。硬件是专门为这些类型的工作负载而设计的。
- 对于已经在 GPU 上的内容(例如加速视频、Canvas2D 或 WebGL),不需要昂贵的读回。
- CPU 和 GPU 之间的并行性,可以同时运行,以创建高效的图形管道。
第 1 部分:Blink 渲染基础
Blink 渲染引擎的源代码庞大、复杂,而且文档很少。为了理解 GPU 加速在 Chrome 中的工作原理,首先要了解 Blink 渲染页面的基本构成要素。
节点和 DOM 树
在 Blink 中,网页内容在内部存储为 Node 对象树,称为 DOM 树。页面上的每个 HTML 元素以及元素之间出现的文本都与一个 Node 相关联。DOM 树的顶级节点始终是文档节点。
从节点到渲染对象
DOM 树中产生视觉输出的每个节点都有相应的 RenderObject。RenderObject 存储在称为 Render Tree 的并行树结构中。RenderObject 知道如何在显示表面上绘制 Node 的内容。它通过向 GraphicsContext 发出必要的绘制调用来实现这一点。GraphicsContext 负责将像素写入位图,最终显示在屏幕上。在 Chrome 中,GraphicsContext 包装了我们的 2D 绘图库 Skia。
传统上,大多数 GraphicsContext 调用都变成了对 SkCanvas 或 SkPlatformCanvas 的调用,即立即绘制到软件位图中(有关 Chrome 如何使用 Skia 的旧模型的更多详细信息,请参阅此文档)。但为了将绘制从主线程移出(本文档后面将详细介绍),这些命令现在被记录到SkPicture中。SkPicture 是一种可序列化的数据结构,可以捕获命令,然后稍后重放命令,类似于显示列表。
从 RenderObjects 到 RenderLayers
每个 RenderObject 都通过祖先 RenderObject 直接或间接与一个 RenderLayer 相关联。
共享相同坐标空间(例如,受相同 CSS 变换影响)的 RenderObject 通常属于同一个 RenderLayer。RenderLayers 的存在是为了使页面元素以正确的顺序组合,从而正确显示重叠内容、半透明元素等。有多种条件会触发为特定 RenderObject 创建新的 RenderLayer,如RenderBoxModelObject::requiresLayer()中所定义,并为某些派生类所覆盖。需要创建 RenderLayer 的 RenderObject 的常见情况:
- 它是页面的根对象
- 它具有明确的 CSS 位置属性(相对、绝对或变换)
- 它是透明的
- 有溢出、alpha 蒙版或反射
- 具有 CSS 过滤器
- 对应于具有 3D (WebGL) 上下文或加速 2D 上下文的 <canvas> 元素
- 对应于 <video> 元素
请注意,RenderObject 和 RenderLayers 之间并不是一一对应的。特定的 RenderObject 要么与为其创建的 RenderLayer 相关联(如果有),要么与具有 RenderLayer 的第一个祖先的 RenderLayer 相关联。
RenderLayers 也形成树状层次结构。根节点是与页面中的根元素相对应的 RenderLayer,每个节点的后代都是在视觉上包含在父层内的层。每个 RenderLayer 的子层都保存在两个按升序排序的排序列表中,negZOrderList 包含具有负 z 索引的子层(因此位于当前层下方的层),posZOrderList 包含具有正 z 索引的子层(位于当前层上方的层)。
从 RenderLayers 到 GraphicsLayers
为了使用合成器,部分(但不是全部)RenderLayer 会获得自己的支持表面(具有自己的支持表面的层通常称为合成层)。每个 RenderLayer 要么有自己的 GraphicsLayer(如果它是合成层),要么使用其第一个祖先的 GraphicsLayer。这类似于 RenderObject 与 RenderLayers 的关系。
每个 GraphicsLayer 都有一个 GraphicsContext,供关联的 RenderLayers 进行绘制。合成器最终负责在后续的合成过程中将 GraphicsContext 的位图输出组合成最终的屏幕图像。
虽然理论上每个 RenderLayer 都可以将自己绘制到单独的背景表面,但实际上这可能会浪费大量内存(尤其是 VRAM)。在当前的 Blink 实现中,RenderLayer 必须满足以下条件之一才能获得自己的合成层(请参阅CompositingReasons.h)
- 图层具有 3D 或透视变换 CSS 属性
- 图层由使用加速视频解码的 <video> 元素使用
- 图层由具有 3D 上下文或加速 2D 上下文的 <canvas> 元素使用
- 图层用于合成插件
- 图层使用 CSS 动画来实现不透明度或使用动画 webkit
- 变换
- 图层使用加速 CSS 滤镜
- 图层具有合成层的后代
- 图层具有 z 索引较低的兄弟,该兄弟具有合成层(换句话说,该图层与合成层重叠,应在其上方渲染)
层压缩
任何规则都离不开例外。如上所述,GraphicsLayers 在内存和其他资源方面可能非常昂贵(例如,某些关键操作的 CPU 时间复杂度与 GraphicsLayer 树的大小成正比)。可以为 RenderLayers 创建许多附加层,这些附加层将 RenderLayer 与其自己的背景表面重叠,这可能会非常昂贵。
我们将内在的合成原因(例如,具有 3D 变换的图层)称为"直接"合成原因。为了防止由于直接合成原因而导致许多元素位于图层之上时出现"图层爆炸",Blink 将多个 RenderLayers 与直接合成原因 RenderLayer 重叠,并将它们"挤压"到单个后备存储中。这可以防止重叠导致的图层爆炸。在此演示文稿中可以找到有关图层挤压的更多详细信息,在此演示文稿中可以找到有关 RenderLayers 和合成图层之间的代码的更多详细信息;尽管此代码在 2014 年经历了重大变化,但这两个代码都是截至 2014 年 1 月左右的最新代码。
从 GraphicsLayers 到 WebLayers 再到 CC Layers
在我们了解 Chrome 的合成器实现之前,只需再了解几个抽象层!GraphicsLayers 可以通过一个或多个 Web*Layers 表示其内容。这些是 WebKit 端口需要实现的接口;请参阅 Blink 的public/platform目录中的接口,例如 WebContentsLayer.h 或 WebScrollbarLayer.h。Chrome 的实现位于src/webkit/renderer/compositor_bindings中,它使用 Chrome 合成器层类型实现抽象的 Web*Layer 接口。
组合起来:合成森林
总而言之,概念上存在四种并行的树结构,它们的渲染目的略有不同:
- DOM 树,这是我们的基本保留模型
- RenderObject 树,它与 DOM 树的可见节点有 1:1 的映射。RenderObject 知道如何绘制其相应的 DOM节点。
- RenderLayer 树,由映射到 RenderObject 树上的RenderObject 的 RenderLayer 组成。映射是多对一的,因为每个RenderObject 要么与其自己的 RenderLayer 相关联,要么与其第一个祖先的具有 RenderLayer 的 RenderLayer 相关联。RenderLayer 树保留了各层之间的 z 顺序。
- GraphicsLayer 树,将 GraphicsLayers 一对多地映射到RenderLayers。
每个 GraphicsLayer 都有在 Chrome 中实现的 Web*Layers,以及 Chrome 合成器层。合成器知道如何操作这些最终的"cc 层"(cc = Chrome 合成器)。
从本文档中开始,"层"将指通用 cc 层,因为对于硬件合成来说,这些是最有趣的 - 但不要被愚弄,在其他地方当有人说"层"时,他们可能指的是上述任何构造!

合成森林
现在我们已经(简要地)介绍了 Blink 中将 DOM 链接到合成器的数据结构,我们已经准备好认真探索合成器本身了。
第 2 部分:合成器
Chrome 的合成器是一个用于管理 GraphicsLayer 树和协调帧生命周期的软件库。它的代码位于 Blink 之外的src/cc目录中。
合成器简介
回想一下,渲染分为两个阶段:首先绘制,然后合成。这允许合成器在每个合成层上执行额外的工作。例如,合成器负责在合成每个合成层的位图之前对其应用必要的转换(由层的 CSS 变换属性指定)。此外,由于层的绘制与合成是分离的,因此使其中一个层无效只会导致重新绘制该层的内容并重新合成。
每次浏览器需要创建新的帧时,合成器都会进行绘制。请注意这个(令人困惑的)术语区别:绘制是合成器将图层组合成最终的屏幕图像;而绘画是图层背景的填充(采用软件光栅化的位图;采用硬件光栅化的纹理)。
GPU将何去何从?
那么 GPU 是如何发挥作用的呢?合成器可以使用 GPU 来执行其绘制步骤。这与旧的软件渲染模型有很大不同,在旧的软件渲染模型中,渲染器进程将包含页面内容的位图(通过 IPC 和共享内存)传递给浏览器进程进行显示(有关其工作原理的更多信息,请参阅"旧版软件渲染路径"附录)。
在硬件加速架构中,合成通过调用特定于平台的 3D API(Windows 上为 D3D;其他地方为 GL)在 GPU 上进行。渲染器的合成器本质上是使用 GPU 将页面的矩形区域(即所有合成层,根据层树的变换层次结构相对于视口定位)绘制成单个位图,即最终的页面图像。
架构插曲:GPU 流程
在我们进一步探索合成器生成的 GPU 命令之前,了解渲染器进程如何向 GPU 发出命令非常重要。在 Chrome 的多进程模型中,我们有一个专门用于此任务的进程:GPU 进程。GPU 进程的存在主要是为了安全。请注意,Android 是一个例外,Chrome 使用进程内 GPU 实现,该实现作为浏览器进程中的线程运行。Android 上的 GPU 线程的行为与其他平台上的 GPU 进程相同。
受其沙箱的限制,渲染器进程(包含 Blink 和 cc 的实例)无法直接调用操作系统提供的 3D API(GL / D3D)。因此,我们使用单独的进程来访问设备。我们将此进程称为 GPU 进程。GPU 进程专门设计用于从渲染器沙箱或更严格的Native Client "监狱"内提供对系统 3D API 的访问。它通过客户端-服务器模型工作,如下所示:

GPU 进程
命令缓冲区
GPU 进程接受的命令与 GL ES 2.0 API 非常相似(例如,有一个命令对应于 glClear,一个命令对应于 glDrawArrays,等等)。由于大多数 GL 调用没有返回值,因此客户端和服务器可以大部分异步工作,从而将性能开销保持在相当低的水平。客户端和服务器之间任何必要的同步(例如客户端通知服务器还有其他工作要做)都是通过 IPC 机制处理的。
从客户端的角度来看,应用程序可以选择将命令直接写入命令缓冲区,也可以通过我们提供的客户端库使用 GL ES 2.0 API,该库在后台处理序列化。为了方便起见,合成器和 WebGL 目前都使用 GL ES 客户端库。在服务器端,通过命令缓冲区收到的命令将通过ANGLE转换为对桌面 OpenGL 或 Direct3D 的调用。
资源共享与同步
除了为命令缓冲区提供存储空间外,Chrome 还使用共享内存在客户端和服务器之间传递较大的资源,例如纹理位图、顶点数组等。有关命令格式和数据传输的更多信息,请参阅命令缓冲区文档。
另一个构造称为邮箱,它提供了一种在命令缓冲区之间共享纹理并管理其生命周期的方法。邮箱是一个简单的字符串标识符,可以将其附加(使用)到任何命令缓冲区的本地纹理 ID,然后通过该纹理 ID 别名进行访问。以这种方式附加的每个纹理 ID 都持有对底层真实纹理的引用,一旦通过删除本地纹理 ID 释放所有引用,真实纹理也会被销毁。
同步点用于在想要通过邮箱共享纹理的命令缓冲区之间提供非阻塞同步。在命令缓冲区 A 上插入同步点,然后在命令缓冲区 B 上"等待"同步点,可确保您随后在 B 上插入的命令不会在同步点之前在 A 上插入的命令之前运行。
命令缓冲区复用
目前,Chrome 每个浏览器实例使用一个 GPU 进程,处理来自所有渲染器进程和任何插件进程的请求。GPU 进程可以在多个命令缓冲区之间进行多路复用,每个命令缓冲区都与其自己的渲染上下文相关联。
每个渲染器可以有多个 GL 源,例如 WebGL Canvas 元素直接创建 GL 命令流。直接在 GPU 上创建内容的图层的合成工作方式如下:它们不是直接渲染到后缓冲区,而是渲染到纹理中(使用帧缓冲区对象),合成器上下文在渲染该 GraphicsLayer 时会抓取并使用该纹理。需要注意的是,为了让合成器的 GL 上下文能够访问由屏幕外 GL 上下文(即用于其他 GraphicsLayers 的 FBO 的 GL 上下文)生成的纹理,GPU 进程使用的所有 GL 上下文都应创建为共享资源。
最终的架构如下所示:

处理多个上下文
概括
GPU 流程架构具有多种优势,包括
- 安全性:大部分渲染逻辑仍保留在沙盒渲染器进程中,平台 3D API 的访问仅限于 GPU 进程。
- 稳健性:GPU 进程崩溃(例如由于驱动程序故障)不会导致浏览器崩溃。
- 统一性:无论平台如何,都将 OpenGL ES 2.0 标准化为浏览器的渲染 API,这样可以在 Chrome 的所有操作系统端口上使用单一、更易于维护的代码库。
- 并行性:渲染器可以快速将命令发送到命令缓冲区并返回到 CPU 密集型渲染活动,让 GPU 进程来处理它们。借助此管道,我们可以同时充分利用多核机器上的两个进程以及 GPU 和 CPU。