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 线程,Commit
,Activate
,Submit
运行在 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
本身可以保存 Childcc::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
对象,此时 Pendin服务器托管网g 树并不会被销毁,而是退化为 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. 参考文献
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.fwqtg.net
Python 累乘函数是一个可以计算一个序列中所有元素的乘积的函数。有多种方法可以实现 Python 累乘函数,例如: 使用循环结构,如 for 或 while,遍历序列中的每个元素,并用一个变量存储累乘的结果。 使用递归函数,即自己调用自己的函数,将序列中的…