Flutter Framework层UI绘制super.drawFrame()方法(三)
本文记录自己学习 Flutter 绘制所见源码,并不是全面详细的解析 Blog。
Flutter 源码环境:1.22.1
//此方法由[handleDrawFrame]调用,当需要布置和绘制框架时,引擎会自动调用该方法。
void drawFrame() {
...
try {
if (renderViewElement != null)
// 1. 调用 buildScope()
buildOwner.buildScope(renderViewElement);
//2.调用super.drawFrame();
super.drawFrame();
// 3. 调用finalizeTree()
buildOwner.finalizeTree();
} finally {
...
}
...
}
上篇学习了 buildScope()方法主要是刷新绘制控件的所需参数,我们在看看 super.drawFrame()这个方法干了些什么?
flutter/lib/src/rendering/binding.dart
void drawFrame() {
assert(renderView != null);
//调用2.1
pipelineOwner.flushLayout();
//调用2.2
pipelineOwner.flushCompositingBits();
//调用2.3
pipelineOwner.flushPaint();
if (sendFramesToEngine) {
//调用2.4
renderView.compositeFrame(); // this sends the bits to the GPU
//调用2.5
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
_firstFrameSent = true;
}
}
机翻
布局阶段:对系统中所有脏的[RenderObject]进行布局(请参阅[RenderObject.performLayout])。有关将对象标记为布局不干净的更多详细信息,请参见[RenderObject.markNeedsLayout]
合成位阶段: 系统中任何脏[RenderObject] 对象的合成位都将会被更新,参考 [RenderObject.markNeedsCompositingBitsUpdate]
绘制阶段:重新绘制系统中所有脏的[RenderObject](请参阅[RenderObject.paint])。这阶段会生成[Layer]树。关于更多将对象标记为脏画的详细信息请查阅[RenderObject.markNeedsPaint]方法。
合成阶段:将 Layer Tree 变成[场景]并发送到 GPU,成像。
语义阶段:系统中所有脏的[RenderObject]的语义都会被更新。并将语义发送给操作系统,这将生成[SemanticsNode]树。有关将对象标记为语义脏的更多详细信息,请参见[RenderObject.markNeedsSemanticsUpdate]。
2.1 pipelineOwner.flushLayout()
flutter/lib/src/rendering/object.dart
void flushLayout() {
if (!kReleaseMode) {
Timeline.startSync('Layout', arguments: timelineArgumentsIndicatingLandmarkEvent);
}
try {
//遍历所有 需要更新多的RenderObject 对象
while (_nodesNeedingLayout.isNotEmpty) {
final List<RenderObject> dirtyNodes = _nodesNeedingLayout;
_nodesNeedingLayout = <RenderObject>[];
//进行排序,把depth 深度越小的对象排在越前面
for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
if (node._needsLayout && node.owner == this)
//看方法名就能猜到,对RenderObject对象重新进行Layout
// 调用 2.1.1
node._layoutWithoutResize();
}
}
} finally {
if (!kReleaseMode) {
Timeline.finishSync();
}
}
}
该方法将所有所需更新的节点进行排序后,依次调用 node._layoutWithoutResize()进行重新 layout。
2.1.1 node._layoutWithoutResize()
flutter/lib/src/rendering/object.dart
void _layoutWithoutResize() {
assert(_relayoutBoundary == this);
RenderObject? debugPreviousActiveLayout;
...
try {
//执行布局操作,具体的控件中会重写该方法
performLayout();
markNeedsSemanticsUpdate();
} catch (e, stack) {
_debugReportException('performLayout', e, stack);
}
...
_needsLayout = false;
//调用 2.1.2
markNeedsPaint();
}
该方法在调用 performLayout()方法后,接着会调起 RenderObject.markNeedsPaint 方法,也就是说每次重新 layout 都会触发一次 paint。
2.1.2
flutter/lib/src/rendering/object.dart
void markNeedsPaint() {
if (_needsPaint)
return;
_needsPaint = true;
if (isRepaintBoundary) {
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 we're the root of the render tree (probably a RenderView),
// then we have to paint ourselves, since nobody else can paint
// us. We don't add ourselves to _nodesNeedingPaint in this
// case, because the root is always told to paint regardless.
if (owner != null)
owner!.requestVisualUpdate();
}
}
该方法先判断当前的 RenderObject.isRepaintBoundary 是否为 true,如果是则把当前 RenderObject 加入到_nodesNeedingPaint 列表中等待接下来的 flushPaint 处理,并触发下一帧的绘制;当 isRepaintBoundary 不为 true 的时候,则会一直往上查找直到找到 isRepaintBoundary 为 true 的 RenderObject,也就是有可能会找到根节点 RenderView,然后加入到_nodesNeedingPaint 列表中。
因此,对于经常需要重绘区域,最好把 isRepaintBoundary 标记 true,这样就尽量避免触发全局重绘,提高性能,对应的 flutter 就已经提供了一个 RepaintBoundary 控件,自动把 isRepaintBoundary 标记为 true,非常方便我们去做优化。
2.2 pipelineOwner.flushCompositingBits()
flutter/lib/src/rendering/object.dart
void flushCompositingBits() {
if (!kReleaseMode) {
Timeline.startSync('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();//调用2.2.1
}
////清空需要位合成的渲染对象
_nodesNeedingCompositingBitsUpdate.clear();
if (!kReleaseMode) {
Timeline.finishSync();
}
}
2.2.1 node._updateCompositingBits()
flutter/lib/src/rendering/object.dart
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 (oldNeedsCompositing != _needsCompositing)
markNeedsPaint();
_needsCompositingBitsUpdate = false;
}
2.3 pipelineOwner.flushPaint()
flutter/lib/src/rendering/object.dart
void flushPaint() {
if (!kReleaseMode) {
Timeline.startSync('Paint', arguments: timelineArgumentsIndicatingLandmarkEvent);
}
try {
final List<RenderObject> dirtyNodes = _nodesNeedingPaint;
_nodesNeedingPaint = <RenderObject>[];
// Sort the dirty nodes in reverse order (deepest first).
//排序脏节点,深度最大的节点排第一位
for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
assert(node._layer != null);
if (node._needsPaint && node.owner == this) {
//此节点是否连接到树中,如果连接则重绘,否则跳过
if (node._layer!.attached) {
//调用2.3.1
PaintingContext.repaintCompositedChild(node);
} else {
node._skippedPaintingOnLayer();
}
}
}
} finally {
if (!kReleaseMode) {
Timeline.finishSync();
}
}
}
该方法也是遍历排序,但是这里是从从节点最深的 RenderObject 开始绘制。
2.3.1 PaintingContext.repaintCompositedChild(node)
flutter/lib/src/rendering/object.dart
static void _repaintCompositedChild(
RenderObject child, {
bool debugAlsoPaintedParent = false,
PaintingContext? childContext,
}) {
...
OffsetLayer? childLayer = child._layer as OffsetLayer?;
if (childLayer == null) {
// Not using the `layer` setter because the setter asserts that we not
// replace the layer for repaint boundaries. That assertion does not
// apply here because this is exactly the place designed to create a
// layer for repaint boundaries.
child._layer = childLayer = OffsetLayer();
} else {
childLayer.removeAllChildren();
}
childContext ??= PaintingContext(child._layer!, child.paintBounds);
//调用2.3.2 绘制
child._paintWithContext(childContext, Offset.zero);
// Double-check that the paint method did not replace the layer (the first
// check is done in the [layer] setter itself).
childContext.stopRecordingIfNeeded();
}
2.3.2 _paintWithContext
flutter/lib/src/rendering/object.dart
void _paintWithContext(PaintingContext context, Offset offset) {
if (_needsLayout)
return;
RenderObject? debugLastActivePaint;
_needsPaint = false;
try {
//调用 paint 开始绘制
paint(context, offset);
} catch (e, stack) {
_debugReportException('paint', e, stack);
}
}
由此可见 flushPaint() 方法最终会调用到 paint 方法进行界面绘制。
2.4 compositeFrame()
flutter/lib/src/rendering/view.dart
void compositeFrame() {
Timeline.startSync('Compositing', arguments: timelineArgumentsIndicatingLandmarkEvent);
try {
final ui.SceneBuilder builder = ui.SceneBuilder();
final ui.Scene scene = layer!.buildScene(builder);
if (automaticSystemUiAdjustment)
_updateSystemChrome();
_window.render(scene);
scene.dispose();
assert(() {
if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)
debugCurrentRepaintColor = debugCurrentRepaintColor.withHue((debugCurrentRepaintColor.hue + 2.0) % 360.0);
return true;
}());
} finally {
Timeline.finishSync();
}
}
该方法主要工作:
分别创建 Flutter 框架(dart)和引擎层(C++)的两个 SceneBuilder;
分别创建 Flutter 框架(dart)和引擎层(C++)的两个 Scene;
执行 render()将 layer 树发送给 GPU 线程;
2.5 flushSemantics()
flutter/lib/src/rendering/object.dart
void flushSemantics() {
if (_semanticsOwner == null)
return;
if (!kReleaseMode) {
Timeline.startSync('Semantics');
}
try {
final List<RenderObject> nodesToProcess = _nodesNeedingSemantics.toList()
..sort((RenderObject a, RenderObject b) => a.depth - b.depth);
_nodesNeedingSemantics.clear();
//遍历_nodesNeedingSemantics,更新需要更新语义的渲染对象
for (final RenderObject node in nodesToProcess) {
if (node._needsSemanticsUpdate && node.owner == this)
//调用 更新语义
node._updateSemantics();
}
//发送语义更新
_semanticsOwner!.sendSemanticsUpdate();
} finally {
if (!kReleaseMode) {
Timeline.finishSync();
}
}
}
由上可知,super.drawFrame()主要做了更新 布局,合成,绘制,合成,语义阶段的功能。