Flutter渲染原理系列之合成Layer树

前言
一、Layer概述
1、定义:
Layer是一种渲染对象。它通过创建隔离的上下文,将不同的绘制内容分隔到不同的层中, 这有助于避免不必要的重绘,并提高渲染效率。
2、补充说明:
- 它是所有合成层的
基类。 - 在
绘制过程中,渲染树会生成的一个合成树上传到引擎中并由合成器显示。
3、主要作用:
- 性能优化:
分离渲染上下文可以减少渲染树的重绘范围,因为Layer可以缓存其内容,从而避免每次帧更新时的完全重绘。Layer可以利用GPU加速,特别是当涉及到复杂的变换或透明度效果时。
- 视觉效果:
- 应用
透明度或Alpha通道,如使用OpacityLayer。 - 应用
变换,如旋转、缩放和平移,通过TransformLayer。 剪裁或限制绘制范围,使用ClipRectLayer。- 混合模式和遮罩,如
ShaderMaskLayer或BackdropFilterLayer。 - 其他高级图形效果,如
阴影、模糊等。
- 应用
- 动画支持:
Layer可以独立于其他部分进行动画化,这意味着你可以对特定的视觉元素应用动画,而不影响整个场景的其余部分。
- 硬件加速:
Layer可以利用硬件加速来提高渲染速度和质量。
二、Layer衍生类



如上图所示,Layer是所有合成类的基类,可分为两种类型: Container和非Container。下面分别对一部分关键类做进一步的了解。
1、TextureLayer:
- 主要用于显示来自原生代码提供的
纹理数据,它能够直接从底层图形API获取纹理数据,并将其高效地渲染到屏幕上的指定位置。 - 通常与
PlatformChannel结合使用,以便于在Dart层和原生层之间传输纹理ID和相关数据。
2、PictureLayer:
- 主要用于渲染一个预先记录的
Picture对象。它可以看着是一系列Canvas命令的快照,这些命令可以包括绘制文本、形状、图像、路径等。 - 主要优势在于它可以提供额外的
渲染优化,因为它可以将复杂的图形操作记录并缓存为单一的Picture,然后在后续的渲染周期中重用,避免了重复的计算和绘制。
3、ContainerLayer:
- 主要用于管理一组
Layers,是唯一可以拥有子Layer的Layer。 - 当在
Container中应用了某些特定的效果,如transform、clipBehavior或opacity,这时,Flutter可能��自动创建相应的层(如TransformLayer、ClipRectLayer或OpacityLayer),以便更高效地处理这些效果。
三、Layer树是如何合成的?
---->[01/RenderObject]----
@protected
ContainerLayer? get layer {
return _layerHandle.layer;
}
@protected
set layer(ContainerLayer? newLayer) {
_layerHandle.layer = newLayer;
}
final LayerHandle<ContainerLayer> _layerHandle = LayerHandle<ContainerLayer>();
---->[02/RenderObject子类重写]----
@override
void paint(PaintingContext context, Offset offset) {
//...
//绘制过程中生成的layer
_clipRectLayer.layer = context.pushClipRect(
needsCompositing,
offset,
Offset.zero & size,
defaultPaint,
clipBehavior: clipBehavior,
oldLayer: _clipRectLayer.layer,
);
//...
}
---->[03/PaintingContext]----
ClipRectLayer? pushClipRect(bool needsCompositing, Offset offset, Rect clipRect, PaintingContextCallback painter, { Clip clipBehavior = Clip.hardEdge, ClipRectLayer? oldLayer }) {
if (clipBehavior == Clip.none) {
painter(this, offset);
return null;
}
final Rect offsetClipRect = clipRect.shift(offset);
if (needsCompositing) {
final ClipRectLayer layer = oldLayer ?? ClipRectLayer();
layer
..clipRect = offsetClipRect
..clipBehavior = clipBehavior;
pushLayer(layer, painter, offset, childPaintBounds: offsetClipRect);
return layer;
} else {
clipRectAndPaint(offsetClipRect, clipBehavior, offsetClipRect, () => painter(this, offset));
return null;
}
}
1、代码01可以看出在RenderObject中,提供了set 和 get处理类型为ContainerLayer的层。并通过 LayerHandle 对象间接持有的layer对象。
2、代码02可以看出paint是渲染树(RenderObject)中的提供的绘制的方法, 子类会重写该方法生成不同种类的合成Layer。
3、代码03可以看出当needsCompositing为true时,才会合成Layer,并通过pushLayer添加到Layer树中。由此看出Layer树添加节点的时机:是在渲染对象绘制期间。最终该合成树会被上传到引擎中并由合成器显示。
通过上述的了解, 对Layer树的合成过程做了如下总结:
1、Layer树构建:
- 当
Flutter引擎需要绘制一个新的帧时,它首先从RenderObject树开始遍历。 RenderObject树中的节点会检查自己是否需要创建Layer。例如,RenderOpacity会创建一个OpacityLayer,RenderTransform会创建一个TransformLayer等。- 每个
RenderObject可能会创建一个或多个Layer,或者根本不创建Layer,这取决于其自身的属性和渲染需求。 - 当一个
Layer被创建时,它会被添加到其RenderObject的Layer列表中。
2、Layer树合成:
- 一旦
Layer树被构建完成,Flutter引擎会从Layer树的根节点开始,递归地遍历整个树。 - 在遍历过程中,每个
Layer都会调用其paint()方法来绘制自身,这通常涉及到将Layer的内容绘制到一个PictureRecorder中,从而生成一个Picture对象。 Picture对象是一个记录了所有绘画指令的序列,它可以在不同的上下文中被多次重放,从而避免重复的计算和提高性能。- 绘制完成后,
Layer可能还需要调用addToScene()方法将自身添加到一个SceneBuilder对象中,该对象负责构建最终的场景图。
3、光栅化和呈现:
SceneBuilder收集了所有Layer的信息后,会构建出一个完整的场景图,描述了如何将Layer树的内容渲染到屏幕上。- 场景图随后被发送给
GPU,由GPU进行光栅化,即将矢量图形转换为像素数据。 - 光栅化后的
像素数据被发送回Framebuffer,然后显示在屏幕上。
四、总结
Layer树的合成过程优化了渲染性能,尤其是对于那些包含复杂图形操作的场景,因为Layer允许对特定区域进行局部更新和离屏渲染,从而减少了不必要的重绘和提高了渲染效率。此外,Layer树也支持硬件加速,这对于实现流畅的动画效果至关重要。
码字不易,记得 关注 + 点赞 + 收藏 + 评论
转载自:https://juejin.cn/post/7401176351057264649