0%

Flutter渲染流程(一)-UI线程工作

概览

这一部分的主要工作是WidgetTree转化成LayerTree,主要分为下面几个阶段

  • Animate: 首先执行动画,这个过程可能会改变状态state
  • Build: 重新build脏节点的widget(状态改变)
  • Layout: 对脏节点进行重新的layout
  • Paint: 脏节点重新绘制
  • 更新layertree,提交给gpu线程

流程还是比较清晰、容易理解的

流程源码

流程入口

一般更新是通过setState函数触发,经过一系列流程之后,调用到Window.ccBeginFrame,开始进行渲染

  • setState经过一些列调用,调用到window.scheduleFrame
  • scheduleFrame会注册一个vsync
  • 在下次vsync到来后,进过一些列调用到window.cc上的BeginFrame方法
1
2
3
4
5
6
7
8
9
10
//window.cc
void Window::BeginFrame(fml::TimePoint frameTime) {

tonic::DartInvokeField(library_.value(), "_beginFrame",...);

UIDartState::Current()->FlushMicrotasksNow();

tonic::DartInvokeField(library_.value(), "_drawFrame", {});
}

  • _beginFrame对应dart上window对象的onBeginFrame方法
  • onDrawFrame对应dart上window对象的_handleBeginFrame方法

SchedulerBinding初始化时,分别指向了_handleBeginFrame_handleDrawFrame上。

1
2
3
4
5
//SchedulerBinding
void ensureFrameCallbacksRegistered() {
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
}

Animate过程

进入handleBeginFrame方法

1
2
3
4
5
6
7
8
9
//SchedulerBinding
void handleBeginFrame(Duration rawTimeStamp) {
final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
//执行动画回调
if (!_removedIds.contains(id))
_invokeFrameCallback(...);
});
}

拿到所有注册的动画callbac,依次执行。这个过程中

  • 可能会修改widget的状态(state)
  • 可能会有一些future代码,挂载到Microtasks队列上

在执行完handleBeginFrame方法后,window.cc中会执行FlushMicrotasksNow将产生的Microtask统统执行。

drawFrame核心流程函数

接着看drawFrame方法

1
2
3
4
5
6
7
8
// lib/src/widgets/binding.dart
void drawFrame() {
buildOwner.buildScope(renderViewElement);
//执行`RendererBinding`核心的渲染流程函数`drawFrame`中
super.drawFrame();
//...
buildOwner.finalizeTree();
}

关注RendererBinding核心的渲染流程函数drawFrame

1
2
3
4
5
6
7
8
//RendererBinding
void drawFrame() {
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
}

所以,这里整个流程是这样的

  • build阶段
    • buildOwner.buildScope
  • layout阶段
    • flushLayout 从新进行布局计算标记为dirty的renderobject
    • flushCompositingBits 更新有dirty标记的renderobject的合成位
  • paint阶段
    • flushPaint 重绘标记为dirty的renderobject,这一步会生成layer
  • 生成scene阶段,
    • compositeFrame layertree会转成Scene并且发送给gpu
    • flushSemantics 更新dirty的renderboject的语义
  • 回收阶段
    • buildOwner.finalizeTree

Build阶段

主流程函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//framework.dart
void buildScope(Element context,...){
int dirtyCount = _dirtyElements.length;
int index = 0;

//重新构建dirty的element
while (index < dirtyCount) {
_dirtyElements[index].rebuild();
}

//清楚dirty标记位
for (final Element element in _dirtyElements) {
element._inDirtyList = false;
_dirtyElements.clear();
}
}
  • 执行dirty节点的rebuild函数,构建一个新的Widget
  • 构建完后,清除dirty节点标记。

标记Diry时机

节点是在什么时候被标记为dirty呢?

1
2
3
4
//framework.dart
setState(...){
_element.markNeedsBuild();
}

markNeedsBuild会调用到BuildOwnerscheduleBuildFor方法

1
2
3
4
5
6
//BuildOwner
void scheduleBuildFor(Element element) {
//dirty 标记。
_dirtyElements.add(element);
element._inDirtyList = true;
}

Rebuild过程

1
2
3
4
5
6
7
8
9
10
11
12
13
//framework.dart
class Element{
void rebuild() {
performRebuild();
}

void performRebuild() {
Widget built;
//重新执行了widget
built = build();
_child = updateChild(_child, built, slot);
}
}

build函数即是我们在Widget中经常打交道的

1
2
3
Widget build(BuildContext context) {
//...
}

可以看到,由于状态的改变,重新构建了一个新的Widget

Layout阶段

flushLayout

build阶段后,会执行到RendererBindingdrawFrame函数,触发flushLayout

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//pipelineOwner
void flushLayout() {
//对应工具链Layout阶段耗时
Timeline.startSync('Layout', ...);

while (_nodesNeedingLayout.isNotEmpty) {
for (RenderObject node in dirtyNodes) {
if (node._needsLayout && node.owner == this)
node._layoutWithoutResize();
}
}
}

//RenderObject
void _layoutWithoutResize() {
//进行lalyout操作
performLayout();
//标记需要更新语义
markNeedsSemanticsUpdate();
_needsLayout = false;
//标记需要重绘
markNeedsPaint();
}

