How cc Works 中文翻译 浏览器的图层 layer 如何合成

How cc Works

中文

 cc readme文档:cc readme - Bigben - 博客园 (cnblogs.com)

Original google doc

Chinese | Korean

 

Chromium 的工程师们写了两篇技术文章 How Blink Works (中文译文) 和 How cc Works,分别介绍了 Chrome 浏览器内核内部的两个重要模块 Blink 和 cc 内部设计和实现的一些细节。对于想要了解 Chromium 内核内部实现的同学,这两篇文章提供了不错的入门指引。在征得作者同意后,我将其翻译成中文,以馈读者。

文中部分术语觉得难以翻译成合适的中文的,我会保留原文。对于一些较为复杂的概念会根据自己的理解给出一些额外的解释。如果有我理解有误,没有正确翻译的地方,也请读者留言指出。

另外,How cc Works 的内容比较深,要看明白这篇文章的内容,多少需要对网页渲染有一定的了解,起码需要理解光栅化,合成,图层,分块这些概念,需要理解软件合成和 gpu 合成,软件光栅化和 gpu 光栅化的区别。

Chromium 目前实际支持三种不同的光栅化和合成的组合方式:软件光栅化 + 软件合成,软件光栅化 + gpu 合成,gpu 光栅化 + gpu 合成。在移动平台上,大部分设备和移动版网页使用的都是 gpu 光栅化 + gpu 合成的渲染方式,理论上性能也最佳。

tl;dr

cc/ is historically but inaccurately called the Chrome Compositor. It's neither “the” chrome compositor (of course we have many), nor a compositor at all any more. danakj suggests “content collator” as an alternative name.

cc is embedded via ui/compositor or Android code in the browser process, as well as ui/compositor in mus utility processes. It is also embedded in the renderer process via Blink / RenderWidget. cc is responsible for taking painted inputs from its embedder, figuring out where and if they appear on screen, rasterizing and decoding and animating images from the painted input into gpu textures, and finally forwarding those textures on to the display compositor in the form of a compositor frame. cc also handles input forwarded from the browser process to handle pinch and scroll gestures responsively without involving Blink.

cc被使用的地方包括

  • 在chrome ui界面(即非网页部分,菜单栏,状态栏,工具栏等),或者安卓 在浏览器进程
  • 在render进程绘制网页。把各种元素,图片解码绘制到gpu textures

 

Process / thread architecture

由于cc的多线程和单线程版本实现的切换。需要有个代理,方便转换。

browser用单线程,因为它的主线程任务轻;而render用多线程,它的main thread就是blink任务繁重。单线程或者多线程都用cc::Scheduler决定什么时候提交帧。一个例外是在某些测试用例中,用  LayerTreeHost::Composite,而不通过scheduler。这是历史原因也为了方便控制流程。

cc can be embedded in both single-threaded and multi-threaded incarnations. The single-threaded version has less overhead. The multi-threaded version incurs a latency cost, but allows for input and animations to be responsively handled on one thread while the other thread is busy. In general, the browser uses the single-threaded version as its main thread is cheap and light, whereas the renderer uses the multi-threaded version as its main thread (Blink) can be quite busy on some pages.

Both single and multi-threaded versions drive themselves using the cc::Scheduler, which determines when to submit frames. The one exception (a third mode that is only used in one place) is Blink layout tests and sim tests, which do not (always) use a scheduler and tell cc when to composite synchronously, via LayerTreeHost::Composite. This is for historical reasons, and also to have more control during testing.

Content Data Flow Overview

data flow diagram

The main interface to cc by embedders is a LayerTreeHost (created with various LayerTreeSettings) and a tree of Layers. A Layer is a rectangle of content, with various properties describing how that content should appear on screen. cc is responsible for turning a painted representation of that content (e.g. a PaintRecord) into a rasterized representation (a software bitmap or a gpu texture) and figuring out where that rectangle appears on screen.

嵌入的浏览器内容展示部分,它利用主要的cc接口是LayerTreeHost和 layer tree。cc的主要用来将网页内容的绘制指令,比如 paintRecord,进行光栅化(通过软件或者gpu),并且最终将它显示在正确的屏幕位置上。

cc turns this layer tree input into a set of property trees via a PropertyTreeBuilder and simplifies the layer tree down to an ordered list of visible layers. As a part of the slimming paint project, Blink will set property trees and layer list directly instead of going through the more historical layer tree interface and avoid the work of this part of the pipeline.

cc将输入的layer tree通过PropertyTreeBuilder转化为属性树和可视层的有序列表。

在slimming paint优化项目中,blink直接生成属性树和layer有序列表,而不用像以前需要遍历父类layer才能决定绘制的属性。 

During the commit process, cc forwards all of the inputs from the main thread data structures to compositor thread data structures. At this point, cc determines which parts of each Layer are visible and proceeds to decode images and raster content. Once all the content is ready to appear on screen, cc activates the committed tree and can then “draw” from it.

将主线程的输入同步到合成线程。然后cc决定哪块是可视化的,解码图片。执行光栅化。一旦所有内容都准备好出现在屏幕,cc将激活提交的树并且draw(这里draw的意思是生成quad,见后面)它。

cc unfortunately still uses the language of “draw” and “swap” in a number of places, despite the fact that it no longer does either of these things. “Draw” in cc means constructing a compositor frame full of quads and render passes for eventual drawing on screen. “Swap” in cc means submitting that frame to the display compositor via a CompositorFrameSink. These frames get sent to the viz SurfaceAggregator where compositor frames from all frame producers are aggregated together.

draw方法在cc中意味着构建compositor frame,它由quads即矩阵构成,组合成 RenderPass。renderPass最终可以被draw在屏幕上。

Swap在cc中意思是通过CompositorFrameSink提交compositor frame到display compositor。这些frames发送到了viz GPU进程的SurfaceAggregator 最终聚合到一起。

cc的一个输入是 layer tree;另外一种输入就是用户的输入,比如鼠标,键盘,触摸。

Input Data Flow Overview

The other main piece of input to cc is user input, such as mouse clicks, mouse wheels, and touch gestures. In the renderer process, input is forwarded from the browser process. It is processed by ui::InputHandlerProxy (a cc::InputHandlerClient).

Some of this input is forwarded to the LayerTreeHostImpl (a cc::InputHandler) at specific times. This allows it to modify the active layer’s property tree and scroll or pinch as needed. Some input can’t be handled by the compositor thread (e.g. there’s a synchronous Javascript touch or wheel handler) and so that input is forwarded along to Blink to handle directly. This input flow follows the opposite path of the content data flow in the section above.

cc的另一个主要输入是用户输入,如鼠标点击、鼠标滚轮和触摸手势。从browser进程转发到renderer进程,通过 ui::InputHandlerProxy (继承自a cc::InputHandlerClient)进行处理.

