美文网首页
flutter 绘制过程 系列3-绘制渲染

flutter 绘制过程 系列3-绘制渲染

作者: cg1991 | 来源:发表于2020-05-07 17:46 被阅读0次

    流程图如下:


    image

    1、预热帧

    1.1 scheduleWarmUpFrame

    runApp调用的最后一个方法是scheduleWarmUpFrame,从这里开始渲染之前遍历的widget。

    packages\flutter\lib\src\scheduler\binding.dart\ScheduleBinding

    void scheduleWarmUpFrame() {
        handleDrawFrame();
    }
    
    void handleDrawFrame() {
        for (FrameCallback callback in _persistentCallbacks)
            _invokeFrameCallback(callback, _currentFrameTimeStamp);
    }
    

    _persistentCallbacks是一个FrameCallback类型的List,RendererBinding的initInstances方法中添加了一个回调到这个List中,调用的地方是:

    packages\flutter\lib\src\rendering\binding.dart\RendererBinding

    void _handlePersistentFrameCallback(Duration timeStamp) {
        drawFrame();
    }
    
    @protected
    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.
    }
    

    这里的pipelineOwner就是RendererBinding类的initInstances方法中初始化的PipelineOwner对象。

    renderView就是RendererBinding类的initRenderView方法中初始化的RenderView对象。

    2、PipelineOwner

    D:\software-installpath\flutter-1.0.0\packages\flutter\lib\src\rendering\object.dart\PipelineOwner

    2.1 flushLayout

    void flushLayout() {
        while (_nodesNeedingLayout.isNotEmpty) {
            final List<RenderObject> dirtyNodes = _nodesNeedingLayout;
            _nodesNeedingLayout = <RenderObject>[];
            for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) {
              if (node._needsLayout && node.owner == this)
                node._layoutWithoutResize();
            }
        }
    }
    

    _nodesNeedingLayout里面存放的就是需要布局的RenderObject对象。将这些对象先以深度从小到大排序,然后遍历出来渲染布局。

    D:\software-installpath\flutter-1.0.0\packages\flutter\lib\src\rendering\object.dart\RenderObject

    2.1.1 _layoutWithoutResize

    void _layoutWithoutResize() {
        performLayout();
        markNeedsSemanticsUpdate();
        _needsLayout = false;
        markNeedsPaint();
    }
    

    这个performLayout实际是虚类RenderObject定义的一个方法,需要有一个类继承RenderObject并实现performLayout方法。

    看看_nodesNeedingLayout中最开始的元素是谁。在packages\flutter\lib\src\rendering\object.dart文件的scheduleInitialLayout方法中,将this添加到了_nodesNeedingLayout list中,而scheduleInitialLayout方法其实是在initRenderView方法中,通过RenderView调用prepareInitialFrame方法而来。

    于是performLayout到了RenderView中。之前提到过RenderView是根View,看看RenderView的performLayout方法:

    2.1.2 布局RenderView

    RenderView

    @override
    void performLayout() {
      if (child != null)
      child.layout(BoxConstraints.tight(_size));
    }
    

    这个child是ChildType类型,看看下面关于他的定义:

    mixin RenderObjectWithChildMixin<ChildType extends RenderObject> on RenderObject {
    }
    

    ChildType继承自RenderObject,作为RenderObjectWithChildMixin的类型约束,看看RenderObjectWithChildMixin是谁。

    在RenderObjectToWidgetAdapter初始化的时候,会传一个container参数过来,这个container所属的类型就是RenderObjectWithChildMixin。而这个container就是RenderView。为啥呢,看看RenderView的构造函数:

    RenderView

    class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> {
        RenderView({
        RenderBox child,
        @required ViewConfiguration configuration,
        @required ui.Window window,
      }) : assert(configuration != null),
           _configuration = configuration,
           _window = window {
        this.child = child;
      }
    }
    

    在初始化成员列表里面,看到了this.child = child;,this.child指的就是performLayout方法里面那个child了。

    这里的RenderBox定义如下:

    abstract class RenderBox extends RenderObject {
    }
    

    到这里基本上就可以知道performLayout里面的child其实是个RenderObject类型。

    2.1.3 layout布局

    2.1.3.1 RenderObject的layout

    RenderObject的layout方法如下:

    RenderObject

    void layout(Constraints constraints, { bool parentUsesSize = false }) {
        RenderObject relayoutBoundary;
        if (!parentUsesSize || sizedByParent || constraints.isTight || parent is! RenderObject) {
          relayoutBoundary = this;
        } else {
          final RenderObject parent = this.parent;
          relayoutBoundary = parent._relayoutBoundary;
        }
        if (!_needsLayout && constraints == _constraints && relayoutBoundary == _relayoutBoundary) {
            return;
        }
        _constraints = constraints;
        if (_relayoutBoundary != null && relayoutBoundary != _relayoutBoundary) {
            visitChildren((RenderObject child) {
                child._cleanRelayoutBoundary();
          });
        }
        _relayoutBoundary = relayoutBoundary;
        if (sizedByParent) {
            performResize();
        }
        performLayout();
        markNeedsSemanticsUpdate();
        _needsLayout = false;
        markNeedsPaint();
    }
    

    根据layout方法,我们大概可以知道布局的基本逻辑是:

    如果父视图没有约束子视图的大小,那么子视图唯一要做的就是布局自己的边界;另外一种情况是父视图明确约束了大小,子视图要按照约束重新布局自身边界大小,并在获取边界大小之后重新布局,布局完成加入需要绘制列表:

    RenderObject

    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;
            parent.markNeedsPaint();
        } else {
            if (owner != null)
                owner.requestVisualUpdate();
        }
    }
    

    第一种情况是子视图只需要关注自身边界;第二种情况是有父布局的约束;第三种情况是根视图(RenderView),直接刷新显示就行了。
    requestVisualUpdate对应的是:

    PipelineOwner

    void requestVisualUpdate() {
        if (onNeedVisualUpdate != null)
            onNeedVisualUpdate();
    }
    

    PipelineOwner类初始化的时候,对应的是ensureVisualUpdate方法,这个方法在ScheduleBinding类中定义:

    ScheduleBinding

    void ensureVisualUpdate() {
        switch (schedulerPhase) {
          case SchedulerPhase.idle:
          case SchedulerPhase.postFrameCallbacks:
            scheduleFrame();
            return;
          case SchedulerPhase.transientCallbacks:
          case SchedulerPhase.midFrameMicrotasks:
          case SchedulerPhase.persistentCallbacks:
            return;
        }
    }
    

    在postFrameCallbacks阶段,调用scheduleFrame方法到引擎申请渲染帧。

    2.2 flushCompositingBits

    PipelineOwner

    void flushCompositingBits() {
        if (!kReleaseMode) {
          Timeline.startSync('Compositing bits');
        }
        _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth);
        for (RenderObject node in _nodesNeedingCompositingBitsUpdate) {
          if (node._needsCompositingBitsUpdate && node.owner == this)
            node._updateCompositingBits();
        }
        _nodesNeedingCompositingBitsUpdate.clear();
        if (!kReleaseMode) {
          Timeline.finishSync();
        }
    }
    

    按照深度depth从浅到深,更新语义:

    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 (oldNeedsCompositing != _needsCompositing)
          markNeedsPaint();
        _needsCompositingBitsUpdate = false;
    }
    

    递归查找需要更新的语义,然后添加到需要绘制List中。

    2.3 flushPaint

    PipelineOwner

    void flushPaint() {
        if (!kReleaseMode) {
          Timeline.startSync('Paint', arguments: timelineWhitelistArguments);
        }
        try {
          final List<RenderObject> dirtyNodes = _nodesNeedingPaint;
          _nodesNeedingPaint = <RenderObject>[];
          // Sort the dirty nodes in reverse order (deepest first).
          for (RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) {
            if (node._needsPaint && node.owner == this) {
              if (node._layer.attached) {
                PaintingContext.repaintCompositedChild(node);
              } else {
                node._skippedPaintingOnLayer();
              }
            }
          }
        } finally {
          if (!kReleaseMode) {
            Timeline.finishSync();
          }
        }
    }
    

    从这个方法可以看出,所有添加到_nodesNeedingPaint里面的RenderObject,都认为是dirty节点,需要绘制刷新。并且刷新的机制是从深到浅,也就是先从子再到父。

    2.3.1 repaintCompositedChild

    看看repaintCompositedChild方法:

    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._layer;
        if (childLayer == null) {
          child._layer = childLayer = OffsetLayer();
        } else {
          childLayer.removeAllChildren();
        }
        childContext ??= PaintingContext(child._layer, child.paintBounds);
        child._paintWithContext(childContext, Offset.zero);
    
        childContext.stopRecordingIfNeeded();
      }
    

    先检查RenderObject的Layer,如果为空,就新建一个OffsetLayer类型的Layer。否则的话删除当前RenderObject下所有child的Layer,包含他的左右兄弟(为什么要删除呢?上一步的dirtyNodes中其实已经包含了所有需要重新paint的child),其实这里还隐含了复用_layer的逻辑。

    另外childContext为空的话,新建一个PaintingContext,否则复用。
    看看_paintWithContext方法:

    RenderObject

    void _paintWithContext(PaintingContext context, Offset offset) {
        paint(context, offset);
    }
    

    2.3.2 paint

    这个paint被各个Widget的Element所创建的RenderObject重载,以switch.dart文件中的_RenderCupertinoSwitch类为例:

    _RenderCupertinoSwitch paint方法太长,只写大概

    @override
    void paint(PaintingContext context, Offset offset) {
        final Canvas canvas = context.canvas;
        final Paint paint = Paint();
        if (needsCompositing) {
            appendLayer(childLayer);
        }
        painter(PaintingContext, offset)
    }
    
    void paint(Canvas canvas, Rect rect) {
        final RRect rrect = RRect.fromRectAndRadius(
          rect,
          Radius.circular(rect.shortestSide / 2.0),
        );
        
        for (BoxShadow shadow in shadows)
          canvas.drawRRect(rrect.shift(shadow.offset), shadow.toPaint());
        
        canvas.drawRRect(
          rrect.inflate(0.5),
          Paint()..color = _kThumbBorderColor,
        );
        canvas.drawRRect(rrect, Paint()..color = color);
    }
    
    void drawRRect(RRect rrect, Paint paint) {
        _drawRRect(rrect._value32, paint._objects, paint._data);
    }
    
    void _drawRRect(Float32List rrect,
                  List<dynamic> paintObjects,
                  ByteData paintData) native 'Canvas_drawRRect';
    

    类似的,每个重载paint方法的RenderObject都有自己的绘制策略,但最终都是在Canvas画布上面绘制。

    2.4 compositeFrame

    RenderView

    void compositeFrame() {
        Timeline.startSync('Compositing', arguments: timelineWhitelistArguments);
        try {
          final ui.SceneBuilder builder = ui.SceneBuilder();
          final ui.Scene scene = layer.buildScene(builder);//native 'SceneBuilder_build'
          if (automaticSystemUiAdjustment) 
            _updateSystemChrome();
          _window.render(scene);//native 'Window_render'
          scene.dispose();//native 'Scene_dispose'
        } finally {
          Timeline.finishSync();
        }
    }
    

    从这个方法可以看出,这里要开始渲染了。先创建一个场景构造器,layer创建场景(layer的append方法将渲染树的layer都追加在这个layer里面),最后通过_window调用render渲染。

    2.4.1 Window

    Window

    void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
      natives->Register({
          {"Window_render", Render, 2, true},
      });
    }
    

    Window_render注册到了Render方法:

    void Render(Dart_NativeArguments args) {
      Dart_Handle exception = nullptr;
      Scene* scene =
          tonic::DartConverter<Scene*>::FromArguments(args, 1, exception);
      UIDartState::Current()->window()->client()->Render(scene);
    }
    

    2.4.2 RuntimeController

    Render方法实际调用到了RuntimeController类的Render方法:

    void RuntimeController::Render(Scene* scene) {
      client_.Render(scene->takeLayerTree());
    }
    

    2.4.3 Engine

    然后到Engine中:

    void Engine::Render(std::unique_ptr<flutter::LayerTree> layer_tree) {
      if (!layer_tree)
        return;
    
      // Ensure frame dimensions are sane.
      if (layer_tree->frame_size().isEmpty() ||
          layer_tree->frame_physical_depth() <= 0.0f ||
          layer_tree->frame_device_pixel_ratio() <= 0.0f)
        return;
    
      animator_->Render(std::move(layer_tree));
    }
    

    2.4.4 Animator

    再到Animator类:

    void Animator::Render(std::unique_ptr<flutter::LayerTree> layer_tree) {
      if (dimension_change_pending_ &&
          layer_tree->frame_size() != last_layer_tree_size_) {
        dimension_change_pending_ = false;
      }
      last_layer_tree_size_ = layer_tree->frame_size();
    
      if (layer_tree) {
        // Note the frame time for instrumentation.
        layer_tree->RecordBuildTime(last_frame_begin_time_,
                                    last_frame_target_time_);
      }
    
      // Commit the pending continuation.
      producer_continuation_.Complete(std::move(layer_tree));
    
      delegate_.OnAnimatorDraw(layer_tree_pipeline_);
    }
    

    2.4.5 Shell

    delegate_其实是Shell类型,OnAnimatorDraw方法如下:

    void Shell::OnAnimatorDraw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline) {
      FML_DCHECK(is_setup_);
    
      task_runners_.GetGPUTaskRunner()->PostTask(
          [& waiting_for_first_frame = waiting_for_first_frame_,
           &waiting_for_first_frame_condition = waiting_for_first_frame_condition_,
           rasterizer = rasterizer_->GetWeakPtr(),
           pipeline = std::move(pipeline)]() {
            if (rasterizer) {
              rasterizer->Draw(pipeline);
    
              if (waiting_for_first_frame.load()) {
                waiting_for_first_frame.store(false);
                waiting_for_first_frame_condition.notify_all();
              }
            }
          });
    }
    

    2.4.6 Rasterizer

    在引擎初始化的时候有构造rasterizer对象,在GPU线程中执行Draw方法,看看Draw方法:

    Rasterizer

    void Rasterizer::Draw(fml::RefPtr<Pipeline<flutter::LayerTree>> pipeline) {
        Pipeline<flutter::LayerTree>::Consumer consumer =
          [&](std::unique_ptr<LayerTree> layer_tree) {
            raster_status = DoDraw(std::move(layer_tree));
        };
        PipelineConsumeResult consume_result = pipeline->Consume(consumer);
    }
    
    RasterStatus Rasterizer::DoDraw(
        std::unique_ptr<flutter::LayerTree> layer_tree) {
        RasterStatus raster_status = DrawToSurface(*layer_tree);
    }
    
    RasterStatus Rasterizer::DrawToSurface(flutter::LayerTree& layer_tree) {
        auto frame = surface_->AcquireFrame(layer_tree.frame_size());
        auto* external_view_embedder = surface_->GetExternalViewEmbedder();
        SkCanvas* embedder_root_canvas = nullptr;
        if (external_view_embedder != nullptr) {
            external_view_embedder->BeginFrame(layer_tree.frame_size(),surface_->GetContext(),layer_tree.device_pixel_ratio());
            embedder_root_canvas = external_view_embedder->GetRootCanvas();
        }
        auto root_surface_canvas =
            embedder_root_canvas ? embedder_root_canvas : frame->SkiaCanvas();
    
        auto compositor_frame = compositor_context_->AcquireFrame(
          surface_->GetContext(),       // skia GrContext
          root_surface_canvas,          // root surface canvas
          external_view_embedder,       // external view embedder
          root_surface_transformation,  // root surface transformation
          true,                         // instrumentation enabled
          frame->supports_readback(),   // surface supports pixel reads
          gpu_thread_merger_            // thread merger
        );
        if (compositor_frame) {
            RasterStatus raster_status = compositor_frame->Raster(layer_tree, false);
            if (external_view_embedder != nullptr) {
                external_view_embedder->SubmitFrame(surface_->GetContext(),root_surface_canvas);
                frame->Submit();
                external_view_embedder->FinishFrame();
            } else {
                frame->Submit();
            }
        }
    }
    

    在光栅类Rasterizer中前后调用了Draw,DoDraw,DrawToSurface方法,主要任务是:通过layer_tree的frame数量请求frame;获取绘制画布SkiaCanvas;获取复合帧;调用Raster方法传递layer_tree形成栅格;提交栅格到画布;完成在画布上的绘制,然后提交。

    2.5 flushSemantics

    PipelineOwner

    void flushSemantics() {
        final List<RenderObject> nodesToProcess = _nodesNeedingSemantics.toList()
                ..sort((RenderObject a, RenderObject b) => a.depth - b.depth);
        _nodesNeedingSemantics.clear();
        for (RenderObject node in nodesToProcess) {
        if (node._needsSemanticsUpdate && node.owner == this)
          node._updateSemantics();
        }
        _semanticsOwner.sendSemanticsUpdate();
    }
    

    刷新语义。在绘制的过程中,如果有渲染对象通过markNeedsSemanticsUpdate方法被标记为需要更新语义,其节点就会被加入到_nodesNeedingSemantics List中。

    2.5.1 flushSemantics

    最后通过sendSemanticsUpdate方法执行语义更新:

    SemanticsOwner

    void sendSemanticsUpdate() {
        final ui.SemanticsUpdateBuilder builder = ui.SemanticsUpdateBuilder();
        SemanticsBinding.instance.window.updateSemantics(builder.build());//native 'Window_updateSemantics'
    }
    

    相关文章

      网友评论

          本文标题:flutter 绘制过程 系列3-绘制渲染

          本文链接:https://www.haomeiwen.com/subject/eqtzghtx.html