RenderFlex为例,看下performLayout实际的工作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//flex.dart
void performLayout() {
if (flex > 0) {
totalFlex += childParentData.flex;
lastFlexChild = child;
}else{
BoxConstraints innerConstraints;
if (crossAxisAlignment == CrossAxisAlignment.stretch) {
switch (_direction) {
case Axis.horizontal:
break;
case Axis.vertical:
break;
}
} else {
switch (_direction) {
case Axis.horizontal:
break;
case Axis.vertical:
break;
}
}
child.layout(innerConstraints, parentUsesSize: true);
}
//...
}

不用去细究具体的实现,从代码关键字中可以看出来,这是在按照Flex 布局的规范,对节点进行布局计算,其它的组件也是类似。

flushCompositingBits

在执行完performLayout后,继续执行flushCompositingBits函数

1
2
3
4
5
6
7
8
9
10
11
12
//pipelineOwner
void flushCompositingBits() {
//对应工具链 compositing bits阶段耗时

Timeline.startSync('Compositing bits');
//遍历需要更新的节点,进行位合成
for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
if (node._needsCompositingBitsUpdate && node.owner == this)
node._updateCompositingBits();
}
_nodesNeedingCompositingBitsUpdate.clear();
}

_layoutWithoutResize方法中,会调用markNeedsSemanticsUpdate标记_needsSemanticsUpdate标志,这里会遍历标记的的RenderObject,进行更新_updateCompositingBits

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//RenderObject
void _updateCompositingBits() {
//遍历自节点,更新标志位
visitChildren((RenderObject child) {
child._updateCompositingBits();
//如果自节点为true,当前节点也设置为true
if (child.needsCompositing)
_needsCompositing = true;
});
//如果有这2个标志,也设置为true
if (isRepaintBoundary || alwaysNeedsCompositing)
_needsCompositing = true;

}

needsCompositing这个标志,有什么作用呢?__如果needsCompositing为true,都会创建一个新的Layer__。

TransformLayer为例,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
TransformLayer pushTransform(bool needsCompositing...) {

if (needsCompositing) {
final TransformLayer layer = oldLayer ?? TransformLayer();
pushLayer(layer,...);
return layer;
} else {
canvas
..save()
..transform(effectiveTransform.storage);
canvas.restore();
return null;
}
}

这个标志位如果位true,会生成一个新的Layer进行操作,否则直接调用Canvasapi操作。

Paint阶段

_layoutWithoutResize函数中,对dirty节点进行了markNeedsPaint操作,表示当前节点需要重绘。之后流程调用到flushPaint函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void flushPaint() {
//对应工具链中paint过程耗时
Timeline.startSync('Paint', arguments: timelineWhitelistArguments);

//遍历dirynode,如果在树上,进行重绘,否则跳过
for (RenderObject node in dirtyNodes) {
if (node._needsPaint && node.owner == this) {
if (node._layer.attached) {
PaintingContext.repaintCompositedChild(node);
} else {
node._skippedPaintingOnLayer();
}
}
}
}

遍历dirtyNodes,调用repaintCompositedChild进行重绘

1
2
3
4
5
6
7
8
9
10
11
12
13
//PaintingContext
static void _repaintCompositedChild(RenderObject child,...}) {
OffsetLayer childLayer = child._layer;
if (childLayer == null) {
child._layer = childLayer = OffsetLayer();
} else {
childLayer.removeAllChildren();
}
child._paintWithContext(childContext, Offset.zero);
}
void _paintWithContext(PaintingContext context, Offset offset) {
paint(context, offset);
}

重绘前,清空_layer上的子节点,辗转调用到RenderObjectpaint方法。为了说明问题,我们以image为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//image.dart
@override
void paint(PaintingContext context, Offset offset) {
paintImage(
canvas: context.canvas,
rect: offset & size,
image: _image,
scale: _scale,
colorFilter: _colorFilter,
fit: _fit,
alignment: _resolvedAlignment,
centerSlice: _centerSlice,
repeat: _repeat,
flipHorizontally: _flipHorizontally,
invertColors: invertColors,
filterQuality: _filterQuality,
);
}

看各种参数基本可以猜出来,最终会调用canvas的方法,根据各种参数rectfilterQualityimage绘制出来。

canvas.drawImg(…);

生成scene

继续执行renderView.compositeFrame()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//view.dart
void compositeFrame() {
//对应工具链 Compositing 阶段耗时
Timeline.startSync('Compositing', ...);

//将layer转换成scene
//会在dart和engine层(c++)创建对应的 builder和scene
final ui.SceneBuilder builder = ui.SceneBuilder();
final ui.Scene scene = layer.buildScene(builder);

//调用render接口,将scene发送给gpu线程
_window.render(scene);
scene.dispose();
}
  • 第1步,将layer转化成scene
  • 第2步,将scene发送给_window对象

Layer转化成scene

RenderView继承自RenderObject,layer是RenderObject上的_layer对象

1
2
3
class RenderObject{
ContainerLayer _layer;
}

跟进ContainerLayerbuildScene方法