其中一些输入在特定时间转发到LayerTreeHostImpl(一个cc::InputHandler)。这允许它修改活动层的属性树并根据需要滚动或捏合。有些输入不能由合成器线程处理(例如,有一个同步的Javascript触摸或滚轮处理程序),因此该输入被转发到Blink直接处理。这种输入流程与上面内容数据流的路径相反。。他们都是在renderer进程中。

To summarize the input data flow:

  1. User input is forwarded from the browser process to the renderer process.
  2. Input is processed by ui::InputHandlerProxy (a cc::InputHandlerClient).
  3. Some input is forwarded to LayerTreeHostImpl (a cc::InputHandler) to modify the active layer's property tree and handle scroll or pinch actions.
  4. The input that can't be handled by the compositor thread is forwarded to Blink for direct handling.

The key takeaway is that input is processed by the ui::InputHandlerProxy and then forwarded to either LayerTreeHostImpl for handling by the compositor thread or to Blink for direct handling, depending on the type of input and its compatibility with the compositor thread chromium.googlesource.com.

Commit Flow

Commit is a method of getting data atomically from the main thread to the compositor thread. (Even when running in single threaded mode, this operation occurs to move data into the right data structures.) Rather than sending an ipc, commit is done by blocking the main thread and copying data over.

commit flow diagram

主线程有几种方式发起的向合成线程请求提交。大多数页面都是通过requestAnimationFrame,最终会调到在LayerTreeHost 的 SetNeedsAnimate。

主线程可以通过几种方式请求提交。大多数网页通过requestAnimationFrame发起请求,最终调用LayerTreeHost上的SetNeedsAnimate。此外,修改cc的任何输入(例如层属性,如其变换或层内容的更改),都会在LayerTreeHost上调用SetNeedsAnimate、SetNeedsUpdate或SetNeedsCommit。不同的SetNeeds函数允许在确定他们的回调函数里没有工作需要做时进行不同级别的提前退出提交。(例如,如果requestAnimationFrame回调没有做任何工作,那么就没有必要进行提交甚至更新层。)如果这些函数当前没有在处理,所有这些函数都会向调度程序scheduler发起BeginMainFrame请求

在某个时刻,调度程序会响应ScheduledActionBeginMainFrame。这会将BeginFrameArgs从合成器线程发送到主线程,以启动BeginMainFrame。BeginFrameArgs包含一个时间(用于动画目的)以及合成器线程上应用的任何滚动增量(主要是由于处理用户滚动手势而产生的),Blink不知道这些增量。当Blink嵌入cc时,BeginMainFrame将任何合成器滚动增量应用于Blink,启动requestAnimationFrame逻辑,并完成Blink渲染生命周期的一半。

The main thread can request a commit in several ways. Most webpages request one via requestAnimationFrame, which eventually calls SetNeedsAnimate on LayerTreeHost

Additionally, modifying any of cc’s inputs (e.g. a layer property, such as its transform or a change to the layer’s content), will call either SetNeedsAnimate, SetNeedsUpdate, or SetNeedsCommit on LayerTreeHost.

The different SetNeeds functions allow for different levels of early-out aborts of the commit if no work is determined to be needed. (For example, if the requestAnimationFrame callback does no work, then there’s no need to do the commit or even update layers.) All of these functions request a BeginMainFrame from the scheduler, if not currently in the middle of servicing one.

At some point, the scheduler will respond with a ScheduledActionBeginMainFrame. This sends BeginFrameArgs from the compositor thread to the main thread to start a BeginMainFrame. BeginFrameArgs contain a time (for animation purposes) as well as any scroll deltas that have been applied on the compositor thread (mainly as a result of handling user scroll gestures) that Blink doesn’t know about. When Blink is embedding cc, a BeginMainFrame applies any compositor scroll deltas to Blink, kicks off requestAnimationFrame logic, and finishes the Blink half of the rendering lifecycle.

Once this is done, cc updates all layers. If at any point in this update pipeline, cc determines that no work left is required (e.g. a compositor thread scroll updates Blink, but Blink makes no changes to the page in response to that scroll), then it may abort the commit early. (Single-threaded cc never aborts commits, currently.) Once the embedder has finished its BeginMainFrame work and if the commit has not been aborted, then ProxyMain sends a synchronous NotifyReadyToCommit and blocks by forwarding a mutex to the compositor thread.

一旦这些完后,cc会更新主线程的所有layers。在这个更新的流水线上任何阶段,如果cc确定不再需要任何工作(比如合成器线程滚动了,需要更新Blink,但Blink的页面内容对该滚动没有做出任何更改),那么cc就可以提前中止提交。(目前,单线程cc从不中止提交。)。一旦完成BeginMainFrame 并且提交没有被中断,ProxyMain(main线程中) 会发一个同步NotifyReadyToCommit调用 并通过转发一个mutex到合成线程而最终自己被mutex锁定阻塞等待。  

When the scheduler is ready to commit, it will respond with a ScheduledActionCommit. The ProxyImpl on the compositor thread then does all the work of copying the data from the main thread (while it is blocked) to compositor thread data structures. It then releases the mutex so that the main thread can proceed.

当调度器准备好提交后,它将响应ScheduledActionCommit。合成器线程上的ProxyImpl然后负责完成从主线程(当它被阻塞时)复制数据到合成器线程数据结构的所有工作。然后它释放互斥锁,以便主线程可以继续。

ProxyImpl是唯一一个能访问主线程和合成线程的数据结构的类(去同步主线程的LayerTreeHost和PropertyTree到合成线程)。它只在主线程被阻塞时访问主线程上的LayerTreeHost和Layers,并通过访问器DCHECKS来强制执行这一点。

ProxyMain是它在主线程上存在的对应于合成线程的副本,并由LayerTreeHost拥有。对于单线程情况,SingleThreadProxy包含了ProxyMain和ProxyImpl的任务。

ProxyImpl is the only class that accesses data structures on both the main thread and the compositor thread. It only accesses the LayerTreeHost and Layers on the main thread when the main thread is blocked and enforces this through accessor DCHECKs. ProxyMain is its counterpart that exists on the main thread and is owned by the LayerTreeHost. For the single threaded case, SingleThreadProxy encompasses the tasks of both ProxyMain and ProxyImpl.

AI 总结:

The commit flow is a crucial part of rendering in a browser, as it ensures data is transferred from the main thread to the compositor thread atomically. This process is responsible for updating layers and handling animations, among other things. In this answer, we will delve into the various ways the main thread can request a commit and the commit flow stages.

There are several ways the main thread can request a commit:

  • Most webpages use requestAnimationFrame, which eventually calls SetNeedsAnimate on LayerTreeHost [1].
  • Modifying any of Chrome's compositor (cc) inputs, such as a layer property (e.g., its transform or a change to the layer's content), will call either SetNeedsAnimateSetNeedsUpdate, or SetNeedsCommit on LayerTreeHost [1].

