Chromium插件(Plugin)执行3D渲染的过程分析
Posted 罗升阳
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Chromium插件(Plugin)执行3D渲染的过程分析相关的知识,希望对你有一定的参考价值。
Chromium为网页的<embed>标签创建了Plugin之后,Plugin就负责渲染<embed>标签的内容。Chromium为Plugin提供了OpenGL接口,使得Plugin可在网页上渲染3D内容。当然,我们也可通过WebGL接口在网页上渲染3D内容。不过,前者渲染效率会更高些,因为它是Native接口,后者是javascript接口。本文接下来就详细分析Plugin执行3D渲染的过程。
老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!
《android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!
我们分析Plugin执行3D渲染的过程,更多的是为了理解Chromium与Plugin的交互过程,包括Chromium操作Plugin的过程,以及Plugin调用Chromium提供的接口的过程。接下来我们就以Chromium插件(Plugin)机制简要介绍和学习计划一文提到的GLES2 Example为例,分析Plugin执行3D渲染的过程。
从前面Chromium网页加载过程简要介绍和学习计划这个系列的文章可以知道,WebKit会将网页抽象为一个DOM Tree。网页中的每一个<embed>标签在这个DOM Tree中都会有一个对应的节点。从前面Chromium网页渲染机制简要介绍和学习计划这个系列的文章又可以知道,网页的DOM Tree又会被Chromium转化为一个CC Layer Tree。其中,DOM Tree中的<embed>节点将对应于CC Layer Tree中的一个Texture Layer,如图1所示:
图1 DOM Tree中的<embed>标签与CC Layer Tree中的Texture Layer
Plugin在调用Chromium提供的OpenGL接口的时候,实际上是将<embed>标签的内容渲染在它在CC Layer Tree中对应的Texture Layer上。当Chromium对网页的CC Layer Tree进行绘制的时候,它内部的Texture Layer的内容就会显示在屏幕上。Texture Layer描述的实际上是一个纹理。在前面Chromium硬件加速渲染的UI合成过程分析一文中,我们提到,网页的canvas标签在CC Layer Tree同样是对应有一个Texture Layer。因此,<embed>标签对应的Texture Layer与canvas标签对应的Texture Layer显示在屏幕上的过程是一样的。这一点可以参考前面Chromium硬件加速渲染的UI合成过程分析一文。
在Android平台上,Chromium提供给Plugin调用的OpenGL接口称为PPB_OPENGLES2_INTERFACE接口。PPB_OPENGLES2_INTERFACE接口提供了一系列的函数,每一个函数都对应于一个glXXX接口,如图2所示:
图2 Plugin调用OpenGL接口的过程
在调用PPB_OPENGLES2_INTERFACE接口提供的函数时,GLES2Implementation类的相应成员函数会被调用。例如,当PPB_OPENGLES2_INTERFACE接口提供的函数ActiveTexture被调用时,GLES2Implementation类的成员函数ActiveTexture。GLES2Implementation类的成员函数会将要执行的GPU命令写入到一个缓冲区中去,然后通过一个PpapiCommandBufferProxy对象通知Render进程执行缓冲区中的GPU命令。Render进程又会通过一个CommandBufferProxyImpl对象将要执行的GPU命令转发给GPU进程中的一个GpuCommandBufferStub处理。这意味Plugin最终是通过GPU进程执行GPU命令的。关于GLES2Implementation、CommandBufferProxyImpl和GpuCommandBufferStub这三类执行GPU命令的过程,可以参考前面Chromium硬件加速渲染的OpenGL命令执行过程分析一文。
Plugin在通过PPB_OPENGLES2_INTERFACE接口执行OpenGL函数(GPU命令)之前,首先要初始化一个OpenGL环境。这个初始化操作发生在Plugin第一次知道自己的视图大小时,也就是知道它对应的<embed>标签的视图大小时。初始化过程如图3所示:
图3 Plugin的OpenGL环境初始化过程
Plugin的视图大小是由WebKit计算出来的。计算出来之后,WebKit会通过运行在Render进程中的Plugin Instance Proxy,也就是一个PepperPluginInstanceImpl对象,向运行Plugin进程中的Plugin Instance发出通知。Plugin Instance获得了这个通知之后,就可以初始化一个OpenGL环境了。
Plugin Instance Proxy是通过调用PPP_INSTANCE_INTERFACE_1_1接口提供的一个函数DidChangeView向Plugin Instance发出通知的,后者又是通过向目标Plugin进程发送一个类型为PpapiMsg_PPPInstance_DidChangeView的IPC消息发出该通知的。
类型为PpapiMsg_PPPInstance_DidChangeView的IPC消息携带了一个参数PpapiMsg_PPPInstance_DidChangeView。这个参数描述的是一个Routing ID,表示Plugin进程要将类型为PpapiMsg_PPPInstance_DidChangeView的IPC消息交给一个PPP_Instance_Proxy对象处理。这个PPP_Instance_Proxy对象获得该消息后,又会在当前Plugin进程中获得一个PPP_INSTANCE_INTERFACE_1_1接口,并且调用该接口提供的函数DidChangeView。该函数会找到目标Plugin Instance,并且调用它的成员函数DidChangeView。这样,Plugin Instance就可以初始化OpenGL环境了。以我们在前面Chromium插件(Plugin)机制简要介绍和学习计划一文提到的GLES2 Example为例,它的成员函数DidChangeView就通过调用另外一个成员函数InitGL初始化OpenGL环境的。
Plugin在初始化OpenGL环境的过程中,有两件重要的事情要做。第一件事情是创建一个OpenGL上下文,过程如图4所示:
图4 Plugin的OpenGL上文创建过程
在Plugin进程中,OpenGL上下文通过Graphics3D类描述。因此,创建OpenGL上下文意味着是创建一个Graphics3D对象。这个Graphics3D对象在创建的过程中,会调用PPB_GRAPHICS_3D_INTERFACE_1_0接口提供的一个函数Create。该函数又会通过一个APP_ID_RESOURCE_CREATION接口向Render进程发送一个类型为PpapiHostMsg_PPBGraphics3D_Create的IPC消息。在Plugin进程中,APP_ID_RESOURCE_CREATION接口是通过一个ResourceCreationProxy对象实现的,因此,Plugin进程实际上是通过ResourceCreationProxy类向Render进程发送一个类型为PpapiHostMsg_PPBGraphics3D_Create的IPC消息的。
Plugin进程在向Render进程发送类型为PpapiHostMsg_PPBGraphics3D_Create的IPC消息时,会指定一个参数APP_ID_PPB_GRAPHICS_3D,表示Render进程在接收到该消息后,要将其分发给一个PPB_Graphics3D_Proxy对象处理。这个PPB_Graphics3D_Proxy对象又会通过调用PPB_Graphics3D_Impl类的静态成员函数CreateRaw创建一个PPB_Graphics3D_Impl对象。这个PPB_Graphics3D_Impl对象在Render进程描述的就是一个OpenGL上下文。
Plugin在初始化OpenGL环境的过程中做的第二件事情就是将刚刚创建出来的OpenGL上下文指定为当前要使用的OpenGL上下文。这个过程称为OpenGL上下文绑定,如图5所示:
图5 Plugin绑定OpenGL上下文的过程
Plugin是通过调用PPB_INSTANCE_INTERFACE_1_0接口提供的函数BindGraphics进行OpenGL上下文绑定的。该函数又会通过一个APP_ID_PPB_INSTANCE接口向Render进程发送一个类型为PpapiHostMsg_PPBIntance_BindGraphics的IPC消息。在Plugin进程中,APP_ID_PPB_INSTANCE接口是通过一个PPB_Instance_Proxy对象实现的,因此,Plugin进程实际上是通过PPB_Instance_Proxy类向Render进程发送一个类型为PpapiHostMsg_PPBIntance_BindGraphics的IPC消息的。
Plugin进程在向Render进程发送类型为PpapiHostMsg_PPBIntance_BindGraphics的IPC消息时,会指定一个参数APP_ID_PPB_INSTANCE,表示Render进程在接收到该消息后,要将其分发给一个PPB_Instance_Proxy对象处理。这个PPB_Instance_Proxy对象又会找到目标Plugin Instance在Render进程中对应的Proxy,也就是一个PepperPluginInstanceImpl对象,并且调用该PepperPluginInstanceImpl对象的成员函数BindGraphics。
PepperPluginInstanceImpl类的成员函数BindGraphics在执行的过程中,会将指定的OpenGL上下文,也就是前面创建一个PPB_Graphics3D_Impl对象标记为被绑定,这样它接下来就会作为Plugin当前使用的OpenGL上下文了。
OpenGL环境初始化完成之后,Plugin就可以使用图2所示的PPB_OPENGLES2_INTERFACE接口来执行3D渲染了。一帧渲染完毕,Plugin需要交换当前使用的OpenGL上下文的前后两个缓冲区,也就是执行一个SwapBuffers操作。这个操作的执行过程如图6所示:
图6 Plugin执行SwapBuffers操作的过程
前面提到,在Plugin进程中,OpenGL上下文是通过一个Graphics3D对象描述的。这个Graphics3D对象可以通过PPB_GRAPHICS_3D_INTERFACE_1_0接口提供的成员函数SwapBuffers完成SwapBuffers操作。
PPB_GRAPHICS_3D_INTERFACE_1_0接口提供的成员函数SwapBuffers在执行的过程中,会向Render进程发送一个类型为PpapiHostMsg_PPBGraphics3D_SwapBuffers的IPC消息。这个消息携带了一个参数API_ID_PPB_GRAPHICS_3D,表示Render进程需要将该消息分发给一个PPB_Graphics3D_Proxy对象处理。这个PPB_Graphics3D_Proxy对象会找到Plugin当前绑定的OpenGL上下文,也就是一个PPB_Graphics3D_Impl对象,并且调用该PPB_Graphics3D_Impl对象的成员函数DoSwapBuffers。这时候就可以完成一个SwapBuffers操作,从而也完成一个帧的渲染流程。
接下来,我们就从WebKit通知Plugin视图大小开始,分析Plugin执行3D渲染的完整流程。从前面Chromium插件(Plugin)模块(Module)加载过程分析一文可以知道,WebKit为<embed>标签创建了一个Plugin Instance之后,会将其放在一个由WebPluginContainerImpl类描述的Plugin View中。当<embed>标签的视图大小发生变化时,WebPluginContainerImpl类的成员函数reportGeometry就会被调用,它的实现如下所示:
void WebPluginContainerImpl::reportGeometry()
{
......
IntRect windowRect, clipRect;
Vector<IntRect> cutOutRects;
calculateGeometry(frameRect(), windowRect, clipRect, cutOutRects);
m_webPlugin->updateGeometry(windowRect, clipRect, cutOutRects, isVisible());
......
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/web/WebPluginContainerImpl.cpp中。
WebPluginContainerImpl类的成员函数reportGeometry首先是调用成员函数calulateGeometry计算<embed>标签的视图大小,然后再将计算得到的信息告知Content层,这是通过成员变量m_webPlugin指向的一个PepperWebPluginImpl对象的成员函数updateGeometry实现的。这个PepperWebPluginImpl对象的创建过程可以参考前面Chromium的Plugin进程启动过程分析一文。
接下来我们继续分析PepperWebPluginImpl类的成员函数updateGeometry的实现,以便了解Render进程通知Plugin它描述的<embed>标签的大小的过程,如下所示:
void PepperWebPluginImpl::updateGeometry(
const WebRect& window_rect,
const WebRect& clip_rect,
const WebVector<WebRect>& cut_outs_rects,
bool is_visible) {
plugin_rect_ = window_rect;
if (!instance_->FlashIsFullscreenOrPending()) {
std::vector<gfx::Rect> cut_outs;
for (size_t i = 0; i < cut_outs_rects.size(); ++i)
cut_outs.push_back(cut_outs_rects[i]);
instance_->ViewChanged(plugin_rect_, clip_rect, cut_outs);
}
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/pepper_webplugin_impl.cc中。
PepperWebPluginImpl类的成员变量instance_指向的是一个PepperPluginInstanceImpl对象。从前面Chromium插件(Plugin)实例(Instance)创建过程分析一文可以知道,这个PepperPluginInstanceImpl对象是用来在Render进程中描述一个Plugin Instance Proxy的。
PepperWebPluginImpl类的成员函数updateGeometry首先调用上述PepperPluginInstanceImpl对象的成员函数FlashIsFullscreenOrPending判断当前正在处理的Plugin是否是一个Flash Plugin。如果是一个Flash Plugin,并且它当前处于全屏状态或者即将进入全屏状态,那么PepperWebPluginImpl类的成员函数updateGeometry就不会通知Plugin它描述的<embed>标签的视图大小发生了变化。
我们假设当前正在处理的Plugin不是一个Flash Plugin。这时候PepperWebPluginImpl类的成员函数updateGeometry就会调用上述PepperPluginInstanceImpl对象的成员函数ViewChanged通知Plugin它描述的<embed>标签的视图大小发生了变化。
PepperPluginInstanceImpl类的成员函数ViewChanged的实现如下所示:
void PepperPluginInstanceImpl::ViewChanged(
const gfx::Rect& position,
const gfx::Rect& clip,
const std::vector<gfx::Rect>& cut_outs_rects) {
......
view_data_.rect = PP_FromGfxRect(position);
view_data_.clip_rect = PP_FromGfxRect(clip);
......
SendDidChangeView();
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/pepper_plugin_instance_impl.cc中。
PepperPluginInstanceImpl类的成员函数ViewChanged首先将当前正在处理的Plugin描述的<embed>标签的视图大小信息保存在成员变量view_data_描述的一个ppapi::ViewData对象中,然后再调用另外一个成员函数SendDidChangeView向运行在Plugin进程中的Plugin Instance发送一个视图大小变化通知。
PepperPluginInstanceImpl类的成员函数SendDidChangeView的实现如下所示:
void PepperPluginInstanceImpl::SendDidChangeView() {
......
ScopedPPResource resource(
ScopedPPResource::PassRef(),
(new PPB_View_Shared(ppapi::OBJECT_IS_IMPL, pp_instance(), view_data_))
->GetReference());
......
if (instance_interface_) {
instance_interface_->DidChangeView(
pp_instance(), resource, &view_data_.rect, &view_data_.clip_rect);
}
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/pepper_plugin_instance_impl.cc中。
PepperPluginInstanceImpl类的成员函数SendDidChangeView首先是将成员变量view_data_描述的ppapi::ViewData对象封装在一个PPB_View_Shared对象。从前面的分析可以知道,被封装的ppapi::ViewData对象描述的当前正在处理的Plugin描述的<embed>标签的视图大小信息。封装得到的PPB_View_Shared对象接下来会传递给PPP_INSTANCE_INTERFACE_1_1接口提供的函数DidChangeView使用。
从前面Chromium插件(Plugin)实例(Instance)创建过程分析一文可以知道,PepperPluginInstanceImpl类的成员变量instance_interface_指向的是一个PPP_Instance_Combined对象。这个PPP_Instance_Combined对象描述的是一个PPP_INSTANCE_INTERFACE_1_1接口。运行在Render进程中的Plugin Instance Proxy可以通过这个接口与运行在Plugin进程中的Plugin Instance通信。
PepperPluginInstanceImpl类的成员函数SendDidChangeView主要就是调用上述PPP_INSTANCE_INTERFACE_1_1接口提供的函数DidChangeView通知运行在Plugin进程中的Plugin Instance,它描述的<embed>标签的视图大小发生了变化,也就是通过调用成员变量instance_interface_指向的PPP_Instance_Combined对象的成员函数DidChangeView进行通知。
PPP_Instance_Combined类的成员函数DidChangeView的实现如下所示:
void PPP_Instance_Combined::DidChangeView(PP_Instance instance,
PP_Resource view_changed_resource,
const struct PP_Rect* position,
const struct PP_Rect* clip) {
if (instance_1_1_.DidChangeView) {
CallWhileUnlocked(
instance_1_1_.DidChangeView, instance, view_changed_resource);
}
......
}
这个函数定义在文件external/chromium_org/ppapi/shared_impl/ppp_instance_combined.cc中。
从前面Chromium插件(Plugin)实例(Instance)创建过程分析一文可以知道,PPP_Instance_Combined类的成员变量instance_1_1_描述的是一个PPP_Instance_1_1对象。这个PPP_Instance_1_1对象描述的就是上述的PPP_INSTANCE_INTERFACE_1_1接口。PPP_Instance_Combined类的成员函数DidChangeView通过一个帮助函数CallWhilleUnlocked调用这个PPP_INSTANCE_INTERFACE_1_1接口提供的函数DidChangeView,以便向运行在Plugin进程中的Plugin Instance发出一个视图大小变化通知。
从前面Chromium插件(Plugin)实例(Instance)创建过程分析一文还可以知道,上述PPP_INSTANCE_INTERFACE_1_1接口提供的函数DidChangeView实现在ppp_instance_proxy.cc文件中,如下所示:
void DidChangeView(PP_Instance instance, PP_Resource view_resource) {
HostDispatcher* dispatcher = HostDispatcher::GetForInstance(instance);
EnterResourceNoLock<PPB_View_API> enter_view(view_resource, false);
......
dispatcher->Send(new PpapiMsg_PPPInstance_DidChangeView(
API_ID_PPP_INSTANCE, instance, enter_view.object()->GetData(),
flash_fullscreen));
}
这个函数定义在文件external/chromium_org/ppapi/proxy/ppp_instance_proxy.cc中。
参数instance的类型为PP_Instance。PP_Instance描述的实际上是一个32位的有符号整数。这个32位的有符号整数是用来描述一个Plugin的ID。通过这个ID,运行在Render进程中的Plugin Instance Proxy与运行在Plugin进程中的Plugin Instance就能一一对应起来的。
函数DidChangeView首先通过调用HostDispatcher类的静态成员函数GetForInstance获得一个HostDispatcher对象。从前面Chromium插件(Plugin)模块(Module)加载过程分析一文可以知道,每一个Plugin进程在Render进程中都有一个对应的HostDispatcher对象。这个HostDispatcher对象就是用来与它对应的Plugin进程通信的。给出一个PP_Instance,HostDispatcher类的静态成员函数GetForInstance就可以获得这个PP_Instance描述的Plugin Instance所运行在的Plugin进程对应的HostDispatcher对象。
获得了目标Plugin进程对应的HostDispatcher对象之后,就可以向它发送一个类型为PpapiMsg_PPPInstance_DidChangeView的IPC消息。这个IPC消息携带了四个参数:
1. API_ID_PPP_INSTANCE,要求Plugin进程将该IPC消息分发给API_ID_PPP_INSTANCE接口处理。
2. instance,表示目标Plugin Instance。
3. ppapi::ViewData,表示目标Plugin Instance的当前视图大小。
4. flash_fullscreen,表示目标Plugin Instance是否处于全屏状态。
其中,第3个参数描述的ppapi::ViewData对象来自于前面在PepperPluginInstanceImpl类的成员函数SendDidChangeView中封装的PPB_View_Shared对象。这个PPB_View_Shared对象对象是通过函数DidChangeView的第2个参数view_resource传递进来的。
函数DidChangeView首先将上述PPB_View_Shared对象封装在一个EnterResourceNoLock<PPB_View_API>对象中。接下来通过调用这个EnterResourceNoLock<PPB_View_API>对象的成员函数object就可以获得它封装的PPB_View_Shared对象。再调用这个PPB_View_Shared对象的成员函数GetData即可以获得它内部封装的一个ppapi::ViewData对象。这个ppapi::ViewData对象内部保存了目标Plugin Instance的当前视图大小信息,因此可以作为上述IPC消息的第3个参数。
从前面Chromium的Plugin进程启动过程分析一文可以知道,每一个Plugin进程都存在一个PluginDispatcher对象。Plugin进程将会通过这个PluginDispatcher对象的成员函数OnMessageReceived接收Render进程发送过来的IPC消息。这意味着前面从Render进程发送过来的类型为PpapiMsg_PPPInstance_DidChangeView的IPC消息是通过PluginDispatcher类的成员函数OnMessageReceived接收的。
PluginDispatcher类的成员函数OnMessageReceived是从父类Dispatcher继承下来的,它的实现如下所示:
bool Dispatcher::OnMessageReceived(const IPC::Message& msg) {
......
InterfaceProxy* proxy = GetInterfaceProxy(
static_cast<ApiID>(msg.routing_id()));
......
return proxy->OnMessageReceived(msg);
}
这个函数定义在文件external/chromium_org/ppapi/proxy/dispatcher.cc中。
从前面的分析可以知道,此时参数msg指向的Message对象描述的是一个类型为PpapiMsg_PPPInstance_DidChangeView的IPC消息,该消息的Routing ID为API_ID_PPP_INSTANCE,表示要将该消息分发给一个API_ID_PPP_INSTANCE接口处理。这个API_ID_PPP_INSTANCE接口可以通过调用调用另外一个成员函数GetInterfaceProxy获得。
在前面Chromium插件(Plugin)实例(Instance)创建过程分析一文中,我们已经分析过Dispatcher类的成员函数GetInterfaceProxy的实现,因此这里不再复述。再结合前面Chromium插件(Plugin)模块(Module)加载过程分析一文,我们可以知道,在Plugin进程中,API_ID_PPP_INSTANCE接口是由一个PPP_Instance_Proxy对象实现的,Dispatcher类的成员函数GetInterfaceProxy接下来会将参数msg描述的类型为PpapiMsg_PPPInstance_DidChangeView的IPC消息分发给它处理,也就是调用它的成员函数OnMessageReceived。
PPP_Instance_Proxy类的成员函数OnMessageReceived的实现如下所示:
bool PPP_Instance_Proxy::OnMessageReceived(const IPC::Message& msg) {
......
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PPP_Instance_Proxy, msg)
......
IPC_MESSAGE_HANDLER(PpapiMsg_PPPInstance_DidChangeView,
OnPluginMsgDidChangeView)
......
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
这个函数定义在文件external/chromium_org/ppapi/proxy/ppp_instance_proxy.cc中。
从这里可以看到,PPP_Instance_Proxy类的成员函数OnMessageReceived将类型为PpapiMsg_PPPInstance_DidChangeView的IPC消息分发给另外一个成员函数OnPluginMsgDidChangeView处理,如下所示:
void PPP_Instance_Proxy::OnPluginMsgDidChangeView(
PP_Instance instance,
const ViewData& new_data,
PP_Bool flash_fullscreen) {
......
combined_interface_->DidChangeView(instance, resource,
&new_data.rect,
&new_data.clip_rect);
}
这个函数定义在文件external/chromium_org/ppapi/proxy/ppp_instance_proxy.cc中。
从前面Chromium插件(Plugin)实例(Instance)创建过程分析一文可以知道,PPP_Instance_Proxy类的成员变量combined_interface_指向的是一个PPP_Instance_Combined对象。PPP_Instance_Proxy类的成员函数OnPluginMsgDidChangeView主要是调用这个PPP_Instance_Combined对象的成员函数DidChangeView通知参数instance描述的Plugin Instance,它的视图大小发生了变化。
PPP_Instance_Combined类的成员函数DidChangeView的实现如下所示:
void PPP_Instance_Combined::DidChangeView(PP_Instance instance,
PP_Resource view_changed_resource,
const struct PP_Rect* position,
const struct PP_Rect* clip) {
if (instance_1_1_.DidChangeView) {
CallWhileUnlocked(
instance_1_1_.DidChangeView, instance, view_changed_resource);
}
......
}
这个函数定义在文件external/chromium_org/ppapi/shared_impl/ppp_instance_combined.cc中。
从前面Chromium插件(Plugin)实例(Instance)创建过程分析一文可以知道,PPP_Instance_Combined类的成员变量instance_1_1_指向的是一个PPP_Instance对象。这个PPP_Instance对象的成员变量DidChangeView是一个函数指针,它指向的函数为Instance_DidChangeView。PPP_Instance_Combined类的成员函数DidCreate主要是调用这个函数通知参数instance描述的Plugin Instance,它的视图大小发生了变化。
函数Instance_DidChangeView的实现,如下所示:
void Instance_DidChangeView(PP_Instance pp_instance,
PP_Resource view_resource) {
Module* module_singleton = Module::Get();
......
Instance* instance = module_singleton->InstanceForPPInstance(pp_instance);
......
instance->DidChangeView(View(view_resource));
}
这个函数定义在文件external/chromium_org/ppapi/cpp/module.cc中。
函数Instance_DidChangeView首先调用Module类的静态成员函数Get获得当前Plugin进程中的一个pp::Module单例对象。从前面Chromium插件(Plugin)模块(Module)加载过程分析一文可以知道,这个pp::Module单例对象描述的就是在当前Plugin进程中加载的Plugin Module。
从前面Chromium插件(Plugin)实例(Instance)创建过程分析一文可以知道,一个Plugin Module创建的所有Plugin Instance都会以它们的ID为键值,保存在一个std::map中。因此,函数Instance_DidChangeView可以在这个std::map中找到与参数pp_instance对应的Plugin Instance,即一个pp::Instance对象。这个通过调用前面获得的pp::Module单例对象的成员函数InstanceForPPInstance实现的。获得了目标pp::Instance对象之后,就可以调用它的成员函数DidChangeView,以便通知它视图大小发生了变化。
我们在开发一个Plugin的时候,会自定义一个pp::Instance类。例如,在前面Chromium插件(Plugin)机制简要介绍和学习计划一文提到的GLES2 Example,它自定义的pp::Instance类为GLES2DemoInstance。自定义的GLES2DemoInstance类是从pp::Instance类继承下来的,并且会重写成员函数DidChangeView。这意味着接下来GLES2DemoInstance类的成员函数DidChangeView会被调用。
GLES2DemoInstance类的成员函数DidChangeView的实现如下所示:
void GLES2DemoInstance::DidChangeView(
const pp::Rect& position, const pp::Rect& clip_ignored) {
......
plugin_size_ = position.size();
// Initialize graphics.
InitGL(0);
}
这个函数定义在文件external/chromium_org/ppapi/examples/gles2/gles2.cc中。
参数position描述的一个pp::Rect对象记录了当前正在处理的Plugin Instance的当前视图大小。GLES2DemoInstance类的成员函数DidChangeView首先将这个视图大小记录在成员变量plugin_size_中,接下来又调用另外一个成员函数InitGL初始化一个OpenGL环境。
GLES2DemoInstance类的成员函数InitGL的实现如下所示:
void GLES2DemoInstance::InitGL(int32_t result) {
......
if (context_) {
context_->ResizeBuffers(plugin_size_.width(), plugin_size_.height());
return;
}
int32_t context_attributes[] = {
PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8,
PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8,
PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8,
PP_GRAPHICS3DATTRIB_RED_SIZE, 8,
PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0,
PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0,
PP_GRAPHICS3DATTRIB_SAMPLES, 0,
PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0,
PP_GRAPHICS3DATTRIB_WIDTH, plugin_size_.width(),
PP_GRAPHICS3DATTRIB_HEIGHT, plugin_size_.height(),
PP_GRAPHICS3DATTRIB_NONE,
};
context_ = new pp::Graphics3D(this, context_attributes);
......
assert(BindGraphics(*context_));
......
FlickerAndPaint(0, true);
}
这个函数定义在文件external/chromium_org/ppapi/examples/gles2/gles2.cc中。
初始化Plugin的OpenGL环境需要执行两个操作:
1. 创建一个OpenGL上下文。这个OpenGL上下文用一个pp::Graphics3D对象描述。
2. 将创建出来的OpenGL上下文与Plugin进行绑定,也就是将它指定为Plugin当前使用的OpenGL上下文。这可以通过调用父类pp::Instance继承下来的成员函数BindGraphics来完成。
GLES2DemoInstance类的成员变量context_就是用来描述Plugin当前使用的OpenGL上下文。如果这个OpenGL上下文还没有创建,那么GLES2DemoInstance类的成员函数InitGL就会根据Plugin当前的视图大小进行创建。否则的话,就只会改变这个OpenGL上下文描述的绘图缓冲区的大小。
完成以上两个操作之后,GLES2DemoInstance类的成员函数InitGL就会调用另外一个成员函数FlickerAndPaint渲染Plugin的视图。渲染出来的内容最后就会合成在网页的UI中显示出来。
接下来我们先分析OpenGL上下文的创建过程,也就是一个pp::Graphics3D对象的创建过程,我们pp::Graphics3D类的构造函数开始分析,它的实现如下所示:
Graphics3D::Graphics3D(const InstanceHandle& instance,
const int32_t attrib_list[]) {
if (has_interface<PPB_Graphics3D_1_0>()) {
PassRefFromConstructor(get_interface<PPB_Graphics3D_1_0>()->Create(
instance.pp_instance(), 0, attrib_list));
}
}
这个函数定义在文件external/chromium_org/ppapi/cpp/graphics_3d.cc中。
pp::Graphics3D类的构造函数首行调用模板函数has_interface<PPB_Graphics3D_1_0>检查在当前Plugin进程中加载的Plugin Module是否支持PPB_GRAPHICS_3D_INTERFACE_1_0。如果支持的话,那么就会再调用另外一个模板函数get_interface<PPB_Graphics3D_1_0>获得这个PPB_GRAPHICS_3D_INTERFACE_1_0接口。获得了PPB_GRAPHICS_3D_INTERFACE_1_0接口之后,就可以调用它提供的函数Create请求Render进程创建一个OpenGL上下文了。
从后面的分析我们可以知道,PPB_GRAPHICS_3D_INTERFACE_1_0接口提供的函数Create创建出来的OpenGL上下文用一个proxy::proxy::Graphics3D对象描述,不过它返回给调用者的是分配给proxy::proxy::Graphics3D对象的一个资源ID。pp::Graphics3D类的构造函数会调用从父类pp::Resource继承下来的成员函数PassRefFromConstructor将这个资源ID保存在成员变量pp_resource_中,如下所示:
void Resource::PassRefFromConstructor(PP_Resource resource) {
PP_DCHECK(!pp_resource_);
pp_resource_ = resource;
}
这个函数定义在文件external/chromium_org/ppapi/cpp/resource.cc中。
以后通过调用pp::Resource类的成员函数pp_resource即可以获得这个资源ID,如下所示:
class Resource {
public:
......
PP_Resource pp_resource() const { return pp_resource_; }
......
};
这个函数定义在文件external/chromium_org/ppapi/cpp/resource.h中。
回到 pp::Graphics3D类的构造函数中,接下来我们首先分析PPB_GRAPHICS_3D_INTERFACE_1_0接口的获取过程,也就是模板函数get_interface<PPB_Graphics3D_1_0>的实现,如下所示:
template <typename T> inline T const* get_interface() {
static T const* funcs = reinterpret_cast<T const*>(
pp::Module::Get()->GetBrowserInterface(interface_name<T>()));
return funcs;
}
这个模板函数定义在文件external/chromium_org/ppapi/cpp/module_impl.h中。
这里的模板参数T为PPB_Graphics3D_1_0,展开后得到模板函数get_interface<PPB_Graphics3D_1_0>的具体实现为:
inline PPB_Graphics3D_1_0 const* get_interface() {
static PPB_Graphics3D_1_0 const* funcs = reinterpret_cast<PPB_Graphics3D_1_0 const*>(
pp::Module::Get()->GetBrowserInterface(interface_name<PPB_Graphics3D_1_0>()));
return funcs;
}
它首先会调用另外一个模板函数interface_name<PPB_Graphics3D_1_0>获得要获取的接口名称,接着再调用前面提到的pp::Module类的静态成员函数Get获得当前Plugin进程中的一个pp::Module单例对象。有了这个pp::Module单例对象之后,就可以调用它的成员函数GetBrowserInterface根据名称获得接口。
我们首先分析模板函数interface_name<PPB_Graphics3D_1_0>的实现,以便知道要获取的接口名称是什么,如下所示:
template <> const char* interface_name<PPB_Graphics3D_1_0>() {
return PPB_GRAPHICS_3D_INTERFACE_1_0;
}
这个模板函数定义在文件ppapi/cpp/graphics_3d.cc中。
从这里可以看到,要获取的接口名称为PPB_GRAPHICS_3D_INTERFACE_1_0,也就我们要获取的是PPB_GRAPHICS_3D_INTERFACE_1_0接口。这个接口可以通过调用前面获得的pp::Module单例对象的GetBrowserInterface获得。
在前面Chromium插件(Plugin)模块(Module)加载过程分析一文中,我们已经分析过了pp::Module类的GetBrowserInterface的实现,并且我们也知道,在Plugin进程中,PPB_GRAPHICS_3D_INTERFACE_1_0接口是由一个PPB_Graphics3D_1_0对象实现的。这个PPB_Graphics3D_1_0对象的定义如下所示:
const PPB_Graphics3D_1_0 g_ppb_graphics3d_thunk_1_0 = {
&GetAttribMaxValue,
&Create,
&IsGraphics3D,
&GetAttribs,
&SetAttribs,
&GetError,
&ResizeBuffers,
&SwapBuffers
};
这个对象定义在文件external/chromium_org/ppapi/thunk/ppb_graphics_3d_thunk.cc中。
这个PPB_Graphics3D_1_0对象的成员变量Create是一个函数指针。这个函数指针指向了函数Create。这个函数也是定义在文件ppb_graphics_3d_thunk.cc中。
回到Graphics3D类的构造函数中,现在我们就可以知道,它实际上是通过调用上述函数Create请求Render进程为当前正在处理的Plugin创建一个OpenGL上下文的,如下所示:
PP_Resource Create(PP_Instance instance,
PP_Resource share_context,
const int32_t attrib_list[]) {
......
EnterResourceCreation enter(instance);
......
return enter.functions()->CreateGraphics3D(instance,
share_context,
attrib_list);
}
这个函数定义在文件external/chromium_org/ppapi/thunk/ppb_graphics_3d_thunk.cc中。
OpenGL上下文属于一种资源。在Plugin中,创建资源要使用到接口API_ID_RESOURCE_CREATION。函数Create通过构造一个EnterResourceCreation对象来封装对接口API_ID_RESOURCE_CREATION的调用。封装过程如下所示:
EnterResourceCreation::EnterResourceCreation(PP_Instance instance)
: EnterBase(),
functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
......
}
这个函数定义在文件external/chromium_org/ppapi/thunk/enter.cc中。
在Plugin进程中,存在一个PluginGlobals单例对象。这个PluginGlobals单例对象可以通过调用PpapiGlobals类的静态成员函数获得。注意,PluginGlobals类是从PpapiGlobals继承下来的。
获得上述PluginGlobals单例对象之后,EnterResourceCreation类的构造函数就调用它的成员函数GetResourceCreationAPI获得一个API_ID_RESOURCE_CREATION接口,如下所示:
thunk::ResourceCreationAPI* PluginGlobals::GetResourceCreationAPI(
PP_Instance instance) {
PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
if (dispatcher)
return dispatcher->GetResourceCreationAPI();
return NULL;
}
这个函数定义在文件external/chromium_org/ppapi/proxy/plugin_globals.cc中。
从前面Chromium的Plugin进程启动过程分析一文可以知道,在Plugin进程中加载的Plugin Module对应有一个PluginDispatcher对象。这个PluginDispatcher对象与运行在Render进程中的HostDispatcher对象对应。所有属于该Plugin Module的Instance都使用相同的PluginDispatcher对象与Render进程通信。
PluginGlobals类的成员函数GetResourceCreationAPI首先调用PluginDispatcher类的静态成员函数GetForInstance获得与参数instance描述的Plugin Instance对应的PluginDispatcher对象。有了这个PluginDispatcher对象之后,就可以调用它的成员函数GetResourceCreationAPI获得一个API_ID_RESOURCE_CREATION接口,如下所示:
thunk::ResourceCreationAPI* PluginDispatcher::GetResourceCreationAPI() {
return static_cast<ResourceCreationProxy*>(
GetInterfaceProxy(API_ID_RESOURCE_CREATION));
}
这个函数定义在文件external/chromium_org/ppapi/proxy/plugin_dispatcher.cc中。
PluginDispatcher类的成员函数GetResourceCreationAPI调用另外一个成员函数GetInterfaceProxy获得一个API_ID_RESOURCE_CREATION接口。PluginDispatcher类的成员函数GetInterfaceProxy是从父类Dispatcher继承下来的,在前面Chromium插件(Plugin)实例(Instance)创建过程分析一文中,我们已经分析过它的实现了。
结合前面Chromium插件(Plugin)模块(Module)加载过程分析一文,我们可以知道,在Plugin进程中,API_ID_RESOURCE_CREATION接口被指定为ResourceCreationProxy类的静态成员函数Create,Dispatcher类的成员函数GetInterfaceProxy将会调用这个函数创建一个ResourceCreationProxy对象,如下所示:
InterfaceProxy* ResourceCreationProxy::Create(Dispatcher* dispatcher) {
return new ResourceCreationProxy(dispatcher);
}
这个函数定义在文件external/chromium_org/ppapi/proxy/resource_creation_proxy.cc中。
参数dispatcher指向的是一个PluginDispatcher对象。这个PluginDispatcher对象就是在前面分析的PluginGlobals类的成员函数GetResourceCreationAPI中获取的PluginDispatcher对象。ResourceCreationProxy类的静态成员函数Create使用该PluginDispatcher对象创建了一个ResourceCreationProxy对象,并且返回给最初的调用者,也就是EnterResourceCreation类的构造函数。EnterResourceCreation类的构造函数又会将这个ResourceCreationProxy对象保存在成员变量functions_中。
这一步执行完成后,回到前面分析的函数Create中。这时候就它构造了一个EnterResourceCreation对象,并且这个EnterResourceCreation对象的成员变量functions_指向了一个ResourceCreationProxy对象。
函数Create接下来会调用上述EnterResourceCreation对象的成员函数functions它的成员变量functions_指向的ResourceCreationProxy对象。有了这个ResourceCreationProxy对象之后,就可以调用它的成员函数CreateGraphics3D请求Render进程为当前正在处理的Plugin Instance创建一个OpenGL上下文了。
ResourceCreationProxy类的成员函数CreateGraphics3D的实现如下所示:
PP_Resource ResourceCreationProxy::CreateGraphics3D(
PP_Instance instance,
PP_Resource share_context,
const int32_t* attrib_list) {
return PPB_Graphics3D_Proxy::CreateProxyResource(
instance, share_context, attrib_list);
}
这个函数定义在文件external/chromium_org/ppapi/proxy/resource_creation_proxy.cc中。
ResourceCreationProxy类的成员函数CreateGraphics3D调用PPB_Graphics3D_Proxy类的静态成员函数CreateProxyResource请求Render进程为当前正在处理的Plugin Instance创建一个OpenGL上下文,如下所示:
PP_Resource PPB_Graphics3D_Proxy::CreateProxyResource(
PP_Instance instance,
PP_Resource share_context,
const int32_t* attrib_list) {
PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
......
HostResource share_host;
gpu::gles2::GLES2Implementation* share_gles2 = NULL;
if (share_context != 0) {
EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
if (enter.failed())
return PP_ERROR_BADARGUMENT;
PPB_Graphics3D_Shared* share_graphics =
static_cast<PPB_Graphics3D_Shared*>(enter.object());
share_host = share_graphics->host_resource();
share_gles2 = share_graphics->gles2_impl();
}
std::vector<int32_t> attribs;
if (attrib_list) {
for (const int32_t* attr = attrib_list;
attr[0] != PP_GRAPHICS3DATTRIB_NONE;
attr += 2) {
attribs.push_back(attr[0]);
attribs.push_back(attr[1]);
}
}
attribs.push_back(PP_GRAPHICS3DATTRIB_NONE);
HostResource result;
dispatcher->Send(new PpapiHostMsg_PPBGraphics3D_Create(
API_ID_PPB_GRAPHICS_3D, instance, share_host, attribs, &result));
......
scoped_refptr<Graphics3D> graphics_3d(new Graphics3D(result));
if (!graphics_3d->Init(share_gles2))
return 0;
return graphics_3d->GetReference();
}
这个函数定义在文件external/chromium_org/ppapi/proxy/ppb_graphics_3d_proxy.cc中。
PPB_Graphics3D_Proxy类的静态成员函数CreateProxyResource首先调用PluginDispatcher类的静态成员函数GetForInstance获得与参数instance描述的Plugin Instance对应的一个PluginDispatcher对象。后面将会通过这个PluginDispatcher对象向Render进程发送一个类型为PpapiHostMsg_PPBGraphics3D_Create的IPC消息,用来请求Render进程创建一个OpenGL上下文了。
类型为PpapiHostMsg_PPBGraphics3D_Create的IPC消息传递了4个参数给Render进程,分别是:
1. API_ID_PPB_GRAPHICS_3D,表示Render进程要将该IPC消息分发给API_ID_PPB_GRAPHICS_3D接口处理。
2. instance,描述要创建OpenGL上下文的Plugin Instance。
3. share_host,描述另外一个OpenGL上下文,新创建的OpenGL上下文要与它在同一个共享组中。
4. attribs,描述新创建的OpenGL上下文应具有的属性。
Render进程根据要求为目标Plugin Instance创建了一个OpenGL上下文之后,会为该OpenGL上下文分配一个ID,并且会将该ID返回给Plugin进程。Plugin进程再将这个ID封装在一个HostResource对象,然后将这个HostResource对象返回给PPB_Graphics3D_Proxy类的静态成员函数CreateProxyResource。
PPB_Graphics3D_Proxy类的静态成员函数CreateProxyResource再根据获得的HostResource对象创建一个ppapi::proxy::Graphics3D对象,并且调用它的成员函数Init对它进行初始化,相当于是对新创建的OpenGL上下文进行初始化。初始化完成后,就可以进行使用了。
新创建的Graphics3D对象初始化完成之后,PPB_Graphics3D_Proxy类的静态成员函数CreateProxyResource再调用它的成员函数GetReference获得一个类型为PP_Resource的引用。以后通过这个引用就可以使用该Graphics3D对象,也就是新创建的OpenGL上下文。
接下来,我们首先分析Render进程为目标Plugin Instance创建OpenGL上下文的过程,即处理类型为PpapiHostMsg_PPBGraphics3D_Create的IPC消息的过程,然后再分析新创建的OpenGL上下文在Plugin进程的初始化过程。
前面提到,Render进程会将类型为PpapiHostMsg_PPBGraphics3D_Create的IPC消息分发给API_ID_PPB_GRAPHICS_3D接口处理。从前面Chromium插件(Plugin)实例(Instance)创建过程分析一文可以知道,在Render进程中,API_ID_PPB_GRAPHICS_3D接口是由一个PPB_Graphics3D_Proxy对象实现的,也就是Render进程会将类型为PpapiHostMsg_PPBGraphics3D_Create的IPC消息分发给该PPB_Graphics3D_Proxy对象处理,这是通过调用它的成员函数OnMessageReceived实现的。
类型为PpapiHostMsg_PPBGraphics3D_Create的IPC消息在Render进程中的分发过程类似我们在前面Chromium插件(Plugin)实例(Instance)创建过程分析一文提到的类型为PpapiMsg_PPPInstance_DidCreate的IPC消息在Plugin进程中的分发过程,因此这里不再详细描述。
接下来我们继续分析PPB_Graphics3D_Proxy类的成员函数OnMessageReceived处理类型为PpapiHostMsg_PPBGraphics3D_Create的IPC消息的过程,如下所示:
bool PPB_Graphics3D_Proxy::OnMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PPB_Graphics3D_Proxy, msg)
#if !defined(OS_NACL)
IPC_MESSAGE_HANDLER(PpapiHostMsg_PPBGraphics3D_Create,
OnMsgCreate)
......
#endif // !defined(OS_NACL)
......
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
// FIXME(brettw) handle bad messages!
return handled;
}
这个函数定义在文件external/chromium_org/ppapi/proxy/ppb_graphics_3d_proxy.cc中。
从这里可以看到,PPB_Graphics3D_Proxy类的成员函数OnMessageReceived将类型为PpapiHostMsg_PPBGraphics3D_Create的IPC消息分发给另外一个成员函数OnMsgCreate处理,如下所示:
void PPB_Graphics3D_Proxy::OnMsgCreate(PP_Instance instance,
HostResource share_context,
const std::vector<int32_t>& attribs,
HostResource* result) {
......
thunk::EnterResourceCreation enter(instance);
if (enter.succeeded()) {
result->SetHostResource(
instance,
enter.functions()->CreateGraphics3DRaw(instance,
share_context.host_resource(),
&attribs.front()));
}
}
这个函数定义在文件external/chromium_org/ppapi/proxy/ppb_graphics_3d_proxy.cc中。
参数instance要描述的是要创建OpenGL上下文的Plugin Instance在Render进程中对应的Proxy,也就是一个PepperPluginInstanceImpl对象。PPB_Graphics3D_Proxy类的成员函数OnMsgCreate首先是使用它构造一个EnterResourceCreation对象。前面提到,EnterResourceCreation类是用来封装资源创建接口API_ID_RESOURCE_CREATION的。因此,有了前面构造的EnterResourceCreation对象之后,PPB_Graphics3D_Proxy类的成员函数OnMsgCreate就可以使用API_ID_RESOURCE_CREATION接口来为参数instance描述的Plugin Instance创建一个OpenGL上下文了。
EnterResourceCreation对象在Render进程的构造过程与在Plugin进程的构造过程是不一样的。前面我们已经分析过EnterResourceCreation对象在Plugin进程的构造过程,接下来我们继续分析EnterResourceCreation对象在Render进程的构造过程,以便了解Render进程是如何实现API_ID_RESOURCE_CREATION接口的。
我们从EnterResourceCreation类的构造函数开始分析EnterResourceCreation对象在Render进程的构造过程,它的实现如下所示:
EnterResourceCreation::EnterResourceCreation(PP_Instance instance)
: EnterBase(),
functions_(PpapiGlobals::Get()->GetResourceCreationAPI(instance)) {
......
}
这个函数定义在文件external/chromium_org/ppapi/thunk/enter.cc中。
在Render进程中,存在一个HostGlobals单例对象。这个HostGlobals单例对象可以通过调用PpapiGlobals类的静态成员函数Get获得。HostGlobals类与前面提到的PluginGlobals类一样,都是从PpapiGlobals类继承下来的。
获得上述HostGlobals单例对象之后,EnterResourceCreation类的构造函数就调用它的成员函数GetResourceCreationAPI获得一个API_ID_RESOURCE_CREATION接口,如下所示:
ppapi::thunk::ResourceCreationAPI* HostGlobals::GetResourceCreationAPI(
PP_Instance pp_instance) {
PepperPluginInstanceImpl* instance = GetInstance(pp_instance);
if (!instance)
return NULL;
return &instance->resource_creation();
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/host_globals.cc中。
HostGlobals类的成员函数GetResourceCreationAPI首先调用成员函数GetInstance获得参数pp_instance描述的一个Plugin Instance Proxy,也就是一个PepperPluginInstanceImpl对象。有了这个PepperPluginInstanceImpl对象之后,就可以调用它的成员函数resource_creation获得一个API_ID_RESOURCE_CREATION接口,如下所示:
class CONTENT_EXPORT PepperPluginInstanceImpl
: public base::RefCounted<PepperPluginInstanceImpl>,
public NON_EXPORTED_BASE(PepperPluginInstance),
public ppapi::PPB_Instance_Shared,
public NON_EXPORTED_BASE(cc::TextureLayerClient),
public RenderFrameObserver {
public:
......
ppapi::thunk::ResourceCreationAPI& resource_creation() {
return *resource_creation_.get();
}
......
private:
......
scoped_ptr<ppapi::thunk::ResourceCreationAPI> resource_creation_;
......
};
这个函数定义在文件external/chromium_org/content/renderer/pepper/pepper_plugin_instance_impl.h中。
PepperPluginInstanceImpl类的成员变量resource_creation_指向的是一个PepperInProcessResourceCreation对象。PepperPluginInstanceImpl类的成员函数resource_creation将这个PepperInProcessResourceCreation对象返回给调用者。这意味在Render进程中,API_ID_RESOURCE_CREATION接口是通过PepperInProcessResourceCreation类实现的。
这一步执行完成后,回到前面分析的PPB_Graphics3D_Proxy类的成员函数OnMsgCreate中。这时候就它构造了一个EnterResourceCreation对象,并且这个EnterResourceCreation对象的成员变量functions_指向了一个PepperInProcessResourceCreation对象。
PPB_Graphics3D_Proxy类的成员函数OnMsgCreate接下来会调用上述EnterResourceCreation对象的成员函数functions它的成员变量functions_指向的PepperInProcessResourceCreation对象。有了这个PepperInProcessResourceCreation对象之后,就可以调用它的成员函数CreateGraphics3DRaw为参数pp_instance描述的Plugin Instance创建一个OpenGL上下文。
PepperInProcessResourceCreation类的成员函数CreateGraphics3DRaw是从父类ResourceCreationImpl继承下来的,它的实现如下所示:
PP_Resource ResourceCreationImpl::CreateGraphics3DRaw(
PP_Instance instance,
PP_Resource share_context,
const int32_t* attrib_list) {
return PPB_Graphics3D_Impl::CreateRaw(instance, share_context, attrib_list);
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/resource_creation_impl.cc中。
ResourceCreationImpl类的成员函数CreateGraphics3DRaw调用PPB_Graphics3D_Impl类的静态成员函数CreateRaw为参数pp_instance描述的Plugin Instance创建一个OpenGL上下文,如下所示:
PP_Resource PPB_Graphics3D_Impl::CreateRaw(PP_Instance instance,
PP_Resource share_context,
const int32_t* attrib_list) {
PPB_Graphics3D_API* share_api = NULL;
if (share_context) {
EnterResourceNoLock<PPB_Graphics3D_API> enter(share_context, true);
if (enter.failed())
return 0;
share_api = enter.object();
}
scoped_refptr<PPB_Graphics3D_Impl> graphics_3d(
new PPB_Graphics3D_Impl(instance));
if (!graphics_3d->InitRaw(share_api, attrib_list))
return 0;
return graphics_3d->GetReference();
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/ppb_graphics_3d_impl.cc中。
在Render进程中,Plugin使用的OpenGL上下文通过一个PPB_Graphics3D_Impl对象描述。因此,PPB_Graphics3D_Impl类的静态成员函数CreateRaw会创建一个PPB_Graphics3D_Impl对象,并且调用它的成员函数InitRaw对它进行初始化,也就是对它描述的OpenGL上下文进行初始化。初始化完成后,就会获得它的一个PP_Resource引用。这个引用将会返回给Plugin进程。Plugin进程以后就可以通过这个引用来使用前面创建出来的OpenGL上下文了。
接下来我们继续分析Render进程为Plugin创建的OpenGL上下文的初始化过程,也就是PPB_Graphics3D_Impl类的成员函数InitRaw的实现,如下所示:
bool PPB_Graphics3D_Impl::InitRaw(PPB_Graphics3D_API* share_context,
const int32_t* attrib_list) {
......
RenderThreadImpl* render_thread = RenderThreadImpl::current();
......
channel_ = render_thread->EstablishGpuChannelSync(
CAUSE_FOR_GPU_LAUNCH_PEPPERPLATFORMCONTEXT3DIMPL_INITIALIZE);
......
command_buffer_ = channel_->CreateOffscreenCommandBuffer(
surface_size, share_buffer, attribs, GURL::EmptyGURL(), gpu_preference);
......
return true;
}
这个函数定义在文件external/chromium_org/content/renderer/pepper/ppb_graphics_3d_impl.cc中。
PPB_Graphics3D_Impl类的成员函数InitRaw首先调用RenderThreadImpl类的静态成员函数current获得一个RenderThreadImpl对象。这个RenderThreadImpl对象描述的是当前Render进程的Render线程。有了这个RenderThreadImpl对象之后,就可以调用它的成员函数EstablishGpuChannelSync创建一个GPU通道,也就是一个GpuChannelHost对象。这个GPU通道的详细创建过程,可以参考前面Chromium的GPU进程启动过程分析一文。
PPB_Graphics3D_Impl类的成员函数InitRaw接下来又会调用前面创建出来的GpuChannelHost对象的成员函数CreateOffscreenCommandBuffer请求GPU进程为其创建一个离屏渲染类型的OpenGL上下文。这个离屏渲染类型的OpenGL上下文是通过一个CommandBufferProxyImpl对象描述的。Render进程请求GPU进程创建OpenGL上下文的过程,可以参考前面Chromium硬件加速渲染的OpenGL上下文创建过程分析一文。以后Plugin执行GPU命令时,将会作用在上述离屏渲染的OpenGL上下文中。
这一步执行完成后,Render进程就为Plugin创建了一个OpenGL上下文。这个OpenGL上下文通过一个PPB_Graphics3D_Impl对象描述。这个PPB_Graphics3D_Impl对象会被分配一个ID,并且这个ID会返回给Plugin进程。如前所述,Plugin进程获得了这个ID之后,就会将其封装在一个Graphics3D对象中。这个Graphics3D对象是用来Plugin进程描述一个OpenGL上下文的。
回到前面分析的PPB_Graphics3D_Proxy类的成员函数CreateProxyResource中,这时候它就获得了一个OpenGL上下文之后。这个OpenGL上下文在Plugin进程中同样需要进行初始化。如前所述,这是通过调用ppapi::proxy::Graphics3D类的成员函数Init实现的。接下我们就继续分析Plugin进程初始化一个OpenGL上下文的过程,也就是分析ppapi::proxy::Graphics3D类的成员函数Init的实现,如下所示:
bool Graphics3D::Init(gpu::gles2::GLES2Implementation* share_gles2) {
......
command_buffer_.reset(
new PpapiCommandBufferProxy(host_resource(), dispatcher));
return CreateGLES2Impl(kCommandBufferSize, kTransferBufferSize,
share_gles2);
}
这个函数定义在文件external/chromium_org/ppapi/proxy/ppb_graphics_3d_proxy.cc中。
ppapi::proxy::Graphics3D类的成员函数Init首
以上是关于Chromium插件(Plugin)执行3D渲染的过程分析的主要内容,如果未能解决你的问题,请参考以下文章
Android WebView启动Chromium渲染引擎的过程分析
Error: Chromium revision is not downloaded. Failed to download Chromium