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

2019-01-27 12:51

稳定状态,而不会停留在上面提到的中间状态。

一旦决定需要请求调度执行下一个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对象,


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

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

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

马上注册会员

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