到的返回值等于true的时候,SchedulerStateMachine类的成员函数BeginFrameNeeded的返回才会等于true。
SchedulerStateMachine类的成员函数BeginFrameNeededToAnimateOrDraw的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 // These are the cases where we definitely (or almost definitely) have a // new frame to animate and/or draw and can draw.
bool SchedulerStateMachine::BeginFrameNeededToAnimateOrDraw() const { // The output surface is the provider of BeginImplFrames, so we are not going // to get them even if we ask for them. if (!HasInitializedOutputSurface()) return false;
// If we can't draw, don't tick until we are notified that we can draw again. if (!can_draw_) return false;
// The forced draw respects our normal draw scheduling, so we need to // request a BeginImplFrame for it.
if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW) return true;
// There's no need to produce frames if we are not visible. if (!visible_) return false;
// We need to draw a more complete frame than we did the last BeginImplFrame, // so request another BeginImplFrame in anticipation that we will have // additional visible tiles.
if (swap_used_incomplete_tile_) return true;
if (needs_animate_) return true;
return needs_redraw_; }
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。
SchedulerStateMachine类的成员函数BeginFrameNeededToAnimateOrDraw返回true需要满足两个必要条件:
1. 网页当前的绘图表面可用,也就是网页的绘图表面已经创建,并且没有失效。这可以通过调用SchedulerStateMachine类的成员函数HasInitializedOutputSurface判断。
2. 网页的上一个CC Pending Layer Tree已经被激活为CC Active Layer Tree。这时候SchedulerStateMachine类的成员变量can_draw_的值会等于true。
满足了以上两个必要条件后,有四种情况会使得SchedulerStateMachine类的成员函数BeginFrameNeededToAnimateOrDraw返回true。
第一种情况是状态机的ForcedRedrawOnTimeoutState状态等于FORCED_REDRAW_STATE_WAITING_FOR_DRAW。从图6可以知道,这时候网页正在等待CC Pending Layer Tree激活为CC Active Layer Tree,以便得到的CC Active Layer Tree。现在既然CC Pending Layer Tree已经激活,因此就需要对网页执行一个渲染操作。
后面三种情况需要满足第三个必要条件,就是网页当前是可见的。这时候SchedulerStateMachine类的成员变量visible_会等于true。
第二种情况是网页上次渲染时,有些分块(Tile)还没有准备就绪,也就是还没有光栅化完成。这时候SchedulerStateMachine类的成员变量swap_used_incomplete_tile_会等于true。这种情况也要求执行一个渲染操作。在执行这个渲染操作的时候,调度器会检查之前未准备就绪的分块是否已经就准备就绪。如果已经准备就绪,那么就可以对它们进行渲染。
第三种情况是网页现在正处于动画显示过程中。这时候SchedulerStateMachine类的成员变量needs_animate_的值会等于true。这时候要求执行一个渲染操作,就可以使得动画持续执行下去。
第四种情况是网页被要求进行重新绘制,或者是因为CC Pending Layer Tree刚刚激活为CC Active Layer Tree,或者网页的CC Layer Tree上一次同步到CC Pending Layer Tree的过程中还没有完成就被取消了。这时候要求执行一个渲染操作,就可以使得刚刚激活得到的CC Active Layer Tree可以马上进行渲染,或者恢复CC Layer Tree同步到CC Pending Layer Tree的操作。
回到SchedulerStateMachine类的成员函数BeginFrameNeeded中,如果网页使用的不是同步合成器,那么除了调用成员函数BeginFrameNeededToAnimateOrDraw得到的返回值等于true的情况,还有另外一种情况也会使得SchedulerStateMachine类的成员函数BeginFrameNeeded的返回值等于true,就是调用另外一个成员函数ProactiveBeginFrameWanted得到的返回值也等于true。
SchedulerStateMachine类的成员函数ProactiveBeginFrameWanted的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
// Proactively requesting the BeginImplFrame helps hide the round trip latency // of the SetNeedsBeginFrame request that has to go to the Browser. bool SchedulerStateMachine::ProactiveBeginFrameWanted() const {
// The output surface is the provider of BeginImplFrames, // so we are not going to get them even if we ask for them. if (!HasInitializedOutputSurface()) return false;
// Do not be proactive when invisible. if (!visible_) return false;
// We should proactively request a BeginImplFrame if a commit is pending // because we will want to draw if the commit completes quickly. if (needs_commit_ || commit_state_ != COMMIT_STATE_IDLE) return true;
// If the pending tree activates quickly, we'll want a BeginImplFrame soon // to draw the new active tree. if (has_pending_tree_) return true;
// Changing priorities may allow us to activate (given the new priorities), // which may result in a new frame. if (needs_manage_tiles_) return true;
// If we just sent a swap request, it's likely that we are going to produce // another frame soon. This helps avoid negative glitches in our
// SetNeedsBeginFrame requests, which may propagate to the BeginImplFrame // provider and get sampled at an inopportune time, delaying the next // BeginImplFrame.
if (HasRequestedSwapThisFrame()) return true;
return false; }
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。
SchedulerStateMachine类的成员函数ProactiveBeginFrameWanted返回true需要满足两个必要条件:
1. 网页的绘图表面已经创建好,并且没有失效。
2. 网页当前是可见的。
满足了以上两个必要条件后,有五种情况会使得SchedulerStateMachine类的成员函
数ProactiveBeginFrameWanted返回true。
第一种情况是Main线程通知调度器网页的CC Layer Tree有新的变化,需要同步到CC Pending Layer Tree去。这时候SchedulerStateMachine类的成员变量needs_commit_会等于true。
第二种情况是状态机的CommitState状态不等于COMMIT_STATE_IDLE。这意味着Compositor线程正在执行同步CC Layer Tree和CC Pending Layer Tree的操作。为了正常推进这个操作,需要一个BEGIN_IMPL_FRAME操作,以便触发Scheduler类的成员函数ProcessScheduledActions的调用。这样就可以保证正常推进CC Layer Tree和CC Pending Layer Tree的同步操作。
第三种情况是网页存在一个CC Pending Layer Tree。这时候SchedulerStateMachine类的成员变量has_pending_tree_会等于true。这时候同样需要通过一个BEGIN_IMPL_FRAME操作推进这个CC Pending Layer Tree激活为CC Active Layer Tree。
第四种情况是Compositor线程需要对网页的分块进行光栅化操作。这时候SchedulerStateMachine类的成员变量needs_manage_tiles_会等于true。这种情况同样需要通过一个BEGIN_IMPL_FRAME操作推进光栅化操作的执行。
第五种情况是网页的当前帧已经渲染好,并且Render进程也已经向GPU发起了一个SwapBuffers操作,也就是请求Browser进程将网页的UI显示出来。这时候调用SchedulerStateMachine类的成员函数HasRequestedSwapThisFrame得到的返回值为true。这种情况请求执行下一个BEGIN_IMPL_FRAME操作,可以尽快地检查网页是否需要绘制下一帧,也就是让Main线程或者Compositor线程尽快准备好下一个帧。
对比SchedulerStateMachine类的成员函数ProactiveBeginFrameWanted和BeginFrameNeededToAnimateOrDraw的实现可以发现,前者比后者更激进地请求执行下一个BEGIN_IMPL_FRAME操作。后者在被动要求重绘网页下一帧的时候才会返回true,而前者会主动去准备网页的下一帧的绘制操作。
回到Scheduler类的成员函数SetupNextBeginFrameIfNeeded中,它通过调用SchedulerStateMachine类的成员函数SupportsProactiveBeginFrame获知是否要发起下一个BEGIN_IMPL_FRAME操作的信息后,如果平台支持VSync信号,那么接下来它会调用另外一个成员函数SetupNextBeginFrameWhenVSyncThrottlingEnable根据VSync信号来准备下一个BEGIN_IMPL_FRAME操作。
Scheduler类的成员函数SetupNextBeginFrameWhenVSyncThrottlingEnable的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 // When we are throttling frame production, we request BeginFrames // from the OutputSurface.
void Scheduler::SetupNextBeginFrameWhenVSyncThrottlingEnabled(
bool needs_begin_frame) { bool at_end_of_deadline =
state_machine_.begin_impl_frame_state() ==
SchedulerStateMachine::BEGIN_IMPL_FRAME_STATE_INSIDE_DEADLINE;
bool should_call_set_needs_begin_frame =
// Always request the BeginFrame immediately if it wasn't needed before. (needs_begin_frame && !last_set_needs_begin_frame_) || // Only stop requesting BeginFrames after a deadline.
(!needs_begin_frame && last_set_needs_begin_frame_ && at_end_of_deadline);
if (should_call_set_needs_begin_frame) {
if (settings_.begin_frame_scheduling_enabled) {
client_->SetNeedsBeginFrame(needs_begin_frame); } else {
synthetic_begin_frame_source_->SetNeedsBeginFrame( needs_begin_frame, &begin_retro_frame_args_); }
last_set_needs_begin_frame_ = needs_begin_frame; }
PostBeginRetroFrameIfNeeded(); }
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。
在两种情况下,Scheduler类的成员函数SetupNextBeginFrameWhenVSyncThrottlingEnable会请求执行下一个BEGIN_IMPL_FRAME操作。
第一种情况是调度器之前没有请求过调度执行下一个BEGIN_IMPL_FRAME操作,这时候Scheduler类的成员变量last_set_needs_begin_frame_等于false。此时参数needs_begin_frame的值又等于true,也就是状态机要求执行一个BEGIN_IMPL_FRAME操作。这时候调度器就必须要调度执行一个BEGIN_IMPL_FRAME操作。否则的话,就永远不会执行下一个BEGIN_IMPL_FRAME操作。
第二种情况是调度器之前请求过调度执行下一个BEGIN_IMPL_FRAME操作,并且这个BEGIN_IMPL_FRAME操作的Deadline已经到来,以及参数needs_begin_frame的值等于false。试想这种情况,如果调度器不继续请求调度执行下一个BEGIN_IMPL_FRAME操作的话,网页的渲染管线在上一次请求的BEGIN_IMPL_FRAME操作执行完成后,就断开了。因此在这种情况下,也需要请求请示调度执行下一个BEGIN_IMPL_FRAME操作。
如果对上述两种情况做一个总结,就是只有在前两个连续的BEGIN_IMPL_FRAME操作期间,状态机都表示不需要绘制网页的下一帧的情况下,调度器才会停止请求调度执行下一个BEGIN_IMPL_FRAME操作。通过这种方式保证网页的渲染管线可以持续地推进到