在 Chromium 中viz
的核心逻辑运行在 GPU 进程中,负责接收其他进程产生的viz::CompositorFrame(简称 CF)
,然后把这些 CF 进行合成,并将合成的结果最终渲染在窗口上。
可以将这个过程拆解成以下几个步骤来分析:
viz
的初始化;viz
的架构设计;- CF 的创建;
- CF 的合成;
- CF 的渲染;
viz
的初始化只是一次性的过程,而且比较枯燥,所以放到最后再介绍。先来看一下viz::CompositorFrame(简称 CF)
的创建,因为它是viz
中的核心数据结构。
1.CF 的创建
一个 CF 对象表示一个矩形显示区域中的一帧画面。内部存储了 3 类数据,分别是 CompositorFrameMetadata, TransferableResoruce 和 RenderPass/DrawQuad,如下图所示:
2.CF 的元数据(Metadata)
viz::CompositorFrameMetadata
记录了 CF 相关的元数据,比如画面的缩放级别,滚动区域,引用到的 Surface 等。需要注意的是这里面还存储了一个ui::LatencyInfo
对象,该对象记录了从 Chromium 接收到用户的交互事件到画面更新每个阶段的延时信息,具体都有哪些时间被记录了下来,参见latency_info.h。通过ui::LatencyInfo
对象可以方便的追踪交互到渲染各阶段的延时信息。
3.CF 引用到的资源(Resource)
viz::TransferableResource
记录了该 CF 引用到的资源,所谓的资服务器托管网源可以理解为一张图片。资源有两种存在形式,一类是存储在内存中的 Software 资源,一类是存储在 GPU 中的 Texture(这样表达并不严格,但目前为止大家都可以先这样理解)。如果没有开启 Chromium 的硬件加速渲染,则只能使用 Software 资源。如果开启了硬件加速,则只能使用硬件加速的资源。
如何创建一个资源呢? 如果是 Software 资源,只需要申请一块内存(如果资源需要夸进程可以使用共享内存),然后将资源的像素数据放进去就可以了。就像下面这样:
上面的代码直接将SkBitmap
中的像素数据拷贝到共享内存中,然后在viz::TransferableResource
中引用该共享内存的地址。
如果是 Texture 资源,就要复杂一点,因为需要先初始化 GL Context。关于如何初始化 GL Context 放在后面的viz
初始化中解释,这里先忽略初始化过程直接看如何创建 GL 资源。示例代码如下:
上面的代码将SkBitmap
中的像素数据使用gpu::SharedImageInterface
接口放入SharedImage
中,然后使用它返回的 Mailbox 创建viz::TransferableResource
。就像代码中注释的那样,不一定非要使用像素数据来创建 SharedImage,也可以使用 GPU-R 和 OOP-R 相关接口来创建 SharedImage。关于 Mailbox,SharedImage 以及 GPU-R 和 OOP-R 的相关内容可以参考Chromium GPU Resource Share (Shared Image)。
哪些内容会以资源的形式存在呢? 结果可能出乎你的意料,网页中的每一个 Tile 都引用一个资源。cc
会把网页分成很多的 Tiles,每一个都以viz::TileDrawQuad
的形式存在,而每一个viz::TileDrawQuad
只是简单引用一个资源(关于 DrawQuad 见下文)。此时,这些资源就是cc
Raster 的产物。
4.CF 包含的绘制操作(RenderPass/DrawQuad)
viz::RenderPass
由一系列相关的viz::DrawQuad
构成。可以对一个 R服务器托管网enderPass 单独应用特效,变换,mipmap,缓存,截图(CopyOutputRequest)等。DrawQuad 有很多种类型,分别用于不同的目的:
viz::TextureDrawQuad
内部引用一个资源。viz::TileDrawQuad
表示一个 Tile 块(Tile的概念见cc
),和 TextureDrawQuad 类似,内部也引用一个资源,DisplayItemList 会被cc
Raster 为 TileDrawQuad;viz::PictureDrawQuad
内部直接存放 DisplayItemList,但是目前只能用于Android WebView;viz::SolidColorDrawQuad
表示一个颜色块;viz::RenderPassDrawQuad
内部引用另外一个 RenderPass 的 Id;viz::SurfaceDrawQuad
内部保存一个 viz::SurfaceId,该 Surface 的内容由其他CompositorFrameSinkClient
创建,用于 viz 的嵌套,比如 OOPIF,OffscreenCanvas等;
由于viz::RenderPassDrawQuad
的存在使得 CF 中可以存储一个 RenderPass 树,由于viz::SurfaceDrawQuad
的存在使得viz可以实现UI的夸进程嵌套。
下面是一个典型的 CF 示例(图片来自这里):
5. 总结
CF 是viz
中的核心数据结构,它代表某块区域中UI的一帧画面,使用 DrawQuad 来存储 UI 要显示的内容。它代表了 viz 运行时的数据流。
6. 参考文献
https://keyou.github.io/blog/2020/07/29/how-viz-works/
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
相关推荐: KubeSphere 社区双周报 | OpenFunction v1.2.0 发布 | 2023.09.15-09.28
KubeSphere 社区双周报主要整理展示新增的贡献者名单和证书、新增的讲师证书以及两周内提交过 commit 的贡献者,并对近期重要的 PR 进行解析,同时还包含了线上/线下活动和布道推广等一系列社区动态。 本次双周报涵盖时间为:2023.09.15-20…