资源描述
Chromium网页绘图表面(Output Surface)创建过程分析
在Chromium中,Render进程在绘制网页之前,要为网页创建一个绘图表面。绘图表面描述的是网页经过渲染之后得到的输出。这个输出需要交给Browser进程处理,才能显示在屏幕上。在硬件加速渲染条件下,这个输出有可能是一个OpenGL纹理,也有可能是一系列需要进一步进行绘制的OpenGL纹理,取决于Render进程使用直接渲染器还是委托渲染器。本文接下来就对网页的绘图表面的创建过程进行详细分析。
关于网页绘图表面的更详细描述,可以参考一文。本文的重点是分析Chromium的CC模块是如何触发网页绘图表面的创建的,以作为一文的补充。
网页绘图表面是由CC模块的调度器触发创建的,如图1所示:
CC模块内部的状态机一旦检测到网页的Layer Tree创建和初始化完毕,就会通知调度器触发一个创建绘图表面的操作。CC模块在为网页创建
绘图表面的过程中,也有伴随着网页分块管理器、资源池和光栅化工作者线程池等基础设施的创建。一旦这些基础设施准备完毕,网页才能开
始进行绘制,也就是在图1中,第1步完成后,第2到第6步才可以周而复始地执行。
从前面一文可以知道,当网页的Graphics Layer Tree的根节点创建出来之后,WebKit就会通知Chromium的Content层初始化一个CC
Layer Tree,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderWidget::initializeLayerTreeView() {
compositor_ = RenderWidgetCompositor::Create(
this, is_threaded_compositing_enabled_);
......
if (init_complete_)
StartCompositor();
}
这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。
初始化CC Layer Tree的工作是通过调用RenderWidgetCompositor类的静态成员函数Create实现的。在前面一文中,我们分析了此时
RenderWidget类的成员变量init_complete_的值等于true,因此接下来RenderWidget类的成员函数initializeLayerTreeView会调用另外一个成
员函数StartCompositor为网页创建绘图表面。
RenderWidget类的成员函数StartCompositor的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderWidget::StartCompositor() {
......
compositor_->setSurfaceReady();
}
这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。
RenderWidget类的成员变量compositor_指向的是一个RenderWidgetCompositor对象,RenderWidget类的成员函数StartCompositor调用
这个RenderWidgetCompositor对象的成员函数setSurfaceReady,用来通知它可以为网页创建绘图表面了。
RenderWidgetCompositor类的成员函数setSurfaceReady的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void RenderWidgetCompositor::setSurfaceReady() {
layer_tree_host_->SetLayerTreeHostClientReady();
}
这个函数定义在文件external/chromium_org/content/renderer/gpu/render_widget_compositor.cc中。
RenderWidgetCompositor类的成员变量layer_tree_host_指向的是一个LayerTreeHost对象。这个LayerTreeHost对象就是用来管理CC
Layer Tree的。RenderWidgetCompositor类的成员函数setSurfaceReady调用这个LayerTreeHost对象的成员函数SetLayerTreeHostClientReady
,用来通知调度器为网页创建绘图表面。
LayerTreeHost类的成员函数SetLayerTreeHostClientReady的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void LayerTreeHost::SetLayerTreeHostClientReady() {
proxy_->SetLayerTreeHostClientReady();
}
这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。
LayerTreeHost类的成员变量proxy_指向的是一个ThreadProxy对象,LayerTreeHost类的成员函数SetLayerTreeHostClientReady调用这
个ThreadProxy对象的成员函数SetLayerTreeHostClientReady,用来调度器为网页创建绘图表面。
ThreadProxy类的成员函数SetLayerTreeHostClientReady的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void ThreadProxy::SetLayerTreeHostClientReady() {
......
Proxy::ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&ThreadProxy::SetLayerTreeHostClientReadyOnImplThread,
impl_thread_weak_ptr_));
}
这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。
ThreadProxy类的成员函数SetLayerTreeHostClientReady向Compositor线程的消息队列发送一个Task。这个Task绑定了ThreadProxy类
的成员函数SetLayerTreeHostClientReadyOnImplThread。因此接下来ThreadProxy类的成员函数SetLayerTreeHostClientReadyOnImplThread就
会在Compositor线程中执行。
ThreadProxy类的成员函数SetLayerTreeHostClientReadyOnImplThread的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void ThreadProxy::SetLayerTreeHostClientReadyOnImplThread() {
......
impl().scheduler->SetCanStart();
}
这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。
ThreadProxy类的成员函数SetLayerTreeHostClientReadyOnImplThread将调度器设置为启动状态,这是通过调用Scheduler类的成员函
数SetCanStart实现的,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void Scheduler::SetCanStart() {
state_machine_.SetCanStart();
ProcessScheduledActions();
}
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler.cc中。
Scheduler类的成员函数SetCanStart首先将内部的状态机设置为启动状态,这是通过调用SchedulerStateMachine类的成员函数
SetCanStart实现的,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
class CC_EXPORT SchedulerStateMachine {
public:
......
// Set that we can create the first OutputSurface and start the scheduler.
void SetCanStart() { can_start_ = true; }
......
};
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.h中。
SchedulerStateMachine类的成员函数SetCanStart将成员变量can_start_的值设置为true,这样就会触发调度器为网页创建绘图表面。
回到Scheduler类的成员函数SetCanStart中,它将内部的状态机设置为启动状态之后,接着调用另外一个成员函数
ProcessScheduledActions检查下一个要执行的操作,这个操作即为创建绘图表面。
Scheduler类的成员函数ProcessScheduledActions的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void Scheduler::ProcessScheduledActions() {
......
SchedulerStateMachine::Action action;
do {
action = state_machine_.NextAction();
......
state_machine_.UpdateState(action);
......
switch (action) {
......
case SchedulerStateMachine::ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
client_->ScheduledActionBeginOutputSurfaceCreation();
break;
......
}
} while (action != SchedulerStateMachine::ACTION_NONE);
SetupNextBeginFrameIfNeeded();
......
if (state_machine_.ShouldTriggerBeginImplFrameDeadlineEarly()) {
......
ScheduleBeginImplFrameDeadline(base::TimeTicks());
}
}
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。
Scheduler类的成员函数ProcessScheduledAction的详细实现可以参考前面一文,这里
我们只关注它是如何触发网页绘图表面的创建的。
Scheduler类的成员函数ProcessScheduledAction首先调用SchedulerStateMachine类的成员函数NextAction询问状态机下一个应该执行
的操作,后者的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
SchedulerStateMachine::Action SchedulerStateMachine::NextAction() const {
if (ShouldUpdateVisibleTiles())
return ACTION_UPDATE_VISIBLE_TILES;
if (ShouldActivatePendingTree())
return ACTION_ACTIVATE_PENDING_TREE;
if (ShouldCommit())
return ACTION_COMMIT;
if (ShouldAnimate())
return ACTION_ANIMATE;
if (ShouldDraw()) {
if (PendingDrawsShouldBeAborted())
return ACTION_DRAW_AND_SWAP_ABORT;
else if (forced_redraw_state_ == FORCED_REDRAW_STATE_WAITING_FOR_DRAW)
return ACTION_DRAW_AND_SWAP_FORCED;
else
return ACTION_DRAW_AND_SWAP_IF_POSSIBLE;
}
if (ShouldManageTiles())
return ACTION_MANAGE_TILES;
if (ShouldSendBeginMainFrame())
return ACTION_SEND_BEGIN_MAIN_FRAME;
if (ShouldBeginOutputSurfaceCreation())
return ACTION_BEGIN_OUTPUT_SURFACE_CREATION;
return ACTION_NONE;
}
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。
在我们这个情景中,SchedulerStateMachine类的成员函数NextAction在调用到另外一个成员函数ShouldBeginOutputSurfaceCreation时
得到的返回值为true,后者的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
bool SchedulerStateMachine::ShouldBeginOutputSurfaceCreation() const {
// Don't try to initialize too early.
if (!can_start_)
return false;
// We only want to start output surface initialization after the
// previous commit is complete.
if (commit_state_ != COMMIT_STATE_IDLE)
return false;
// Make sure the BeginImplFrame from any previous OutputSurfaces
// are complete before creating the new OutputSurface.
if (begin_impl_frame_state_ != BEGIN_IMPL_FRAME_STATE_IDLE)
return false;
// We want to clear the pipline of any pending draws and activations
// before starting output surface initialization. This allows us to avoid
// weird corner cases where we abort draws or force activation while we
// are initializing the output surface.
if (active_tree_needs_first_draw_ || has_pending_tree_)
return false;
// We need to create the output surface if we don't have one and we haven't
// started creating one yet.
return output_surface_state_ == OUTPUT_SURFACE_LOST;
}
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。
SchedulerStateMachine类的成员函数ShouldBeginOutputSurfaceCreation返回true要满足以下六个条件:
1. 网页的CC Layer Tree已经创建和初始化完成。这时候SchedulerStateMachine类的成员变量can_start_的值等于true。
2. 状态机的CommitState状态等于COMMIT_STATE_IDLE。这意味着当前Main线程不是正在提交变化给Compositor线程渲染。
3. 状态机的BeginImplFrameState状态等于BEGIN_IMPL_FRAME_STATE_IDLE。这意味着调度器已经向Compositor线程发出了一个渲染请
求。
4. Compositor线程的CC Pending Layer Tree已经激活为CC Active Layer Tree。这时候SchedulerStateMachine类的成员变量
has_pending_tree_的值等于false。
5. Compositor线程的CC Active Layer Tree被激活后,已经被执行过至少一次渲染操作了。这时候SchedulerStateMachine类的成员变
量active_tree_needs_first_draw_的值等于false。
6. 状态机的OutputSurfaceState状态等于OUTPUT_SURFACE_LOST。
从前面的分析可以知道,第1个条件是满足的。由于这时候状态机也是刚刚初始化完成,因此后面五个条件也得到满足的。因此
SchedulerStateMachine类的成员函数ShouldBeginOutputSurfaceCreation会返回true值给调用者。
同时,从SchedulerStateMachine类的成员函数ShouldBeginOutputSurfaceCreation的实现也可以看出,如果网页绘图表面在创建之后
,由于其它原因失效了,那么要等到网页的渲染管线达到一个稳定状态之后,才可以重新创建。失效的原因可以参考Chromium网页渲染调度器
(Scheduler)实现分析一文。稳定状态指的是Main线程已经将当前的网页变化提交给了Compositor线程,而Compositor线程也已经处理这些变
化引起的渲染工作。总的来说,就是图1所示的第2到第6个操作均已经连贯执行完成。
回到SchedulerStateMachine类的成员函数NextAction中,当调用另外一个成员函数ShouldBeginOutputSurfaceCreation得到的返回值
等于true的时候,它会返回一个ACTION_BEGIN_OUTPUT_SURFACE_CREATION值给调用者,也就是调度器,表示现在需要为网页创建绘图表面。
回到Scheduler类的成员函数ProcessScheduledActions中,它知道了下一个要执行的操作是ACTION_BEGIN_OUTPUT_SURFACE_CREATION之
后,接下来会先调用SchedulerStateMachine类的成员函数UpdateState更新状态机的状态,如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void SchedulerStateMachine::UpdateState(Action action) {
switch (action) {
......
case ACTION_BEGIN_OUTPUT_SURFACE_CREATION:
......
output_surface_state_ = OUTPUT_SURFACE_CREATING;
......
return;
......
}
}
这个函数定义在文件external/chromium_org/cc/scheduler/scheduler_state_machine.cc中。
当调度器接下要执行的操作是ACTION_BEGIN_OUTPUT_SURFACE_CREATION时,SchedulerStateMachine类的成员函数UpdateState主要就是
修改状态机的OutputSurfaceState状态,也就是将它的状态从OUTPUT_SURFACE_LOST迁移至OUTPUT_SURFACE_CREATING,表示正在为网页创建绘
图表面。
回到Scheduler类的成员函数ProcessScheduledActions中,它更新了状态机的OutputSurfaceState状态之后,接下来就会调用成员变量
client_指向的一个ThreadProxy对象的成员函数ScheduledActionBeginOutputSurfaceCreation为网页创建绘图表面。
ThreadProxy类的成员函数ScheduledActionBeginOutputSurfaceCreation的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void ThreadProxy::ScheduledActionBeginOutputSurfaceCreation() {
......
Proxy::MainThreadTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&ThreadProxy::CreateAndInitializeOutputSurface,
main_thread_weak_ptr_));
}
这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。
ThreadProxy类的成员函数ScheduledActionBeginOutputSurfaceCreation向Main线程的消息队列发送了一个Task,这个Task绑定了
ThreadProxy类的成员函数CreateAndInitializeOutputSurface。因此,接下来ThreadProxy类的成员函数CreateAndInitializeOutputSurface
就会在Main线程中执行。
ThreadProxy类的成员函数CreateAndInitializeOutputSurface的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void ThreadProxy::CreateAndInitializeOutputSurface() {
......
DCHECK(IsMainThread());
scoped_ptr<OutputSurface> output_surface =
layer_tree_host()->CreateOutputSurface();
if (output_surface) {
Proxy::ImplThreadTaskRunner()->PostTask(
FROM_HERE,
base::Bind(&ThreadProxy::InitializeOutputSurfaceOnImplThread,
impl_thread_weak_ptr_,
base::Passed(&output_surface)));
return;
}
......
}
这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc中。
ThreadProxy类的成员函数CreateAndInitializeOutputSurface首先调用另外一个成员函数layer_tree_host获得一个LayerTreeHost对
象。然后再调用这个LayerTreeHost对象的成员函数CreateOutputSurface为网页创建绘图表面。
ThreadProxy类的成员函数CreateAndInitializeOutputSurface接下来又向Compositor线程的消息队列发送一个Task。这个Task绑定的
函数是ThreadProxy类的成员函数InitializeOutputSurfaceOnImplThread,用来在Compositor线程中初始化前面创建好的绘图表面。
接下来我们先分析LayerTreeHost类的成员函数CreateOutputSurface的实现,接着再分析ThreadProxy类的成员函数
InitializeOutputSurfaceOnImplThread的实现。
LayerTreeHost类的成员函数CreateOutputSurface的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
scoped_ptr<OutputSurface> LayerTreeHost::CreateOutputSurface() {
return client_->CreateOutputSurface(num_failed_recreate_attempts_ >= 4);
}
这个函数定义在文件external/chromium_org/cc/trees/layer_tree_host.cc中。
从前面一文可以知道,LayerTreeHost类的成员变量client_指向的是一个RenderWidgetCompositor对象。LayerTreeHost类的成员函数
CreateOutputSurface调用这个RenderWidgetCompositor对象的成员函数CreateOutputSurface为网页创建绘图表面。
RenderWidgetCompositor类的成员函数CreateOutputSurface的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
scoped_ptr<cc::OutputSurface> RenderWidgetCompositor::CreateOutputSurface(
bool fallback) {
return widget_->CreateOutputSurface(fallback);
}
这个函数定义在文件external/chromium_org/content/renderer/gpu/render_widget_compositor.cc中。
从前面一文可以知道,RenderWidgetCompositor类的成员变量widget_指向的是一个RenderViewImpl对象。RenderWidgetCompositor类
的成员函数CreateOutputSurface调用这个RenderViewImpl对象的成员函数CreateOutputSurface为网页创建绘图表面。
RenderViewImpl类的成员函数CreateOutputSurface是从父类RenderWidget继承下来的。RenderWidget类的成员函数
CreateOutputSurface为网页创建绘图表面的过程可以参考前面一文。这个过程实际上就是Render进程请求与GPU进程建立GPU通道的过程。GPU
通道建立起来之后,Render进程就可以发送GPU命令给GPU进程执行了。注意,在Render进程中加载的每一个网页都会与GPU进程建立一个GPU通
道,并且每一个GPU通道在GPU进程又对应有一个OpenGL上下文。我们也可以认为Render进程的一个绘图表面对应于GPU进程中的一个OpenGL上下
文。
假设网页A与GPU进程建立的GPU通道为G1,并且GPU通道为G1在GPU进程中对应的OpenGL上下文C1。当Render进程要渲染网页A的UI时,它
就会通过GPU通道G1向GPU进程发送相应的GPU命令。GPU进程接收到这些GPU命令之后,就会激活OpenGL上下文C1,然后执行接收到的GPU命令。
通过这种方式,GPU进程就可以同时接收不同网页的渲染命令,并且它们不会互相干扰,因为不同网页的渲染命令都是在各自的OpenGL上下文中
执行的。这些都是Chromium硬件加速渲染机制相关的知识,可以参考前面这个系列的文章。
回到ThreadProxy类的成员函数CreateAndInitializeOutputSurface中,它调用LayerTreeHost类的成员函数CreateOutputSurface为网页
创建了一个绘图表面之后,接下来会请求Compositor线程初始化这个绘图表面。这是通过在Compositor线程中调用ThreadProxy类的成员函数
InitializeOutputSurfaceOnImplThread实现的。
ThreadProxy类的成员函数InitializeOutputSurfaceOnImplThread的实现如下所示:
[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
void ThreadProxy::InitializeOutputSurfaceOnImplThread(
scoped_ptr<OutputSurface> output_surface) {
......
DCHECK(IsImplThread());
LayerTreeHostImpl* host_impl = impl().layer_tree_host_impl.get();
bool success = host_impl->InitializeRenderer(output_surface.Pass());
......
if (success)
impl().scheduler->DidCreateAndInitializeOutputSurface();
}
这个函数定义在文件external/chromium_org/cc/trees/thread_proxy.cc。
ThreadProxy类的成员函数InitializeOutputSurfaceOnImplThread首先获得一个LayerTreeHostImpl对象,接着调用这个
LayerTreeHostImpl对象的成员函数InitializeRenderer初始化参数output_surface描述的绘图表面。从前面一文可以知道,参数
output_surface指向的实际上是一个CompositorOutputSurface对象。也就是说,网页的绘图表面是通过一个CompositorOutputSurf
展开阅读全文