很遗憾 Chromium 的工程师没有写一篇 How Viz Works 的文章来详细解析 Viz 这个重要的模块,能找到的一些相关文档都比较零散,所以我打算自己来写,不过个人研究深度有限,无法包括所有的细节,还请读者见谅。
计划写两到三篇文章,第一篇文章对 Viz 做一个概要的介绍,和它在 Chromium 整个合成器架构里面的角色。因为 Viz 的代码变动很快,所以文章的内容只是针对当前的版本 M75,部分内容在未来有可能会失效。
Viz 作为 Visual 的缩写,一开始的代码是源自 cc,gpu 等模块,它是 Chromium 整体架构转向服务化的一个重要组成部分。官方的文档里面说明 Viz 预计为 Chromium 其它模块提供以下四种服务:
- compositing - 合成输出;
- gl - 实际上就是 GPU 访问,对外部提供 GPU Service,内部使用 GL 或者 Vulkan API 访问 GPU;
- hit testing - Surface 的点击测试;
- media - 多媒体,包括视频,VR/AR 等;
Viz clients 分为两类,一种是 privileged client (特权客户),它只有一个,负责启动 Viz 服务,和为 unprivileged clients(非特权客户)提供 Viz 服务的访问接口,而 unprivileged clients 则仅仅是使用 Viz 服务,它可以同时存在多个。
对一个作为浏览器(非 Chrome OS)的 Chromium 来说,只有 Browser 进程可以作为 privileged client,Renderer 进程只能作为 unprivileged client。
Viz 运行在 Viz 进程,也就是以前的 GPU 进程,当然,在单进程架构下,Browser 进程可以兼做 Viz 进程。
Viz 的代码结构
Viz 的代码主要分布在 /services/viz 和 /components/viz 两个目录,从目录名字就差不多能够猜的出来:
- /services/viz 的代码主要是 Viz Mojo Service 对外的接口和 Viz Mojo Service 的启动和注册;
- /components/viz 是 Viz 对外提供服务的内部实现,包括 Service 接口的实现和具体功能的实现,比如 Display Compositor 等;
不过从官方文档的说明来看,/components/viz 目录下的代码未来可能会迁入 /services/viz 里面。
/services/viz
Viz Mojo Service 对外提供了两个最重要的接口是 FrameSinkManager 和 CompositorFrameSink。
privileged client 通过 FrameSinkManager 接口请求创建一个 CompositorFrameSink 的实现,包括对应的 Surface,并把返回的接口传递给 unprivileged client,unprivileged client 则使用 CompositorFrameSink 的接口来提交 CompositorFrame 给 Display Compositor。我们后面还会详细讲述。
/components/viz
- /components/viz/common - 公共的数据结构和辅助类;
- /components/viz/client - unprivileged client ,比如 cc,需要使用的一些类;
- /components/viz/host - privileged client,需要使用的一些类;
- /components/viz/service - Viz Service 的实现,包括 Service 接口的实现和功能的实现;其中 frame_sinks 是 FrameSinkManager 和 CompositorFrameSink 这两个接口相关的实现;gl 是 GPU Service 的实现;display, display_embedder,surfaces 一起构成了 Display Compositor;
合成器架构
在继续解析 Viz 之前,我们需要了解目前 Chromium 合成输出的合成器架构,Compositor Stack 这篇文档比较详细地介绍了 Chromium 合成器架构的历史变迁,可以作为读者的参考。关于 Layer Compositor,更详尽的信息可以参考文章 How cc Works。
上图显示网页内容合成输出的一个合成器架构的简化示意图,需要说明的是 Chromium 目前的合成器架构并不仅仅限于网页内容的合成,包括浏览器 UI,多媒体,插件,Offscreen Canvas 等都可以通过这套合成器架构进行合成输出。Surface 和 Display 并不是一一对应的关系,一个 Display 实际上对应的是一棵父子结构的 Surfae 树。
从上图我们可以看到:
- 以 LayerTreeHostImpl/LayerTreeHost 为核心的 Layer Compositor 是 CompositorFrame 的来源之一,对网页来说,这个 CompositorFrame 包含了当前 Viewport 的网页绘制结果;
- LayerTreeFrameSink 是 Layer Compositor 提交 CompositorFrame 的接口,具体的实现由 cc 的 embedder 提供;
- LayerTreeFrameSink 的实现把 CompositorFrame 通过 CompositorFrameSink 接口提交给 Viz Service;
- Viz Service 把接收到的 CompositorFrame 传递给目标 Surface;
- Display 关联的多个 Surface 被聚合,最终结果输出到 Display 指定的 OutputSurface;
后面的文章再继续讲述不同的 Chromium Configurations 实际的合成器架构。