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. 通信原理知识点总结(XDU网信通原)

    因为感觉第2章和第7章内容特别乱,当时老师讲的时候好像也没有按照一个正确的顺序来讲,所以我就把这两部分的内容按照结构顺序整理了一下,这样更便于理解和记忆 第2章 无线信道传输特性 显示不全点链接看完整 ...

  2. 输入平方米的三种方式㎡ m2 m²

    如何在Word中输入平方米字符? 第1种方法 Win10自带输入法,输入"平方米",默认第5个就出来了㎡,也可以直接复制使用. 这种方式最直接,字母m和右上角的2是1个字符,所以不 ...

  3. KVM vm time setting

    - config file $ grep clock vm02.xml <clock offset='utc'> </clock> - NTP server $ s vm02 ...

  4. Libvirtd networks -- 为libvirtd 中虚拟机指定ip遇到的问题

    backgroup 为libvirtd 中虚拟机指定ip,操作如下: virsh --connect qemu:///system dumpxml vm01 | grep "mac addr ...

  5. 为什么 PostgreSQL 的适用性很强?

    说起使用数量最大的数据库SQLite 它是全球最广泛部署的数据库引擎.它存在于你的手机中,存在于你的浏览器中,如果你搜索你的电脑,你也会在其中找到它的 .db 文件.SQLite 受到 Postgre ...

  6. Confluence 挖矿病毒 升级现有系统

    Confluence 挖矿病毒 升级现有系统 背景 服务器很多服务都很卡,通过检查发现是一起运行的confluence异常,被挖矿病毒挖矿,华为云和官网也有说明. 知道问题之后,处理方式就是将现有的问 ...

  7. Fastjson1.2.24漏洞复现-基于vulhub漏洞平台(文件上传写入-反弹shell)

    Fastjson1.2.24漏洞复现-基于vulhub漏洞平台 环境准备: 192.168.59.130 攻击机 window10 192.168.59.135 靶机 centos8 声明:不涉及互联 ...

  8. redis 中的 set

    set是String中的无序集合  底层是 是 value为null 的hash表  时间复杂化是o(1): sadd  k1 v1 v2 v3  set中添加数据 smembers k1 取出set ...

  9. html中的一些常用标签与标签属性

    label for属性 定义和用法 for 属性规定 label 与哪个表单元素绑定. <span> <label for="username">用户账号& ...

  10. Django:TypeError: view must be a callable or a list/tuple in the case of include().

    错误: path("uploads/(?P<path>.*)$", 'django.views.static.serve', {"document_root& ...