1. Chromium 的渲染流水线

Blink —> Paint -> Commit -> (Tiling ->) Raster -> Activate -> Draw(Submit) —> Viz

Blink 对接 cc 的绘制接口进行 Paint,Paint 生成 cc 模块的数据源(cc::Layer),CC 将数据源进行合成,经过一系列过程最终在 Draw 阶段将合成的结果(viz::CompositorFrame)提交到 Viz。 也就是说,Blink 负责网页内容的绘制,cc 负责将绘制的结果进行合成并提交到 Viz

Blink 负责浏览器网页部分的绘制,//ui/views 模块负责浏览器非网页部分 UI 的绘制,它们的绘制都对接 cc。如果我们扩展 //ui/views 的能力,就可以在 cc 之上建立一套新的 UI 框架,比如我们把这套新的 UI 框架叫做 Flutter。真正的 Flutter 当然没有这么简单,但其实 Flutter 最初是由 Chromium 的开发人员建立的(信息来自网络,未求证),所以 Flutter 渲染流水线或多或少的会吸取 Chromium 渲染的经验。最近 UC 内核团队开始投入 Flutter 的研究也算他们团队的一个新方向了。

2. cc 的架构设计

cc 的设计相对简单,它不直接涉及跨进程操作(它依赖的 SharedImage 以及 Viz 负责夸进程处理),可以理解为一个简单的多线程异步流水线。运行在 Browser 进程中的 cc 负责合成浏览器非网页部分的 UI,运行在 Renderer 进程中的 cc 负责网页的合成。

在 How cc Works 中给出的下图能很好的反应 cc 的核心逻辑:

cc 的多线程体现在不同阶段运行在不同的线程中,Paint 运行在 Main 线程,CommitActivateSubmit 运行在 Compositor 线程,而 Raster 运行在专门的 Raster 线程。

不同的 cc client 有自己的 paint 方式,比如 views 和 blink 都是 cc 的 client,他们有各自不同的 paint 逻辑,但是其他比如 commit,raster 等都是一样的逻辑,因此如果要学习 cc 可以从 views 入手,不必一开始就深入到 blink 中。

下面是基于这个 demo 的 cc 类图:

图中蓝色表示核心数据结构,用于存储数据,橙色表示核心类/接口,用于执行核心逻辑。

3. Paint

Paint 用于产生 cc 的数据源,生成 cc::Layer 树。一个 cc::Layer 表示一个矩形区域内的 UI。它有很多子类,用于存储不同类型的 UI 数据,下面简单介绍一下:

  • cc::PictureLayer 用于实现自绘型的 UI 组件,比如上层的各种 Button,Label 等都可以用它来实现。它允许外部通过实现 cc::ContentLayerClient 接口提供一个 cc::DisplayItemList 对象,它表示一个绘制操作的列表,记录了一系列的绘制操作,比如画线,画矩形,画圆等。通过 cc::PaintCanvas 接口可以方便的创建复杂的自绘 UI。cc::PictureLayer 还是唯一需要 Raster 的 cc::Layer,关于 Raster 见下文。它经过 cc 的流水线之后转换为一个或多个 viz::TileDrawQuad 存储在 viz::CompositorFrame 中。
  • cc::TextureLayer 对应 viz 中的 viz::TextureDrawQuad,所有想要使用自己的逻辑进行 Raster 的 UI 组件都可以使用这种 Layer,比如 Flash 插件,WebGL等。
  • cc::SurfaceLayer 对应 viz 中的 viz::SurfaceDrawQuad,用于嵌入其他的 CompositorFrame。Blink 中的 iframe 和视频播放器可以使用这种 Layer 实现。
  • cc::UIResourceLayer/cc::NinePatchLayer 类似 TextureLayer,用于软件渲染。
  • cc::SolidColorLayer 用于显示纯色的 UI 组件。
  • cc::VideoLayer 以前用于专门显示视频,被 SurfaceLayer 取代。

Blink 和 //ui/views 等上层 UI 绘制引擎通过以上各种 cc::Layer 来描述 UI 并实现和 cc 的对接。由于 cc::Layer 本身可以保存 Child cc::Layer,因此给定一个 Layer 对象,它实际上表示一棵 cc::Layer 树,这个 Layer 树即主线程 Layer 树,因为它运行在主线程中,并且主线程有且只有一棵 cc::Layer 树。

下面是执行 Paint 的堆栈,这里是调用 //ui/views 组件的堆栈,如果是 Blink 则 aura 和 views 命名空间下的堆栈会换成 blink。

blink 的如下:

4. Commit

Commit 阶段的核心作用是将保存在 cc::Layer 中的数据提交(PushPropertiesTo)到 cc::LayerImpl 中。cc::LayerImpl 和 cc::Layer 一一对应,只不过运行在 Compositor 线程中(也称为 Impl 线程)。在 Commit 完成之后会根据需要创建 Tiles 任务,这些任务被 Post 到 Raster 线程中执行。