1
2
3
4
5
6
7
//ContainerLayer
ui.Scene buildScene(ui.SceneBuilder builder) {

addToScene(builder);
final ui.Scene scene = builder.build();
return scene;
}

继续跟进addToScene

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//ContainerLayer
void addToScene(...) {
addChildrenToScene(builder, layerOffset);
}

void addChildrenToScene(ui.SceneBuilder builder, [ Offset childOffset = Offset.zero ]) {
Layer child = firstChild;
while (child != null) {
if (childOffset == Offset.zero) {
child._addToSceneWithRetainedRendering(builder);
} else {
child.addToScene(builder, childOffset);
}
child = child.nextSibling;
}
}

花里胡哨写了一大堆,,其实就是在遍历ContainerLayer上的子Layer,调用子LayeraddToScene方法.

我们以PictureLayer为例

1
2
3
4
//PictureLayer
void addToScene(ui.SceneBuilder builder, [ Offset layerOffset = Offset.zero ]) {
builder.addPicture(layerOffset, picture, isComplexHint: isComplexHint, willChangeHint: willChangeHint);
}

addPicture方法,最终会辗转调用到native的scene_builde.cc上的

1
2
3
4
5
6
7
8
9
//scene_builder.cc
void SceneBuilder::addPicture(double dx,
double dy,
Picture* picture,
int hints) {
auto layer = std::make_unique<flutter::PictureLayer>(
offset, UIDartState::CreateGPUObject(picture->picture()), ...);
current_layer_->Add(std::move(layer));
}

创建了一个PictureLayer,添加到scene_builder持有的current_layer_自节点上。

其它操作类似

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//scene_builder.cc
#define FOR_EACH_BINDING(V) \
V(SceneBuilder, pushOffset) \
V(SceneBuilder, pushTransform) \
V(SceneBuilder, pushClipRect) \
V(SceneBuilder, pushClipRRect) \
V(SceneBuilder, pushClipPath) \
V(SceneBuilder, pushOpacity) \
V(SceneBuilder, pushColorFilter) \
V(SceneBuilder, pushBackdropFilter) \
V(SceneBuilder, pushShaderMask) \
V(SceneBuilder, pushPhysicalShape) \
V(SceneBuilder, pop) \
V(SceneBuilder, addPlatformView) \
V(SceneBuilder, addRetained) \
V(SceneBuilder, addPicture) \
V(SceneBuilder, addTexture) \
V(SceneBuilder, build)

到这一步后,会将所有生成的xxLayer挂载到native对象SceneBuilder上的current_layer_对象中。

将scene发送给_window对象

继续执行window.render(scene)方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//window.cc
void Render(Dart_NativeArguments args) {
//获取上面创建的scene在native的指针
Scene* scene =
tonic::DartConverter<Scene*>::FromArguments(args, 1, exception);
//client 实际上是engine
UIDartState::Current()->window()->client()->Render(scene);
}

//engine.cc
void Engine::Render(std::unique_ptr<flutter::LayerTree> layer_tree) {
animator_->Render(std::move(layer_tree));
}

//Animator.cc
void Animator::Render(std::unique_ptr<flutter::LayerTree> layer_tree) {
producer_continuation_.Complete(std::move(layer_tree));
//delegate_实际指向的是shell
delegate_.OnAnimatorDraw(layer_tree_pipeline_);
}

一路辗转调用,最终执行到shellOnAnimatorDraw方法上,方法很简单,将生成LayerTree抛给GPU线程进行栅格化上屏操作(下一节关注)

1
2
3
4
5
6
7
8
9
10
//shell.cc
// |Animator::Delegate|
void Shell::OnAnimatorDraw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline) {
task_runners_.GetGPUTaskRunner()->PostTask([...]() {
if (rasterizer) {
//gpu线程进行栅格化操作
rasterizer->Draw(pipeline);
}
});
}

flushSemantics

drawFrame函数借着执行flushSemantics

1
2
3
4
5
6
7
8
9
10
11
12
//rendering.object.dart
void flushSemantics() {
//对应工具链Semantics部分
Timeline.startSync('Semantics');

for (RenderObject node in nodesToProcess) {
//遍历更新需要重新语义化的RenderObject
if (node._needsSemanticsUpdate && node.owner == this)
node._updateSemantics();
}
_semanticsOwner.sendSemanticsUpdate();
}

finalizeTree阶段

继续看最后一步finalizeTree函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//BuildOwner  framework.dart
void finalizeTree() {
Timeline.startSync('Finalize tree', ...);

//遍历调用element的unmount函数
_inactiveElements._unmountAll(); // this unregisters the GlobalKeys
}

//_InactiveElements
void _unmountAll() {
elements.reversed.forEach(_unmount);
}

void _unmount(Element element) {
element.visitChildren((Element child) {
_unmount(child);
});
element.unmount();
}

其实就是遍历Element,调用他们的unmount函数,解注册对应Widget上的key

1
2
3
4
5
6
void unmount() {
final Key key = _widget.key;
if (key is GlobalKey) {
key._unregister(this);
}
}

最后

第一部分ui线程工作完毕