打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Layer Tree 绘制


站在老罗的肩膀上:https://blog.csdn.net/luoshengyang/article/details/51148299

网页绘图表面创建完成之后,调度器就会请求绘制CC Layer Tree,这样网页在加载完成之后就能快速显示出来。通过CC Layer Tree可以依次找到Graphics Layer Tree、Paint Layer Tree和Layout Object Tree,就可以执行具体的绘制工作了。

crRendererMain线程实际上并没有对CC Layer Tree执行真正的绘制,它只是将每一个Layer的绘制命令收集起来。这些绘制命令在对网页分块进行光栅化时才会被执行,也就是PREPARE_TILES中执行。

       CC Layer Tree中的每一个Layer都是按照分块进行绘制的。每一个分块的绘制命令都收集在一个SkPicture中。这个SkPicture就类似于Android应用程序UI硬件加速渲染过程形成的Display List。Layer分块并不是简单的区域划分。简单的区域划分在网页缩放过程中会有问题。Layer划分成相互重叠的区域。重叠的区域多大才合适的呢?这与网页的最小缩放因子有关。假设网页最小可以缩小原来的1/16,那么重叠的区域就至少需要15个点。

       当调度器调用SchedulerStateMachine类的成员函数NextAction询问状态机下一步要执行的操作时,SchedulerStateMachine类的成员函数NextAction会调用另外一个成员函数ShouldSendBeginMainFrame。当SchedulerStateMachine类的成员函数ShouldSendBeginMainFrame返回值等于true的时候,状态机就会提示调度器接下来需要执行SEND_BEGIN_MAIN_FRAME操作,也就是对CC Layer Tree进行绘制。

SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {  ...  if (ShouldSendBeginMainFrame())    return Action::SEND_BEGIN_MAIN_FRAME;  ...}

 状态从BeginMainFrameState::IDLE转变成BeginMainFrameState::SENT

void SchedulerStateMachine::WillSendBeginMainFrame() {  ...  begin_main_frame_state_ = BeginMainFrameState::SENT;  needs_begin_main_frame_ = false;  did_send_begin_main_frame_for_current_frame_ = true;  last_frame_number_begin_main_frame_sent_ = current_frame_number_;}
void ProxyImpl::ScheduledActionSendBeginMainFrame(    const viz::BeginFrameArgs& args) {  ...  std::unique_ptr<BeginMainFrameAndCommitState> begin_main_frame_state(      new BeginMainFrameAndCommitState);  ...  MainThreadTaskRunner()->PostTask(      FROM_HERE,      base::BindOnce(&ProxyMain::BeginMainFrame, proxy_main_weak_ptr_,                     base::Passed(&begin_main_frame_state)));  host_impl_->DidSendBeginMainFrame();  devtools_instrumentation::DidRequestMainThreadFrame(layer_tree_host_id_);}

向crRendererMain线程的消息队列发送一个Task,这个Task绑定的函数是ProxyMain::BeginMainFrame。因此,接下来会转入crRendererMain线程

