站在老罗的肩膀上: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(