资料:https://blog.csdn.net/luoshengyang/article/details/51638966
从前面Chromium网页渲染调度器(Scheduler)实现分析一文可以知道,当调度器调用SchedulerStateMachine类的成员函数NextAction询问状态机下一步要执行的操作时,SchedulerStateMachine类的成员函数NextAction会调用另外一个成员函数ShouldActivatePendingTree。当SchedulerStateMachine类的成员函数ShouldActivateSyncTree返回值等于true的时候,状态机就会提示调度器接下来需要执行ACTION_ACTIVATE_PENDING_TREE操作,也就是将CC Pending Layer Tree激活为CC Active Layer Tree,如下所示:
SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
if (ShouldActivateSyncTree())
return Action::ACTIVATE_SYNC_TREE;
...
return Action::NONE;
}
src/cc/scheduler/scheduler_state_machine.cc
接下来我们就继续分析SchedulerStateMachine类的成员函数ShouldActivateSyncTree什么情况下会返回true,它的实现如下所示:
bool SchedulerStateMachine::ShouldActivateSyncTree() const {
// There is nothing to activate.
if (!has_pending_tree_)
return false;
// We should not activate a second tree before drawing the first one.
// Even if we need to force activation of the pending tree, we should abort
// drawing the active tree first.
if (active_tree_needs_first_draw_)
return false;
if (ShouldAbortCurrentFrame())
return true;
// At this point, only activate if we are ready to activate.
return pending_tree_is_ready_for_activation_;
}
src/cc/scheduler/scheduler_state_machine.cc
SchedulerStateMachine类的成员函数ShouldActivatePendingTree的返回值为true有两个前提条件:
1. 当前存在CC Pending Layer Tree。这时候SchedulerStateMachine类的成员变量has_pending_tree_的值等于true。绘制CC Layer Tree时,仅仅是记录网页的绘制命令,那么调度器在请求Main线程在绘制CC Layer Tree之前,会将SchedulerStateMachine类的成员变量has_pending_tree_的值设置为true,表示在CC Layer Tree绘制完成后,会得到一个新的CC Pending Layer Tree。
2. 上一次激活得到的CC Active Layer Tree已经至少被渲染过一次。这时候SchedulerStateMachine类的成员变量active_tree_needs_first_draw_的值等于false。接下来我们会看到,当一个CC Pending Layer Tree激活为CC Active Layer Tree之后,SchedulerStateMachine类的成员变量active_tree_needs_first_draw_的值会被设置为true,表示新激活的CC Active Layer Tree正在等待第一次绘制。这一点用来保证每一个CC Active Layer Tree被激活后,都会至少被渲染一次,避免网页内容的渲染画面出现跳跃。
满足了上述两个前提条件后,SchedulerStateMachine类的成员函数ShouldActivatePendingTree在两种情况下会返回true:
bool SchedulerStateMachine::ShouldAbortCurrentFrame() const {
// Abort the frame if there is no output surface to trigger our
// activations, avoiding deadlock with the main thread.
if (layer_tree_frame_sink_state_ == LayerTreeFrameSinkState::NONE)
return true;
// If we're not visible, we should just abort the frame. Since we
// set RequiresHighResToDraw when becoming visible, we ensure that
// we don't checkerboard until all visible resources are
// done. Furthermore, if we do keep the pending tree around, when
// becoming visible we might activate prematurely causing
// RequiresHighResToDraw flag to be reset. In all cases, we can
// simply activate on becoming invisible since we don't need to draw
// the active tree when we're in this state.
if (!visible_)
return true;
// Abort the frame when viz::BeginFrameSource is paused to avoid
// deadlocking the main thread.
if (begin_frame_source_paused_)
return true;
return false;
}
src/cc/scheduler/scheduler_state_machine.cc
值得注意的是:网页的绘图表面在失效的情况下,需要重新创建。但是重新创建网页的绘图表面,又要求此时不能存在CC Pending Layer Tree。这样在存在CC Pending Layer Tree的时候,网页的绘图表面又出现失效,就会导致死锁。解决办法就是将CC Pending Layer Tree正常地激活为CC Active Layer Tree,使得不再存在CC Pending Layer Tree,从而使得死锁条件不成立,网页的渲染管线可以正常地执行。
2. 此时网页的CC Pending Layer Tree已经准备就绪激活为CC Active Layer Tree。当CC Pending Layer Tree的光栅化操作执行完成后,它就准备就绪激活为CC Active Layer Tree。这时候SchedulerStateMachine类的成员变量pending_tree_is_ready_for_activation_的值就会被设置为true。
回到SchedulerStateMachine类的成员函数NextAction中,当它调用成员函数ShouldActivatePendingTree得到的返回值等于true,它就会返回一个ACTIV_PENDING_TREE给Scheduler类的成员函数ProcessScheduledActions。这时候Scheduler类的成员函数ProcessScheduledActions就会请求Compositor线程将CC Pending Layer Tree激活为CC Active Layer Tree,如下所示:
void Scheduler::ProcessScheduledActions() {
...
SchedulerStateMachine::Action action;
do {
...
switch (action) {
...
case SchedulerStateMachine::Action::ACTIVATE_SYNC_TREE:
compositor_timing_history_->WillActivate();
state_machine_.WillActivate();
client_->ScheduledActionActivateSyncTree();
compositor_timing_history_->DidActivate();
break;
...
}
}
} while (action != SchedulerStateMachine::Action::NONE);
...
}
src/cc/scheduler/scheduler_state_machine.cc
no longer use UpdateState to update state_machine_, use fuction serises instead :
void SchedulerStateMachine::WillActivate() {
if (layer_tree_frame_sink_state_ ==
LayerTreeFrameSinkState::WAITING_FOR_FIRST_ACTIVATION)
layer_tree_frame_sink_state_ = LayerTreeFrameSinkState::ACTIVE;
if (forced_redraw_state_ ==
ForcedRedrawOnTimeoutState::WAITING_FOR_ACTIVATION)
forced_redraw_state_ = ForcedRedrawOnTimeoutState::WAITING_FOR_DRAW;
has_pending_tree_ = false;
pending_tree_is_ready_for_activation_ = false;
active_tree_needs_first_draw_ = pending_tree_needs_first_draw_on_activation_;
pending_tree_needs_first_draw_on_activation_ = false;
needs_redraw_ = true;
previous_pending_tree_was_impl_side_ = current_pending_tree_is_impl_side_;
current_pending_tree_is_impl_side_ = false;
}
src/cc/scheduler/scheduler_state_machine.cc
首先修改需要修改状态机的状态。然后,
1. 将成员变量has_pending_tree_和pending_tree_is_ready_for_activation_的值设置为false,表示这一个CC Pending Layer Tree已经激活为CC Active Layer Tree。
2. 将成员变量active_tree_needs_first_draw_和needs_redraw_的值设置为true,表示刚刚激活的CC Active Layer Tree需要进行第一次渲染。
注意,一旦调度器发出某个操作,那么就认为该操作一定会被执行,于是就会提前修改内部状态。因为调度器发出的操作要么是给CrRendererMain线程执行,要么是给Compositor线程执行。无论是哪一个线程,它们都是消息驱动执行的,并且先被请求执行的操作不会被后请求执行的操作中断或者推迟。这样就可以保证每一个请求执行的操作都会按照请求时的先后顺序执行。因此调度器就可以认为它一旦发出某个操作,这个操作就一定会执行。
回到ProcessScheduledActions中,接下来调用ProxyImpl::ScheduledActionActivatePendingTree请求Compositor线程将CC Pending Layer Tree激活为CC Active Layer Tree,如下所示:
void ProxyImpl::ScheduledActionActivateSyncTree() {
TRACE_EVENT0("cc", "ProxyImpl::ScheduledActionActivateSyncTree");
DCHECK(IsImplThread());
host_impl_->ActivateSyncTree();
}
void LayerTreeHostImpl::ActivateSyncTree() {
if (pending_tree_) {
...
if (pending_tree_->needs_full_tree_sync()) {
TreeSynchronizer::SynchronizeTrees(pending_tree_.get(),
active_tree_.get());
}
PushScrollbarOpacitiesFromActiveToPending();
pending_tree_->PushPropertyTreesTo(active_tree_.get());
TreeSynchronizer::PushLayerProperties(pending_tree(), active_tree());
pending_tree_->PushPropertiesTo(active_tree_.get());
...
// Now that we've synced everything from the pending tree to the active
// tree, rename the pending tree the recycle tree so we can reuse it on the
// next sync.
pending_tree_.swap(recycle_tree_);
...
} else {
active_tree_->ProcessUIResourceRequestQueue();
}
...
active_tree_->DidBecomeActive();
client_->RenewTreePriority();
// If we have any picture layers, then by activating we also modified tile
// priorities.
if (!active_tree_->picture_layers().empty())
DidModifyTilePriorities();
client_->OnCanDrawStateChanged(CanDraw());
client_->DidActivateSyncTree();
...
}
将CC Layer Tree的结构同步到CC Pending Layer Tree也是通过调用TreeSynchronizer::SynchronizeTrees实现的。因此,不再重复分析。
接着通知调度器新激活的CC Active Layer Tree已经准备就绪渲染。
void ProxyImpl::OnCanDrawStateChanged(bool can_draw) {
scheduler_->SetCanDraw(can_draw);
}
void Scheduler::SetCanDraw(bool can_draw) {
state_machine_.SetCanDraw(can_draw);
ProcessScheduledActions();
}
此时调用Scheduler::ProcessScheduledActions,就会触发调度器请求Compositor线程执行一个渲染操作,
void Scheduler::ProcessScheduledActions() {
...
do {
...
switch (action) {
...
case SchedulerStateMachine::Action::DRAW_IF_POSSIBLE:
DrawIfPossible();
break;
case SchedulerStateMachine::Action::DRAW_FORCED:
DrawForced();
break;
case SchedulerStateMachine::Action::DRAW_ABORT: {
// No action is actually performed, but this allows the state machine to
// drain the pipeline without actually drawing.
state_machine_.AbortDraw();
compositor_timing_history_->DrawAborted();
break;
}
...
} while (action != SchedulerStateMachine::Action::NONE);
...
}
void Scheduler::DrawIfPossible() {
...
state_machine_.WillDraw();
DrawResult result = client_->ScheduledActionDrawIfPossible();
state_machine_.DidDraw(result);
...
}
DrawResult ProxyImpl::ScheduledActionDrawIfPossible() {
...
bool forced_draw = false;
return DrawInternal(forced_draw);
}
DrawResult ProxyImpl::DrawInternal(bool forced_draw) {
DCHECK(IsImplThread());
DCHECK(host_impl_.get());
base::AutoReset<bool> mark_inside(&inside_draw_, true);
if (host_impl_->pending_tree())
host_impl_->pending_tree()->UpdateDrawProperties();
// This method is called on a forced draw, regardless of whether we are able
// to produce a frame, as the calling site on main thread is blocked until its
// request completes, and we signal completion here. If CanDraw() is false, we
// will indicate success=false to the caller, but we must still signal
// completion to avoid deadlock.
// We guard PrepareToDraw() with CanDraw() because it always returns a valid
// frame, so can only be used when such a frame is possible. Since
// DrawLayers() depends on the result of PrepareToDraw(), it is guarded on
// CanDraw() as well.
LayerTreeHostImpl::FrameData frame;
frame.begin_frame_ack = scheduler_->CurrentBeginFrameAckForActiveTree();
bool draw_frame = false;
DrawResult result;
if (host_impl_->CanDraw()) {
result = host_impl_->PrepareToDraw(&frame);
draw_frame = forced_draw || result == DRAW_SUCCESS;
} else {
result = DRAW_ABORTED_CANT_DRAW;
}
if (draw_frame) {
if (host_impl_->DrawLayers(&frame))
// Drawing implies we submitted a frame to the LayerTreeFrameSink.
scheduler_->DidSubmitCompositorFrame();
result = DRAW_SUCCESS;
} else {
DCHECK_NE(DRAW_SUCCESS, result);
}
host_impl_->DidDrawAllLayers(frame);
bool start_ready_animations = draw_frame;
host_impl_->UpdateAnimationState(start_ready_animations);
// Tell the main thread that the the newly-commited frame was drawn.
if (next_frame_is_newly_committed_frame_) {
next_frame_is_newly_committed_frame_ = false;
MainThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ProxyMain::DidCommitAndDrawFrame,
proxy_main_weak_ptr_));
}
DCHECK_NE(INVALID_RESULT, result);
return result;
}
Draw this frame
bool LayerTreeHostImpl::DrawLayers(FrameData* frame) {
...
auto compositor_frame = GenerateCompositorFrame(frame);
layer_tree_frame_sink_->SubmitCompositorFrame(std::move(compositor_frame));
...
for (size_t i = 0; i < frame->render_surface_list->size(); i++) {
auto* surface = (*frame->render_surface_list)[i];
surface->damage_tracker()->DidDrawDamagedArea();
}
active_tree_->set_has_ever_been_drawn(true);
...
}