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