Chromium网页渲染调度器(Scheduler)实现分析(5)

2019-01-27 12:51

到的返回值等于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操作。通过这种方式保证网页的渲染管线可以持续地推进到


Chromium网页渲染调度器(Scheduler)实现分析(5).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:建筑工程成本管理办法(重点) - 图文

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: