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(
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值