The different SetNeeds functions allow for different levels of early-out aborts of the commit if no work is determined to be needed [1]. For example, if the requestAnimationFrame callback does no work, there's no need to do the commit or even update layers.

All of these functions request a BeginMainFrame from the scheduler, if not currently in the middle of servicing one [1].

The commit flow can be broken down into several stages:

  1. Scheduler response: At some point, the scheduler will respond with a ScheduledActionBeginMainFrame. This sends BeginFrameArgs from the compositor thread to the main thread to start a BeginMainFrame [1].

  2. BeginMainFrameBeginFrameArgs contain a time (for animation purposes) as well as any scroll deltas that have been applied on the compositor thread (mainly as a result of handling user scroll gestures) that Blink doesn't know about [1].

  3. Blink and cc embedding: When Blink is embedding cc, a BeginMainFrame applies [1].

In summary, the commit flow is an essential mechanism for transferring data atomically from the main thread to the compositor thread, ensuring smooth rendering and updates. The main thread can request a commit in several ways, such as using requestAnimationFrame or modifying layer properties. The commit flow stages include the scheduler response, the BeginMainFrame, and the embedding of cc in Blink.

来自:chromium文档 合成器体系架构:主线程和合成线程 强烈推荐 - Bigben - 博客园 (cnblogs.com)

这两棵树,主线程树和 impl 树彼此独立。impl 树实际上是主线程树的副本,尽管具有不同的数据类型。

定期手动将 impl 树与主线程树同步,这个过程我们称为 commit“提交”。提交是对主树图层的递归遍历,其中我们将“pushPropertiesTo”推送到图层的 impl 端等效物。我们在 impl 线程上执行此操作同时完全阻塞主线程。我们在impl线程执行commit,因为主线程被阻塞了。执行提交的基本逻辑是延迟的。当主树更改时,我们只是记下需要提交(setNeedsCommit)。当图层的内容更改时,例如我们以某种方式更改 HTML div 文本,我们将其视为提交。稍后(在调度程序的自行决定下,稍后讨论),我们决定执行提交。提交是一个阻塞操作,但仍然非常便宜:通常不超过几毫秒。关于我们原始线程模型的旁注:我们假设主线程和 impl 线程都是消息循环。例如,它们具有 postTask 和 postDelayedTask 原语。我们尽量让两个线程尽可能经常处于空闲状态,并且更喜欢异步编程而不是锁定并阻塞线程。提交流程如下

Layers

A layer is a 2d rectangle of content with integer bounds. It has some transform, clip, and effects on it that describe how it should look on screen.

图层是一个具有整数边界的2D矩形内容。它具有一些变换、剪切和效果,描述了它在屏幕上应该如何显示。

有两个独立的layer层次结构,一个对应主线程layer树(派生自Layer);另一个是合成线程的pending,active,recycle的layer树,它们派生自 cc::LayerImpl。大致上是1:1对应的,例如存在SurfaceLayer和SurfaceLayerImpl或PictureLayer和PictureLayerImpl,因此本节将主要同义地谈论这些对。

There are two separate class hierarchies of Layers, one for the main thread layer tree (deriving from cc::Layer) and one for the compositor thread pending, active, and recycle layer trees (deriving from cc::LayerImpl). There is roughly a 1:1 correspondence, such that there exists SurfaceLayer and SurfaceLayerImpl or PictureLayer and PictureLayerImpl, so this section will mostly talk about these pairs synonymously.

On the main thread, Layers are refcounted. LayerTreeHost owns the root layer, and each layer recursively owns its children. Some other parts of Blink also provide layers (e.g. the media system creating surface or video layers, plugins), which is why this is ref-counted. On the compositor thread, layers are unique_ptrs, owned by their parents.

在主线程中,Layers是引用计数的(c++ shared_pointer)。LayerTreeHost 拥有root layer,每个layer再拥有子layer。blink的其他部分也提供其他layer,比如media系统有自己的surface或者 video layer,plugins等。这就是用共享指针的原因。

合成线程,layer都是unique_ptrs 独占指针存储,由他们父类拥有。

Property Trees

两种方式存储属性的层次。图层树方式,在ui模块还使用。子layer继承了父layer的特效(如坐标平移,blur等)。这种递归带来效率等问题。

There are two ways of specifying hierarchical properties in cc. The historical way (and the way that ui/ manages this) is to provide a tree of Layers. If a parent layer has a transform (e.g. a translation, scale, or perspective), a clip, or an effect (e.g. a blur filter, or a mask, or an opacity) then this applies recursively to its children. This abstraction has a lot of corner cases (fixed position layers, scroll parents, scroll children) as well as not being performant (requires traversing a very large tree and calculating all properties at all steps).

属性树是对上面缺点的规避。cc提供各种属性树:transform树,clip树,effect树。每个layer有一个节点id对应到属性树上对应。这样,计算复杂度是O(感兴趣的节点),而不是O(所有layers)。而且不用layers树存储了,只要有序list存layers就行。

Property trees are a way around this. Instead, cc is provided with separate trees of properties: a transform tree, a clip tree, an effect tree. Each layer then has a node id for which transform, clip, and effect node that the layer is using. In this way, the update is O(interesting nodes) instead of O(layers). When there are property trees, there is also no longer a need for a tree of layers, and instead an ordered list of layers can be used.

PictureLayer

包含绘制内容的Layer。这些内容以cc::PaintRecord的形式呈现。PictureLayer负责确定内容应该以哪种比例进行光栅化。每个比例都由一个PictureLayerTiling表示,它是一个稀疏的2D规则平铺,表示特定比例下的内容。

这种平铺中的每个分块是cc::Tile,它表示对应的缩放比例下的内容。它们的光栅化由TileManager组织。如果您在DevTools渲染设置中打开合成层边框,您可以看到瓷砖边框。有许多启发式方法来确定瓷砖大小,但对于软件光栅,瓷砖大约为256x256 px,对于GPU光栅,瓷砖大约为视口宽度x四分之一视口高度。

有许多启发式方法来确定何时以及如何更改光栅化比例。这些并不完美,但请谨慎更改它们。

A layer containing painted content. This content comes in the form of a cc::PaintRecord. PictureLayer is responsible for figuring out which scale(s) the content should be rastered at. Each scale is represented by a PictureLayerTiling, which is a sparse 2d regular tiling of the content at a particular scale.

Each tile in this tiling is a cc::Tile, which represents potential content and their rasterization is organized by the TileManager. If you turn on composited layer borders in the DevTools rendering settings, you can see the tile borders. There are a number of heuristics that determine tile sizes, but for software raster tiles are roughly 256x256 px and for gpu raster tiles are roughly viewport width x one quarter viewport height.

There are a number of heuristics to determine when and how to change rasterization scales. These aren’t perfect, but change them at your own peril. 🐉🐉🐉

Directly composited images