下面是 Commit 部分的堆栈:

 5. Tiling+Raster

在 Commit 阶段创建的 Tiles 任务(cc::RasterTaskImpl)在该阶段被执行。Tiling 阶段最重要的作用是将一个 cc::PictureLayerImpl 根据不同的 scale 级别,不同的大小拆分为多个 cc::TileTask 任务。Raster 阶段会执行每一个 TileTask,将 DisplayItemList 中的绘制操作 Playback 到 viz 的资源中。 由于 Raster 比较耗时,属于渲染的性能敏感路径,因此Chromium在这里实现了多种策略以适应不同的情况。这些策略主要在两方面进行优化,一方面是 Raster 结果(也就是资源)存储的位置,一方面是 Raster 中 Playback 的方式。这些方案被封装在了 cc::RasterBufferProvider 的子类中,下面一一进行介绍:

  • cc::GpuRasterBufferProvider 使用 GPU 进行 Raster,Raster 的结果直接存储在 SharedImage 中(可以理解为 GLTexture,详细信息见 GPU SharedImage)。
  • cc::OneCopyRasterBufferProvider 使用 Skia 进行 Raster,结果先保存到 GpuMemoryBuffer 中,然后再将 GpuMemoryBuffer 中的数据通过 CopySubTexture 拷贝到资源的 SharedImage 中。GpuMemeoryBuffer 在不同平台有不同的实现,也并不是所有的平台都支持,在 Linux 平台上底层实现为 Native Pixmap(来自X11中的概念),在 Windows 平台上底层实现为 DXGI,在 Android 上底层实现为 AndroidHardwareBuffer,在 Mac 上底层实现为 IOSurface。
  • cc::ZeroCopyRasterBufferProvider 使用 Skia 进行 Raster,结果保存到 GpuMemoryBuffer 中,然后使用 GpuMemoryBuffer 直接创建 SharedImage。
  • cc::BitmapRasterBufferProvider 使用 Skia 进行 Raster,结果保存到共享内存中。

Raster 最终会产生一个资源,该资源被(间接)记录在了 cc::PictureLayerImpl 中,他们会在最终的 Draw 阶段被放入 CompositorFrame 中。

除了 cc::PictureLayer 的 Raster 之外,在该阶段还会进行图片的解码,关于这部分的详细信息以后如果有时间再补充。

TODO: 补充 GpuMemoryBuffer 和 图片解码相关内容。

下面是抓取到的 cc::RasterTaskImpl 的执行流程:

 6. Activate

在 Impl 端有三个 cc::LayerImpl 树,分别是 Pending,Active,Recycle 树。Commit 阶段提交的目标其实就是 Pending 树,Raster 的结果也被存储在了 Pending 树中。

在 Activate 阶段,Pending 树中的所有 cc::LayerImpl 会被复制到 Active 树中,为了避免频繁的创建 cc::LayerImpl 对象,此时 Pending 树并不会被销毁,而是退化为 Recycle 树。

和主线程 cc::Layer 树不同,cc::LayerImpl 树并不是自己维护树形结构的,而是由 cc::LayerTreeImpl 对象来维护 cc::LayerImpl 树的。三个 Impl 树分别对应三个 cc::LayerTreeImpl 对象。

下面是 Activate 阶段的执行堆栈:

 7. Draw

Draw 阶段并不执行真正的绘制,而是遍历 Active 树中的 cc::LayerImpl 对象,并调用它的 cc::LayerImpl::AppendQuads 方法创建合适的 viz::DrawQuad 放入 CompositorFrame 的 RenderPass 中。cc::LayerImpl 中的资源会被创建为 viz::TransferabelResource 存入 CompositorFrame 的资源列表中。至此一个 viz::CompositorFrame 对象创建完毕,最后通过 cc::LayerTreeFrameSink 接口将该 CompositorFrame 发送到给 viz 进程(GPU进程)进行渲染。

下面是 Draw 阶段的运行堆栈:

 8. Scheduler 调度器

上面已经介绍了 cc 渲染流水线的各个过程,这些过程是由 cc::Scheduler 进行调度的。它控制 cc 在合适的时机执行合适的动作,内部维护了一个渲染的状态机, 其核心调度逻辑在 cc::Scheduler::ProcessScheduledActions() 中,代码如下:

 9. 参考文献