void ProxyMain::BeginMainFrame(    std::unique_ptr<BeginMainFrameAndCommitState> begin_main_frame_state) {  ...  if (!layer_tree_host_->IsVisible()) {    ...    ImplThreadTaskRunner()->PostTask(        FROM_HERE, base::BindOnce(&ProxyImpl::BeginMainFrameAbortedOnImpl,                                  base::Unretained(proxy_impl_.get()),                                  CommitEarlyOutReason::ABORTED_NOT_VISIBLE,                                  begin_main_frame_start_time,                                  base::Passed(&empty_swap_promises)));    return;  }  layer_tree_host_->ApplyScrollAndScale(      begin_main_frame_state->scroll_info.get());  layer_tree_host_->WillBeginMainFrame();  layer_tree_host_->BeginMainFrame(begin_main_frame_state->begin_frame_args);  layer_tree_host_->AnimateLayers(      begin_main_frame_state->begin_frame_args.frame_time);  if (begin_main_frame_state->evicted_ui_resources)    layer_tree_host_->GetUIResourceManager()->RecreateUIResources();  layer_tree_host_->RequestMainFrameUpdate(      skip_paint_and_commit ? LayerTreeHost::VisualStateUpdate::kPrePaint                            : LayerTreeHost::VisualStateUpdate::kAll);  ...  if (skip_paint_and_commit) {    ...    ImplThreadTaskRunner()->PostTask(        FROM_HERE, base::BindOnce(&ProxyImpl::BeginMainFrameAbortedOnImpl,                                  base::Unretained(proxy_impl_.get()),                                  CommitEarlyOutReason::ABORTED_DEFERRED_COMMIT,                                  begin_main_frame_start_time,                                  base::Passed(&empty_swap_promises)));    ...    return;  }  // If UI resources were evicted on the impl thread, we need a commit.  if (begin_main_frame_state->evicted_ui_resources)    final_pipeline_stage_ = COMMIT_PIPELINE_STAGE;  current_pipeline_stage_ = UPDATE_LAYERS_PIPELINE_STAGE;  bool should_update_layers =      final_pipeline_stage_ >= UPDATE_LAYERS_PIPELINE_STAGE;  ...  bool updated = should_update_layers && layer_tree_host_->UpdateLayers();  // If updating the layers resulted in a content update, we need a commit.  if (updated)    final_pipeline_stage_ = COMMIT_PIPELINE_STAGE;  layer_tree_host_->WillCommit();  devtools_instrumentation::ScopedCommitTrace commit_task(      layer_tree_host_->GetId());  current_pipeline_stage_ = COMMIT_PIPELINE_STAGE;  if (final_pipeline_stage_ < COMMIT_PIPELINE_STAGE) {    ...    ImplThreadTaskRunner()->PostTask(        FROM_HERE, base::BindOnce(&ProxyImpl::BeginMainFrameAbortedOnImpl,                                  base::Unretained(proxy_impl_.get()),                                  CommitEarlyOutReason::FINISHED_NO_UPDATES,                                  begin_main_frame_start_time,                                  base::Passed(&swap_promises)));    ...    return;  }  ...  {    ...    ImplThreadTaskRunner()->PostTask(        FROM_HERE,        base::BindOnce(&ProxyImpl::NotifyReadyToCommitOnImpl,                       base::Unretained(proxy_impl_.get()), &completion,                       layer_tree_host_, begin_main_frame_start_time,                       hold_commit_for_activation));    completion.Wait();  }  ...}

BeginMainFrame主要是做三件事情:


       1. 计算CC Layer Tree的布局。这是通过调用LayerTreeHost类的成员函数BeginMainFrame实现的。


       2. 计算CC Layer Tree的动画。使用网址:https://www.jianshu.com/p/7da4895b3693进行动画渲染调试
当网页的DOM Tree中的某一个Element需要创建动画时,调用Animation::create为其创建一个动画,运行在CrBrowserMain进程,如下所示:

scoped_refptr<Animation> Animation::CreateImplInstance() const {  return Animation::Create(id());}

 更新网页的Graphics Layer Tree的时候,就会将DOM Tree中的动画注册到CC模块中去,接着又会调用DocumentAnimations::UpdateAnimations执行网页的DOM Tree中的动画。

void PaintLayerCompositor::UpdateIfNeededRecursiveInternal(    DocumentLifecycle::LifecycleState target_state,    CompositingReasonsStats& compositing_reasons_stats) {  ...  if (!layout_view_.GetDocument().Printing() ||      RuntimeEnabledFeatures::PrintBrowserEnabled()) {    ...    if (!RuntimeEnabledFeatures::BlinkGenPropertyTreesEnabled()) {      base::Optional<CompositorElementIdSet> composited_element_ids;      DocumentAnimations::UpdateAnimations(layout_view_.GetDocument(),                                           DocumentLifecycle::kCompositingClean,                                           composited_element_ids);    }    ...}void DocumentAnimations::UpdateAnimations(    Document& document,    DocumentLifecycle::LifecycleState required_lifecycle_state,    base::Optional<CompositorElementIdSet>& composited_element_ids) {  if (document.GetPendingAnimations().Update(composited_element_ids)) {    DCHECK(document.View());    document.View()->ScheduleAnimation();  }  ...}

LocalFrameView::ScheduleAnimation调度执行这些动画 。PendingAnimations::Update将动画是注册到CC模块

bool PendingAnimations::Update(    const base::Optional<CompositorElementIdSet>& composited_element_ids,    bool start_on_compositor) {  ...  for (auto& animation : animations) {    bool had_compositor_animation =        animation->HasActiveAnimationsOnCompositor();    // Animations with a start time do not participate in compositor start-time    // grouping.    if (animation->PreCommit(animation->startTime() ? 1 : compositor_group,                             composited_element_ids, start_on_compositor)) {      if (animation->HasActiveAnimationsOnCompositor() &&          !had_compositor_animation) {        started_synchronized_on_compositor = true;      }      if (animation->Playing() && !animation->startTime() &&          animation->TimelineInternal() &&          animation->TimelineInternal()->IsActive()) {        waiting_for_start_time.push_back(animation.Get());      }    } else {      deferred.push_back(animation);    }  }  ...}

 其中的Animation::PreCommit如下:

bool Animation::PreCommit(    int compositor_group,    const base::Optional<CompositorElementIdSet>& composited_element_ids,    bool start_on_compositor) {  ...  if (should_start) {    compositor_group_ = compositor_group;    if (start_on_compositor) {      ...      if (failure_code.Ok()) {        CreateCompositorAnimation();        StartAnimationOnCompositor(composited_element_ids);        compositor_state_ = std::make_unique<CompositorState>(*this);      ...  }  return true;}

最终调用CompositorAnimations::StartAnimationOnCompositor执行该动画。 

void CompositorAnimations::StartAnimationOnCompositor(    const Element& element,    ...,    const EffectModel& effect,    ...) {  ...  GetAnimationOnCompositor(timing, group, start_time, time_offset,                           keyframe_effect, keyframe_models,                           animation_playback_rate);  for (auto& compositor_keyframe_model : keyframe_models) {    int id = compositor_keyframe_model->Id();    compositor_animation.AddKeyframeModel(std::move(compositor_keyframe_model));    started_keyframe_model_ids.push_back(id);  }}

其中compositor_annimation类型为CompositorAnimation,具有类型为cc::SingleKeyframeEffectAnimation的成员变量animation_ 。

 参数element描述的是要执行动画的一个Element。这个Element对应的是网页的DOM Tree中的一个节点。通过调用CompositorAnimations类的成员函数toRenderBoxModelObject可以获得它在网页的Render Layer Tree中对应的节点,也就是一个RenderLayer对象。

       参数effect描述了上述Element要执行的动画,每一个动画会被重新封装成一个WebAnimation对象。这是通过调用CompositorAnimationsImpl类的静态成员函数CompositorAnimations::GetAnimationOnCompositor实现的,如下所示:

void CompositorAnimations::GetAnimationOnCompositor(    const Timing& timing,    ...,    const KeyframeEffectModelBase& effect,    Vector<std::unique_ptr<CompositorKeyframeModel>>& keyframe_models,    double animation_playback_rate) {  ...  for (const auto& property : properties) {    ...    const PropertySpecificKeyframeVector& values =        effect.GetPropertySpecificKeyframes(property);    CompositorTargetProperty::Type target_property;    std::unique_ptr<CompositorAnimationCurve> curve;    switch (property.GetCSSProperty().PropertyID()) {      ...      case CSSPropertyTransform: {        target_property = CompositorTargetProperty::TRANSFORM;        std::unique_ptr<CompositorTransformAnimationCurve> transform_curve =            CompositorTransformAnimationCurve::Create();        AddKeyframesToCurve(*transform_curve, values);        transform_curve->SetTimingFunction(*timing.timing_function);        transform_curve->SetScaledDuration(scale);        curve = std::move(transform_curve);        break;      }      ...    }    std::unique_ptr<CompositorKeyframeModel> keyframe_model =        CompositorKeyframeModel::Create(*curve, target_property, group, 0);    ...    keyframe_models.push_back(std::move(keyframe_model));  }}

每一个动画都封装成CompositorKeyframeModel,保存在compositor_animation中,接下来要把它们关联到Dom节点对应都Graphics Layer中,回到CompositorAnimations::StartAnimationOnCompositor,通过compositor_animation.AddKeyframeModel,最终将动画添加到keyframe_models_

void KeyframeEffect::AddKeyframeModel(    std::unique_ptr<KeyframeModel> keyframe_model) {  ...  keyframe_models_.push_back(std::move(keyframe_model));  ...}

而这个对象keyframe_models_就是CCLayer计算Animation所使用到到对象,通过调用LayerTreeHost类的成员函数AnimateLayers实现的。

void LayerTreeHost::AnimateLayers(base::TimeTicks monotonic_time) {  ...  // slimming path and will do so in a follow up. (762717)  if (IsUsingLayerLists())    return;  std::unique_ptr<MutatorEvents> events = mutator_host_->CreateEvents();  if (mutator_host_->TickAnimations(monotonic_time,                                    property_trees()->scroll_tree, true))    mutator_host_->UpdateAnimationState(true, events.get());  if (!events->IsEmpty())    property_trees_.needs_rebuild = true;}

mutator_host_负责管理动画,类型为AnimationHost. 

注册动画

void KeyframeEffect::Tick(base::TimeTicks monotonic_time) {  DCHECK(has_bound_element_animations());  if (!element_animations_->has_element_in_any_list())    return;  if (needs_to_start_keyframe_models_)    StartKeyframeModels(monotonic_time);  for (auto& keyframe_model : keyframe_models_) {    TickKeyframeModel(monotonic_time, keyframe_model.get(),                      element_animations_.get());  }  last_tick_time_ = monotonic_time;  element_animations_->UpdateClientAnimationState();}void KeyframeEffect::TickKeyframeModel(base::TimeTicks monotonic_time,                                       KeyframeModel* keyframe_model,                                       AnimationTarget* target) {  ...  AnimationCurve* curve = keyframe_model->curve();  base::TimeDelta trimmed =      keyframe_model->TrimTimeToCurrentIteration(monotonic_time);  switch (curve->Type()) {    case AnimationCurve::TRANSFORM:      target->NotifyClientTransformOperationsAnimated(          curve->ToTransformAnimationCurve()->GetValue(trimmed),          keyframe_model->target_property_id(), keyframe_model);      break;    ...  }}

由此Animation的绘制就从Dom关联到了CCLayer 。


       3. 绘制CC Layer Tree。

创建Layout Object Tree时有以下调用栈

从Document::UpdateStyleAndLayoutTree创建或更新Layout Object Tree,并在完成后通过LocalFrameView::UpdateLayout创建或更新Layout

void Document::UpdateStyleAndLayout() {  ...  UpdateStyleAndLayoutTree();  ...  if (frame_view && frame_view->NeedsLayout())    frame_view->UpdateLayout();  ...}
void LocalFrameView::UpdateLayout() {  // We should never layout a Document which is not in a LocalFrame.      ...    // PreLayout is for what    PerformPreLayoutTasks();    ...      bool in_subtree_layout = IsSubtreeLayout();      ...      PerformLayout(in_subtree_layout);      ...}
void LocalFrameView::PerformLayout(bool in_subtree_layout) {  ...  {    ...    if (in_subtree_layout) {      // subtree      ...    } else {      // whole tree      if (HasOrthogonalWritingModeRoots())        LayoutOrthogonalWritingModeRoots();      GetLayoutView()->UpdateLayout();    }  }  ...}

经历如下调用栈,

其中主要的递归逻辑在

void LayoutBlockFlow::LayoutBlockChildren(bool relayout_children,                                          SubtreeLayoutScope& layout_scope,                                          LayoutUnit before_edge,                                          LayoutUnit after_edge) {  ...  // first do all the Layout of children Layout Objects  while (next) {    LayoutBox* child = next;    LayoutObject* next_sibling = child->NextSibling();    CHECK(!next_sibling || next_sibling->IsBox());    next = ToLayoutBox(next_sibling);    ...    // Lay out the child.    LayoutBlockChild(*child, layout_info);    layout_info.ClearIsAtFirstInFlowChild();    last_normal_flow_child = child;  }  // Second do the layout for the current Layout Object  // Now do the handling of the bottom of the block, adding in our bottom  // border/padding and determining the correct collapsed bottom margin  // information.  HandleAfterSideOfBlock(last_normal_flow_child, before_edge, after_edge,                         margin_info);}

 这样得到了完整的Layout,然后可以开始绘制CCLayer,通过调用LayerTreeHost类的成员函数UpdateLayers实现的。

CC Layer Tree与Graphics Layer Tree中的节点是一一对应关系,并且Graphics Layer Tree的每一个Layer代表的都是一个图层。这个图层在硬件加速渲染条件下,就是一个FBO。但是到底要不要为Graphics Layer Tree中的Layer分配一个图层最终是由CC模块决定的。如果CC模块决定要为一个Graphics Layer分配一个图层,那么就会为它创建一个Render Surface。Render Surface才是真正表示一个图层。

       LayerTreeHostCommon类静态成员函数CalculateDrawProperties根据CC Layer Tree创建出来的Render Surface Tree虽然在结构上也是一个Tree,不过它的节点是以列表的形式储存的,也就是储存在本地变量render_surface_list_描述的一个RenderSurfaceList(std::vector<RenderSurfaceImpl*>)中。

(CrBrowserMain)

1.计算以参数root_layer指向的Layer对象为根节点的CC Layer Tree的每一个Layer的绘图属性。此外,LayerTreeHostCommon类静态成员函数CalculateDrawProperties还会根据上述CC Layer Tree创建一个Render Surface Tree。CC Layer Tree中的节点与Render Surface Tree中的节点是多对一的关系。也就是只有CC Layer Tree的某些节点在Render Surface Tree中才拥有Render Surface。

void CalculateDrawPropertiesInternal(    LayerTreeHostCommon::CalcDrawPropsImplInputs* inputs,    PropertyTreeOption property_tree_option) {  inputs->render_surface_list->clear();  const bool should_measure_property_tree_performance =      property_tree_option == BUILD_PROPERTY_TREES;  LayerImplList visible_layer_list;  switch (property_tree_option) {    case BUILD_PROPERTY_TREES: {      if (should_measure_property_tree_performance) {        TRACE_EVENT_BEGIN0(            TRACE_DISABLED_BY_DEFAULT("cc.debug.cdp-perf"),            "LayerTreeHostCommon::ComputeVisibleRectsWithPropertyTrees");      }      PropertyTreeBuilder::BuildPropertyTrees(          inputs->root_layer, inputs->page_scale_layer,          inputs->inner_viewport_scroll_layer,          inputs->outer_viewport_scroll_layer,          inputs->elastic_overscroll_application_layer,          inputs->elastic_overscroll, inputs->page_scale_factor,          inputs->device_scale_factor, gfx::Rect(inputs->device_viewport_size),          inputs->device_transform, inputs->property_trees);      draw_property_utils::UpdatePropertyTreesAndRenderSurfaces(          inputs->root_layer, inputs->property_trees,          inputs->can_adjust_raster_scales);      inputs->property_trees->transform_tree          .set_source_to_parent_updates_allowed(false);      break;    }    ...  }  draw_property_utils::FindLayersThatNeedUpdates(      inputs->root_layer->layer_tree_impl(), inputs->property_trees,      &visible_layer_list);  draw_property_utils::ComputeDrawPropertiesOfVisibleLayers(      &visible_layer_list, inputs->property_trees);  CalculateRenderSurfaceLayerList(      inputs->root_layer->layer_tree_impl(), inputs->property_trees,      inputs->render_surface_list, inputs->max_texture_size);  ...}

       有了上述RenderSurfaceLayerList(CrBrowserMain计算)之后,LayerTreeHost类的成员函数UpdateLayers再调用另外一个成员函数PaintLayerContents对保存在RenderSurfaceLayerList中的Render Surface进行绘制,实际上就是对CC Layer Tree进行绘制。

(CrRendererMain)

bool LayerTreeHost::UpdateLayers() {  ...  // LayerTreeHost::root_layer 获取cc layer的根节点。  bool result = DoUpdateLayers(root_layer());  ...  return result;}bool LayerTreeHost::DoUpdateLayers(Layer* root_layer) {  ...  gfx::Transform identity_transform;  LayerList update_layer_list;  if (!IsUsingLayerLists()) {    PropertyTreeBuilder::BuildPropertyTrees(        root_layer, page_scale_layer, inner_viewport_scroll_layer(),        outer_viewport_scroll_layer(), overscroll_elasticity_layer(),        elastic_overscroll_, page_scale_factor_, device_scale_factor_,        gfx::Rect(device_viewport_size_), identity_transform, &property_trees_);    } else {      ...    }    draw_property_utils::UpdatePropertyTrees(this, &property_trees_);    draw_property_utils::FindLayersThatNeedUpdates(this, &property_trees_,                                                   &update_layer_list);  ...  bool did_paint_content =      PaintContent(update_layer_list, &painted_content_has_slow_paths,                   &painted_content_has_non_aa_paint);  ...  return did_paint_content;}bool LayerTreeHost::PaintContent(const LayerList& update_layer_list,                                 bool* content_has_slow_paths,                                 bool* content_has_non_aa_paint) {  base::AutoReset<bool> painting(&in_paint_layer_contents_, true);  bool did_paint_content = false;  for (const auto& layer : update_layer_list) {    did_paint_content |= layer->Update();    ...  }  return did_paint_content;}

CC Layer Tree中的每一个Layer都是通过一个PictureLayer对象描述的。因此,接下来我们分析PictureLayer::Update的实现,如下所示:

bool PictureLayer::Update() {  update_source_frame_number_ = layer_tree_host()->SourceFrameNumber();  bool updated = Layer::Update();  gfx::Size layer_size = bounds();  recording_source_->SetBackgroundColor(SafeOpaqueBackgroundColor());  recording_source_->SetRequiresClear(      !contents_opaque() &&      !picture_layer_inputs_.client->FillsBoundsCompletely());  ...  picture_layer_inputs_.recorded_viewport =      picture_layer_inputs_.client->PaintableRegion();  updated |= recording_source_->UpdateAndExpandInvalidation(      &last_updated_invalidation_, layer_size,      picture_layer_inputs_.recorded_viewport);  if (updated) {    picture_layer_inputs_.display_list =        picture_layer_inputs_.client->PaintContentsToDisplayList(            ContentLayerClient::PAINTING_BEHAVIOR_NORMAL);    picture_layer_inputs_.painter_reported_memory_usage =        picture_layer_inputs_.client->GetApproximateUnsharedMemoryUsage();    recording_source_->UpdateDisplayItemList(        picture_layer_inputs_.display_list,        picture_layer_inputs_.painter_reported_memory_usage,        layer_tree_host()->recording_scale_factor());    SetNeedsPushProperties();  } else {    ...  }  return updated;}

PictureLayer类的成员函数Update首先调用父类Layer的成员函数Update让其有机会对当前正在处理的Layer执行一些更新工作(实际上什么也没有做)。PictureLayer类有个重要的成员变量last_updated_invalidation_指向的是一个Region对象。这个Region对象描述的是当前正在处理的Layer的待重绘区域。PictureLayer类的成员函数Update在重绘这个区域之前,会先将它的值设置到另外一个成员变量pile_invalidation_中去,以表示Layer的当前重绘区域,同时也会将待重绘区域清空。

其中GraphicsLayer::PaintContentsToDisplayList创建DisplayItemList,存储了ccLayer的绘制指令

scoped_refptr<cc::DisplayItemList> GraphicsLayer::PaintContentsToDisplayList(    PaintingControlSetting painting_control) {  TRACE_EVENT0("blink,benchmark", "GraphicsLayer::PaintContents");  PaintController& paint_controller = GetPaintController();  paint_controller.SetDisplayItemConstructionIsDisabled(      painting_control == DISPLAY_LIST_CONSTRUCTION_DISABLED);  paint_controller.SetSubsequenceCachingIsDisabled(      painting_control == SUBSEQUENCE_CACHING_DISABLED);  if (painting_control == PARTIAL_INVALIDATION)    client_.InvalidateTargetElementForTesting();  // We also disable caching when Painting or Construction are disabled. In both  // cases we would like to compare assuming the full cost of recording, not the  // cost of re-using cached content.  if (painting_control == DISPLAY_LIST_CACHING_DISABLED ||      painting_control == DISPLAY_LIST_PAINTING_DISABLED ||      painting_control == DISPLAY_LIST_CONSTRUCTION_DISABLED)    paint_controller.InvalidateAll();  GraphicsContext::DisabledMode disabled_mode =      GraphicsContext::kNothingDisabled;  if (painting_control == DISPLAY_LIST_PAINTING_DISABLED ||      painting_control == DISPLAY_LIST_CONSTRUCTION_DISABLED)    disabled_mode = GraphicsContext::kFullyDisabled;  // Anything other than PAINTING_BEHAVIOR_NORMAL is for testing. In non-testing  // scenarios, it is an error to call GraphicsLayer::Paint. Actual painting  // occurs in LocalFrameView::PaintTree() which calls GraphicsLayer::Paint();  // this method merely copies the painted output to the cc::DisplayItemList.  if (painting_control != PAINTING_BEHAVIOR_NORMAL)    Paint(nullptr, disabled_mode);  auto display_list = base::MakeRefCounted<cc::DisplayItemList>();  DCHECK(layer_state_) << "No layer state for GraphicsLayer: " << DebugName();  PaintChunksToCcLayer::ConvertInto(      GetPaintController().PaintChunks(), layer_state_->state,      gfx::Vector2dF(layer_state_->offset.X(), layer_state_->offset.Y()),      VisualRectSubpixelOffset(),      paint_controller.GetPaintArtifact().GetDisplayItemList(), *display_list);  paint_controller.SetDisplayItemConstructionIsDisabled(false);  paint_controller.SetSubsequenceCachingIsDisabled(false);  display_list->Finalize();  return display_list;}

 回到ProxyMain::BeginMainFrame,

void ProxyMain::BeginMainFrame(    std::unique_ptr<BeginMainFrameAndCommitState> begin_main_frame_state) {  ...  layer_tree_host_->BeginMainFrame(begin_main_frame_state->begin_frame_args);  layer_tree_host_->AnimateLayers(      begin_main_frame_state->begin_frame_args.frame_time);  layer_tree_host_->RequestMainFrameUpdate(      skip_paint_and_commit ? LayerTreeHost::VisualStateUpdate::kPrePaint                            : LayerTreeHost::VisualStateUpdate::kAll);}

对Graphics Layer进行绘制,最终调用CompositedLayerMapping::PaintContents对Normal Layer, Squashe Layer和ScrollableArea采用不同方式分别绘制

void CompositedLayerMapping::PaintContents(    const GraphicsLayer* graphics_layer,    GraphicsContext& context,    GraphicsLayerPaintingPhase graphics_layer_painting_phase,    const IntRect& interest_rect) const {  ...  PaintLayerFlags paint_layer_flags = 0;  ...  if (graphics_layer == graphics_layer_.get() ||      graphics_layer == foreground_layer_.get() ||      graphics_layer == mask_layer_.get() ||      graphics_layer == child_clipping_mask_layer_.get() ||      graphics_layer == scrolling_contents_layer_.get() ||      graphics_layer == decoration_outline_layer_.get() ||      graphics_layer == ancestor_clipping_mask_layer_.get()) {    ...    DoPaintTask(paint_info, *graphics_layer, paint_layer_flags, context,                interest_rect);  } else if (graphics_layer == squashing_layer_.get()) {    for (size_t i = 0; i < squashed_layers_.size(); ++i) {      DoPaintTask(squashed_layers_[i], *graphics_layer, paint_layer_flags,                  context, interest_rect);    }  } else if (IsScrollableAreaLayer(graphics_layer)) {    PaintScrollableArea(graphics_layer, context, interest_rect);  }  ...}

随后进入对Paint Layer Tree的绘制,如对于Normal调用DoPaintTask,

void CompositedLayerMapping::DoPaintTask(    const GraphicsLayerPaintInfo& paint_info,    const GraphicsLayer& graphics_layer,    PaintLayerFlags paint_layer_flags,    GraphicsContext& context,    const IntRect& clip /* In the coords of rootLayer */) const {  ...  // Paint Layer owns Graphics Layer  if (paint_info.paint_layer->GetCompositingState() !=      kPaintsIntoGroupedBacking) {    // FIXME: GraphicsLayers need a way to split for multicol.    PaintLayerPaintingInfo painting_info(        paint_info.paint_layer, LayoutRect(dirty_rect), kGlobalPaintNormalPhase,        paint_info.paint_layer->SubpixelAccumulation());    PaintLayerPainter(*paint_info.paint_layer)        .PaintLayerContents(context, painting_info, paint_layer_flags);    if (paint_info.paint_layer->ContainsDirtyOverlayScrollbars()) {      PaintLayerPainter(*paint_info.paint_layer)          .PaintLayerContents(              context, painting_info,              paint_layer_flags | kPaintLayerPaintingOverlayScrollbars);    }  } else {    // Paint Layer don't own Graphics Layer    PaintLayerPaintingInfo painting_info(        paint_info.paint_layer, LayoutRect(dirty_rect), kGlobalPaintNormalPhase,        paint_info.paint_layer->SubpixelAccumulation());    PaintLayerPainter(*paint_info.paint_layer)        .Paint(context, painting_info, paint_layer_flags);  }}

 参数paintInfo描述的GraphicsLayerPaintInfo对象的成员变量paint_layer描述的是当前要绘制的Paint Layer。当一个其拥有自己的Graphics Layer时,它会绘制自己的Backing Store中,否则的话,它与其它的Paint Layer一起绘制在别的Backing Store中。

       我们假设当前要绘制的拥有自己的Graphics Layer,这时候调用它的成员函数compositingState得到的返回值不等于PaintsIntoGroupedBacking,因此接下来CompositedLayerMapping类的成员函数doPaintTask就会调用PaintLayerPainter::PaintLayerContents实现如下所示:

PaintResult PaintLayerPainter::PaintLayerContents(    GraphicsContext& context,    const PaintLayerPaintingInfo& painting_info_arg,    PaintLayerFlags paint_flags_arg) {  ...  PaintLayerFragments layer_fragments;  if (should_paint_content || should_paint_self_outline ||      is_painting_overlay_scrollbars) {    ...    paint_layer_for_fragments->CollectFragments(        layer_fragments, local_painting_info.root_layer,        &local_painting_info.paint_dirty_rect,        kIgnorePlatformOverlayScrollbarSize, respect_overflow_clip,        &offset_from_root, local_painting_info.sub_pixel_accumulation);    ...  bool selection_only =      local_painting_info.GetGlobalPaintFlags() & kGlobalPaintSelectionOnly;  {  // Begin block for the lifetime of any filter.    size_t display_item_list_size_before_painting =        context.GetPaintController().NewDisplayItemList().size();    bool is_painting_root_layer = (&paint_layer_) == painting_info.root_layer;    bool should_paint_background =        should_paint_content && !selection_only &&        (is_painting_composited_background ||         (is_painting_root_layer &&          !(paint_flags & kPaintLayerPaintingSkipRootBackground)));    bool should_paint_neg_z_order_list =        (is_painting_scrolling_content && is_painting_overflow_contents) ||        (!is_painting_scrolling_content && is_painting_composited_background);    bool should_paint_own_contents =        is_painting_composited_foreground && should_paint_content;    bool should_paint_normal_flow_and_pos_z_order_lists =        is_painting_composited_foreground;    bool should_paint_overlay_scrollbars = is_painting_overlay_scrollbars;    base::Optional<ScopedPaintChunkProperties>        subsequence_forced_chunk_properties;    if (subsequence_recorder && paint_layer_.HasSelfPaintingLayerDescendant()) {      ...      subsequence_forced_chunk_properties.emplace(          context.GetPaintController(),          paint_layer_.GetLayoutObject()              .FirstFragment()              .LocalBorderBoxProperties(),          paint_layer_, DisplayItem::kUninitializedType);    }    if (should_paint_background) {      if (subsequence_forced_chunk_properties) {        context.GetPaintController().ForceNewChunk(            paint_layer_, DisplayItem::kLayerChunkBackground);      }      PaintBackgroundForFragments(layer_fragments, context,                                  local_painting_info, paint_flags);    }    if (should_paint_neg_z_order_list) {      if (subsequence_forced_chunk_properties) {        context.GetPaintController().ForceNewChunk(            paint_layer_, DisplayItem::kLayerChunkNegativeZOrderChildren);      }      if (PaintChildren(kNegativeZOrderChildren, context, painting_info,                        paint_flags) == kMayBeClippedByPaintDirtyRect)        result = kMayBeClippedByPaintDirtyRect;    }    if (should_paint_own_contents) {      PaintForegroundForFragments(          layer_fragments, context, local_painting_info, selection_only,          !!subsequence_forced_chunk_properties, paint_flags);    }    if (should_paint_self_outline) {      PaintSelfOutlineForFragments(layer_fragments, context,                                   local_painting_info, paint_flags);    }    if (should_paint_normal_flow_and_pos_z_order_lists) {      if (subsequence_forced_chunk_properties) {        context.GetPaintController().ForceNewChunk(            paint_layer_,            DisplayItem::kLayerChunkNormalFlowAndPositiveZOrderChildren);      }      if (PaintChildren(kNormalFlowChildren | kPositiveZOrderChildren, context,                        painting_info,                        paint_flags) == kMayBeClippedByPaintDirtyRect)        result = kMayBeClippedByPaintDirtyRect;    }    if (should_paint_overlay_scrollbars) {      PaintOverflowControlsForFragments(layer_fragments, context,                                        local_painting_info, paint_flags);    }    if (!is_painting_overlay_scrollbars && paint_layer_.PaintsWithFilters() &&        display_item_list_size_before_painting ==            context.GetPaintController().NewDisplayItemList().size()) {      // If a layer with filters painted nothing, we need to issue a no-op      // display item to ensure the filters won't be ignored.      PaintEmptyContentForFilters(context);    }  }  // FilterPainter block  bool should_paint_mask = is_painting_mask && should_paint_content &&                           paint_layer_.GetLayoutObject().HasMask() &&                           !selection_only;  if (should_paint_mask) {    PaintMaskForFragments(layer_fragments, context, local_painting_info,                          paint_flags);  } else if ...  return result;}

       收集到了要绘制的Fragment之后,大概就按照以下顺序绘制自己的内容:

       1. Background

       2. Z-index为负的子Paintr Layer

       3. Foreground

       4. Outline

       5. Z-index为0和正数的子Paint Layer 

       6. Scollbar 

       7. Mask

除了子Paint Layer Layer的内容是通过调用成员函数PaintChildren进行绘制的,其余的内容是通过调用成员函数PaintXXXForFragments进行绘制的。

继而进入Layout Object Tree的绘制。

void BlockPainter::PaintObject(const PaintInfo& paint_info,                               const LayoutPoint& paint_offset) {  ...  // paint background  if (ShouldPaintSelfBlockBackground(paint_phase)) {   ...      layout_block_.PaintBoxDecorationBackground(paint_info, paint_offset);    ...  }  // paint mask  if (paint_phase == PaintPhase::kMask &&      layout_block_.Style()->Visibility() == EVisibility::kVisible) {    layout_block_.PaintMask(paint_info, paint_offset);    return;  }  // paint foreground  if (paint_phase == PaintPhase::kForeground && paint_info.IsPrinting())    ObjectPainter(layout_block_)        .AddPDFURLRectIfNeeded(paint_info, paint_offset);    // paint contents  if (paint_phase != PaintPhase::kSelfOutlineOnly) {    ...    const PaintInfo& contents_paint_info =        scrolled_paint_info ? *scrolled_paint_info : paint_info;    if (layout_block_.IsLayoutBlockFlow()) {      BlockFlowPainter block_flow_painter(ToLayoutBlockFlow(layout_block_));      block_flow_painter.PaintContents(contents_paint_info, paint_offset);      if (paint_phase == PaintPhase::kFloat ||          paint_phase == PaintPhase::kSelection ||          paint_phase == PaintPhase::kTextClip)        block_flow_painter.PaintFloats(contents_paint_info);    } else {      PaintContents(contents_paint_info, paint_offset);    }  }  // paint outline  if (ShouldPaintSelfOutline(paint_phase))    ObjectPainter(layout_block_).PaintOutline(paint_info, paint_offset);  // If the caret's node's layout object's containing block is this block, and  // the paint action is PaintPhaseForeground, then paint the caret.  if (paint_phase == PaintPhase::kForeground &&      layout_block_.ShouldPaintCarets())    PaintCarets(paint_info, paint_offset);}
void BlockPainter::PaintChildren(const PaintInfo& paint_info) {  for (LayoutBox* child = layout_block_.FirstChildBox(); child;       child = child->NextSiblingBox())    PaintChild(*child, paint_info);}void BlockPainter::PaintChild(const LayoutBox& child,                              const PaintInfo& paint_info) {  if (!child.HasSelfPaintingLayer() && !child.IsFloating() &&      !child.IsColumnSpanAll())    child.Paint(paint_info);  // ----->   recursively call LayoutBlock::Paint}

当子Layout Object不拥有自己的Paint Layer,并且它也没有设置Float属性的时候,就会递归调用LayoutBlock::Paint进行绘制,直到中间某个Layout Object的所有子Render Object都具有自己的Paint Layer为止,或者都设置了float属性。

       这样,一个Paint Layer的内容就绘制完成了。PaintResult PaintLayerPainter::PaintLayerContents除了绘制自身内容,还会绘制它的子Paint Layer的内容,

PaintResult PaintLayerPainter::PaintChildren(    unsigned children_to_visit,    GraphicsContext& context,    const PaintLayerPaintingInfo& painting_info,    PaintLayerFlags paint_flags) {  ...  PaintLayer* child = iterator.Next();  if (!child)    return result;  for (; child; child = iterator.Next()) {    // If this Layer should paint into its own backing or a grouped backing,    // that will be done via CompositedLayerMapping::PaintContents() and    // CompositedLayerMapping::DoPaintTask().    if (child->PaintsIntoOwnOrGroupedBacking(            painting_info.GetGlobalPaintFlags()))      continue;    if (child->IsReplacedNormalFlowStacking())      continue;    if (PaintLayerPainter(*child).Paint(context, painting_info, paint_flags) ==        kMayBeClippedByPaintDirtyRect)      result = kMayBeClippedByPaintDirtyRect;  }  return result;}

这一步执行完成之后,回到前面分析的LayerTreeHost::UpdateLayers中,这时候网页的CC Layer Tree的内容就绘制完成了,不过仅仅是将绘制命令记录在一系列的Render Surface,也得到了Property Tree。 

然后返回到ProxyMain的成员函数BeginMainFrame中,这时候它就会向Compositor线程的消息队列发送一个Task。Compositor线程开始执行。期间,CrRendererMain线程进入等待状态。

void ProxyMain::BeginMainFrame(    std::unique_ptr<BeginMainFrameAndCommitState> begin_main_frame_state) {  ...  {    ...    ImplThreadTaskRunner()->PostTask(        FROM_HERE,        base::BindOnce(&ProxyImpl::NotifyReadyToCommitOnImpl,                       base::Unretained(proxy_impl_.get()), &completion,                       layer_tree_host_, begin_main_frame_start_time,                       hold_commit_for_activation));    completion.Wait();  }  ...}
void ProxyImpl::NotifyReadyToCommitOnImpl(    CompletionEvent* completion,    LayerTreeHost* layer_tree_host,    base::TimeTicks main_thread_start_time,    bool hold_commit_for_activation) {  ...  scheduler_->NotifyBeginMainFrameStarted(main_thread_start_time);  host_impl_->ReadyToCommit();  ...  scheduler_->NotifyReadyToCommit();}

Scheduler::NotifyBeginMainFrameStarted把状态机改为BeginMainFrameState::STARTED,继续Scheduler::NotifyReadyToCommit中,它将状态机修改为READY_TO_COMMIT之后,再调用成员函数ProcessScheduledActions将刚刚绘制好的CC Layer Tree同步到一个新的CC Pending Layer Tree中去。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
动画的前制与制程
衣柜安装演示动画
How to implement QListWidget slide animation
Flutter 渲染机制——GPU线程渲染
转场动画
[Android5.1]开机动画显示工作流程分析
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服