A specialized raster mode of PictureLayerImpl. If a PictureLayer's DisplayItemList consists of a single drawImageRect DrawOp, we use different logic to determine what the raster scale of the image should end up being. The layer is rastered at fixed scales such that scaling this image is performant. The default raster scale is chosen such that the image will raster at its intrinsic side, then that scale is adjusted, based on how close it is to the ideal scale. See PictureLayerImpl::ShouldAdjustRasterScale and RecalculateRasterScales for more details.

PictureLayerImpl的特殊光栅化模式。如果PictureLayer的 DisplayItemList存储的绘制指令只包含一个 drawImageRect DrawOp 绘制操作,即图层只包含一个图片 <img>。光栅化缩放逻辑就特殊化处理了,使用固定缩放比例来使性能优化。具体看PictureLayerImpl::ShouldAdjustRasterScale and RecalculateRasterScales。

TextureLayer

Used for plugins, canvas when it does its own raster, and WebGL. The “texture” here refers to a reference to a gpu texture, though under software compositing it would be a shared memory bitmap.

TextureLayer用在plugin,canvas,webgl。其中前两者是自己光栅化,而不用合成器的光栅化。

Texture在gpu渲染时指gpu texture(gpu中存放bitmap);软渲染时存放在共享位图内存(shared memory bitmap)。

SolidColorLayer

如果一个层光栅化时只有一个颜色,纯色,就不用做光栅化或者分配gpu内存。

If a layer is known to be merely a solid color, then there is no need to spend raster work or gpu memory on it. This is an optimization for when a layer’s content is known to be simple.

VideoLayer

Deprecated as a part of the surfaces for video project. Should eventually be deleted.

SurfaceLayer

A surface layer has a surface id, which refers to some other stream of compositor frames in the system. This is a way of having an indirection to other compositor frame producers. See also: surface documentation. For example, Blink embeds references to out of process iframes via SurfaceLayer.

iframe用SurfaceLayer。

SolidColorScrollbarLayer

Android scrollbars are “solid color” scrollbar layers. They are simple boxes that can be drawn on the compositor without creating texture resources for them. Both solid color and painted scrollbar layers exist so that scrolling on the compositor thread can update the scrollbar responsively without going back to the main thread. Without this, the page would scroll smoothly but the scrollbar would jump around jankily.

Painted(Overlay)ScrollbarLayer

Desktop (non-Android) scrollbars are painted scrollbars. Because theme code is not thread safe, the thumb and track are painted and rastered into bitmaps on the main thread. Then, those bitmaps are emitted as quads on the compositor thread. ChromeOS uses PaintedOverlayScrollbarLayer, which is a nine-patch bitmap version.

HeadsUpDisplayLayer

This layer supports devtools rendering settings. It draws an Frame Rendering Stats, as well as overlays for paint invalidation or damage. This layer is special because it must be updated last because its inputs depend on all of the other layers’ damage calculations.

UIResourceLayer / NinePatchLayer

UIResourceLayer is the software bitmap equivalent of TextureLayer. It handles uploading bitmaps and recreating them as needed when contexts are lost. NinePatchLayer is a derived UIResourceLayer class that dices up a UIResource into stretchable pieces.

Trees: commit / activation

There are four types of layer trees, although there always exists 2-3 at any given time:

  • Main thread tree (cc::Layers, main thread, always exists)

  • Pending tree (cc::LayerImpl, compositor thread, staging for rasterization, optional)

  • Active tree (cc::LayerImpl, compositor thread, staging for drawing, always exists)

  • Recycle tree (cc::LayerImpl, compositor thread, mutually exclusive with pending tree)

These are called “trees” as historically they have been trees and they exist in cc/trees/, but they are all lists and not trees (sorry). The main thread tree of Layers is owned by LayerTreeHost. The pending, active, and recycle trees of LayerImpls are all LayerTreeImpl instances owned by LayerTreeHostImpl.

它们被叫成tree,其实都是list。

主线程中,LayerTreeHost拥有layers tree(list)。

合成线程中,LayerTreeHostImpl有那三个tree。pending tree和recycle tree互斥;pending tree:光栅化存在,可选;

active tree:绘制状态时一直存在。

Commit is the process of pushing layer trees and properties from the main thread layer list to the pending tree. Activation is the process of pushing layer trees and properties from the pending tree to the active tree. During each of these processes, a duplicate layer structure is created (with the same layer ids, layer types, and properties). Layer ids are used to find the corresponding layer on each tree. A layer with id 5 on the main thread tree will push to layer id 5 on the pending tree. That pending layer will push to a layer with id 5 on the active tree. If that layer doesn’t exist, during the push it will be created. Similarly layers that no longer exist in the source tree are removed from the destination tree. This is all done via the tree synchronization process.

提交就是把layer树和属性树从主线程推到pending树的过程。激活是把pending list推到active list的过程。这两个过程都是对以前层结构的复制。因为Layer构建是费时费力的,而且大部分层也不会每帧都变化,一旦激活,即pending tree的内容复制到了 avtive tree 后,pending tree就变成了recycle tree,recycle tree除了作为最后一个pengding tree的缓存外,没有其他用途。这只是优化,避免主线程推送到合成线程时,耗费资源的内存分配和属性树重push工作。

每次都复制层数据结构(层的id,类型,属性不变)。通过layer id同步。不管在main线程中的layer,pending tree还是active tree,他们的id都是一样的。同步时总传全量的id,即网页包含的所有的layer id。然后对比这些id,没有的就创建;如果从main中同步过来的,没有pending上的id,那就删除。

Because allocation of Layer(Impl)s is expensive and most layer tree structures do not change from frame to frame, once a pending tree activates, it becomes the “recycle tree”. This tree is never used for anything except for a cache of the last pending tree. This avoids allocation and property pushing work from main thread to pending tree. This is merely an optimization.

 

The reason the pending tree exists is that if there are multiple changes to webpage content in a single Javascript callstack (e.g. an html canvas has a line drawn on it, while a div moves, and some background-color changes to blue), these all must be presented to the user atomically. Commit takes a snapshot of these changes and pushes them to the pending tree, so that Blink can continue to update the main thread tree for a future commit. After commit, these changes need to be rastered, and all of that rasterization must be complete before any of those new tiles can be presented to the user. The pending tree is the staging area to wait until all of the asynchronous rasterization work is complete. While the pending tree is staging all the rasterization work, the active tree can be updated with animations and scrolling to still be responsive to the user.

pending tree存在是因为js的单个调用可能对网页造成多处变动,这些变动要自动一次全部地呈现给用户。主线程将这些变动做成一个快照提交到pending tree,以便主线程就可以继续更新主线程的layer tree为了将来再次提交。

提交后这些变动需要光栅化。所有变动的tile的光栅化,在tile展现给用户前必须一次性全部完成,而不能部分tile完成就呈现给用户。pending tree就是一个暂存(staging)区域,等待所有异步光栅化完成。当pending tree在光栅化时,active tree还能独立的对图片动画和滚动

