likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 41

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

前言

一、Layer概述

1、定义

  • Layer是一种渲染对象。它通过创建隔离的上下文,将不同的绘制内容分隔到不同的层中, 这有助于避免不必要的重绘,并提高渲染效率

2、补充说明

  • 它是所有合成层的基类
  • 绘制过程中, 渲染树会生成的一个合成树上传到引擎中并由合成器显示。

3、主要作用

  • 性能优化
    • 分离渲染上下文可以减少渲染树的重绘范围,因为Layer可以缓存其内容,从而避免每次帧更新时的完全重绘。
    • Layer可以利用GPU加速,特别是当涉及到复杂的变换或透明度效果时。
  • 视觉效果
    • 应用透明度Alpha通道,如使用OpacityLayer
    • 应用变换,如旋转、缩放和平移,通过TransformLayer
    • 剪裁限制绘制范围,使用ClipRectLayer
    • 混合模式和遮罩,如ShaderMaskLayerBackdropFilterLayer
    • 其他高级图形效果,如阴影模糊等。
  • 动画支持
    • Layer可以独立于其他部分进行动画化,这意味着你可以对特定的视觉元素应用动画,而不影响整个场景的其余部分。
  • 硬件加速
    • Layer可以利用硬件加速来提高渲染速度质量

二、Layer衍生类

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

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

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

如上图所示,Layer是所有合成类的基类,可分为两种类型: Container非Container。下面分别对一部分关键类做进一步的了解。

1、TextureLayer

  • 主要用于显示来自原生代码提供的纹理数据,它能够直接从底层图形API获取纹理数据,并将其高效地渲染到屏幕上的指定位置。
  • 通常与PlatformChannel结合使用,以便于在Dart层原生层之间传输纹理ID和相关数据。

2、PictureLayer

  • 主要用于渲染一个预先记录的Picture对象。它可以看着是一系列Canvas命令的快照,这些命令可以包括绘制文本形状图像路径等。
  • 主要优势在于它可以提供额外的渲染优化,因为它可以将复杂的图形操作记录并缓存为单一的Picture,然后在后续的渲染周期中重用,避免了重复的计算和绘制。

3、ContainerLayer

  • 主要用于管理一组Layers,是唯一可以拥有子LayerLayer
  • 当在Container中应用了某些特定的效果,如transformclipBehavioropacity,这时,Flutter可能��自动创建相应的层(如TransformLayerClipRectLayerOpacityLayer),以便更高效地处理这些效果。

三、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可以看出当needsCompositingtrue时,才会合成Layer,并通过pushLayer添加到Layer树中。由此看出Layer树添加节点的时机:是在渲染对象绘制期间。最终该合成树会被上传到引擎中并由合成器显示。

通过上述的了解, 对Layer树的合成过程做了如下总结:

1、Layer树构建:

  • Flutter引擎需要绘制一个新的帧时,它首先从RenderObject树开始遍历。
  • RenderObject树中的节点会检查自己是否需要创建Layer。例如,RenderOpacity会创建一个OpacityLayerRenderTransform会创建一个TransformLayer等。
  • 每个RenderObject可能会创建一个或多个Layer,或者根本不创建Layer,这取决于其自身的属性和渲染需求。
  • 当一个Layer被创建时,它会被添加到其RenderObjectLayer列表中。

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
评论
请登录