经过Build流程,Render Tree中绘制相关的基础信息已经完成更新;经过Layout流程,Render Tree中每个节点的大小和位置完成计算与存储,接下来进入Paint流程:基于Layout的信息生成绘制指令。
Render Tree和Layer Tree的对应关系
使用Layer Tree的好处是可以做Paint流程的局部更新。Render Tree中,每个RenderObject对象都拥有一个needsCompositing属性,用于判断自身及子节点是否有一个要去合成的图层,同是还有一个_needsCompositingBitsUpdate字段 用于标记该属性是否需要更新。Flutter在Paint开始前首先会完成needsCompositing属性的更新,然后开始正式绘制。
Layer的子类分为以下几种类型:
ContainerLayer:容器层,用于包含其他Layer。
PictureLayer:执行实际绘制的节点。通过_picture字段持有一个ui.PictureRecorder对象,用于Engine进行对应绘制指令的记录。
TextureLayer、PlatformLayer:渲染源将有外部提供
Compositing-State Mark阶段 当Render Tree需要挂载(mount)或卸载(unmount)一个子节点时,就会调用markNeedsCompositingBitsUpdate方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class RenderObject { void markNeedsCompositingBitsUpdate() { if (_needsCompositingBitsUpdate) { return ; } _needsCompositingBitsUpdate = true ; if (parent is RenderObject) { final RenderObject parent = this .parent! as RenderObject; if (parent._needsCompositingBitsUpdate) { return ; } if ((!_wasRepaintBoundary || !isRepaintBoundary) && !parent.isRepaintBoundary) { parent.markNeedsCompositingBitsUpdate(); return ; } } if (owner != null ) { owner!._nodesNeedingCompositingBitsUpdate.add(this ); } } }
Compositing-State Flush阶段 Layout完成之后将调用pipelineOwner.flushCompositingBits()
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 class PipelineOwner { void flushCompositingBits() { if (!kReleaseMode) { Timeline.startSync('UPDATING COMPOSITING BITS' ); } _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth); for (final RenderObject node in _nodesNeedingCompositingBitsUpdate) { if (node._needsCompositingBitsUpdate && node.owner == this ) { node._updateCompositingBits(); } } _nodesNeedingCompositingBitsUpdate.clear(); for (final PipelineOwner child in _children) { child.flushCompositingBits(); } if (!kReleaseMode) { Timeline.finishSync(); } } } class RenderObject { void _updateCompositingBits() { if (!_needsCompositingBitsUpdate) { return ; } final bool oldNeedsCompositing = _needsCompositing; _needsCompositing = false ; visitChildren((RenderObject child) { child._updateCompositingBits(); if (child.needsCompositing) { _needsCompositing = true ; } }); if (isRepaintBoundary || alwaysNeedsCompositing) { _needsCompositing = true ; } if (!isRepaintBoundary && _wasRepaintBoundary) { _needsPaint = false ; _needsCompositedLayerUpdate = false ; owner?._nodesNeedingPaint.remove(this ); _needsCompositingBitsUpdate = false ; markNeedsPaint(); } else if (oldNeedsCompositing != _needsCompositing) { _needsCompositingBitsUpdate = false ; markNeedsPaint(); } else { _needsCompositingBitsUpdate = false ; } } }
Paint Mark阶段 Paint和Layout的脏节点标记逻辑比较类似,RenderObject中对绘制有影响的属性更新了就会进行标记,比如RenderImage的image属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 set image(ui.Image? value) { if (value == _image) { return ; } if (value != null && _image != null && value.isCloneOf(_image!)) { value.dispose(); return ; } _image?.dispose(); _image = value; markNeedsPaint(); if (_width == null || _height == null ) { markNeedsLayout(); } }
markNeedsPaint方法的逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class RenderObject { void markNeedsPaint() { if (_needsPaint) { return ; } _needsPaint = true ; if (isRepaintBoundary && _wasRepaintBoundary) { if (owner != null ) { owner!._nodesNeedingPaint.add(this ); owner!.requestVisualUpdate(); } } else if (parent is RenderObject) { final RenderObject parent = this .parent! as RenderObject; parent.markNeedsPaint(); } else { if (owner != null ) { owner!.requestVisualUpdate(); } } } }
Paint Flush阶段 当flushCompositingBits完成之后,会调用pipelineOwner.flushPaint()
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 class PipelineOwner { void flushPaint() { try { final List <RenderObject> dirtyNodes = _nodesNeedingPaint; _nodesNeedingPaint = <RenderObject>[]; for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) { if ((node._needsPaint || node._needsCompositedLayerUpdate) && node.owner == this ) { if (node._layerHandle.layer!.attached) { if (node._needsPaint) { PaintingContext.repaintCompositedChild(node); } else { PaintingContext.updateLayerProperties(node); } } else { node._skippedPaintingOnLayer(); } } } for (final PipelineOwner child in _children) { child.flushPaint(); } } } } class PaintingContext { static void repaintCompositedChild(RenderObject child, { bool debugAlsoPaintedParent = false }) { _repaintCompositedChild( child, debugAlsoPaintedParent: debugAlsoPaintedParent, ); } static void _repaintCompositedChild( RenderObject child, { bool debugAlsoPaintedParent = false , PaintingContext? childContext, }) { OffsetLayer? childLayer = child._layerHandle.layer as OffsetLayer?; if (childLayer == null ) { final OffsetLayer layer = child.updateCompositedLayer(oldLayer: null ); child._layerHandle.layer = childLayer = layer; } else { childLayer.removeAllChildren(); final OffsetLayer updatedLayer = child.updateCompositedLayer(oldLayer: childLayer); } child._needsCompositedLayerUpdate = false ; childContext ??= PaintingContext(childLayer, child.paintBounds); child._paintWithContext(childContext, Offset.zero); childContext.stopRecordingIfNeeded(); } } class RenderObject { void _paintWithContext(PaintingContext context, Offset offset) { if (_needsLayout) { return ; } RenderObject? debugLastActivePaint; _needsPaint = false ; _needsCompositedLayerUpdate = false ; _wasRepaintBoundary = isRepaintBoundary; try { paint(context, offset); } catch (e, stack) { _reportException('paint' , e, stack); } } }
PipelineOwner是『RenderObject Tree』与『RendererBinding』间的桥梁,在两者间起到沟通协调的作用
1 2 3 4 5 6 7 8 9 10 void drawFrame() { pipelineOwner.flushLayout(); pipelineOwner.flushCompositingBits(); pipelineOwner.flushPaint(); renderView.compositeFrame(); pipelineOwner.flushSemantics(); }
Root Widget(RenderObjectToWidgetAdapter)
『 Element Tree 』的根节点(RenderObjectToWidgetElement)
『 RenderObject Tree 』的根节点(RenderView)
『 Layer Tree 』的根节点(TransformLayer)
确定当前组件的布局边界。
判断是否需要重新布局,如果没必要会直接返回,反之才需要重新布局。不需要布局时需要同时满足三个条件:
当前组件没有被标记为需要重新布局。
父组件传递的约束没有发生变化。
当前组件的布局边界也没有发生变化时。
调用 performLayout() 进行布局,因为 performLayout() 中又会调用子组件的 layout 方法,所以这时一个递归的过程,递归结束后整个组件树的布局也就完成了。
请求重绘。
绘制 大致流程:
第一次绘制时,从上到下递归绘制子节点,每当遇到一个边界节点,判断如果该节点的layer属性是否为空,是就创建一个新的OffsetLayer并赋值给它;不是则使用。然后将layer传递给子节点,接下来:
如果子节点是非边界节点,且需要绘制,则:
第一次绘制:创建一个Canvas对象和一个PictureLayer,然后将它们绑定,后续调用Canvas绘制都会落到和其绑定的PictureLayer上,接着这个PictureLayer会加入到边界节点的layer中;
不是第一次绘制:复用已有的边界节点和Canvas对象;
如果子节点是边界节点,则对子节点递归上述过程。当子树递归完成后,就要将子节点的layer添加到父级layer中。
RenderObject调用markNeedsRepaint来发起重绘:
从当前节点一直往父级查找,直到找到一个绘制边界点 时终止查找,然后会将该绘制边界点添加到其PiplineOwner的_nodesNeedingPaint列表中。
在查找的过程中,会将自己到绘制边界点路径上所有节点的_needPaint属性设置为true,表示需要重绘。
请求新的frame,执行重绘流程。下一个frame就会走drawFrame流程,涉及到flushCompositingBits、flushPaint 和 compositeFrame 三个函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void markNeedsPaint() { if (_needsPaint) return ; _needsPaint = true ; if (isRepaintBoundary) { owner!._nodesNeedingPaint.add(this ); owner!.requestVisualUpdate(); } else if (parent is RenderObject) { final RenderObject parent = this .parent! as RenderObject; parent.markNeedsPaint(); } else { if (owner != null ) owner!.requestVisualUpdate(); } }
参考