Single-threaded versions of cc do not have a pending tree and commit directly to the active tree. (The recycle tree is unused in this mode.) This is an optimization to avoid extra work and copies. To work around this, the active tree is unable to be drawn until its tiles are all ready to draw. However, given that this is a single-threaded version of cc, there are no compositor thread animations or scrolling, and so there is little reason to need to draw.

单线程没有pending tree,也就没有提交。没有合成线程,也没有合成线程的动画和滚动

Raster and tile management

TileManager is responsible for rasterizing the world of tiles. Each PictureLayer provides a set of Tiles to rasterize, where each Tile is a subrectangle of painted content at a particular scale.

一个图层layer对应一个绘制命令集的picutureLayer。每个PictureLayer会提供tile集合去光栅化。每个tile是绘制的layer内容的子块,按一定缩放比生成。

(同一个网页会生成几个不同分变率的PictureLayer。为了加快呈现给用户,先显示快速绘制出的低分辨率的,再换成最终高分辨率的。)

The TileManager finds all the tiles that are required to draw on the active tree, all the tiles that are required to activate on the pending tree, less important tiles that are close to the viewport but are not visible, and also offscreen images to decode.

TileManager负责查找所有需要在 active 树上绘制的tile,所有需要在pending 树上激活的tile,靠近视口但不可见的不太重要的tile,以及需要解码的离屏图像。

有三种光栅化模式:

软件光栅化:直接生成软件bitmaps,在光栅化工作线程中。

gpu光栅化:通过在command buffer上发送gl command,产生gpu纹理

oop光栅化:即进程外渲染,同上,只不过发送的是Paint command。即skia的paint 指令。未来由gpu进程再去光栅化。

TileManager 采取哪种模式取决于提交compositor frames的 LayerTreeFrameSink 是否有context上下文提供。LayerTreeFrameSink 是用来提交compositor frames合成帧。

取决于RasterContextProvider的能力。看下面的display合成模式。即在软件合成下,只能是软件光栅化;硬件合成模式时,可以是软件或者硬件光栅化。

gpu光栅化在chrome 114版本已经废弃。现在的gpu光栅化就是以前的oop光栅化模式。

切换模式产生原因是在gpu崩溃后太频繁会切回软件合成模式。

一旦 TileManager 确定了下一个要完成的工作集,它就会生成一个包含任务依赖关系的 TaskGraph 类,然后跨多个  worker thread 进行任务调度。TaskGraphs 不会动态更新,而是整个 graph 会被重新调度。一旦开始运行的任务是无法被取消的。但是已经进入调度还未开始运行的任务,如果新提交的 graph 不包含这些任务,这些任务会被自动取消。

There are currently three modes of raster in cc:

  • software raster: generate software bitmaps in the raster worker

  • gpu raster: generate gpu textures by sending gl commands over the command buffer

  • oop raster: generate gpu textures by sending paint commands over the command buffer

The TileManager is instructed to do software vs hardware raster based on whether the LayerTreeFrameSink that it uses to submit compositor frames on has a context provider or not. It is always in one mode or the other. Switching modes destroys all resources. GPU raster is also currently deprecated and will be replaced by OOP (out-of-process) raster in all cases eventually. A common reason for switching modes is that the gpu process has crashed too much and all of Chrome switches from gpu to software raster and compositing modes.

Once the TileManager decides the set of work to do, it generates a TaskGraph with dependencies and schedules that work across worker threads. TaskGraphs are not updated dynamically, but instead rescheduled as a whole graph. Tasks cannot be cancelled once they have started running. Scheduled tasks that have not yet started are cancelled by submitting another graph that does not include them.

There are currently two modes of raster in cc:

  • software raster: generate software bitmaps in the raster worker

  • gpu raster: generate gpu textures by sending paint commands over the command buffer

The TileManager always uses software raster for software compositing. For GPU compositing the decisions is based on RasterContextProvider capabilities. It is always in one mode or the other. See Raster Buffer Providers for further details. Switching modes destroys all resources. A common reason for switching modes is that the gpu process has crashed too much and all of Chrome switches from gpu to software raster and compositing modes.

Once the TileManager decides the set of work to do, it generates a TaskGraph with dependencies and schedules that work across worker threads. TaskGraphs are not updated dynamically, but instead rescheduled as a whole graph. Tasks cannot be cancelled once they have started running. Scheduled tasks that have not yet started are cancelled by submitting another graph that does not include them.

Image Decoding

Image decoding receives a lot of special care in the TileManager, as they are the most expensive part of raster, especially relative to comparatively speedy gpu raster. Each decode receives its own dependent task in the task graph. There is a separate decode cache for software raster vs gpu raster. The SoftwareImageDecodeCache manages decode, scale, and color correction, whereas the GpuImageDecodeCache also uploads those textures to the gpu process, storing them in gpu discardable memory.

cc also handles all animation of animated gifs in Chrome. When gifs animate, they generate a new pending tree (initiated by the compositor thread instead of the main thread) with some raster invalidations and then re-raster tiles that are covered by that gif.

图片解码被TileManager特殊关注。每个解码在 task graph 里有自己依赖的任务。软件或者gpu光栅化对应有对立的解码cache。

SoftwareImageDecodeCache和GpuImageDecodeCache分别对于不同缓存。

cc处理所有的gif动画。对gif动画,由合成器产生一个新的pending树,而不是主线程。pending tree有一些光栅化失效区域,重新光栅化那些被gif覆盖区域的tile分块。

Raster Buffer Providers

除了软件,硬件的光栅化模式显示显示合成( display compositing)模式也有软硬之分。chrome是先做光栅化,再进行display合成。除了不能在硬件光栅化后,再做软合成。剩下那三种组合是有效:全软(软光栅化软合成),全硬,先软再硬。

Apart from software vs hardware raster modes, Chrome can also run in software vs hardware display compositing modes. Chrome never mixes software compositing with hardware raster, but the other three combinations of raster mode x compositing mode are valid.

合成模式影响着cc提供的RasterBufferProvider 的选择。它(RasterBufferProvider )管理着在光栅化工作线程中的光栅化过程和资源。

  • BitmapRasterBufferProvider: 软件光栅化成位图,然后软件合成。即全软。

  • OneCopyRasterBufferProvider: 软件光栅化,生成的位图放在共享内存,然后gpu合成,通过gpu进程上传成纹理。软硬,依次拷贝内存。

  • ZeroCopyRasterBufferProvider: 同上,但生成的位图直接放在 GpuMemoryBuffer (e.g. IOSurface), 省去了上传(非常耗时耗力)可以直接被display compositor使用。软硬,共享内存。

  • GpuRasterBufferProvider: 光栅化gpu纹理,通过command buffer利用 gl (gpu光栅化) 或者 利用paint commands (oop 光栅化),然后gpu 合成。全硬。

