This article is the sixth in the “Flutter Framework” series, PipelineOwner assists in the UI refresh of the RenderObject Tree, RendererBinding, and Window in Rendering Pipeline.

This article is also published on my personal blog

This series of articles explores the core concepts and processes of the Flutter Framework step by step, including:

  • “Widgets of the Flutter Framework”
  • “BuildOwner of the Flutter Framework”
  • Elements of the Flutter Framework
  • “PaintingContext of the Flutter Framework”
  • “Layer of the Flutter Framework”
  • PipelineOwner of the Flutter Framework
  • RenderObejct of the Flutter Framework
  • “Binding of the Flutter Framework”
  • “The Rendering Pipeline of the Flutter Framework”
  • “Custom Widgets from the Flutter Framework”

Overview


PipelineOwner plays an important role in Rendering Pipeline:

  • Collect “Dirty Render Objects” as UI changes
  • Then drive Rendering Pipeline to refresh the UI

PipelineOwner is a bridge between the RenderObject Tree and the RendererBinding.

Relationship between


As shown above:

  • RendererBindingCreate and holdPipelineOwnerInstance, Code1- the8 ~ 12line
  • At the same time,RendererBindingRenderView creates the root node of the “RenderObject Tree” and assigns it toPipelineOwner#rootNodeCode1 – the first13 ~ 24line
  • Every time a new node is inserted during the “RenderObject Tree” build process, it willPipelineOwnerThe instance attaches to the node, that is, all nodes on the “RenderObject Tree” share the samePipelineOwnerInstance, Code2- the4line
