Chromium 硬件加速合成

优质
小牛编辑
133浏览
2023-12-01

在介绍硬件加速合成(hardware accelerated compositing)之前,让我们先大概了解一下基本的做法是如何渲染生成网页内容的。 一个网页通常可以包含很多层,这个我们在WebKit渲染基础中讲过,例如有透明效果的节点, Canvas节点等,这些节点都可以是页面中的一层,这些层的内容最后组成一个可视化的网页内容,如下图所示。

enter image description here

在没有硬件加速的情况下,浏览器通常是依赖于CPU来渲染生成网页的内容,大致的做法是遍历这些层,然后按照顺序把这些层的内容依次绘制在一个内部存储空间上(例如bitmap),最后把这个内部表示显示出来,这种做法就是软件渲染(software rendering),我们会有专门针对它的介绍。

随着GPU硬件能力的增强,包括在很多小型设备上也是如此,浏览器可以借助于其处理图形方面的性能来对渲染实现加速,本章要介绍的就是利用GPU来实现对页面分层渲染并加速合成网页的可视化结果。

前面我们已经介绍了WebKit中对于渲染所做的一些基础设施,包括Render树和RenderLayer树,对于每一个RenderLayer,我们可以为其单独创建一块内部存储(有些情况下可以为多个layer创建同一块存储),这些存储会被用来保存该层中的内容,浏览器最后会把这些所有的层通过GPU合成起来,形成最终网页渲染的内容,这就是硬件加速合成。

本节主要介绍WebKit中为硬件加速所做的另外一些基础设施,接着是chromium中为硬件加速合成所做的支持,最后介绍chromium中最新的合成器:chromium compositor(cc)。

WebKit 的支持

在建立DOM树后,如前面所述, WebKit会创建相应的Render树和RenderLayer树。如果启用“加速合成(accelerated_compositing)”选项,WebKit会做一些特别的处理。

它为每个RenderLayer创建一个RenderLayerBacking,这个类非常的有用,它是用来管理和控制RenderLayer的合成相关的事情,决定对该层是否做优化,同时包含了很多GraphicsLayer的对象。GraphicsLayer类是一种抽象,用于表示有后端存储(backing store)的渲染surface,同时,也包括作用于之上的各种变换(transformation)和动画(animation)。 RenderLayerBacking类中包含的这些GraphicsLayer有内容层,可能还有前景层(Foreground),剪切层(clipping)等。下图中的WebKit部分是WebKit中为渲染和硬件加速所涉及的类,在此一一介绍。

  • RenderLayerCompositor: WebKit中渲染部分‘掌控大局’的类, 管理RenderLayer树结构,它通过浏览器的设置来决定是否创建RenderLayer以及是否硬件加速合成,同时也决定是否为RenderLayer创建RenderLayerBacking
  • RenderLayerBacking:用来管理和控制相对应的RenderLayer的合成行为,包含很多GraphicsLayer对象,这些对象用于表示层内容,前景(foreground)内容等等
  • GraphicsLayer: 用于表示有后端存储(backingstore)的渲染surface及其相应的变换等,该类是一个抽象类,不同平台的移植有不同的实现

enter image description here

Chromium的支持

WebKit提供了基础设施, 但要实现硬件加速合成,各个移植还有很多事要做,chromium也是如此,见上图中的Chromium部分。 大致地,可以把Chromium中的关于硬件加速合成部分分成两块,第一块是对WebKit接口的具体实现,第二块是Chromium合成的管理和实现部分。 第一块包括上图中Chromium部分的两个类GraphicsLayerChromium和LayerChromium: GraphicsLayerChromium:继承自GraphicsLayer,实现了GraphicsLayer的虚函数接口,提供了chromium自已的移植。 LayerChromium:Chromium中的PlatformLayer类,具体的就是chromium的实际的被合成层的基类,被GraphicsLayerChromium所创建。LayerChromium有多个子类,例如对WebGL和video支持的WebGLLayerChromium,VideoLayerChromium类 第二块则是Chromium部分的CC开头的类和LayerRenderChromium: LayerRendererChromium:一个很重要的类,负责实现合成功能,利用GL来绘制被合成的各个CCLayerImpl层 CCLayerImpl:该类主要是负责一个层的绘制(drawing)工作,由LayerChromium创建,同样构成一个树型结构,被CCLayerTreeHostImpl来管理。每个对象可以有自己的渲染surface对象,也可以使用其祖先的,这取决于具体的策略。 CCRenderSurface:渲染的surface类,被CCLayerImpl所创建和使用,有自己的存储后端 CCLayerTreeHost: 一个CCLayerImpl树的客户端类,用于响应Chromium的合成请求后,处理LayerChromium准备工作,并把请求最后传递给CCProxy,由其转发给具体的实现者,该类被主线程所调用 CCProxy:一个代理抽象类,用于将主线程的合成操作桥接到具体的合成器实现,合成器的线程可能并非主线程。 CCSingleThreadProxy:CCProxy的子类,该类表明合成任务没有在单独的线程中执行,该类将客户端的请求发送到合成的具体类CCLayerTreeHostImpl,并响应合成器调用客户端的回调函数 CCThreadProxy:CCProxy的子类,将合成任务放在单独的新线程中执行, 目前没有被使用 CCLayerTreeHostImpl:该类主要管理CCLayerImpl树及相应的状态,接受来自CCProxy的请求,将实际的合成任务交给LayerRendererChromium来处理 在早期的版本中,Chromium中对合成的支持主要就是LayerRenderChromium,该类负责处理所有关于合成方面的工作。后来, Chromium新引入了CC(chromium compositor)的设计。