注意,由于上下文需要上锁,GPU光栅化一次只能在一个工作线程上进行,尽管图像解码可以在其他线程上并行进行。这种单线程限制是通过锁而不是通过线程亲和力来解决的。

The compositing mode affects the choice of RasterBufferProvider that cc provides, which manages the raster process and resource management on the raster worker threads:

  • BitmapRasterBufferProvider: rasters software bitmaps for software compositing

  • OneCopyRasterBufferProvider: rasters software bitmaps for gpu compositing into shared memory, which are then uploaded in the gpu process

  • ZeroCopyRasterBufferProvider: rasters software bitmaps for gpu compositing directly into a GpuMemoryBuffer (e.g. IOSurface), which can immediately be used by the display compositor

  • GpuRasterBufferProvider: rasters gpu textures for gpu compositing over a command buffer via gl (for gpu raster) or via paint commands (for oop raster)

Note, due to locks on the context, gpu and oop raster are limited to one worker thread at a time, although image decoding can proceed in parallel on other threads. This single thread limitation is solved with a lock and not with thread affinity.

Animation

This directory implements an animation framework (used by LayerTreeHost(Impl) through the cc::MutatorHost interface). The framework supports keyframe based animations of transform lists, opacity, and filter lists which directly manipulate those values on the relevant TransformNode / EffectNode in the property tree (identified by ElementId).

这个目录实现了动画框架。由 LayerTreeHost(Impl) 通过MutatorHost 接口使用。这个框架主要是实现css中定义的animation属性。基于keyframe值的变化,包括transform list, opacity和filter list对应于属性树上的  TransformNode / EffectNode。用ElementId标识。

An animation is represented by an instance of Animation which has one (or more in the future) KeyframeEffects, each of which has multiple KeyframeModels. Animation manages the play state, start time, etc of an animation, KeyframeEffect represents a target element of the animation, and each KeyframeModel describes the animation of a particular property (e.g. transform / opacity / filter) on that element. An animation may either represent an embedder animation (e.g., a Blink animation of a transform property) or it can be an animation from cc itself (e.g., a scroll animation for smooth scrolling).

动画通过Animation 对象表示。它有KeyframeEffects。KeyframeEffects又有多个KeyframeModels。Animation 管理动画播放状态,启动时间。KeyframeEffect 表示对目标元素的动画。每个KeyframeModel 描述在那个元素上的特定属性动画(transform / opacity / filter)。

动画可以指内嵌的blink引擎的css动画,比如transform属性动画,也可以是从cc自身的动画,比如平滑滚动动画。

LayerTreeHostImpl informs AnimationHost of new and removed elements, which in turn will update the state of animations which depend on those elements. It calls NeedsTickAnimations to know if more animation frames should be scheduled, and TickAnimations every frame to update animation timing, state, generate animation events, and update the actual output value of property tree nodes based on the animation.

LayerTreeHostImpl 通知AnimationHost 更新,增删元素,导致动画状态变换。它通过调用NeedsTickAnimations 来知道是否更多动画帧要调度。TickAnimations 去通知每帧产生动画,更新状态达到最终更新输出的属性树。

cc/paint/

这个目录中放着呈现的绘制内容的类。它们和skia数据结构一致(比如blink内部的PaintRecord(又名PaintOpBuffer)就是skia层面的SkPicture 的等价物。只不过skia层面的skPicture是不可变对象),只是它们在所有情况下都是是可变,自省和可序列化的。它们还需考虑skia层面不想考虑的安全问题。

PaintRecord(又名PaintOpBuffer)就是SkPicture 的等价物。存储一系列绘制指令 PaintOps。PaintRecord可以通过软件或者gpu光栅化方式来生成位图或者gpu纹理,也可以用oop光栅化方式来序列化绘制指令。

PaintCanvas是个抽象类,用来记录绘制命令。它可以由 SkiaPaintCanvas (通过把paint Ops转化为skCanvas)或者PaintRecordCanvas (把 paint Ops转为可记录的PaintRecord)具体集成实现。

PaintCanvas是一个抽象类,用于记录绘制命令。SkiaPaintCanvas(将绘制操作转换为SkCanvas)或PaintRecordCanvas(将绘制操作转换为已记录的PaintRecord)支持。

using PaintRecord = PaintOpBuffer;

// PaintCanvas is the cc/paint wrapper of SkCanvas.  It has a more restricted
// interface than SkCanvas (trimmed back to only what Chrome uses).  Its reason
// for existence is so that it can do custom serialization logic into a
// PaintOpBuffer which (unlike SkPicture) is mutable, handles image replacement,
// and can be serialized in custom ways (such as using the transfer cache).
//
// PaintCanvas is usually implemented by either:
// (1) SkiaPaintCanvas, which is backed by an SkCanvas, usually for rasterizing.
// (2) RecordPaintCanvas, which records paint commands into a PaintOpBuffer.
//
// SkiaPaintCanvas allows callers to go from PaintCanvas to SkCanvas (or
// PaintRecord to SkPicture), but this is a one way trip.  There is no way to go
// from SkCanvas to PaintCanvas or from SkPicture back into PaintRecord.

This directory stores a number of classes that represent painted content. They are extremely similar to Skia data structures, but are mutable, introspectable, and serializable in all cases. They also handle security concerns (e.g. TOCTOU issues serializing out of shared memory that a malicious renderer could be manipulating as it is read by the gpu process) that Skia does not want to think about.

PaintRecord (aka PaintOpBuffer) is the SkPicture equivalent that stores a number of PaintOps. A PaintRecord can either be rasterized by a raster buffer provider into a bitmap or a gpu texture (when using software or gpu raster), or it can be serialized (when using oop raster).

PaintCanvas is the abstract class to record paint commands. It can be backed by either a SkiaPaintCanvas (to go from paint ops to SkCanvas) or a PaintRecordCanvas (to turn paint ops into a recorded PaintRecord).

Scheduling

cc调度器cc::Scheduler,是众多chrome调度器之一,包括Blink调度器,viz的display调度器,浏览器自身ui的任务调度器和gpu调度器。

cc’s actions are driven by a cc::Scheduler. This is one of many schedulers in Chrome, including the Blink scheduler, the viz::DisplayScheduler, the browser UI task scheduler, and the gpu scheduler.

合成线程的代理ProxyImpl (or SingleThreadProxy)拥有cc调度器。它接收各种输入(窗口可见,开始帧的消息,需要重绘,准备好重绘,准备好激活等)。这些输入驱动调度状态机(cc::SchedulerStateMachine),由它决定SchedulerClient (==LayerTreeHostImpl)采取的动作(“Commit” or “ActivateSyncTree” or “PrepareTiles”)。这些动作都是费时费力的流水线上的环节,我们要避免频繁调用和注意它们相互间的状态依赖。