1 // Code1-RendererBinding#init
2 // The code has been deleted
3 mixin RendererBinding {
4   @override
5   void initInstances() {
6     super.initInstances();
7     _instance = this;
8     _pipelineOwner = PipelineOwner(
9       onNeedVisualUpdate: ensureVisualUpdate,
10      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
11      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
12    );
13    initRenderView();
14 addPersistentFrameCallback(_handlePersistentFrameCallback);
15  }
16
17  void initRenderView() {
18    renderView = RenderView(configuration: createViewConfiguration(), window: window);
19    renderView.prepareInitialFrame();
20  }
21
22  set renderView(RenderView value) {
23    _pipelineOwner.rootNode = value;
24  }
Copy the code
// Code2-RenderObject#adoptChild
//
1   void adoptChild(covariant AbstractNode child) {
2     child._parent = this;
3     if (attached)
4       child.attach(_owner);
5     redepthChild(child);
6   }
7
8   void attach(covariant Object owner) {
9     _owner = owner;
10  }
Copy the code

RendererBinding is a mixin, and the real class behind it is WidgetsFlutterBinding

As described above, there is normally only one PipelineOwner instance during Flutter operation and it is held by a RendererBinding that manages all “on-screen RenderObjects”. However, if there are “off-screen RenderObjects”, new PipelineOwner instances can be created to manage them. The On-screen PipelineOwner is completely separate from the off-screen PipelineOwner, which is maintained and driven by the creator.

mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable as above, RendererBinding requires its ancillary classes mixin BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding and HitTestable quarters, For the sake of description, the method on the RendererBinding mentioned in the text may also come from several other bindings.

drive


Dirty RenderObjects

The Render Object has four “Dirty states” that need to be maintained by the PipelineOwner:

  • Nicer Layout: Render Obejct needs to re-layout
  • Annotated Compositing Bits Update: Render Obejct Compositing
  • Warm Paint: Render Obejct needs to be repainted
  • The Semantics of the Render Object is variable

As shown above:

  • Called when RenderObject needs to reseleoutmarkNeedsLayoutMethod that adds the current RenderObject toPipelineOwner#_nodesNeedingLayoutOr pass it to the parent node for processing;
  • Called when the Compositing Bits of the RenderObject changemarkNeedsCompositingBitsUpdateMethod that adds the current RenderObject toPipelineOwner#_nodesNeedingCompositingBitsUpdateOr pass it to the parent node for processing;
  • Called when RenderObject needs to be repaintedmarkNeedsPaintMethod that adds the current RenderObject toPipelineOwner#_nodesNeedingPaintOr pass to the parent node for processing;
  • Called if there is a variation in the Semantics of the RenderObjectmarkNeedsSemanticsUpdateMethod that adds the current RenderObject toPipelineOwner#_nodesNeedingSemanticsOr pass it to the parent node for processing

That’s how PipelineOwner continuously collects “Dirty RenderObjects.”

The logic inside RenderObject will be examined in detail in a later article.

Request Visual Update

The above four markNeeds * method, in addition to markNeedsCompositingBitsUpdate, other methods will be called last PipelineOwner# requestVisualUpdate. The markNeedsCompositingBitsUpdate won’t call PipelineOwner# requestVisualUpdate, because they won’t be alone, must be accompanied by one of the other three along with them.

PipelineOwner#requestVisualUpdate-> RenderErinding# scheduleFrame-> Windows #scheduleFrame calls the chain, The information that the UI needs to refresh is eventually passed to the Engine layer. Windows #scheduleFrame calls Windows #onBeginFrame and Windows #onDrawFrame methods when the Engine requests a refresh for the next frame.

Windows #onBeginFrame and Windows #onDrawFrame are essentially two callbacks that the RendererBinding injects into them (_handleBeginFrame and _handleDrawFrame) :

// Code3-SchedulerBinding
//
1   void ensureFrameCallbacksRegistered() {
2    window.onBeginFrame ?? = _handleBeginFrame;3    window.onDrawFrame ?? = _handleDrawFrame;4  }
Copy the code

Handle Draw Frame

As shown above, the Engine will call it in the next frame refresh when it receives a UI updateWindow#onDrawFrameBy registering in advancePersistentFrameCallback, and finally call toRendererBinding#drawFrameMethods:

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

Rendererinding# drawFrame calls the flushLayout, flushCompositingBits, flushPaint and flushSemantics methods of PipelineOwner. To handle renderObjects in the corresponding state.

Flush Layout

// Code5-PipelineOwner#flushLayout
//
1   void flushLayout() {
2     while (_nodesNeedingLayout.isNotEmpty) {
3       final List<RenderObject> dirtyNodes = _nodesNeedingLayout;
4       _nodesNeedingLayout = <RenderObject>[];
5       for (RenderObject node indirtyNodes.. sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {6         if (node._needsLayout && node.owner == this)
7           node._layoutWithoutResize();
8       }
9     } 
10  }
Copy the code

First, PipelineOwner sorts the collected “contrasting Layout RenderObjects” in ascending order of their depth on the “RenderObject Tree”, The main reason is to avoid the child node to repeat the Layout (because the parent node Layout, also recursively to the subtree Layout); Next, the layout operation is performed by calling _layoutWithoutResize on the sorted RenderObjects that meet the criteria.

When the parent layout is finished, all of its children are finished and their _needsLayout flag is set to flase, so the judgment on line 6 is needed in Code5 to avoid repeating the layout.

Flush Compositing Bits

// Code6-PipelineOwner#flushCompositingBits
//
1   void flushCompositingBits() {
2     _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
3     for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
4       if (node._needsCompositingBitsUpdate && node.owner == this)
5         node._updateCompositingBits();
6     }
7     _nodesNeedingCompositingBitsUpdate.clear();
8   }
Copy the code

RenderObjects#_updateCompositingBits: char char char (); char char ()

Flush Paint

// Code7-PipelineOwner#flushPaint
//
1   void flushPaint() {
2     final List<RenderObject> dirtyNodes = _nodesNeedingPaint;
3     _nodesNeedingPaint = <RenderObject>[];
4     // Sort the dirty nodes in reverse order (deepest first).
5     for (RenderObject node indirtyNodes.. sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {6       if (node._needsPaint && node.owner == this) {
7         if (node._layer.attached) {
8             PaintingContext.repaintCompositedChild(node);
9         } 
10      }
11    }
12  }
Copy the code

For the Paint operation, the parent node needs to use the result of the child node, so the child node needs to be drawn before the parent. Therefore, unlike the first two Flush operations, the “contrast Paint RenderObjects” need to be sorted by descending depth. Figure below, inPaintingContext of the Flutter FrameworkA detailed analysis fromPipelineOwner#flushPainttoPaintingContextThe process of internal operation is not described here.

Flush Semantics

// Code8-PipelineOwner#flushSemantics
//
1   void flushSemantics() {
2     final List<RenderObject> nodesToProcess = _nodesNeedingSemantics.toList()
3       ..sort((RenderObject a, RenderObject b) => a.depth - b.depth);
4     _nodesNeedingSemantics.clear();
5     for (RenderObject node in nodesToProcess) {
6       if (node._needsSemanticsUpdate && node.owner == this)
7         node._updateSemantics();
8     }
9     _semanticsOwner.sendSemanticsUpdate();
10  }
Copy the code

Flush Semantics does exactly the same thing as Flush Layout.

That concludes the PipelineOwner section.

summary

PipelineOwner plays a critical role in Rendering Pipeline as a communication and coordination bridge between RenderObject Tree and RendererBinding/Window. Collect “Dirty RenderObjects” and notify the Engine during the life of the Flutter application. When the frame is refreshed, the collected items are processed in turn via a callback from the RendererBinding:

  • “Nicer Layout RenderObjects”
  • Duration Compositing Bits Update RenderObjects
  • “Warm Paint RenderObjects”
  • “Perceptual Semantics RenderObjects”

Finally, the UI is refreshed.