稳定状态,而不会停留在上面提到的中间状态。
一旦决定需要请求调度执行下一个BEGIN_IMPL_FRAME操作,本地变量should_call_set_needs_begin_frame的值就会被设置为true,这时候如果Scheduler类的成员变量settings_描述的SchedulerSettings对象的成员变量begin_frame_scheduling_enabled的值等于true,那么Scheduler类的成员函数SetupNextBeginFrameWhenVSyncThrottlingEnabled就会调用成员变量client_指向的一个ThreadProxy对象的成员函数SetNeedsBeginFrame请求调度执行下一个BEGIN_IMPL_FRAME操作,否则的话,调用另外一个成员变量synthetic_begin_frame_source_指向的一个SyntheticBeginFrameSource对象的成员函数SetNeedsBeginFrame请求调度执行下一个BEGIN_IMPL_FRAME操作。
SyntheticBeginFrameSource类是通过软件方式调度执行BEGIN_IMPL_FRAME操作的,实际上就是通过定时器,根据屏幕的刷新频率模拟生成VSync信号。这种方式产生的VSync信号会受到定时器精度的影响。例如,假设屏幕刷新频率为60fps,那么就应该每16.67ms生成一个VSync信号。如果定时器只能精确到整数毫秒,那么就意味着定时器只能在16ms或者17ms后触发定时器。如果一直都是使用16ms,那么就会导致VSync信号的产生频率大于 60fps。如果一直都使用17ms,那么就会导致VSync信号的产生频率小于 60fps。为了弥补定时精度带来的缺陷,SyntheticBeginFrameSource类会自动调整定时器的Timeout时间,使得VSync信号的平均周期等于16.67ms,也就是第一个VSync信号16ms后产生,第二个VSync信号17ms后产生,第三个VSync信号17ms后产生,第四个VSync信号16ms后产生......,依次类推。这一点是SyntheticBeginFrameSource类通过使用另外一个类DelayBasedTimeSource实现的。有兴趣的读者可以自行分析DelayBasedTimeSource类的实现。
我们假设Scheduler类的成员变量settings_描述的SchedulerSettings对象的成员变量begin_frame_scheduling_enabled的值等于true,这表示屏幕支持以硬件方式产生VSync信号。在这种情况下,就直接利用屏幕产生的VSync信号调度BEGIN_IMPL_FRAME操作即可。如前所述,这是通过调用ThreadProxy类的成员函数SetNeedsBeginFrame实现的。
请求了调度下一个BEGIN_IMPL_FRAME操作之后,Scheduler类的成员函数SetupNextBeginFrameWhenVSyncThrottlingEnabled还会调用另外一个成员函数PostBeginRetroFrameIfNeeded检查之前调度的BEGIN_IMPL_FRAME操作是否都已经得到执行。有时候之前请求调度的BEGIN_IMPL_FRAME操作在下一个VSync信号到来时,会被延时执行。这些被延时的BEGIN_IMPL_FRAME操作会被保存在一个队列中,等待Scheduler类的成员函数SetupNextBeginFrameWhenVSyncThrottlingEnabled执行。注意,这个延时与BEGIN_IMPL_FRAME操作设置的Deadline是无关的。一个BEGIN_IMPL_FRAME操作只有被执行时,才可以设置Deadline。这一点我们后面再进行分析。
接下来我们继续分析ThreadProxy类的成员函数SetNeedsBeginFrame的实现,以便了解调度器通过VSync信号调度执行BEGIN_IMPL_FRAME操作的过程。
ThreadProxy类的成员函数SetNeedsBeginFrame的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void ThreadProxy::SetNeedsBeginFrame(bool enable) { ......
impl().layer_tree_host_impl->SetNeedsBeginFrame(enable); ...... }
这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。
ThreadProxy类的成员函数SetNeedsBeginFrame实现是调用负责管理网页的CC Pending Layer Tree和CC Active Layer Tree的LayerTreeHostImpl对象的成员函数SetNeedsBeginFrame请求在下一个VSync信号到来时获得通知。
参数enable表示是否需要持续地请求VSync信号通知。从前面的调用过程可以知道,当网页需要渲染下一帧时,参数enable的值就会等于true。当参数enable的值等于false的时候,实际上是表示调度器要停止接收VSync信号。
LayerTreeHostImpl类的成员函数SetNeedsBeginFrame的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 void LayerTreeHostImpl::SetNeedsBeginFrame(bool enable) { if (output_surface_)
output_surface_->SetNeedsBeginFrame(enable); else
DCHECK(!enable); }
这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host_impl.cc中。 LayerTreeHostImpl类的成员变量output_surface_描述的就是网页的绘图表面。在接下来一篇文章中,我们会分析这个绘图表面的创建过程。这个绘图表面是通过一个CompositorOutputSurface对象描述的,因此LayerTreeHostImpl类的成员函数SetNeedsBeginFrame接下来会调用CompositorOutputSurface类的成员函数SetNeedsBeginFrame请求接收下一个VSync信号通知。
CompositorOutputSurface类的成员函数SetNeedsBeginFrame的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片 void CompositorOutputSurface::SetNeedsBeginFrame(bool enable) { DCHECK(CalledOnValidThread());
Send(new ViewHostMsg_SetNeedsBeginFrame(routing_id_, enable)); }
这个函数定义在文件external/chromium_org/content/renderer/gpu/compositor_output_surface.cc中。
CompositorOutputSurface类的成员函数SetNeedsBeginFrame向Browser进程发送一个类型为ViewHostMsg_SetNeedsBeginFrame的IPC消息。在Android平台上,这个IPC消息由RenderWidgetHostViewAndroid类的成员函数OnMessageReceived接收,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
bool RenderWidgetHostViewAndroid::OnMessageReceived( const IPC::Message& message) { bool handled = true;
IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewAndroid, message) ......
IPC_MESSAGE_HANDLER(ViewHostMsg_SetNeedsBeginFrame, OnSetNeedsBeginFrame) ......
IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; }
这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc中。 负责接收消息的RenderWidgetHostViewAndroid对象描述的是Browser进程为在Render进程中加载的网页创建一个RenderWidgetHostView控件。这个RenderWidgetHostView控件的创建过程,可以参考前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文。
RenderWidgetHostViewAndroid类的成员函数OnMessageReceived将类型为ViewHostMsg_SetNeedsBeginFrame的IPC消息分发给成员函数OnSetNeedsBeginFrame处理,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame(bool enabled) { if (enabled == needs_begin_frame_) return;
TRACE_EVENT1(\ \ if (content_view_core_ && enabled)
content_view_core_->GetWindowAndroid()->RequestVSyncUpdate();
needs_begin_frame_ = enabled; }
这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc中。 RenderWidgetHostViewAndroid类的成员变量content_view_core_指向一个ContentViewCoreImpl对象。调用这个ContentViewCoreImpl对象的成员函数GetWindowAndroid可以获得一个WindowAndroid对象。有了这个WindowAndroid对象之后,就可以调用它的成员函数RequestVSyncUpdate请求获得下一个VSync信号通知。不过,在参数enabled等于true的情况下,才会执行这个操作。
WindowAndroid类的成员函数RequestVSyncUpdate通过Java层的Choreographer接
口请求系统在下一个VSync信号到来时,给当前进程发送一个通知。当前进程获得这个通知后,会调用RenderWidgetHostViewAndroid类的成员函数OnVSync,这样RenderWidgetHostViewAndroid类就获得了VSync信号通知。
RenderWidgetHostViewAndroid类的成员函数OnVSync的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderWidgetHostViewAndroid::OnVSync(base::TimeTicks frame_time,
base::TimeDelta vsync_period) { ......
base::TimeTicks display_time = frame_time + vsync_period;
// TODO(brianderson): Use adaptive draw-time estimation. base::TimeDelta estimated_browser_composite_time = base::TimeDelta::FromMicroseconds(
(1.0f * base::Time::kMicrosecondsPerSecond) / (3.0f * 60));
base::TimeTicks deadline = display_time - estimated_browser_composite_time;
host_->Send(new ViewMsg_BeginFrame( host_->GetRoutingID(),
cc::BeginFrameArgs::Create(frame_time, deadline, vsync_period)));
if (needs_begin_frame_)
content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); }
这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc中。 参数frame_time表示当前帧的时间,另外一个参数vsync_period表示屏幕的刷新周期。这两个参数相加,就得到下一帧的显示时间,记录在本地变量display_time中。
RenderWidgetHostViewAndroid类的成员函数OnVSync接下来计算Browser进程合成网页所需要的时间,记录在本地变量estimated_browser_composite_time中。这个时间等于1/3屏幕刷新周期,并且假设屏幕刷新频率为60fps。
用下一帧的显示时间display_time减去Browser进程合成网页所需要的时间estimated_browser_composite_time,就可以得到Render进程将UI提交给Browser进程合成的最迟时间,这个时间记录在本地变量deadline中。
再接下来,RenderWidgetHostViewAndroid类的成员函数OnVSync根据时间frame_time、deadline和vsync_period创建了一个BeginFrameArgs对象中,并且将该BeginFrameArgs对象封装成一个类型为ViewMsg_BeginFrame的IPC消息,发送给Render进程。Render进程接收到该消息后,就会根据上面计算出来的deadline时间调度执行一个
BEGIN_IMPL_FRAME操作。
同时,我们还会看到,当RenderWidgetHostViewAndroid类的成员变量needs_begin_frame_等于true的时候,RenderWidgetHostViewAndroid类的成员函数OnVSync会继续请求获得下一个VSync信号通知,这样就能持续地通知Render进程执行BEGIN_IMPL_FRAME操作。
Render进程是通过CompositorOutputSurface类的成员函数OnMessageReceived接收类型为ViewMsg_BeginFrame的IPC消息的,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void CompositorOutputSurface::OnMessageReceived(const IPC::Message& message) { ......
IPC_BEGIN_MESSAGE_MAP(CompositorOutputSurface, message) ......
#if defined(OS_ANDROID)
IPC_MESSAGE_HANDLER(ViewMsg_BeginFrame, OnBeginFrame); #endif
IPC_END_MESSAGE_MAP() }
这个函数定义在文件external/chromium_org/content/renderer/gpu/compositor_output_surface.cc中。
从这里可以看到,CompositorOutputSurface类的成员函数OnMessageReceived将类型为ViewMsg_BeginFrame的IPC消息分发给成员函数OnBeginFrame处理,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void CompositorOutputSurface::OnBeginFrame(const cc::BeginFrameArgs& args) { DCHECK(CalledOnValidThread()); client_->BeginFrame(args); }
这个函数定义在文件external/chromium_org/content/renderer/gpu/compositor_output_surface.cc中。
CompositorOutputSurface类的成员变量client_指向的是一个LayerTreeHostImpl对象,CompositorOutputSurface类的成员函数OnBeginFrame调用它的成员函数BeginFrame通知它现在可以执行一个BEGIN_IMPL_FRAME操作了。
LayerTreeHostImpl类的成员函数BeginFrame的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void LayerTreeHostImpl::BeginFrame(const BeginFrameArgs& args) { client_->BeginFrame(args); }
这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host_impl.cc中。 LayerTreeHostImpl类的成员变量client_指向的是一个ThreadProxy对象,