The cc::Scheduler is owned by ProxyImpl (or SingleThreadProxy). It takes various inputs (visibility, begin frame messages, needs redraw, ready to draw, ready to activate, etc). These inputs drive the cc::SchedulerStateMachine, which then determines actions for the SchedulerClient (LayerTreeHostImpl) to take, such as “Commit” or “ActivateSyncTree” or “PrepareTiles”. These actions are generally expensive parts of the pipeline that we want to carefully rate limit or that have state-related dependencies.

cc调度器的区分 begin frames:

display compositor 发送的 begin frames 是 BeginImplFrame(即 cc 应该生成 compositor frame);

从embedder主线程中来的的开始帧 BeginMainFrame (比如cc让blink运行requestAnimationFrame 来产生一次commit 或者 在浏览器中cc告诉UI做类似事情)。

BeginImplFrame 是被viz服务的viz::BeginFrameSource驱动,而BeginFrameSource反过来又被 display compositor 驱动。

cc::Scheduler code differentiates begin frames from the display compositor as BeginImplFrame (i.e. should cc produce a compositor frame) and a begin frame for its embedder as BeginMainFrame (i.e. should cc tell Blink to run requestAnimationFrame and produce a commit, or in the browser if should cc tell ui to do something similar). The BeginImplFrame is driven by a viz::BeginFrameSource which in turn is driven the the display compositor.

在一个低延时和快速光栅化的流水线中,一般调度流程是BeginImplFrame -> BeginMainFrame -> Commit -> ReadyToActivate -> Activate -> ReadyToDraw -> Draw.

In a full pipeline update with low latency and fast rasterization, the general scheduling flow is BeginImplFrame -> BeginMainFrame -> Commit -> ReadyToActivate -> Activate -> ReadyToDraw -> Draw.

另外,如果光栅化较慢,下一个BeginMainFrame 在这次的 Activate 前就到达了。它将阻塞在NotifyReadyToCommit 直到这次 Activate  完成。调度状态机阻止提交因为本次的pending tree还没被激活。这样就允许主线程继续并行产生下一帧的工作而不是干等。理论上一个耗时光栅化可能是:

BeginImplFrame1 -> BeginMainFrame1 -> Commit1 -> (slow raster) -> BeginImplFrame2 -> BeginMainFrame2 -> ReadyToActivate1 -> Activate1 -> Commit2 -> ReadyToDraw1 -> Draw1.

Additionally, if rasterization is slow, a second BeginMainFrame can be sent before activation, and it will block in NotifyReadyToCommit until the activation completes, as the SchedulingStateMachine will prevent the commit from starting while there is a pending tree that hasn’t activated yet. This allows the main thread to work on the next frame in parallel instead of sitting idle at the expense of latency. One hypothetical ordering of events with slow raster could be:

BeginImplFrame1 -> BeginMainFrame1 -> Commit1 -> (slow raster) -> BeginImplFrame2 -> BeginMainFrame2 -> ReadyToActivate1 -> Activate1 -> Commit2 -> ReadyToDraw1 -> Draw1.

调度器维护一个等主线程产生帧的响应的最后期限。若超时,就不再等commit而开始draw。这时调度器被认为是高延时模式(即主线程产生的帧太慢,没有更新内容绘制了)。若将来主线程生成帧速度恢复了,调度器会试图跳过BeginMainFrame 为了追上帧,返回低延时模式。高延时模式抹掉延时通过补充流水线,它保存时间历史和调整由于延时带来的卡顿等现象。

The cc::Scheduler maintains a deadline by which it expects its embedder to respond. If the main thread is slow to respond, then the Scheduler may draw without waiting for a commit. If this happens, then Scheduler is considered to be in high latency mode. If future frames start becoming faster again, the scheduler can attempt to skip a BeginMainFrame in order to “catch up” and re-enter low latency mode. High latency mode trades off latency for throughput by increasing pipelining. It maintains this distinction by keeping a history of times and trying to adjust with heuristics.

Compositor frames, render passes, quads

The output of cc is a compositor frame. A compositor frame consists of metadata (device scale, color space, size) and an ordered set of render passes. A render pass contains an ordered set of quads that have references to resources (e.g. gpu textures) and information about how to draw those resources (sizes, scales, texture coordinates, etc). A quad is a single rectangle on screen, and is what you see when composited layer borders are visualized. Layers themselves produce quads via derived AppendQuads function. This produces a set of quads that fill (without overlapping or intersecting) the visible rect of the layer.

cc的输出是合成帧。它包括metadata(设备的缩放,颜色空间,尺寸)和有序排列的Render Pass集合。Render Pass是有序排列的quads集合,它有引用的资源(如gpu纹理)和如何画资源(尺寸,缩放,纹理坐标等)。一个quad是屏幕上的一个矩形块儿,即打开chrome合成层边框显示选项时看到的边框。

render pass的有序,它是按照相互依赖顺序排序。比如 id 1的依赖 id 9,那么9就要在前面。root会排在最后。

层产生quad通过继承的AppendQuads。这些quads集合将填充layer的可视区域,而且它们之间不会覆盖或者交叉。

这些不同种类的quads对应不同种类的layer类型:ContentDrawQuad, TextureDrawQuad, SolidColorDrawQuad。

因为一个图层(比如PictureLayerImpl)会产生很多有相同信息的quands,我们用SharedQuadState做优化,它 收集了这些共享信息,使单个quad变小。

RenderSurfaceImpls :render passes是1:1的,RenderSurfaceImpl中的AppendQuads 逻辑,就像 layer图层添加 quads一样,RenderSurfaceImpl 添加RenderPassDrawQuads。

There are various types of quads that roughly correspond to different layer types (ContentDrawQuad, TextureDrawQuad, SolidColorDrawQuad). Because layers that produce many quads (i.e. PictureLayerImpl) produce many quads with the same info, SharedQuadState is an optimization that collects this shared information so that each individual quad is slimmer. RenderSurfaceImpls are 1:1 with render passes and exist mostly to provide the same AppendQuads logic that Layers do for their quads, in that RenderSurfaceImpl produces RenderPassDrawQuads.

compositor frame diagram

上面第一行是id=3的 render pass,包含三个quad。最后一行id=1的render pass,其中第三个quad是个RenderPassDrawQuad,它引用的是id为3的RenderPassDrawQuad。其实就是刚才说的第一行的 id=3的 render pass,只用引用就替代了,利用共享,节省了空间。

A render pass exists to support composited effects (see: effect tree). These can be cases where compositing is required to perform an effect. It can also be cases where doing compositing first can make the effect easier to implement (because then it applies to a single render pass texture, instead of an arbitrary set of quads produced by some subtree of layers). Common cases for render passes are: masks, filters (e.g. blur), clipping rotated layers, or opacity applied to a subtree of content.

render pass 的存在是为了实现合成特效(请参阅:特效树)。有些情况是需要先合成到 render surface 后,再实现特效。还有就是先合成在做特效会容易些,比如这些特效应用在单个rander pass纹理,而不是layer的某个子树产生的任意quads集合。一般特效有masks,过滤,剪切旋转和蒙版。