Chromium Compositor

为了把合成这一功能独立出来,chromium设计了一套新的合成架构chromium compositor(后面简称CC)。

Chromium定义了自己的层(CCLayerImpl)和层的后端存储方式(CCRenderSurface),所以它的合成任务是基于它们来完成。从架构上来讲,新的CC设计了一套更加灵活的机制来实现合成:合成的任务可以放在独立的线程中来做,而不仅仅是当前线程,该机制的好处是合成不会阻碍当前线程的响应,比较灵活。

结合上面的类图,我们来理解一下其内部的机制。简单来讲,可以把合成分成调用端和实现端。调用段由CCLayerTreeHost来提供相应的接口,被render的主线程调用,当需要合成时,就会调用CCLayerTreeHost::composite来完成, 这里它可以被看成调用端。但是实际的实现者却不是该类,而是CCLayerTreeHostImpl(和LayerRenderChromium),它们是实现端。它们这两端如何连接起来呢?答案是CCProxy,它提供了一组抽象的接口,而它的子类(CCSingleThreadProxy和CCThreadProxy)来决定具体的实现,它们可以让合成在调用端的线程来完成,也可以创建一个独立的线程来完成。目前,在Renderer进程中的合成是在主线程中来完成的。

假以时日,我们有理由相信, chromium compositor应该会被抽取出来,形成一个独立的库(2012-11-12更新:该部分已经成为一个独立的库),这样它会被同时用在网页的合成和chromium主界面的合成,借助于灵活的架构,合成的工作也不会影响正常的主界面对用户的响应。

## GPU硬件加速合成过程

在了解这些基础模块类之后,下面让我们来看看合成是如何来完成的。下图给出的是一个完整的Renderer进程的合成过程。

enter image description here

来解释一下这个过程。Browser进程或者页面发起一个失效(Invalidate)的请求后,Renderer进程会调度一个任务来更新相关区域。当该任务被调度执行时,如果硬件加速合成被打开后,Renderer进程便开始执行对网页的合成。

合成任务由CC来管理和执行的。首先,调用CCSingleThreadProxy::commitIfNeeded,它主要做两件事,第一件,CCLayerTreeHost::updateLayers为合成做更新LayerChromium的准备工作,包括创建RenderSurface,设置绘制的区域,计算和设置需要更新的层。第二件,如果CCLayerImpl树来没有建立的话,那么就根据LayerChromium树来建立它。

再次,就是来绘制并合成相关的层,CCLayerTreeHostImpl::drawLayers基于CCLayerImpl树做绘制工作,把需要的绘制的所有层以此重新绘制并合成,该任务由LayerRenderChromium类负责处理完成(调用GPU Process来完成GL的绘制)。

最后,调用swapbuffers把命令发给GPU进程用来交换前后端buffer,完成了页面的显示。

源文件目录

third_party/WebKit/Source/WebCore/platform/graphics/chromium/
         与硬件加速相关的chromium移植的相关类
third_party/WebKit/Source/WebCore/platform/graphics/chromium/cc
         Chromium新的合成器CC,包含所有CC的基础和管理类
cc/
       最新的CC部分的相关代码,已经成为一个新的独立的库

参考文献

  1. http://www.chromium.org/developers/design-documents/aura/graphics-architecture
  2. http://www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome
  3. http://www.chromium.org/developers/design-documents/rendering-architecture-diagrams