Chromium CC渲染层工作流详解的更多相关文章

  1. ISO七层模型详解

    ISO七层模型详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在我刚刚接触运维这个行业的时候,去面试时总是会做一些面试题,笔试题就是看一个运维工程师的专业技能的掌握情况,这个很 ...

  2. 3dmax联机分布式渲染方法技巧详解

      3dmax联机分布式渲染方法技巧详解 \测试环境:win7系统 3DMAX2009 Vray2.0 .首先要保证你的两台电脑能在局域网里互相访问如图: 其他电脑上也一样都能打开对方的电脑! 步! ...

  3. 微信小程序开发教程(八)视图层——.wxml详解

    框架的视图层由WXMKL(WeiXin Markup language)与WXSS(WeiXin Style Sheet)编写,由组件进行展示. 对于微信小程序而言,视图层就是所有.wxml文件与.w ...

  4. 14.LINUX-platform机制实现驱动层分离(详解)

    版权声明:本文为博主原创文章,未经博主允许不得转载. 本节目标:        学习platform机制,如何实现驱动层分离 1.先来看看我们之前分析输入子系统的分层概念,如下图所示: 如上图所示,分 ...

  5. Transformer各层网络结构详解!面试必备!(附代码实现)

    1. 什么是Transformer <Attention Is All You Need>是一篇Google提出的将Attention思想发挥到极致的论文.这篇论文中提出一个全新的模型,叫 ...

  6. 微信小程序开发教程(七)逻辑层——.js详解

    逻辑层,是事务逻辑处理的地方.对于小程序而言,逻辑层就是.js脚本文件的集合.逻辑层将数据进行处理后发送给视图层,同时接收视图层的事件反馈. 微信小程序开发框架的逻辑层是由JavaScript编写.在 ...

  7. 微信小程序开发教程(九)视图层——.wxss详解

    WXSS是一套样式语言,用于描述WXML的组件样式. 官方文档表示,WXSS的选择器目前支持(“.class”.“#id”.“elemnt”.“element,element”.“::after”.“ ...

  8. OSI七层模型详解 TCP/IP协议

      总结 OSI中的层 功能 TCP/IP协议族 应用层 文件传输,电子邮件,文件服务,虚拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等 表示层 数据格式化,代码转 ...

  9. python网络编程-OSI七层模型详解

    OSI 七层模型通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通讯,因此其最主要的功能就是帮助不同类型的主机实现数据传输 . 完成中继功能的节点通常称为中继系统.在OSI七层模型中,处于 ...

  10. OSI7层模型详解

    首先我们借用百度百科上的图片来基本了解一下OSI7层模型的名称以及结构.下面我将从最底层一层一层往上介绍. 物理层:基于Bit传输,是属于物理信道,最基本的机械.电子.定时接口通信信道. 数据链路层: ...

随机推荐

  1. 【转载】Linux虚拟化KVM-Qemu分析(十一)之virtqueue

    转载自: 作者:LoyenWang 出处:https://www.cnblogs.com/LoyenWang/ 公众号:LoyenWang 版权:本文版权归作者和博客园共有 转载:欢迎转载,但未经作者 ...

  2. Redis的设计与实现(5)-整数集合

    整数集合(intset)是集合键的底层实现之一: 当一个集合只包含整数值元素, 并且这个集合的元素数量不多时, Redis 就会使用整数集合作为集合键的底层实现. 整数集合 (intset) 是 Re ...

  3. 好用的windows工具分享(20个)

    1.截图.画图全屏截图快捷键:printSreen窗口截图快捷键:alt+printSreen自定义截图工具:win+shift+s画图快捷键:cmd - mspaint可配合画图工具实现图片的编辑, ...

  4. OO第一次大作业

    前言 前言的前言 这是我的第一篇blog,有点小激动,我还找教程设置了一下我的背景,本来还想弄个页面小宠物,但是看了一下感觉有点复杂,下次一定.如果对我blog的内容有任何修正或者建议可以评论让我知道 ...

  5. YUM histoy 与 RPM -qa --last

    查看Linux yum安装包的安装时间,可以使用以下命令: rpm -qa --last 该命令将显示已安装的所有rpm包及其安装日期和时间. 可以使用管道符 '|' 和 grep 命令来查找特定的包 ...

  6. 2021-7-7 VUE笔记2

    if实例 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <scri ...

  7. PLE-实践小结-2308-cnblogs

    某场景介绍 前状:三模型,权重融合 解决问题:融合目标行为,充分利用样本信息,节省资源开销. 当前效果 主场景人均真实曝光+0.26%,不显著:子场景人均真实曝光+0.35%,不显著 千曝互动+2.6 ...

  8. [windows]远程桌面失败提示CredSSP加密修正

    前言 windows远程桌面失败,提示"CredSSP加密--" 远程桌面服务器的系统版本:Windows Server 2016 本地电脑的系统版本:Windows 10 方式1 ...

  9. 《CUDA编程:基础与实践》读书笔记(2):CUDA内存

    1. 全局内存 核函数中的所有线程都能够访问全局内存(global memory).全局内存的容量是所有设备内存中最大的,但由于它没有放在GPU芯片内部,因此具有相对较高的延迟和较低的访问速度,cud ...

  10. BUUCTF-RE-[BJDCTF2020]BJD hamburger competition

    啊这,点进去康康 dnspy反编译的题,https://www.52pojie.cn/thread-495115-1-1.html 里面有详细介绍 然后文件很多,我不知道找哪一个下手 看其他师傅的wp ...