Inside a compositor frame, render passes and the quads within a render pass are ordered. The render passes are a flattened list that represent that dependency tree of render passes. If render pass 1 depends on render pass 9 (because it contains a RenderPassDrawQuad referencing the output of 9), then 9 will appear in the list before 1. Therefore, the root render pass is always last in the list. Inside a single render pass, the quads are ordered back to front (Painter’s algorithm).

compositor frame内部, render passes和它们内部的quads都是排序的。render pass的列表化处理代表了它们之间的相互依赖,使得绘制有背景到前景推进。不用树那么麻烦。用的是画家算法,被依赖的排在列表的前面。

In general, quads are not considered to live in a 3d space (even if transformed by 3d transforms) and are still drawn in order, on top of anything drawn before it. However, there is a mode where a set of quads can be in a 3d context (caused by css transform-style: preserve-3d). A BSP tree is used to sort and intersect these against each other in the same 3d context.

通常,quads 不被认为是存在于 3d 空间中(即使通过三维矩阵变换进行变换),它们仍然按顺序绘制。一个 quad 是绘制在它之前的 quad 之上。但是有一种模式,其中一组 quads 可以在一个 3d 上下文中(由 css transform-style:preserve-3d 引起)。这时我们使用 BSP 树在同一个 3d 上下文中对它们进行排序和交集检查。

Glossary

See: cc/README.md

Other Resources

For a list of presentations, videos, and design docs, see: https://www.chromium.org/developers/design-documents/chromium-graphics

Miscellaneous Corner Cases That Don’t Fit Anywhere Else, Sorry

Damage

chrome有不同的失效术语。“Paint invalidation” 是blink网页的dom节点更新需重新paint,即生成skia绘制指令。

光栅化失效是图层的一部分变化导致了需要重新光栅化(如由于上面的“Paint invalidation”导致;合成失效比如层第一次光栅化,或者纹理失效后又再次需要)。

“draw invalidation”,是部分屏幕需重绘。

Chrome has different notions of invalidation throughout the system. “Paint invalidation” is portions of the document that need to be repainted in Blink. “Raster invalidation” is parts of a layer that have changed and need to be re-rastered (possibly due to paint invalidation, but also synthetic invalidations such as the first time a layer is rastered or when textures are thrown away and then needed again). Finally, damage is another word for “draw invalidation”. It’s the part of the screen that needs to be redrawn.

There’s two types of damage: invalidation damage and expose damage. Invalidation damage is due to raster invalidation, where a part of a texture has changed and the screen needs to be updated. Expose damage is when a layer goes away, gets added for the first time, or gets reordered. There’s no raster invalidation in these cases, but the screen still needs to be updated.

有两种类型的 damage:invalidation damage 和 expose damage。Invalidation damage 是由于 raster invalidation 造成的,其中纹理的一部分内容已经改变导致屏幕需要更新显示。Expose damage 发送在图层删除,第一次被添加或者图层的层叠顺序发生变化时。在这些情况下虽然没有发生重新光栅化,但仍需要更新屏幕。

cc calculates damage in the DamageTracker and forwards it along with a CompositorFrame. One reason damage is needed in the display compositor is to do partial swap (where only a portion of the screen is updated), which saves power. Another reason is when using hardware overlays, such that the display compositor can know that only an overlay was damaged and not have to re-composite the rest of the scene.

cc 通过 DamageTracker 来计算 damage,并将其与 CompositorFrame 一起发送。这样 display compositor只要做局部swap(仅更新局部屏幕),优化减少耗电。另一个原因是当使用硬件覆盖层时,display compositor 可以知道只有“覆盖层”被更新,而不必重新合成场景的其余部分。

Mask Layers

Mask layers are layers used to implement a masking effect. They sit outside the layer tree, without parents. They’re owned by the layer the mask is applied to. They can be any type of layer subclass (e.g. PictureLayer or SolidColorLayer). Any time layers are iterated over, they are a special case that needs to be considered because they aren’t part of the normal parent/child tree. They get treated the same as other layers in terms of rasterization and tile management, although their AppendQuads function goes through RenderSurfaceImpl instead of in the top level iteration, because they are part of an effect and not a layer drawn on its own.

蒙版层只被使用它的层拥有。尽管他们在添加到quad时是特效一部分,没有自己的层绘制,但他们也做同样的光栅化和tile。

Mask layers是用于实现蒙版效果的layer图层。他们不在图层树里面,没有父亲节点。它们由使用蒙版的图层所拥有。 它们可以是任何类型的图层子类(例如 PictureLayer 或 SolidColorLayer)。 任何时候遍历图层,它们都是需要考虑的特殊情况,因为它们不是普通父/子树结构的一部分。虽然它们在光栅化和分块管理方面与其他图层一样,但是它们的 AppendQuads 调用是通过额外 RenderSurfaceImpl, 而不是顶层的图层遍历,这是因为它们是用于绘制特效而不是绘制自身的内容。

“Impl”

cc uses the “impl” suffix ...differently than the rest of Chrome or other software engineers. In cc, “impl” means that the class is used on the compositor thread and not on the main thread.

The historical reason for this is that at one point we had Layer on the main thread and we needed an equivalent class to run on the compositor thread. jamesr@ consulted with nduca@ who made the very logical argument that things on the compositor thread were internal to the compositor and would really be the implementation of the main thread version, and hence LayerImpl. See: https://bugs.webkit.org/show_bug.cgi?id=55013#c5

Then if you need a tree of LayerImpls, you have LayerTreeImpl, and a place to hang those trees is LayerTreeHostImpl. Suddenly, then the “impl thread” was the thread where all the “impl classes” lived. If you’re moving rasterization to the compositor thread, then suddenly that’s called “impl-side painting”.

"Impl" 后缀

cc 使用 “impl” 后缀... 但是涵义与 Chrome 的其它模块或软件工程常用的普遍涵义并不一样。 在 cc 中,“impl”表示该类在合成器线程上使用,而不是在主线程上使用。

这样做的历史原因是,cc 在设计时,我们在主线程上有 Layer,我们需要一个运行在合成器线程上等价的类。jamesr@ 咨询了 nduca@,他提出了一个非常合乎逻辑的论点,即合成器线程上的东西是合成器内部的,并且实际上是主线程版本的实现,所以我们就从 Layer 得到了 LayerImpl。参见:

然后,如果你需要一个LayerImpls 树,LayerTreeImpl 就出现了,而拥有和管理这些树的对象就被命名为 LayerTreeHostImpl。突然之间,“impl 线程” 就演变成所有 “impl 类” 存在的线程。如果你将光栅化移动到合成器线程,那么就会很诡异地被称为 “impl-side painting”。

 

 

posted @ 2020-08-20 09:24  Bigben  阅读(872)  评论(0编辑  收藏  举报