WebKit之GPU进程启动过程分析

Chromium除了有Browser进程和Render进程,还有GPU进程。GPU进程负责Chromium的GPU操作,例如Render进程通过GPU进程离屏渲染网页,Browser进程也是通过GPU进程将离屏渲染好的网页显示在屏幕上。Chromium之所以将GPU操作运行在独立进程中,是考虑到稳定性问题。毕竟GPU操作是硬件相关操作,硬件的差异性会引发一定的不稳性。本文分析GPU进程的启动过程。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

       GPU进程由Browser进程负责启动,它的启动过程与Render进程的启动过程是类似的,因此在阅读本文之前,最好先阅读Chromium的Render进程启动过程分析一文。不过,GPU进程启动之后,Browser进程会与它建立两个IPC通道。一个IPC通道用来传输普通的IPC消息,另一个IPC通道专门用来执行GPU操作,称为GPU通道。类似地,Render进程需要执行GPU操作时,也会通过Browser进程与GPU进程建立一个专门用来执行GPU操作的IPC通道。Render进程之所以要通过Browser进程间接地与GPU进程建立GPU通道,是因为GPU进程是由Browser进程启动的,Render进程对它一无所知。

       以上描述的Browser进程、Render进程和GPU进程的关系可以通过图1概括,如下所示:

WebKit之GPU进程启动过程分析_第1张图片

图1 Browser进程、Render进程和GPU进程的关系

       在图1中,Browser进程与Render进程的IPC通道的建立过程可以参考前面Chromium的Render进程启动过程分析一文,本文只分析以下三部分内容:

       1. Browser进程与GPU进程的IPC通道的建立过程。 

       2. Browser进程与GPU进程的GPU通道的建立过程。

       3. Render进程与GPU进程的GPU通道的建立过程。

       Browser进程通过一个GpuProcessHost对象描述由它启动的GPU进程。GPU进程启动起来之后,会创建一个GpuProcess对象用来与Browser进程进行IPC。接下来,Browser进程中的GpuProcessHost对象会通过已经建立起来的IPC通道请求GPU进程中创建一个GPU通道,以便以后可以执行GPU操作。

       Render进程需要通过GPU渲染网页的时候,会通过之前与Browser进程建立的IPC通道请求Browser进程为它创建一个GPU通道,并且将该GPU通道封装在一个WebGraphicsContext3DCommandBufferImpl对象,以后就可以通过该WebGraphicsContext3DCommandBufferImpl对象向GPU进程请求执行GPU操作了。

       GPU进程在启动的过程中,也会像Browser进程和Render进程一样,启动一个IO线程,专门用来执行IPC。以后每当GPU进程通过上述IPC通道接收到一个创建GPU通道的请求的时候,都会在内部创建一个OpenGL上下文。这个OpenGL上下文通过一个GLContext对象描述。这样在GPU进程中,就会存在若干个OpenGL上下文。这些OpenGL上下文都运行在同一个线程中,这个线程称为GPU Child Thread。这样就会涉及到一个OpenGL上下文调度问题,即每当GPU进程接收到一个GPU操作请求时,都要先切换到请求的GPU操作所在的OpenGL上下文,然后才能执行请求的GPU操作。关于GPU进程的OpenGL上下文调度问题,我们在下一系列的文章中再详细分析。

       接下来,我们就先分析Browser进程启动GPU进程的过程。这个过程主要是涉及到Browser进程和GPU进程的IPC通道的建立过程。

       在前面Chromium多线程模型设计和实现分析一文中,我们提到,Browser进程,也就是Chromium应用程序的主进程,在启动的时候,会调用BrowserMainLoop类的成员函数CreateStartupTasks。BrowserMainLoop类的成员函数CreateStartupTasks会请求启动一个GPU进程,相关的代码如下所示:

[cpp]  view plain copy
  1. void BrowserMainLoop::CreateStartupTasks() {  
  2.   ......  
  3.   
  4.   // First time through, we really want to create all the tasks  
  5.   if (!startup_task_runner_.get()) {  
  6.     startup_task_runner_ = make_scoped_ptr(new StartupTaskRunner(    
  7.         base::Bind(&BrowserStartupComplete),    
  8.         base::MessageLoop::current()->message_loop_proxy()));   
  9.     ......  
  10.   
  11.     StartupTask browser_thread_started = base::Bind(  
  12.         &BrowserMainLoop::BrowserThreadsStarted, base::Unretained(this));  
  13.     startup_task_runner_->AddTask(browser_thread_started);  
  14.   
  15.     ......  
  16.   
  17.     if (BrowserMayStartAsynchronously()) {  
  18.       startup_task_runner_->StartRunningTasksAsync();  
  19.     }  
  20.   }  
  21.   
  22.   if (!BrowserMayStartAsynchronously()) {  
  23.     ......  
  24.     startup_task_runner_->RunAllTasksNow();  
  25.   }  
  26. }  

       这个函数定义在文件external/chromium_org/content/browser/browser_main_loop.cc中。

       BrowserMainLoop类的成员函数CreateStartupTasks首先是会创建一个StartupTaskRunner对象,并且保存在成员变量startup_task_runner_中。这个StartupTaskRunner对象封装了当前线程的一个消息循环,因此通过它可以向当前线程的消息队列发送消息。当前线程即为Browser进程的主线程,因此有了这个StartupTaskRunner对象之后,接下来可以向其主线程的消息队列发送消息。

       BrowserMainLoop类的成员函数CreateStartupTasks接下来创建了一个StartupTask,这个StartupTask绑定的函数为BrowserMainLoop类的成员函数BrowserThreadsStarted,用来执行一个Browser线程启动完毕任务,并且会保存在前面创建的一个StartupTaskRunner对象的内部等待执行。

       最后,取决于Browser进程使用同步还是异步方式启动,BrowserMainLoop类的成员函数CreateStartupTasks使用不同的方式来执行保存在成员变量startup_task_runner_指向的一个StartupTaskRunner对象中的StartupTask:

       1. 如果是使用同步方式启动,那么就调用上述StartupTaskRunner对象的成员函数RunAllTasksNow立即执行保存在它里面的各个StartupTask对象所描述的任务。

       2. 如果是使用异步方式启动,那么就调用上述StartupTaskRunner对象的成员函数StartRunningTasksAsync向主线程的消息队列发送一个消息,当该消息被处理时,再执行保存在上述StartupTaskRunner对象里面的各个StartupTask对象所描述的任务。

       无论是同步方式,还是异步方式,最终都会在主线程中调用BrowserMainLoop类的成员函数BrowserThreadsStarted,与GPU进程启动相关的代码如下所示:

[cpp]  view plain copy
  1. int BrowserMainLoop::BrowserThreadsStarted() {  
  2.   ......  
  3.   
  4.   bool initialize_gpu_data_manager = true;  
  5. #if defined(OS_ANDROID)  
  6.   // On Android, GLSurface::InitializeOneOff() must be called before initalizing  
  7.   // the GpuDataManagerImpl as it uses the GL bindings. crbug.com/326295  
  8.   if (!gfx::GLSurface::InitializeOneOff()) {  
  9.     ......  
  10.     initialize_gpu_data_manager = false;  
  11.   }  
  12. #endif  
  13.   
  14.   if (initialize_gpu_data_manager)  
  15.     GpuDataManagerImpl::GetInstance()->Initialize();  
  16.   
  17.   bool always_uses_gpu = true;  
  18.   bool established_gpu_channel = false;  
  19. #if defined(USE_AURA) || defined(OS_MACOSX)  
  20.   if (ShouldInitializeBrowserGpuChannelAndTransportSurface()) {  
  21.     established_gpu_channel = true;  
  22.     if (!GpuDataManagerImpl::GetInstance()->CanUseGpuBrowserCompositor()) {  
  23.       established_gpu_channel = always_uses_gpu = false;  
  24.     }  
  25.     BrowserGpuChannelHostFactory::Initialize(established_gpu_channel);  
  26.     ......  
  27.   }  
  28. #elif defined(OS_ANDROID)  
  29.   established_gpu_channel = true;  
  30.   BrowserGpuChannelHostFactory::Initialize(established_gpu_channel);  
  31. #endif  
  32.   
  33.   ......  
  34. }  

       这个函数定义在文件external/chromium_org/content/browser/browser_main_loop.cc中。

       在Android平台上,BrowserMainLoop类的成员函数BrowserThreadsStarted首先调用gfx::GLSurface类的静态成员函数InitializeOneOff在当进程中加载合适的OpenGL库,以及创建一个EGLDisplay。这样做有两个原因,一是后面调用GpuDataManagerImpl类的成员函数Initialize时,在Android平台上需要通过加载的OpenGL库来获取GPU信息,二是Android平台的Chromium实际上并没有独立的GPU进程,而是在Browser进程中创建一个GPU线程,不过这个GPU线程起到的作用与GPU进程是一样的。上述第二个原因要求Browser进程要做一些GPU相关的初始化工作,即加载合适的OpenGL库,以及创建一个EGLDisplay,以后创建OpenGL上下文时需要使用到这个EGLDisplay。对于独立GPU进程的情况,上述的GPU初始化也是需要做的。后面我们就会看到,GPU进程在启动的时候,会调用gfx::GLSurface类的静态成员函数InitializeOneOff。

       只有在gfx::GLSurface类的静态成员函数InitializeOneOff的返回值为true,即在当进程中成功加载了合适的OpenGL库之后,BrowserMainLoop类的成员函数Initialize才会被调用,负责检查当前设备使用的GPU及其相关的驱动是否在黑名单中。如果在黑名单中,那么Chromium就不会采用GPU对网页进行硬件加速渲染。这是由于不是所有的GPU都能够很好地支持Chromium进行硬件加速渲染,因此就需要设置一个黑名单,避免在渲染网页的过程中出现错误。一旦不能使用GPU对网页进行硬件加速渲染,那么Chromium就会退而求其次,使用CPU进行渲染。

       如果Chromium在编译时定义了宏USE_AURA,那么就表示要使用GPU对网页进行硬件加速渲染,这时候就可能需要启动GPU进程。AURA是Chromium 35引入的一个窗口管理框架,通过GPU来实现界面上的像按钮、滚动条和对话框等界面控件。但是由于GPU黑名单的存在,因此就不一定能够如愿地使用GPU对网页进行硬件加速渲染。这时候就需要进一步调用函数ShouldInitializeBrowserGpuChannelAndTransportSurface以及GpuDataManagerImpl类的成员函数CanUseGpuBrowserCompositor进行判断。如果最终确定不能够使用GPU对网页进行硬件加速渲染,那么接下来在调用BrowserGpuChannelHostFactory类的静态成员函数Initialize的时候,传递进去的参数就会等于false。对于Mac OS X平台,也会进行相同的处理。

       如果Chromium在编译时没有定义宏USE_AURA,但是当前平台是Android,那么BrowserMainLoop类的成员函数BrowserThreadsStarted就直接将本地变量established_gpu_channel设置为true,并且以其为参数,调用BrowserGpuChannelHostFactory类的静态成员函数Initialize启动一个GPU进程。这表明在Android平台上,要求GPU能够支持Chromium对网页进行硬件加速渲染。

      为了更好地理解上面分析的内容,接下来我们在分析GPU进程的启动过程,也就是BrowserGpuChannelHostFactory类的静态成员函数Initialize之前,先分析以下几个函数:

      1. gfx::GLSurface::InitializeOneOff

      2. GpuDataManagerImpl::Initialize

      3. ShouldInitializeBrowserGpuChannelAndTransportSurface

      4. GpuDataManagerImpl::CanUseGpuBrowserCompositor

      gfx::GLSurface类的静态成员函数InitializeOneOff负责在当前进程中加载合适的OpenGL库,它的实现如下所示:

[cpp]  view plain copy
  1. bool GLSurface::InitializeOneOff() {  
  2.   ......  
  3.   
  4.   std::vector allowed_impls;  
  5.   GetAllowedGLImplementations(&allowed_impls);  
  6.   ......  
  7.   CommandLine* cmd = CommandLine::ForCurrentProcess();  
  8.   
  9.   // The default implementation is always the first one in list.  
  10.   GLImplementation impl = allowed_impls[0];  
  11.   bool fallback_to_osmesa = false;  
  12.   if (cmd->HasSwitch(switches::kOverrideUseGLWithOSMesaForTests)) {  
  13.     impl = kGLImplementationOSMesaGL;  
  14.   } else if (cmd->HasSwitch(switches::kUseGL)) {  
  15.     std::string requested_implementation_name =  
  16.         cmd->GetSwitchValueASCII(switches::kUseGL);  
  17.     if (requested_implementation_name == "any") {  
  18.       fallback_to_osmesa = true;  
  19.     } else if (requested_implementation_name == "swiftshader") {  
  20.       impl = kGLImplementationEGLGLES2;  
  21.     } else {  
  22.       impl = GetNamedGLImplementation(requested_implementation_name);  
  23.       if (std::find(allowed_impls.begin(),  
  24.                     allowed_impls.end(),  
  25.                     impl) == allowed_impls.end()) {  
  26.         ......  
  27.         return false;  
  28.       }  
  29.     }  
  30.   }  
  31.   
  32.   ......  
  33.   
  34.   return InitializeOneOffImplementation(  
  35.       impl, fallback_to_osmesa, gpu_service_logging, disable_gl_drawing);  
  36. }  
       这个函数定义在文件external/chromium_org/ui/gl/gl_surface.cc中。

       gfx::GLSurface类的静态成员函数InitializeOneOff首先是调用另外一个函数GetAllowedGLImplementations获得当前平台所支持的OpenGL实现版本列表。对于Android平台,它的实现如下所示:

[cpp]  view plain copy
  1. void GetAllowedGLImplementations(std::vector* impls) {  
  2.   impls->push_back(kGLImplementationEGLGLES2);  
  3.   impls->push_back(kGLImplementationOSMesaGL);  
  4. }  
       这个函数定义在文件external/chromium_org/ui/gl/gl_implementation_android.cc中。

       从这里可以看到,在Android平台上,Chromium支持两个版本的OpenGL实现,其中一个是kGLImplementationEGLGLES2,另一个是kGLImplementationOSMesaGL。kGLImplementationEGLGLES2描述的OpenGL即为Android系统本身提供的OpenGL实现,这个就是由底层的GPU实现的OpenGL库。kGLImplementationOSMesaGL描述的OpenGL是由Mesa实现的OpenGL库。Mesa是一个开源的OpenGL实现框架,它可以以软件方式模拟GPU硬件加速渲染,也可以通过底层真实的GPU来实现硬件加速渲染。

       回到gfx::GLSurface类的静态成员函数InitializeOneOff中,它获得当前平台所支持的OpenGL实现版本列表之后,取出列表中的第一个版本作为默认版本。从前面的分析就可以知道,对于Android平台,这个默认的OpenGL实现版本就是kGLImplementationEGLGLES2描述的版本。

       接下来,gfx::GLSurface类的静态成员函数InitializeOneOff再根据命令行参数选择最终使用的OpenGL实现版本:

       1. 如果设置了switches::kOverrideUseGLWithOSMesaForTests选项,那么就表示要使用kGLImplementationOSMesaGL描述的OpenGL版本,方便用来测试。

       2. 如果设置了switches::kUseGL选项,那么就根据这个选项的值选择指定的OpenGL实现。不过有两种特殊情况。一是当该选项值等于"any"时,默认使用之前选择的OpenGL实现版本,但是如果不能成功加载该版本的库,那么就改为使用kGLImplementationOSMesaGL描述的OpenGL版本。二是当该选项的值等于"swiftshader"时,使用kGLImplementationEGLGLES2描述的OpenGL版本。SwiftShader是一件纯软件实现的3D渲染引擎工具,由TransGaming公司实现,宣称支持所有的Pixel和Vertex Shader DX9特效,并且可以获得比微软D3D的REF设备(reference rasterizer)快50倍的速度。在Android平台上,没有提供SwiftShader,因此用kGLImplementationEGLGLES2描述的OpenGL版本替代。

       最后,gfx::GLSurface类的静态成员函数InitializeOneOff调用另外一个成员函数InitializeOneOffImplementation在当前进程中加载前面所选择的OpenGL实现版本,它的实现如下所示:

[cpp]  view plain copy
  1. bool GLSurface::InitializeOneOffImplementation(GLImplementation impl,  
  2.                                                bool fallback_to_osmesa,  
  3.                                                bool gpu_service_logging,  
  4.                                                bool disable_gl_drawing) {  
  5.   bool initialized =  
  6.       InitializeStaticGLBindings(impl) && InitializeOneOffInternal();  
  7.   if (!initialized && fallback_to_osmesa) {  
  8.     ClearGLBindings();  
  9.     initialized = InitializeStaticGLBindings(kGLImplementationOSMesaGL) &&  
  10.                   InitializeOneOffInternal();  
  11.   }  
  12.   if (!initialized)  
  13.     ClearGLBindings();  
  14.   
  15.   ......  
  16.   
  17.   return initialized;  
  18. }  
       这个函数定义在文件external/chromium_org/ui/gl/gl_surface.cc中。

       gfx::GLSurface类的静态成员函数InitializeOneOffImplementation首先调用函数InitializeStaticGLBindings加载由参数impl指定的OpenGL实现版本相关的库。如果能成功加载,再调用函数InitializeOneOffInternal在当前进程中创建一个EGLDisplay。如果也能成功创建这个EGLDisplay,那么就说明参数指定的OpenGL实现版本是能够正确使用的。

       如果不能成功加载参数impl指定的OpenGL实现版本相关的库,或者能够成功加载,但是不能成功创建一个EGLDisplay,并且参数fallback_to_osmesa的值为true,那么就如前所述,改为使用kGLImplementationOSMesaGL描述的OpenGL实现版本,也就是由Mesa实现的OpenGL库。

       接下来,我们先分析函数InitializeStaticGLBindings的实现,接着再分析函数InitializeOneOffInternal的实现。

       函数InitializeStaticGLBindings的实现如下所示:

[cpp]  view plain copy
  1. bool InitializeStaticGLBindings(GLImplementation implementation) {  
  2.   ......  
  3.   
  4.   switch (implementation) {  
  5.     case kGLImplementationEGLGLES2: {  
  6.       base::NativeLibrary gles_library =  
  7.           LoadLibraryAndPrintError("libGLESv2.so");  
  8.       ......  
  9.       base::NativeLibrary egl_library = LoadLibraryAndPrintError("libEGL.so");  
  10.       ......  
  11.   
  12.       GLGetProcAddressProc get_proc_address =  
  13.           reinterpret_cast(  
  14.               base::GetFunctionPointerFromNativeLibrary(  
  15.                   egl_library, "eglGetProcAddress"));  
  16.       ......  
  17.   
  18.       SetGLGetProcAddressProc(get_proc_address);  
  19.       AddGLNativeLibrary(egl_library);  
  20.       AddGLNativeLibrary(gles_library);  
  21.       SetGLImplementation(kGLImplementationEGLGLES2);  
  22.   
  23.       InitializeStaticGLBindingsGL();  
  24.       InitializeStaticGLBindingsEGL();  
  25.       ......  
  26.       break;  
  27.     }  
  28.     ......  
  29.   }  
  30.   
  31.   return true;  
  32. }  
       这个函数定义在文件external/chromium_org/ui/gl/gl_implementation_android.cc中。

       从这里就可以看到,与kGLImplementationEGLGLES2描述的OpenGL实现版本相关的库有两个,分别为libGLESv2.so和libEGL.so,前者描述的是OpenGL实现,后者描述的是EGL实现。调用函数LoadLibraryAndPrintError加载了这两个库之后,最后分别调用了函数InitializeStaticGLBindingsGL和InitializeStaticGLBindingsEGL创建了一个RealGLApi接口和一个RealEGLApi接口,这样以后就可以通过这两个接口调用由前面加载的libGLESv2.so和libEGL.so所导出的gl*和egl*函数。

       这一步执行完成之后,回到前面分析的gfx::GLSurface类的静态成员函数InitializeOneOffImplementation中,它接下来调用函数InitializeOneOffInternal创建一个EGLDisplay,以验证前面加载的OpenGL相关的库的正确性。

       函数InitializeOneOffInternal的实现如下所示:

[cpp]  view plain copy
  1. bool GLSurface::InitializeOneOffInternal() {  
  2.   switch (GetGLImplementation()) {  
  3.     case kGLImplementationEGLGLES2:  
  4.       if (!GLSurfaceEGL::InitializeOneOff()) {  
  5.         LOG(ERROR) << "GLSurfaceEGL::InitializeOneOff failed.";  
  6.         return false;  
  7.       }  
  8.     default:  
  9.       break;  
  10.   }  
  11.   return true;  
  12. }  
      这个函数定义在文件external/chromium_org/ui/gl/gl_surface_android.cc中。

      从这里可以看到, 当使用kGLImplementationEGLGLES2描述的OpenGL实现版本时,函数InitializeOneOffInternal调用GLSurfaceEGL类的静态成员函数InitializeOneOff创建一个EGLDisplay。

      GLSurfaceEGL类的静态成员函数InitializeOneOff的实现如下所示:

[cpp]  view plain copy
  1. bool GLSurfaceEGL::InitializeOneOff() {  
  2.   static bool initialized = false;  
  3.   if (initialized)  
  4.     return true;  
  5.   
  6.   g_native_display = GetPlatformDefaultEGLNativeDisplay();  
  7.   g_display = eglGetDisplay(g_native_display);  
  8.   if (!g_display) {  
  9.     ......  
  10.     return false;  
  11.   }  
  12.   
  13.   if (!eglInitialize(g_display, NULL, NULL)) {  
  14.     ......  
  15.     return false;  
  16.   }  
  17.   
  18.   static const EGLint kConfigAttribs[] = {  
  19.     EGL_BUFFER_SIZE, 32,  
  20.     EGL_ALPHA_SIZE, 8,  
  21.     EGL_BLUE_SIZE, 8,  
  22.     EGL_GREEN_SIZE, 8,  
  23.     EGL_RED_SIZE, 8,  
  24.     EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,  
  25.     EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,  
  26.     EGL_NONE  
  27.   };  
  28.   
  29.   ......  
  30.   
  31.   const EGLint* config_attribs = kConfigAttribs;  
  32.   
  33.   ......  
  34.   
  35.   EGLint num_configs;  
  36.   if (!eglChooseConfig(g_display,  
  37.                        config_attribs,  
  38.                        NULL,  
  39.                        0,  
  40.                        &num_configs)) {  
  41.     ......  
  42.     return false;  
  43.   }  
  44.   
  45.   ......  
  46.   
  47.   initialized = true;  
  48.   
  49.   return true;  
  50. }  
       这个函数定义在文件external/chromium_org/ui/gl/gl_surface_egl.cc中。

       从这里可以看出,GLSurfaceEGL类的静态成员函数InitializeOneOff通过调用egl函数eglGetDisplay、eglInitialize和eglChooseConfig创建一个EGLDisplay,并且保存在全局变量g_display中,以后就可以通过这个EGLDisplay来创建OpenGL上下文。实际上,eglGetDisplay、eglInitialize和eglChooseConfig是定义在out/target/product/generic/obj/GYP/shared_intermediates/ui/gl/gl_bindings_autogen_egl.h文件中的三个宏,它们分别定义为前面加载的libEGL.so所导出的三个函数eglGetDisplay、eglInitialize和eglChooseConfig。

       事实上,文件out/target/product/generic/obj/GYP/shared_intermediates/ui/gl/gl_bindings_autogen_egl.h定义了所有的egl*宏,并且这些宏都定义为前面加载的libEGL.so所导出的对应的egl*函数。类似地,文件out/target/product/generic/obj/GYP/shared_intermediates/ui/gl/gl_bindings_autogen_gl.h也定义了所有的gl*宏,并且这些宏都定义为前面加载的libGLESv2.so所导出的对应的gl*函数。

       最后,out/target/product/generic/obj/GYP/shared_intermediates/ui/gl/gl_bindings_autogen_egl.h和out/target/product/generic/obj/GYP/shared_intermediates/ui/gl/gl_bindings_autogen_gl.h也定义了所有的gl*宏,并且这些宏都定义为前面加载的libGLESv2.so所导出的对应的gl*函数。这两个文件又被文件external/chromium_org/ui/gl/gl_bindings.h所included,因此只要include了external/chromium_org/ui/gl/gl_bindings.h文件,就可以直接调用所有的由前面加载的libGLESv2.so和libEGL.so导出的gl*和egl*函数。

       这一步执行完成之后,回到前面分析的BrowserMainLoop类的成员函数BrowserThreadsStarted中,假设gfx::GLSurface类的静态成员函数InitializeOneOff的返回值为true,即它成功加载了OpenGL相关的库,那么接下来就会调用GpuDataManagerImpl类的成员函数Initialize检查设备配置的GPU是否在黑名单列表中。

       GpuDataManagerImpl类的成员函数Initialize的实现如下所示:

[cpp]  view plain copy
  1. void GpuDataManagerImpl::Initialize() {  
  2.   base::AutoLock auto_lock(lock_);  
  3.   private_->Initialize();  
  4. }  
      这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_data_manager_impl.cc中。

      GpuDataManagerImpl类的成员变量private_指向的是一个GpuDataManagerImplPrivate对象,这里调用它的成员函数Initialize检查设备配置的GPU是否在黑名单列表中。

      GpuDataManagerImplPrivate类的成员函数Initialize的实现如下所示:

[cpp]  view plain copy
  1. void GpuDataManagerImplPrivate::Initialize() {  
  2.   ......  
  3.   
  4.   gpu::GPUInfo gpu_info;  
  5.   if (command_line->GetSwitchValueASCII(  
  6.           switches::kUseGL) == gfx::kGLImplementationOSMesaName) {  
  7.     // If using the OSMesa GL implementation, use fake vendor and device ids to  
  8.     // make sure it never gets blacklisted. This is better than simply  
  9.     // cancelling GPUInfo gathering as it allows us to proceed with loading the  
  10.     // blacklist below which may have non-device specific entries we want to  
  11.     // apply anyways (e.g., OS version blacklisting).  
  12.     gpu_info.gpu.vendor_id = 0xffff;  
  13.     gpu_info.gpu.device_id = 0xffff;  
  14.   
  15.     // Also declare the driver_vendor to be osmesa to be able to specify  
  16.     // exceptions based on driver_vendor==osmesa for some blacklist rules.  
  17.     gpu_info.driver_vendor = gfx::kGLImplementationOSMesaName;  
  18.   } else {  
  19.     ......  
  20.     gpu::CollectBasicGraphicsInfo(&gpu_info);  
  21.   }  
  22.   
  23.   std::string gpu_blacklist_string;  
  24.   std::string gpu_driver_bug_list_string;  
  25.   if (!command_line->HasSwitch(switches::kIgnoreGpuBlacklist) &&  
  26.       !command_line->HasSwitch(switches::kUseGpuInTests)) {  
  27.     gpu_blacklist_string = gpu::kSoftwareRenderingListJson;  
  28.   }  
  29.   if (!command_line->HasSwitch(switches::kDisableGpuDriverBugWorkarounds)) {  
  30.     gpu_driver_bug_list_string = gpu::kGpuDriverBugListJson;  
  31.   }  
  32.   InitializeImpl(gpu_blacklist_string,  
  33.                  gpu_driver_bug_list_string,  
  34.                  gpu_info);  
  35. }  

       这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_data_manager_impl_private.cc中。

       如果Chromium启动时,指定了switches::kUseGL选项,并且将该选项的值设置为gfx::kGLImplementationOSMesaName,那么就意味着要使用Mesa版本的OpenGL库来渲染Chromium的UI。由于这里使用的Mesa版本的OpenGL库是纯软件实现的,因此它不会像GPU版本的OpenGL库一样存在一些不友好特性。也就是说Mesa版本的OpenGL库是可用的,尽管它的性能会差一些。为了避免后面加载GPU黑名单列表时,列表中的某些通用规则会禁用Mesa版本的OpenGL库的某些特性,GpuDataManagerImplPrivate类的成员函数Initialize会手工构造一个GPUInfo对象,并且给该GPUInfo对象设置假的Vendor ID和Device ID,以及Driver Vendor。

       如果Chromium启动时,没有指定switches::kUseGL选项,或者指定了switches::kUseGL选项,但是该选项的值没有设置为gfx::kGLImplementationOSMesaName,那么上述GPUInfo对象就需要通过函数gpu::CollectBasicGraphicsInfo来构造。函数gpu::CollectBasicGraphicsInfo通过OpenGL接口glGetString获得当前使用的OpenGL库的Vendor ID和Device ID,以及Driver Vendor等值。

       接下来,GpuDataManagerImplPrivate类的成员函数Initialize检查Chromium的命令行参数是否设置了switches::kIgnoreGpuBlacklist和switches::kUseGpuInTests选项。如果都没有设置,就说明Chromium是运行在发布版本中,这时候需要启用GPU黑名单列表。这个GPU黑名单列表通过全局变量gpu::kSoftwareRenderingListJson描述的一个JSON格式的字符串描述,它定义在文件external/chromium_org/gpu/config/software_rendering_list_json.cc中。例如,在该GPU黑名单列表中,存在以下一项:

[plain]  view plain copy
  1. {  
  2.   "id": 1,  
  3.   "description": "ATI Radeon X1900 is not compatible with WebGL on the Mac",  
  4.   "webkit_bugs": [47028],  
  5.   "os": {  
  6.     "type": "macosx"  
  7.   },  
  8.   "vendor_id": "0x1002",  
  9.   "device_id": ["0x7249"],  
  10.   "features": [  
  11.     "webgl",  
  12.     "flash_3d",  
  13.     "flash_stage3d"  
  14.   ]  
  15. }  
       它表示Vendor ID和Device ID分别等于0x1002和0x7249的GPU在Mac OS X上不能够用来支持Chromium的webgl、flash_3d和flash_stage3d特性。

       再接下来,GpuDataManagerImplPrivate类的成员函数Initialize检查Chromium的命令行参数是否设置了switches::kDisableGpuDriverBugWorkarounds选项。如果没有设置,也说明Chromium是运行在发布版本中,这时候需要启用GPU驱动BUG列表。GPU驱动BUG列表描述了那些已知的GPU驱动的BUG,这些BUG也会导致OpenGL的某些特性不能使用。这个GPU驱动BUG列表通过全局变量gpu::kGpuDriverBugListJson描述的一个JSON格式的字符串描述,它定义在文件external/chromium_org/gpu/config/gpu_driver_bug_list_json.cc中。例如,在该GPU驱动BUG列表中,存在以下一项:

[plain]  view plain copy
  1. {  
  2.   "id": 8,  
  3.   "description": "A few built-in glsl functions on Mac behave incorrectly",  
  4.   "cr_bugs": [349137],  
  5.   "os": {  
  6.     "type": "macosx",  
  7.     "version": {  
  8.       "op": "<",  
  9.       "value": "10.9"  
  10.     }  
  11.   },  
  12.   "vendor_id": "0x1002",  
  13.   "features": [  
  14.     "needs_glsl_built_in_function_emulation"  
  15.   ]  
  16. }  
      它表示Vender ID等于0x1002的GPU驱动的编号为349137的BUG会出现在版本号小于10.9的Mac OS X上,Chromium的needs_glsl_built_in_function_emulation特性不可用。

      最后,GpuDataManagerImplPrivate类的成员函数Initialize将前面获得的GPUInfo对象,以及GPU黑名单列表和GPU驱动BUG列表,传递给另外一个成员函数InitializeImpl进一步处理,如下所示:

[cpp]  view plain copy
  1. void GpuDataManagerImplPrivate::InitializeImpl(  
  2.     const std::string& gpu_blacklist_json,  
  3.     const std::string& gpu_driver_bug_list_json,  
  4.     const gpu::GPUInfo& gpu_info) {  
  5.   ......  
  6.   
  7.   if (!gpu_blacklist_json.empty()) {  
  8.     gpu_blacklist_.reset(gpu::GpuBlacklist::Create());  
  9.     ......  
  10.     bool success = gpu_blacklist_->LoadList(  
  11.         gpu_blacklist_json, gpu::GpuControlList::kCurrentOsOnly);  
  12.     ......  
  13.   }  
  14.   if (!gpu_driver_bug_list_json.empty()) {  
  15.     gpu_driver_bug_list_.reset(gpu::GpuDriverBugList::Create());  
  16.     ......  
  17.     bool success = gpu_driver_bug_list_->LoadList(  
  18.         gpu_driver_bug_list_json, gpu::GpuControlList::kCurrentOsOnly);  
  19.     ......  
  20.   }  
  21.   
  22.   gpu_info_ = gpu_info;  
  23.   UpdateGpuInfo(gpu_info);  
  24.   
  25.   ......  
  26. }  

       这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_data_manager_impl_private.cc中。

       GpuDataManagerImplPrivate类的成员函数InitializeImpl首先是创建一个GpuBlackList对象,保存在成员变量gpu_blacklist_中,并且调用该GpuBlackList对象的成员函数LoadList解析参数gpu_blacklist_json描述的一个JSON格式的字符串,以便可以获得一系列GPU黑名单。

       GpuDataManagerImplPrivate类的成员函数InitializeImpl接下来又创建一个GpuDriverBugList对象,保存在成员变量gpu_driver_bug_list_中,并且调用该GpuDriverBugList对象的成员函数LoadList解析参数gpu_driver_bug_list_json描述的一个JSON格式的字符串,以便可以获得一系列GPU驱动BUG列表。

       GpuDataManagerImplPrivate类的成员函数InitializeImpl最后调用了另外一个成员函数UpdateGpuInfo继续处理GPU黑名单和GPU驱动BUG列表,它的实现如下所示:

[cpp]  view plain copy
  1. void GpuDataManagerImplPrivate::UpdateGpuInfo(const gpu::GPUInfo& gpu_info) {  
  2.   // No further update of gpu_info if falling back to SwiftShader.  
  3.   if (use_swiftshader_)  
  4.     return;  
  5.   
  6.   .....  
  7.   
  8.   UpdateGpuInfoHelper();  
  9. }  
        这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_data_manager_impl_private.cc中。

       当GpuDataManagerImplPrivate类的成员变量use_swiftshader_的值等于true时,表示通过软件方式来渲染网页,因此这时候就无需对GPU黑名单列表和GPU驱动BUG列表进行处理了,因为后面不会用到GPU。

       另一方面,当GpuDataManagerImplPrivate类的成员变量use_swiftshader_的值等于false时,需要调用另外一个成员函数UpdateGpuInfoHelper处理GPU黑名单列表和GPU驱动BUG列表,它的实现如下所示:

[cpp]  view plain copy
  1. void GpuDataManagerImplPrivate::UpdateGpuInfoHelper() {  
  2.   ......  
  3.   
  4.   if (gpu_blacklist_) {  
  5.     std::set<int> features = gpu_blacklist_->MakeDecision(  
  6.         gpu::GpuControlList::kOsAny, std::string(), gpu_info_);  
  7.     ......  
  8.   
  9.     UpdateBlacklistedFeatures(features);  
  10.   }  
  11.   if (gpu_driver_bug_list_) {  
  12.     gpu_driver_bugs_ = gpu_driver_bug_list_->MakeDecision(  
  13.         gpu::GpuControlList::kOsAny, std::string(), gpu_info_);  
  14.   }  
  15.   gpu::GpuDriverBugList::AppendWorkaroundsFromCommandLine(  
  16.       &gpu_driver_bugs_, *CommandLine::ForCurrentProcess());  
  17.   
  18.   ......  
  19. }  
       这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_data_manager_impl_private.cc中。

       GpuDataManagerImplPrivate类的成员函数UpdateGpuInfoHelper首先调用成员变量gpu_blacklist_描述的一个GpuBlackList对象的成员函数MakeDecision根据设备自带的GPU的信息在GPU黑名单中找到被禁止使用的Chromium特性。这些特性保存在本地变量features描述的一个std::set中,并且会进一步交给GpuDataManagerImplPrivate类的成员函数UpdateBlacklistedFeatures处理。

       GpuDataManagerImplPrivate类的成员函数UpdateGpuInfoHelper接下来又调用了成员变量gpu_driver_bug_list_描述的一个GpuDriverBugList对象的成员函数MakeDecision根据设备自带的GPU的信息在GPU驱动BUG列表中找到被禁止使用的Chromium特性。这些特性保存在GpuDataManagerImplPrivate类的成员变量gpu_driver_bugs_中,并且会增加到Chromium的启动命令行参数里面去。

       接下来, 我们继续分析GpuDataManagerImplPrivate类的成员函数UpdateBlacklistedFeatures,以便可以了解它是如何处理那些禁止使用GPU实现的Chromium特性,如下所示:

[cpp]  view plain copy
  1. void GpuDataManagerImplPrivate::UpdateBlacklistedFeatures(  
  2.     const std::set<int>& features) {  
  3.   blacklisted_features_ = features;  
  4.   
  5.   ......  
  6. }  
      这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_data_manager_impl_private.cc中。

      GpuDataManagerImplPrivate类的成员函数UpdateBlacklistedFeatures主要就是将参数features描述的禁止使用GPU实现的Chromium特性记录在成员变量blacklisted_features_描述的一个std::set中。

      这一步执行完成后,对GPU黑名单列表和GPU驱动BUG列表的处理就完毕,回到前面分析的BrowserMainLoop类的成员函数BrowserThreadsStarted中,如果Chromium在编译时定义了宏USE_AURA,或者对于Mac OS X平台,它接下来就会调用函数ShouldInitializeBrowserGpuChannelAndTransportSurface第一次判断是否需要启动一个GPU进程。

      函数ShouldInitializeBrowserGpuChannelAndTransportSurface的实现如下所示:

[cpp]  view plain copy
  1. #if defined(USE_AURA)  
  2. bool ShouldInitializeBrowserGpuChannelAndTransportSurface() {  
  3.   return true;  
  4. }  
  5. #elif defined(OS_MACOSX) && !defined(OS_IOS)  
  6. bool ShouldInitializeBrowserGpuChannelAndTransportSurface() {  
  7.   return IsDelegatedRendererEnabled();  
  8. }  
  9. #endif  
       这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_data_manager_impl_private.cc中。

       从这里可以看到,如果Chromium在编译时定义了宏USE_AURA,那么函数ShouldInitializeBrowserGpuChannelAndTransportSurface的返回值固定为true,因为这时候表示要使用GPU来渲染Chromium的UI,也就是可以启动一个GPU进程。

       另一方面,如果Chromium在编译时没有定义宏USE_AURA,但是当前平台是非iOS版的Max OS X平台,那么函数ShouldInitializeBrowserGpuChannelAndTransportSurface调用另外一个函数IsDelegatedRendererEnabled判断是否可以启动一个GPU进程。

       函数IsDelegatedRendererEnabled的实现如下所示:

[cpp]  view plain copy
  1. bool IsThreadedCompositingEnabled() {  
  2.   const CommandLine& command_line = *CommandLine::ForCurrentProcess();  
  3.   
  4.   // Command line switches take precedence over blacklist.  
  5.   if (command_line.HasSwitch(switches::kDisableThreadedCompositing))  
  6.     return false;  
  7.   if (command_line.HasSwitch(switches::kEnableThreadedCompositing))  
  8.     return true;  
  9.   
  10. #if defined(USE_AURA) || defined(OS_MACOSX)  
  11.   // We always want threaded compositing on Aura and Mac (the fallback is a  
  12.   // threaded software compositor).  
  13.   return true;  
  14. #else  
  15.   return false;  
  16. #endif  
  17. }  
  18.   
  19. bool IsDelegatedRendererEnabled() {  
  20.   const CommandLine& command_line = *CommandLine::ForCurrentProcess();  
  21.   bool enabled = false;  
  22.   
  23.   ......  
  24.   
  25.   // Flags override.  
  26.   enabled |= command_line.HasSwitch(switches::kEnableDelegatedRenderer);  
  27.   enabled &= !command_line.HasSwitch(switches::kDisableDelegatedRenderer);  
  28.   
  29.   // Needs compositing, and thread.  
  30.   if (enabled && !IsThreadedCompositingEnabled()) {  
  31.     enabled = false;  
  32.     ......  
  33.   }  
  34.   
  35.   return enabled;  
  36. }  

       这两个函数定义在文件external/chromium_org/content/browser/gpu/compositor_util.cc中。

       从这里可以看到,函数IsDelegatedRendererEnabled的返回值等于true,也就是可以启动一个GPU进程,需要同时满足以下条件:

       1. Chromium的命令行参数设置了switches::kEnableDelegatedRenderer选项;

       2. Chromium的命令行参数没有设置switches::kDisableDelegatedRenderer选项;

       3. Chromium的命令行参数设置了switches::kEnableThreadedCompositing选项,或者没有设置switches::kEnableThreadedCompositing选项,但是Chromium编译时定义了宏USE_AURA,或者当平台是Mac OS X平台。

       4. Chromium的命令行参数没有设置switches::kDisableThreadedCompositing选项。

       要理解上述四个条件,关键在于理解Delegated Renderer和Threaded Compositing两个概念。

       Delegated Renderer描述的是一个委托渲染器。所谓委托渲染,就是Render进程的合成器管理的所有GPU资源都会转交给Browser进程的合成器,再由Browser进程的合成器进行统一的合成,最后显示在屏幕上。Threaded Compositing称为线程化合成,指的是在Render进程中,合成器运行在一个独立的线程中,这个线程称为实现(IMPL)线程。在Chromium里面,还有一个特性称为In-Thread Compositing,称为线程内合成。这个特性是给Android平台上的单进程模式的Chromium使用的,这时候Render端的合成器将运行在主线程中,目的是可以让网页UI的合成发生在应用程序UI的绘制过程中。

       这意味着,如果Chromium在编译时没有定义宏USE_AURA,但是当前平台是非iOS版的Max OS X平台,那么Delegated Renderer和Threaded Compositing这两个特性都是需要GPU支持的,因此当它们都启用时,就需要启动一个GPU进程。

       这一步执先完成后,回到前面分析的BrowserMainLoop类的成员函数BrowserThreadsStarted中,这时候如果函数ShouldInitializeBrowserGpuChannelAndTransportSurface的返回值为true,那么只是初步确定Chromium需要一个GPU进程。至于这个GPU进程最终能不能启动起来,还要取决于设备自带的GPU能否用来实现GPU_FEATURE_TYPE_GPU_COMPOSITING特性。这是通过调用GpuDataManagerImpl类的成员函数CanUseGpuBrowserCompositor进行判断的,如下所示:

[cpp]  view plain copy
  1. bool GpuDataManagerImpl::CanUseGpuBrowserCompositor() const {  
  2.   base::AutoLock auto_lock(lock_);  
  3.   return private_->CanUseGpuBrowserCompositor();  
  4. }  
      这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_data_manager_impl.cc中。

      GpuDataManagerImpl类的成员函数CanUseGpuBrowserCompositor调用了成员变量private_描述的一个GpuDataManagerImplPrivate对象的成员函数CanUseGpuBrowserCompositor判断设备自带的GPU能否用来实现GPU_FEATURE_TYPE_GPU_COMPOSITING特性,如下所示:

[cpp]  view plain copy
  1. bool GpuDataManagerImplPrivate::CanUseGpuBrowserCompositor() const {  
  2.   if (ShouldUseSwiftShader())  
  3.     return false;  
  4.   if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_GPU_COMPOSITING))  
  5.     return false;  
  6.   return true;  
  7. }  
       这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_data_manager_impl_private.cc中。

       GpuDataManagerImplPrivate类的成员函数CanUseGpuBrowserCompositor首先调用另外一个成员函数ShouldUseSwiftShader判断Chromium的UI是否是通过软件方式渲染的,如下所示:

[cpp]  view plain copy
  1. bool GpuDataManagerImplPrivate::ShouldUseSwiftShader() const {  
  2.   return use_swiftshader_;  
  3. }  
       这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_data_manager_impl_private.cc中。

       前面提到,当GpuDataManagerImplPrivate类的成员变量use_swiftshader的值等于true的时候,就表示Chromium的UI是通过软件方式渲染的。在这种情况下,回到GpuDataManagerImplPrivate类的成员函数CanUseGpuBrowserCompositor中,那么它就可以直接返回一个false值给调用者,表示不需要启动一个GPU进程。

       假设GpuDataManagerImplPrivate类的成员函数CanUseGpuBrowserCompositor的返回值为false,那么它接下来就调用成员函数IsFeatureBlacklisted判断设备自带的GPU能否用来实现GPU_FEATURE_TYPE_GPU_COMPOSITING特性,如下所示:

[cpp]  view plain copy
  1. bool GpuDataManagerImplPrivate::IsFeatureBlacklisted(int feature) const {  
  2.   ......  
  3.   
  4.   return (blacklisted_features_.count(feature) == 1);  
  5. }  
       这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_data_manager_impl_private.cc中。

       从前面的分析可以知道,GpuDataManagerImplPrivate类的成员变量blacklisted_features_描述的一个std::set保存了禁止使用设备自带的GPU实现的Chromium特性,因此通过它就可以判断参数feature描述的特性是否被支持。       

       这一步执行完成之后,回到前面分析的BrowserMainLoop类的成员函数BrowserThreadsStarted中,如果GpuDataManagerImpl类的成员函数CanUseGpuBrowserCompositor的返回值为false,也就是设备自带的GPU被禁止用来实现GPU_FEATURE_TYPE_GPU_COMPOSITING特性,那么本地变量established_gpu_channel的值就会由true值修改为false时,表示不需要启动一个GPU进程。

       最后,BrowserMainLoop类的成员函数BrowserThreadsStarted调用BrowserGpuChannelHostFactory类的静态成员函数Initialize检查本地变量established_gpu_channel的值是否等于true。如果等于true,那么就可能需要启动一个GPU进程,如下所示:

[cpp]  view plain copy
  1. void BrowserGpuChannelHostFactory::Initialize(bool establish_gpu_channel) {  
  2.   DCHECK(!instance_);  
  3.   instance_ = new BrowserGpuChannelHostFactory();  
  4.   if (establish_gpu_channel) {  
  5.     instance_->EstablishGpuChannel(CAUSE_FOR_GPU_LAUNCH_BROWSER_STARTUP,  
  6.                                    base::Closure());  
  7.   }  
  8. }  
      这个函数定义在文件external/chromium_org/content/browser/gpu/browser_gpu_channel_host_factory.cc中。

      BrowserGpuChannelHostFactory类的静态成员函数Initialize首先是创建了一个BrowserGpuChannelHostFactory对象,并且保存在静态成员变量instance_中。在参数establish_gpu_channel的值等于true的情况下,BrowserGpuChannelHostFactory类的静态成员函数Initialize继续调用前面创建的BrowserGpuChannelHostFactory对象的成员函数EstablishGpuChannel启动一个GPU进程,以及创建一个到该GPU进程的GPU通道。

      BrowserGpuChannelHostFactory类的成员函数EstablishGpuChannel的实现如下所示:

[cpp]  view plain copy
  1. void BrowserGpuChannelHostFactory::EstablishGpuChannel(  
  2.     CauseForGpuLaunch cause_for_gpu_launch,  
  3.     const base::Closure& callback) {  
  4.   ......  
  5.   
  6.   if (!gpu_channel_ && !pending_request_) {  
  7.     // We should only get here if the context was lost.  
  8.     pending_request_ = EstablishRequest::Create(  
  9.         cause_for_gpu_launch, gpu_client_id_, gpu_host_id_);  
  10.   }  
  11.   
  12.   ......  
  13. }  
       这个函数定义在文件external/chromium_org/content/browser/gpu/browser_gpu_channel_host_factory.cc中。

       BrowserGpuChannelHostFactory类的成员变量gpu_channel_描述的是Browser进程到GPU进程的一个GPU通道,另外一个成员变量pending_request_描述的是正在创建上述GPU通道的一个请求。当这两个成员变量的值均等于NULL的时候,就说明GPU通道还没有建立起来,因此接下来就会调用EstablishRequest类的静态成员函数Create创建一个GPU通道。

       EstablishRequest类的静态成员函数Create的实现如下所示:

[cpp]  view plain copy
  1. scoped_refptr  
  2. BrowserGpuChannelHostFactory::EstablishRequest::Create(CauseForGpuLaunch cause,  
  3.                                                        int gpu_client_id,  
  4.                                                        int gpu_host_id) {  
  5.   scoped_refptr establish_request =  
  6.       new EstablishRequest(cause, gpu_client_id, gpu_host_id);  
  7.   scoped_refptr loop =  
  8.       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO);  
  9.   // PostTask outside the constructor to ensure at least one reference exists.  
  10.   loop->PostTask(  
  11.       FROM_HERE,  
  12.       base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO,  
  13.                  establish_request));  
  14.   return establish_request;  
  15. }  
      这个函数定义在文件external/chromium_org/content/browser/gpu/browser_gpu_channel_host_factory.cc中。

      EstablishRequest类的静态成员函数Create首先将参数cause、gpu_client_id和gpu_host_id封装在一个EstablishRequest对象中,接着向当前进程的IO的消息队列发送一个Task,该Task绑定的函数为EstablishRequest类的成员函数EstablishOnIO,并且当该函数被调用时,this指针指向的是上述封装的EstablishRequest对象。

      EstablishRequest类的成员函数EstablishOnIO的实现如下所示:

[cpp]  view plain copy
  1. void BrowserGpuChannelHostFactory::EstablishRequest::EstablishOnIO() {  
  2.   GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);  
  3.   if (!host) {  
  4.     host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,  
  5.                                cause_for_gpu_launch_);  
  6.     ......  
  7.     gpu_host_id_ = host->host_id();  
  8.     ......  
  9.   }   
  10.   
  11.   ......  
  12.   
  13.   host->EstablishGpuChannel(  
  14.       gpu_client_id_,  
  15.       true,  
  16.       base::Bind(  
  17.           &BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO,  
  18.           this));  
  19. }  
       这个函数定义在文件external/chromium_org/content/browser/gpu/browser_gpu_channel_host_factory.cc中。

       在Chroimum中,可能会存在两个GPU进程,其中一个是受限GPU进程,运行在沙箱中,通过枚举值GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED描述,另外一个是特权GPU进程,不用运行在沙箱中,通过枚举值GpuProcessHost::GPU_PROCESS_KIND_UNSANDBOXED描述。同时,每一个GPU进程都分配有一个Host ID,这个ID值保存在EstablishRequest类的成员变量gpu_host_id_中。

       EstablishRequest类的成员函数EstablishOnIO首先是调用GpuProcessHost类的静态成员函数FromID检查与它的成员变量gpu_host_id_对应的GPU进程是否已经启动过了。如果已经启动过了,那么就会得到一个GpuProcessHost对象。也就是说,每一个GPU进程在Browser进程中,都通过一个GpuProcessHost对象描述,并且每一个GpuProcessHost对象都有一个对应的Host ID。

       如果与EstablishRequest类的成员变量gpu_host_id_对应的GPU进程还没有创建,那么接下来就会调用GpuProcessHost类的静态成员函数Get启动一个类型为GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED的GPU进程,也就是创建一个运行沙箱中的GPU进程。创建启动成功,那么就可以获得一个GpuProcessHost对象。由于分配给这个GpuProcessHost对象的Host ID可能发生了变化,因此就通过调用它的成员函数host_id获得它现在使用的Host ID,并且保存在EstablishRequest类的成员变量gpu_host_id_中。

       最后,EstablishRequest类的成员函数EstablishOnIO就调用上述获得的GpuProcessHost对象的成员函数EstablishGpuChannel创建一个到它所描述的GPU进程的GPU通道。接下来,我们先分析GpuProcessHost类的静态成员函数Get启动GPU进程的过程,接下来再分析GpuProcessHost类的成员函数EstablishGpuChannel创建GPU通道的过程。

       GpuProcessHost类的静态成员函数Get的实现如下所示:

[cpp]  view plain copy
  1. GpuProcessHost* g_gpu_process_hosts[GpuProcessHost::GPU_PROCESS_KIND_COUNT];  
  2.   
  3. ......  
  4.   
  5. GpuProcessHost* GpuProcessHost::Get(GpuProcessKind kind,  
  6.                                     CauseForGpuLaunch cause) {  
  7.   ......  
  8.   
  9.   if (g_gpu_process_hosts[kind] && ValidateHost(g_gpu_process_hosts[kind]))  
  10.     return g_gpu_process_hosts[kind];  
  11.   
  12.   ......  
  13.   
  14.   static int last_host_id = 0;  
  15.   int host_id;  
  16.   host_id = ++last_host_id;  
  17.   
  18.   ......  
  19.   
  20.   GpuProcessHost* host = new GpuProcessHost(host_id, kind);  
  21.   if (host->Init())  
  22.     return host;  
  23.   
  24.   ......  
  25. }  
       这个函数定义在文件xternal/chromium_org/content/browser/gpu/gpu_process_host.cc中。

       全局变量g_gpu_process_hosts是一个GpuProcessHost数组,它的大小为GpuProcessHost::GPU_PROCESS_KIND_COUNT,即等于2,它是用来保存上面提到的用来描述GPU进程的GpuProcessHost对象。也就是说,每当我们创建一个GPU进程的时候,就会创建一个GpuProcessHost对象,并且将该GpuProcessHost对象按照它的类型(puProcessHost::GPU_PROCESS_KIND_UNSANDBOXED或者puProcessHost::GPU_PROCESS_KIND_SANDBOXED)保存在全局g_gpu_process_hosts描述的GpuProcessHost数组中。

       GpuProcessHost类的静态成员函数Get首先做的便是根据参数kind指定的GPU进程类型在全局变量g_gpu_process_hosts描述的GpuProcessHost数组中检查是否已经存在一个对应的GpuProcessHost对象。如果已经存在,那么就说明请求启动的GPU进程已经启动起来了,因此就不用再往下处理了。否则的话,就会创建一个GpuProcessHost对象,并且调用该GpuProcessHost对象的成员函数Init启动一个GPU进程。

       我们首先分析GpuProcessHost对象的创建过程,即GpuProcessHost类的构造函数的实现,如下所示:

[cpp]  view plain copy
  1. GpuProcessHost::GpuProcessHost(int host_id, GpuProcessKind kind)  
  2.     : ......,  
  3.       in_process_(false),  
  4.       ...... {  
  5.   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) ||  
  6.       CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessGPU)) {  
  7.     in_process_ = true;  
  8.   }  
  9.   
  10.   ......  
  11.   
  12.   g_gpu_process_hosts[kind] = this;  
  13.   
  14.   ......  
  15.   
  16.   process_.reset(new BrowserChildProcessHostImpl(PROCESS_TYPE_GPU, this));  
  17. }  
       这个函数定义在文件xternal/chromium_org/content/browser/gpu/gpu_process_host.cc中。

       GpuProcessHost类有一个重要的成员变量in_process_。当Chromium的命令行参数中设置了switches::kSingleProcess或者switches::kInProcessGPU选项的时候,它的值等于true。否则的话,它的值就等于false。

       当GpuProcessHost类的成员变量in_process_的值等于true的时候,表示在Browser进程中创建一个GPU线程来代替GPU进程。这种GPU渲染方式在Chromium中就称为In-Process GPU渲染。这意味着,Browser进程接收到GPU相关的操作时,都转发给它的GPU线程处理。由于Browser进程与GPU线程和GPU进程的通信过程都统一封装为IPC通道或者GPU通道操作,因此不管采用的是In-Process GPU渲染方式,还是独立GPU进程的渲染方式,它们提供给Browser进程的接口,都是一样的。

       GpuProcessHost类还有另外一个成员变量process_,它指向的是一个BrowserChildProcessHostImpl对象。通过该BrowserChildProcessHostImpl对象,GpuProcessHost类可以向GPU线程或者GPU进程发送IPC消息,或者接收从GPU线程或者GPU进程发送过来的IPC消息。

       从这里还可以看到,正在创建的GpuProcessHost对象被保存在全局变量g_gpu_process_hosts中,这样以后就可以检查指定类型的GPU进程是否已经启动过了。

       回到GpuProcessHost类的静态成员函数Get中,当它创建了一个GpuProcessHost对象之后,接下来就会调用这个GpuProcessHost对象的成员函数Init启动一个GPU进程或者一个GPU线程,它的实现如下所示:

[cpp]  view plain copy
  1. bool GpuProcessHost::Init() {  
  2.   ......  
  3.   
  4.   std::string channel_id = process_->GetHost()->CreateChannel();  
  5.   if (channel_id.empty())  
  6.     return false;  
  7.   
  8.   if (in_process_) {  
  9.     ......  
  10.   
  11.     in_process_gpu_thread_.reset(g_gpu_main_thread_factory(channel_id));  
  12.     in_process_gpu_thread_->Start();  
  13.   
  14.     ......  
  15.   } else if (!LaunchGpuProcess(channel_id)) {  
  16.     return false;  
  17.   }  
  18.   
  19.   if (!Send(new GpuMsg_Initialize()))  
  20.     return false;  
  21.   
  22.   return true;  
  23. }  
      这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_process_host.cc中。

      GpuProcessHost类的成员函数Init首先通过调用成员变量process_指向的BrowserChildProcessHostImpl对象的成员函数GetHost获得其内部的一个ChildProcessHostImpl对象。有了这个ChildProcessHostImpl对象之后,再调用它的成员函数CreateChannel创建一个IPC通道,如下所示:

[cpp]  view plain copy
  1. std::string ChildProcessHostImpl::CreateChannel() {  
  2.   channel_id_ = IPC::Channel::GenerateVerifiedChannelID(std::string());  
  3.   channel_ = IPC::Channel::CreateServer(channel_id_, this);  
  4.   if (!channel_->Connect())  
  5.     return std::string();  
  6.   
  7.   ......  
  8.   
  9.   return channel_id_;  
  10. }  
       这个函数定义在文件external/chromium_org/content/common/child_process_host_impl.cc中。

       ChildProcessHostImpl类的成员函数CreateChannel首先是调用IPC::Channel类的静态成员函数GenerateVerifiedChannelID生成一个唯一的UNIX Socket名称,接着再调用IPC::Channel类的静态成员函数CreateServer根据上述UNIX Socket名称创建一个UNIX Socket。

       IPC::Channel类的静态成员函数GenerateVerifiedChannelID的实现可以参考前面Chromium的Render进程启动过程分析一文,它的另外一个静态成员函数CreateServer的实现如下所示:

[cpp]  view plain copy
  1. scoped_ptr Channel::CreateServer(  
  2.     const IPC::ChannelHandle &channel_handle, Listener* listener) {  
  3.   return Channel::Create(channel_handle, Channel::MODE_SERVER, listener);  
  4. }  
       这个函数定义在文件external/chromium_org/ipc/ipc_channel_common.cc中。

       IPC::Channel类的静态成员函数CreateServer调用了Channel类的静态成员函数Create创建了一个UNIX Socket,并且将其Server端文件描述符封装在一个ChannelPosix对象。这个过程可以参考前面Chromium的Render进程启动过程分析一文。

       回到ChildProcessHostImpl类的成员函数CreateChannel中,它通过IPC::Channel类的静态成员函数CreateServer获得一个ChannelPosix对象之后,再调用该ChannelPosix对象的成员函数Connect,这样就可以将前面所创建的一个UNIX Socket的Server端文件描述符增加到当前进程的IO线程的消息队列中去监控,也就是监控它的IO事件,用来接收以后GPU进程或者GPU线程给它发送的IPC消息。这个过程也可以参考前面Chromium的Render进程启动过程分析一文。

       再回到GpuProcessHost类的成员函数Init中,它调用ChildProcessHostImpl类的成员函数CreateChannel创建了一个Server端的IPC通道之后,接下来根据成员变量in_process_的值决定是要在当前进程中创建一个GPU线程,还是要创建一个独立的GPU进程。

       根据前面的分析,当GpuProcessHost类的成员变量in_process_的值等于true的时候,表示要在当前进程,也就是Browser进程,创建一个GPU线程。接下来我们就首先分析GPU线程的创建过程。

       g_gpu_main_thread_factory是一个类型为GpuMainThreadFactoryFunction的函数指针,它的定义如下所示:

[cpp]  view plain copy
  1. GpuMainThreadFactoryFunction g_gpu_main_thread_factory = NULL;  
       这个全局变量定义在文件external/chromium_org/content/browser/gpu/gpu_process_host.cc中。

       GpuMainThreadFactoryFunction的定义如下所示:

[cpp]  view plain copy
  1. typedef base::Thread* (*GpuMainThreadFactoryFunction)(const std::string& id);  
       这个函数指针类型定义在文件external/chromium_org/content/browser/gpu/gpu_process_host.h中。

       从这里可以看到,GpuMainThreadFactoryFunction函数指针指向的函数有一个类型为std::string&的参数,这个参数描述的是一个UNIX Socket名称,并且该函数的返回值是一个 Thread对象,该Thread对象描述的就是一个线程。

       Browser进程启动的过程中,会调用一个函数RegisterMainThreadFactories设置全局变量g_gpu_main_thread_factory的值,如下所示:

[cpp]  view plain copy
  1. static void RegisterMainThreadFactories() {  
  2.   ......  
  3.   
  4.   GpuProcessHost::RegisterGpuMainThreadFactory(  
  5.       CreateInProcessGpuThread);  
  6.   
  7.   ......  
  8. }  
      这个函数定义在文件external/chromium_org/content/app/content_main_runner.cc中。

      函数RegisterMainThreadFactories通过调用GpuProcessHost类的静态成员函数RegisterGpuMainThreadFactory设置全局变量g_gpu_main_thread_factory的值,如下所示:

[cpp]  view plain copy
  1. void GpuProcessHost::RegisterGpuMainThreadFactory(  
  2.     GpuMainThreadFactoryFunction create) {  
  3.   g_gpu_main_thread_factory = create;  
  4. }  
       这个函数指针类型定义在文件external/chromium_org/content/browser/gpu/gpu_process_host.cc中。

       这意味着全局变量g_gpu_main_thread_factory指向的函数为CreateInProcessGpuThread。

       回到GpuProcessHost类的成员数Init中,它首先通过调用全局变量g_gpu_main_thread_factory指向的函数创建一个线程对象,也就是通过调用函数CreateInProcessGpuThread创建一个线程对象,接着再调用该线程对象的成员函数Start启动一个GPU线程。 

       函数CreateInProcessGpuThread的实现如下所示:

[cpp]  view plain copy
  1. base::Thread* CreateInProcessGpuThread(const std::string& channel_id) {  
  2.   return new InProcessGpuThread(channel_id);  
  3. }  
       这个函数定义在文件external/chromium_org/content/gpu/in_process_gpu_thread.cc中。

       函数CreateInProcessGpuThread返回的是一个InProcessGpuThread对象,该对象的创建过程如下所示:

[cpp]  view plain copy
  1. InProcessGpuThread::InProcessGpuThread(const std::string& channel_id)  
  2.     : base::Thread("Chrome_InProcGpuThread"),  
  3.       channel_id_(channel_id),  
  4.       gpu_process_(NULL) {  
  5. }  
       这个函数定义在文件external/chromium_org/content/gpu/in_process_gpu_thread.cc中。

       InProcessGpuThread类的构造函数主要做两件事件。第一件事情是调用父类Thread的构造函数对父类部分对象进行初始化,第二件事件是将前面创建的一个UNIX Socket的名称保存在成员变量channel_id_中。

       这意味着,Browser进程中的GPU线程是通过一个InProcessGpuThread对象来描述的。从前面Chromium多线程模型设计和实现分析一文可以知道,当调用这个InProcessGpuThread对象的成员函数Start(从父类Thread继承下来)的时候,就可以启动一个线程。这个线程在进入消息循环之前,会先调用由子类实现的成员函数Init,也就是InProcessGpuThread类的成员函数Init,对线程执行初始化工作。

       InProcessGpuThread类的成员函数Init的实现如下所示:

[cpp]  view plain copy
  1. void InProcessGpuThread::Init() {  
  2.   gpu_process_ = new GpuProcess();  
  3.   // The process object takes ownership of the thread object, so do not  
  4.   // save and delete the pointer.  
  5.   gpu_process_->set_main_thread(new GpuChildThread(channel_id_));  
  6. }  
       这个函数定义在文件external/chromium_org/content/gpu/in_process_gpu_thread.cc中。

       InProcessGpuThread类的成员函数Init首先创建了一个GpuProcess对象,并且保存在成员变量gpu_process_中。这个GpuProcess对象就是图1所示的GpuProcess对象,用来与Browser进程进行IPC。这个GpuProcess对象本来是应该在GPU进程中创建的,但是由于在我们这个情景中,只有GPU线程没有GPU进程,不过两者是等价的,因此,在GPU线程中创建一个GpuProcess对象起到的效果和作用与在GPU进程中创建一个GpuProcess对象是相同的。

       GpuProcess类是从ChildProcess类继承下来的,如下所示:

[cpp]  view plain copy
  1. class GpuProcess : public ChildProcess {  
  2.  .....  
  3. };  
       这个类定义在文件external/chromium_org/content/gpu/gpu_process.h中。

       这意味着在创建GpuProcess对象的过程中,会触发对ChildProcess类的构造函数的调用,如下所示:

[cpp]  view plain copy
  1. GpuProcess::GpuProcess() {  
  2. }  
       这个函数定义在文件external/chromium_org/content/gpu/gpu_process.cc中。

       GpuProcess类的构造函数调用的是父类ChildProcess的默认构造函数。我们在前面Chromium的Render进程启动过程分析一文中已经分析过ChildProcess的默认构造函数了,它主要就是创建一个IO线程。这个IO线程就是负责与Browser进程或者其它进程(例如Render进程和Plugin进程)的IO线程进行IPC的。

       回到InProcessGpuThread类的成员函数Init中,它在GPU线程中创建了一个GpuProcess对象之后,就获得了一个IO线程,接下来它又创建了一个GpuChildThread对象。这个GpuChildThread对象也是用来描述当前所在的GPU线程,并且这个GpuChildThread对象会被设置到前面创建的GpuProcess对象中,表示前面创建的GpuProcess对象所描述的“进程”的主线程就是这里创建的GpuChildThread对象所描述的GPU线程。

       GpuChildThread对象的创建过程如下所示:

[cpp]  view plain copy
  1. GpuChildThread::GpuChildThread(const std::string& channel_id)  
  2.     : ChildThread(channel_id),  
  3.       ......,  
  4.       in_browser_process_(true) {  
  5.   ......  
  6. #if !defined(OS_ANDROID)  
  7.   // For single process and in-process GPU mode, we need to load and  
  8.   // initialize the GL implementation and locate the GL entry points here.  
  9.   // On Android, GLSurface::InitializeOneOff() is called from BrowserMainLoop  
  10.   // before getting here. crbug.com/326295  
  11.   if (!gfx::GLSurface::InitializeOneOff())  
  12.     VLOG(1) << "gfx::GLSurface::InitializeOneOff failed";  
  13. #endif  
  14.   ......  
  15. }  
      这个函数定义在文件external/chromium_org/content/gpu/gpu_child_thread.cc。

      GpuChildThread类的构造函数主要做两件事情。

      第一件事情是将参数channel_id描述的UNIX Socket的名称传递给父类ChildThread的构造函数,以便后者可以根据该UNIX Socket的名称获得前面在ChildProcessHostImpl类的成员函数CreateChannel中创建的一个UNIX Socket的Client端文件描述符。有了这个UNIX Socket的Client端文件描述符之后,就可以创建一个Client端IPC通道了。这个Client端IPC通道以后就可以用来与Browser进程或者其它进程(例如Render进程和Plugin进程)的Server端IPC通道进行通信。ChildThread类的构造函数创建Client端IPC通道的过程可以参考前面Chromium的Render进程启动过程分析一文。

      第二件事情是在非Android平台才会做的,就是调用前面分析过的gfx::GLSurface类的静态成员函数InitializeOneOff在当前进程中加载OpenGL相关的库,以及创建一个EGLDisplay,以便以后可以创建OpenGL上下文。这是由于在Android平台上,前面在调用BrowserMainLoop类的成员函数BrowserThreadsStarted的时候,已经调用过gfx::GLSurface类的静态成员函数InitializeOneOff了,因此这里就不需要再重复调用。

       这样,在Browser进程中创建一个GPU线程的过程就分析完成了。这个GPU线程通过InProcessGpuThread类描述,并且在它的启动的过程中,主要是做了以下两件事情:

       1. 创建了一个GpuProcess对象,用来描述一个假设存在的GPU进程,这将会触发创建一个IO线程。注意,Browser进程本来也有一个IO线程。这个原有的IO线程是Browser进程用来与其它进程(例如GPU进程、Render进程和Plugin进程)进行IPC的。而这里新创建的IO线程是GPU线程用来与其他进程(例如Browser进程、Render进程和Plugin进程)进行IPC的。

       2. 创建了一个GpuChildThread对象,该GpuChildThread对象描述的也是Browser进程中的GPU线程,这将会触发创建一个Client端IPC通道。这个Client端IPC通道是GPU线程用来与其他进程(例如Browser进程、Render进程和Plugin进程)的Server端IPC通道进行IPC的。

       上述两个对象在GPU进程的启动过程也同样会创建的。正是通过这种相同的方式,使得Browser进程与GPU线程和GPU进程的通信方式和过程都是一样的,也就是说,Browser进程需要执行GPU操作时,不需要知道这些GPU操作是在它的一个GPU线程中执行,还是在一个独立的GPU进程中执行。同样的,Render进程和Plugin进程需要执行GPU操作时,也不需要关心这些GPU操作是在Browser进程中的一个GPU线程中执行,还是在一个独立日GPU进程中执行。

       回到GpuProcessHost类的成员函数Init中,接下来我们分析另外一个情景,即启动GPU进程的情况。这是通过调用GpuProcessHost类的成员函数LaunchGpuProcess实现的,如下所示:

[cpp]  view plain copy
  1. bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) {  
  2.   ......  
  3.   
  4.   base::FilePath exe_path = ChildProcessHost::GetChildPath(child_flags);  
  5.   ......  
  6.   
  7.   CommandLine* cmd_line = new CommandLine(exe_path);  
  8.   cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kGpuProcess);  
  9.   ......  
  10.   
  11.   process_->Launch(  
  12.       new GpuSandboxedProcessLauncherDelegate(cmd_line,  
  13.                                               process_->GetHost()),  
  14.     
  15.   ......  
  16. }  

       这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_process_host.cc中。

       GpuProcessHost类的成员函数LaunchGpuProcess首先是创建了GPU进程的命令行参数。这个命令行参数设置了一个重要的选项switches::kProcessType,它的值等于switches::kGpuProcess。

[cpp]  view plain copy
  1.   

       GpuProcessHost类的成员函数LaunchGpuProcess接下来将上述命令行参数传递给成员变量process_指向的一个BrowserChildProcessHostImpl对象的成员函数Launch,以便后者可以启动一个GPU进程。

       BrowserChildProcessHostImpl类的成员函数Launch的实现如下所示:

[cpp]  view plain copy
  1. void BrowserChildProcessHostImpl::Launch(  
  2.     SandboxedProcessLauncherDelegate* delegate,  
  3.     CommandLine* cmd_line) {  
  4.   ......  
  5.   
  6.   child_process_.reset(new ChildProcessLauncher(  
  7.       delegate,  
  8.       cmd_line,  
  9.       data_.id,  
  10.       this));  
  11. }  
       这个函数定义在文件external/chromium_org/content/browser/browser_child_process_host_impl.cc中。

       从这里可以看到,BrowserChildProcessHostImpl类的成员函数Launch主要是创建了一个ChildProcessLauncher对象,并且保存在成员变量child_process_中。从前面Chromium的Render进程启动过程分析一文可以知道,在ChildProcessLauncher对象的创建过程中,也就是在ChildProcessLauncher类的构造函数中,将会通过JNI调用Java层的一个Conext接口的成员函数bindService启动一个Service进程。

       从前面Chromium的Render进程启动过程分析一文还可以知道,当前进程,也就是Browser进程,会将前面在ChildProcessHostImpl类的成员函数CreateChannel中创建的一个UNIX Socket的Client端文件描述符通过Binder IPC传递给上述Service进程,以便后者可以创建一个Client端IPC通道与Browser进程进行通信。最后,上述Service进程会通过JNI调用Native层的函数RunNamedProcessTypeMain,后者的实现如下所示:

[cpp]  view plain copy
  1. int RunNamedProcessTypeMain(    
  2.     const std::string& process_type,    
  3.     const MainFunctionParams& main_function_params,    
  4.     ContentMainDelegate* delegate) {    
  5.   static const MainFunction kMainFunctions[] = {    
  6. #if !defined(CHROME_MULTIPLE_DLL_CHILD)    
  7.     { "",                            BrowserMain },    
  8. #endif    
  9. #if !defined(CHROME_MULTIPLE_DLL_BROWSER)    
  10. #if defined(ENABLE_PLUGINS)    
  11. #if !defined(OS_LINUX)    
  12.     { switches::kPluginProcess,      PluginMain },    
  13. #endif    
  14.     { switches::kWorkerProcess,      WorkerMain },    
  15.     { switches::kPpapiPluginProcess, PpapiPluginMain },    
  16.     { switches::kPpapiBrokerProcess, PpapiBrokerMain },    
  17. #endif  // ENABLE_PLUGINS    
  18.     { switches::kUtilityProcess,     UtilityMain },    
  19.     { switches::kRendererProcess,    RendererMain },    
  20.     { switches::kGpuProcess,         GpuMain },    
  21. #endif  // !CHROME_MULTIPLE_DLL_BROWSER    
  22.   };    
  23.     
  24.   ......    
  25.     
  26.   for (size_t i = 0; i < arraysize(kMainFunctions); ++i) {    
  27.     if (process_type == kMainFunctions[i].name) {    
  28.       if (delegate) {    
  29.         int exit_code = delegate->RunProcess(process_type,    
  30.             main_function_params);    
  31. #if defined(OS_ANDROID)    
  32.         // In Android's browser process, the negative exit code doesn't mean the    
  33.         // default behavior should be used as the UI message loop is managed by    
  34.         // the Java and the browser process's default behavior is always    
  35.         // overridden.    
  36.         if (process_type.empty())    
  37.           return exit_code;    
  38. #endif    
  39.         if (exit_code >= 0)    
  40.           return exit_code;    
  41.       }    
  42.       return kMainFunctions[i].function(main_function_params);    
  43.     }    
  44.   }    
  45.     
  46.   ......    
  47. }    
       这个函数定义在文件external/chromium_org/content/app/content_main_runner.cc中。

       在前面Chromium的Render进程启动过程分析一文中,我们已经分析过函数RunNamedProcessTypeMain的实现了,不过这时候它的参数process_type的值等于switches::kGpuProcess,也就是来自于前面提到的命令行参数中的switches::kProcessType选项,因此接下来函数RunNamedProcessTypeMain将调用函数GpuMain作为GPU进程的入口函数。

       函数GpuMain的实现如下所示:

[cpp]  view plain copy
  1. int GpuMain(const MainFunctionParams& parameters) {  
  2.   ......  
  3.   
  4.   base::MessageLoop main_message_loop(base::MessageLoop::TYPE_IO);  
  5.   ......  
  6.   
  7.   // Warm up resources that don't need access to GPUInfo.  
  8.   if (WarmUpSandbox(command_line)) {  
  9.     ......  
  10.   
  11.     // Determine if we need to initialize GL here or it has already been done.  
  12.     bool gl_already_initialized = false;  
  13.     ......  
  14.     if (command_line.HasSwitch(switches::kInProcessGPU)) {  
  15.       // With in-process GPU, GLSurface::InitializeOneOff() is called from  
  16.       // GpuChildThread before getting here.  
  17.       gl_already_initialized = true;  
  18.     }  
  19.   
  20.     // Load and initialize the GL implementation and locate the GL entry points.  
  21.     bool gl_initialized =  
  22.         gl_already_initialized  
  23.             ? gfx::GetGLImplementation() != gfx::kGLImplementationNone  
  24.             : gfx::GLSurface::InitializeOneOff();  
  25.       
  26.     ......  
  27.   }  
  28.   
  29.   ......  
  30.   
  31.   GpuProcess gpu_process;  
  32.   ......  
  33.   
  34.   GpuChildThread* child_thread = new GpuChildThread(watchdog_thread.get(),  
  35.                                                     dead_on_arrival,  
  36.                                                     gpu_info,  
  37.                                                     deferred_messages.Get());  
  38.   ......  
  39.   
  40.   gpu_process.set_main_thread(child_thread);  
  41.   
  42.   {  
  43.     ......  
  44.     main_message_loop.Run();  
  45.   }  
  46.   
  47.   ......  
  48.   
  49.   return 0;  
  50. }  
       这个函数定义在文件external/chromium_org/content/gpu/gpu_main.cc中。

       函数GpuMain主要做了以下几件事情:

       1. 创建了一个IO类型的消息循环,后面将会将该消息循环作为当前线程的消息循环。

       2. 调用函数WarmUpSandbox判断是否需要执行一些预加载操作。如果需要的话,接下来就会继续判断当前进程的命令行参数是否包含了switches::kInProcessGPU选项。如果没有包括,那么再接下来就会调用我们前面分析过的gfx::GLSurface类的静态成员函数InitializeOneOff在当前进程加载OpenGL相关的库,以及创建一个EGLDisplay,以便以后可以用来创建OpenGL上下文。函数WarmUpSandbox的返回值总为true,并且当前进程的命令行参数没有包含switches::kInProcessGPU选项,因此最后会调用gfx::GLSurface类的静态成员函数InitializeOneOff。

       3. 创建了一个GpuProcess对象。从前面的分析可以知道,创建GpuProcess对象将会导致在当前进程中创建一个IO线程。这个IO线程是用来与其他进程(例如Browser进程、Render进程和Plugin进程)进行IPC的。

       4. 创建了一个GpuChildThread对象,该GpuChildThread对象描述的线程即为当前线程,并且会将当前线程设置为当前进程(也就是GPU进程)的主线程。从前面的分析可以知道,创建GpuChildThread对象将会触发创建一个Client端IPC通道。这个Client端IPC通道是GPU进程用来与其他进程(例如Browser进程、Render进程和Plugin进程)的Server端IPC通道进行IPC的。

       5. 通过前面创建的消息循环使得当前线程进入消息循环状态。

       这样,GPU进程的启动过程就完成了。回到前面分析的GpuProcessHost类的成员函数Init中,无论它之前启动的是一个GPU线程,还是一个GPU进程,都会向它发送一个类型为GpuMsg_Initialize的IPC消息,用来对GPU进程执行初始化工作。

       GpuMsg_Initialize是一个类型为MSG_ROUTING_CONTROL的IPC消息,根据前面Chromium的IPC消息发送、接收和分发机制分析一文的IPC消息分发机制,这个消息首先是分给在GPU进程或者GPU线程中创建的GpuChildThread对象的父类ChildThread的成员函数OnMessageReceived处理,如下所示:

[cpp]  view plain copy
  1. bool ChildThread::OnMessageReceived(const IPC::Message& msg) {  
  2.   ......  
  3.   
  4.   if (msg.routing_id() == MSG_ROUTING_CONTROL)  
  5.     return OnControlMessageReceived(msg);  
  6.   
  7.   ......  
  8. }  
       这个函数定义在文件external/chromium_org/content/child/child_thread.cc中。

       ChildThread的成员函数OnMessageReceived发现这是一个类型为MSG_ROUTING_CONTROL的IPC消息,于是就会将它分发给另外一个成员函数OnControlMessageReceived处理。GpuChildThread类重写了父类ChildThread的成员函数OnControlMessageReceived,因此接下来参数msg描述的IPC消息继续分发给GpuChildThread类的成员函数OnControlMessageReceived处理。

       GpuChildThread类的成员函数OnControlMessageReceived的实现如下所示:

[cpp]  view plain copy
  1. bool GpuChildThread::OnControlMessageReceived(const IPC::Message& msg) {  
  2.   bool handled = true;  
  3.   IPC_BEGIN_MESSAGE_MAP(GpuChildThread, msg)  
  4.     IPC_MESSAGE_HANDLER(GpuMsg_Initialize, OnInitialize)  
  5.     ......  
  6.     IPC_MESSAGE_UNHANDLED(handled = false)  
  7.   IPC_END_MESSAGE_MAP()  
  8.   
  9.   if (handled)  
  10.     return true;  
  11.   
  12.   ......  
  13. }  
       这个函数定义在文件external/chromium_org/content/gpu/gpu_child_thread.cc中。

       从这里可以看到,IPC消息GpuMsg_Initialize最后将由GpuChildThread类的成员函数OnInitialize进行处理,它的主要处理过程如下所示:

[cpp]  view plain copy
  1. void GpuChildThread::OnInitialize() {  
  2.   ......  
  3.   
  4.   gpu_channel_manager_.reset(  
  5.       new GpuChannelManager(GetRouter(),  
  6.                             watchdog_thread_.get(),  
  7.                             ChildProcess::current()->io_message_loop_proxy(),  
  8.                             ChildProcess::current()->GetShutDownEvent()));  
  9.     
  10.   ......  
  11. }  
       这个函数定义在文件external/chromium_org/content/gpu/gpu_child_thread.cc中。

       GpuChildThread类的成员函数OnInitialize主要是创建了一个GpuChannelManager对象,并且保存在成员变量gpu_channel_manager_中。这个GpuChannelManager是用来负责管理GPU进程与其它进程之间的GPU通道的,接下来我们分析GPU通道的创建过程时就会看到这一点。

       注意,GpuChildThread类的成员函数OnInitialize在创建上述GpuChannelManager对象时,会传递给它两个重要的参数,即第一个参数和第三个参数。其中,第三个参数是GPU进程负责用来与其它进程执行IPC的IO线程的消息循环。第一个参数是一个MessageRouter对象,是通过调用从父类ChildThread继承下来的成员函数GetRouter获得的,如下所示:

[cpp]  view plain copy
  1. MessageRouter* ChildThread::GetRouter() {  
  2.   ......  
  3.   return &router_;  
  4. }  
       这个函数定义在文件external/chromium_org/content/child/child_thread.cc中。

       ChildThread类的成员函数GetRouter返回的是成员变量router_描述的一个MessageRouter对象。后面我们分析GPU通道的创建过程时再解释它的作用。

       上述两个参数最后将会保存在GpuChannelManager类的成员变量router_和io_message_loop_中,如下所示:

[cpp]  view plain copy
  1. GpuChannelManager::GpuChannelManager(MessageRouter* router,  
  2.                                      GpuWatchdog* watchdog,  
  3.                                      base::MessageLoopProxy* io_message_loop,  
  4.                                      base::WaitableEvent* shutdown_event)  
  5.     : ......,  
  6.       io_message_loop_(io_message_loop),  
  7.       ......,  
  8.       router_(router),  
  9.       ...... {  
  10.   ......  
  11. }  
       这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel_manager.cc。

       以后涉及到IPC消息的分发过程中,在没有必要的情况下,我们将会略过中间的分发过程,而直接进入到最终的处理函数。中间略过的过程读者可以参考Chromium的IPC消息发送、接收和分发机制一文自行分析。

       这样,Browser进程启动GPU线程或者GPU进程的过程就分析完成了,但是这时候Browser进程与GPU线程或者GPU进程仅仅是建立了一个普通的IPC通道。从前面的图1可以看到,Browser进程与GPU进程之间的GPU操作是通过一个专门的GPU通道进行的,这一点也适用于GPU线程。因此,接下来Browser进程还需要与GPU进程或者GPU线程建立一个GPU通道。

       前面分析EstablishRequest类的成员函数EstablishOnIO的时候提到,它在启动了GPU进程或者GPU线程之后,会调用GpuProcessHost类的成员函数EstablishGpuChannel创建一个GPU通道,如下所示:

[cpp]  view plain copy
  1. void GpuProcessHost::EstablishGpuChannel(  
  2.     int client_id,  
  3.     bool share_context,  
  4.     const EstablishChannelCallback& callback) {  
  5.   ......  
  6.   
  7.   if (Send(new GpuMsg_EstablishChannel(client_id, share_context))) {  
  8.     channel_requests_.push(callback);  
  9.   }   
  10.   
  11.   ......  
  12. }  
       这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_process_host.cc中。

       GpuProcessHost类的成员函数EstablishGpuChannel向刚才启动起来的GPU进程或者GPU线程发送一个类型为GpuMsg_EstablishChannel的IPC消息。发送成功后,参数callback指向的一个EstablishChannelCallback对象将会保存在GpuProcessHost类的成员变量channel_requests_描述的一个std::queue中,以便等到发送出去的IPC消息得到回复时进行相应的处理。

       从前面的调用过程可以知道,参数callback指向的一个EstablishChannelCallback对象描述的是一个Task,该Task绑定的函数为BrowserGpuChannelHostFactory::EstablishRequest类的成员函数OnEstablishedOnIO,因此,当类型为GpuMsg_EstablishChannel的IPC消息得到回复时,BrowserGpuChannelHostFactory::EstablishRequest类的成员函数OnEstablishedOnIO会被调用。

       类型为GpuMsg_EstablishChannel的IPC消息由GpuChildThread类的成员函数OnControlMessageReceived分发给GpuChannelManager类的成员函数OnMessageReceived处理,如下所示:

[cpp]  view plain copy
  1. bool GpuChildThread::OnControlMessageReceived(const IPC::Message& msg) {  
  2.   ......  
  3.   
  4.   return gpu_channel_manager_.get() &&  
  5.       gpu_channel_manager_->OnMessageReceived(msg);  
  6. }  
      这个函数定义在文件external/chromium_org/content/gpu/gpu_child_thread.cc中。

      GpuChildThread类的成员变量gpu_channel_manager_指向的GpuChannelManager对象是GPU进程或者GPU线程前面接收到类型为GpuMsg_Initialize的IPC消息时创建的,GpuChildThread类的成员函数OnControlMessageReceived将类型为GpuMsg_EstablishChannel的IPC消息分给它处理,如下所示:

[cpp]  view plain copy
  1. bool GpuChannelManager::OnMessageReceived(const IPC::Message& msg) {  
  2.   bool handled = true;  
  3.   IPC_BEGIN_MESSAGE_MAP(GpuChannelManager, msg)  
  4.     IPC_MESSAGE_HANDLER(GpuMsg_EstablishChannel, OnEstablishChannel)  
  5.     ......  
  6.     IPC_MESSAGE_UNHANDLED(handled = false)  
  7.   IPC_END_MESSAGE_MAP()  
  8.   return handled;  
  9. }  
      这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel_manager.cc中。

      GpuChannelManager类的成员函数OnMessageReceived将类型为GpuMsg_EstablishChannel的IPC消息分给另外一个成员函数OnEstablishChannel处理,如下所示:

[cpp]  view plain copy
  1. void GpuChannelManager::OnEstablishChannel(int client_id, bool share_context) {  
  2.   IPC::ChannelHandle channel_handle;  
  3.   
  4.   gfx::GLShareGroup* share_group = NULL;  
  5.   gpu::gles2::MailboxManager* mailbox_manager = NULL;  
  6.   if (share_context) {  
  7.     if (!share_group_.get()) {  
  8.       share_group_ = new gfx::GLShareGroup;  
  9.       DCHECK(!mailbox_manager_.get());  
  10.       mailbox_manager_ = new gpu::gles2::MailboxManager;  
  11.     }  
  12.     share_group = share_group_.get();  
  13.     mailbox_manager = mailbox_manager_.get();  
  14.   }  
  15.   
  16.   scoped_ptr channel(new GpuChannel(  
  17.       this, watchdog_, share_group, mailbox_manager, client_id, false));  
  18.   channel->Init(io_message_loop_.get(), shutdown_event_);  
  19.   channel_handle.name = channel->GetChannelName();  
  20.   
  21. #if defined(OS_POSIX)  
  22.   // On POSIX, pass the renderer-side FD. Also mark it as auto-close so  
  23.   // that it gets closed after it has been sent.  
  24.   int renderer_fd = channel->TakeRendererFileDescriptor();  
  25.   DCHECK_NE(-1, renderer_fd);  
  26.   channel_handle.socket = base::FileDescriptor(renderer_fd, true);  
  27. #endif  
  28.   
  29.   gpu_channels_.set(client_id, channel.Pass());  
  30.   
  31.   Send(new GpuHostMsg_ChannelEstablished(channel_handle));  
  32. }  

       这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel_manager.cc中。

       从前面的调用过程可以知道,参数share_context的值等于true,表示要创建的GPU通道与其它的GPU通道共享相同的OpenGL组,同时它们也共享相同的Mailbox管理器。这个相同的OpenGL共享组和Mailbox共享管理器由GpuChannelManager类的成员变量share_group_和mailbox_manager_描述。

       因此,当参数share_context的值等于true,并且GpuChannelManager类的成员变量share_group_和mailbox_manager_又等于NULL的时候,GpuChannelManager类的成员函数OnEstablishChannel就分别创建一个gfx::GLShareGroup对象和一个gpu::gles2::MailboxManager对象保存在它们之上。

       关于OpenGL共享组和Mailbox管理器,在后面一个系列的文章中分析Chromium的GPU渲染机制时,我们再详细分析。

       GpuChannelManager类的成员函数OnEstablishChannel接下来创建了一个GpuChannel对象来描述一个GPU通道。这个GPU通道与前面分析的IPC通道一样,底层都是通过一个UNIX Socket来描述的。在创建GpuChannel对象的过程中,也就是在GpuChannel类的构造函数的执行过程中,主要是生成了一个UNIX Socket的名称,最后需要调用该GpuChannel对象的成员函数Init,才会根据前面生成的UINX Socket名称创建一个UNIX Socket。

       调用前面创建的GpuChannel对象的成员函数GetChannelName和TakeRendererFileDescriptor可以分别获得由它创建的UNIX Socket的名称和Client端文件描述符。获得的UNIX Socket的名称和Client端文件描述符封装在一个ChannelHandle对象中,该ChannelHandle对象又会进一步封装在一个类型为GpuHostMsg_ChannelEstablished的IPC消息中。这个IPC消息最终被发送到Browser进程中去,作为Browser进程发送过来的类型为GpuMsg_EstablishChannel的IPC消息的回复。

       注意,类型为GpuMsg_EstablishChannel的IPC消息封装的是一个ChannelHandle对象,也就是说,类型为GpuMsg_EstablishChannel的IPC消息的内容由ChannelHandle类描述。由于上述发送的类型为GpuMsg_EstablishChannel的IPC消息封装的ChannelHandle对象包含了文件描述符,即一个UNIX Socket的Client端文件描述符,因此该IPC消息最后将通过sendmsg接口发送出去。这一点可以参考前面Chromium的IPC消息发送、接收和分发机制分析一文。

       参数client_id描述的是请求创建GPU通道的Client端的ID。从前面的调用过程可以知道,这个GPU通道的Client端即为Browser进程。最后,创建的GPU通道将会以参数client_id为键值,保存在GpuChannelManager类的成员变量gpu_channels_描述的一个GPU通道表中。也就是说,GpuChannelManager类负责管理GPU进程或者GPU线程中的所有GPU通道。

       接下来,我们分析GPU通道的创建过程,也就是GpuChannel类的构造函数和成员函数Init的实现。

       GpuChannel类的构造函数的实现如下所示:

[cpp]  view plain copy
  1. GpuChannel::GpuChannel(GpuChannelManager* gpu_channel_manager,  
  2.                        GpuWatchdog* watchdog,  
  3.                        gfx::GLShareGroup* share_group,  
  4.                        gpu::gles2::MailboxManager* mailbox,  
  5.                        int client_id,  
  6.                        bool software)  
  7.     : gpu_channel_manager_(gpu_channel_manager),  
  8.       ......,  
  9.       client_id_(client_id),  
  10.       share_group_(share_group ? share_group : new gfx::GLShareGroup),  
  11.       mailbox_manager_(mailbox ? mailbox : new gpu::gles2::MailboxManager),  
  12.       ...... {  
  13.   ......  
  14.   channel_id_ = IPC::Channel::GenerateVerifiedChannelID("gpu");  
  15.   ......  
  16. }  
       这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel.cc中。

       GpuChannel类的构造函数调用IPC::Channel类的静态成员函数GenerateVerifiedChannelID生成了一个UNIX Socket名称,并且保存在成员变量中。这个UNIX Socket名称接下来就是用来创建UNIX Socket的。

       此外,我们还可以看到,如果参数share_group和mailbox的值等于NULL,即当前正在创建的GPU通道不与其它GPU通道共享OpenGL组和Mailbox管理器,那么GpuChannel类的构造函数就会为当前正在创建的GPU通道创建一个独享的OpenGL组和Mailbox管理器,并且分别保存在成员变量share_group_和mailbox_manager_中。

       GpuChannel类的成员函数Init的实现如下所示:

[cpp]  view plain copy
  1. void GpuChannel::Init(base::MessageLoopProxy* io_message_loop,  
  2.                       base::WaitableEvent* shutdown_event) {  
  3.   ......  
  4.   
  5.   // Map renderer ID to a (single) channel to that process.  
  6.   channel_ = IPC::SyncChannel::Create(channel_id_,  
  7.                                       IPC::Channel::MODE_SERVER,  
  8.                                       this,  
  9.                                       io_message_loop,  
  10.                                       false,  
  11.                                       shutdown_event);  
  12.   
  13.   filter_ =  
  14.       new GpuChannelMessageFilter(weak_factory_.GetWeakPtr(),  
  15.                                   gpu_channel_manager_->sync_point_manager(),  
  16.                                   base::MessageLoopProxy::current());  
  17.   io_message_loop_ = io_message_loop;  
  18.   channel_->AddFilter(filter_.get());  
  19.   
  20.   ......  
  21. }  
       这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel.cc中。

       GpuChannel类的成员函数Init调用IPC::SyncChannel类的静态成员函数Create根据前面生成的名称创建一个UNIX Socket,并且将这个UNIX Socket的Server端文件描述符封装在一个SyncChannel对象中。这个SyncChannel对象保存在GpuChannel类的成员变量channel_中。IPC::SyncChannel类的静态成员函数Create创建UNIX Socket以及Server端SyncChannel对象的过程可以参考前面Chromium的Render进程启动过程分析一文。

       GpuChannel类的成员函数Init接下来创建了一个类型为GpuChannelMessageFilter的Filter,并且注册到了前面创建的Server端SyncChannel对象中去。这样以后通过该SyncChannel对象接收到的IPC消息,根据我们前面Chromium的IPC消息发送、接收和分发机制分析一文的分析,首先是分发给GpuChannelMessageFilter类的成员函数OnMessageReceived处理。如果GpuChannelMessageFilter类的成员函数OnMessageReceived不处理,那么再接着再分发给GpuChannel类的成员函数OnMessageReceived处理。GpuChannelMessageFilter类主要是用来拦截GPU相关的操作消息,然后做GPU调度状态切换的。关于GPU调度,我们在后面一个系列文章中再详细分析。

       这一步执行完成之后,回到GpuChannelManager类的成员函数OnEstablishChannel中,它最后将前面创建的UNIX Socket的名称和Client端文件描述符通过一个类型为GpuHostMsg_ChannelEstablished的IPC消息发送给Browser进程。

       Browser进程通过GpuProcessHost类的成员函数OnMessageReceived接收上述类型为GpuHostMsg_ChannelEstablished的IPC消息,如下所示:

[cpp]  view plain copy
  1. bool GpuProcessHost::OnMessageReceived(const IPC::Message& message) {  
  2.   ......  
  3.   IPC_BEGIN_MESSAGE_MAP(GpuProcessHost, message)  
  4.     ......  
  5.     IPC_MESSAGE_HANDLER(GpuHostMsg_ChannelEstablished, OnChannelEstablished)  
  6.     ......  
  7.   IPC_END_MESSAGE_MAP()  
  8.   
  9.   return true;  
  10. }  
      这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_process_host.cc中。

      从这里可以看到,GpuProcessHost类的成员函数OnMessageReceived将类型为GpuHostMsg_ChannelEstablished的IPC消息发分给另外一个成员函数OnChannelEstablished处理。

      GpuProcessHost类的成员函数OnChannelEstablished的实现如下所示:

[cpp]  view plain copy
  1. void GpuProcessHost::OnChannelEstablished(  
  2.     const IPC::ChannelHandle& channel_handle) {  
  3.   ......  
  4.   
  5.   EstablishChannelCallback callback = channel_requests_.front();  
  6.   channel_requests_.pop();  
  7.   
  8.   ......  
  9.   
  10.   callback.Run(channel_handle,  
  11.                GpuDataManagerImpl::GetInstance()->GetGPUInfo());  
  12. }  
       这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_process_host.cc中。

       前面在分析GpuProcessHost类的成员函数EstablishGpuChannel时提到,Browser进程向GPU进程或者GPU线程发送一个类型为GpuMsg_EstablishChannel的IPC消息之后,会将一个绑定了BrowserGpuChannelHostFactory::EstablishRequest类的成员函数OnEstablishedOnIO的EstablishChannelCallback对象保存在GpuProcessHost类的成员变量channel_requests_描述的一个std::queue中。

       现在既然已经收到了类型为GpuMsg_EstablishChannel的IPC消息的回复,即一个类型为GpuHostMsg_ChannelEstablished的IPC消息,那么就可以调用上述EstablishChannelCallback对象绑定的函数了,即BrowserGpuChannelHostFactory::EstablishRequest类的成员函数OnEstablishedOnIO,它的实现如下所示:

[cpp]  view plain copy
  1. void BrowserGpuChannelHostFactory::EstablishRequest::OnEstablishedOnIO(  
  2.     const IPC::ChannelHandle& channel_handle,  
  3.     const gpu::GPUInfo& gpu_info) {  
  4.   if (channel_handle.name.empty() && reused_gpu_process_) {  
  5.     // We failed after re-using the GPU process, but it may have died in the  
  6.     // mean time. Retry to have a chance to create a fresh GPU process.  
  7.     DVLOG(1) << "Failed to create channel on existing GPU process. Trying to "  
  8.                 "restart GPU process.";  
  9.     EstablishOnIO();  
  10.   } else {  
  11.     channel_handle_ = channel_handle;  
  12.     gpu_info_ = gpu_info;  
  13.     FinishOnIO();  
  14.   }  
  15. }  
       这个函数定义在文件external/chromium_org/content/browser/gpu/browser_gpu_channel_host_factory.cc中。

       从前面的分析过程可以知道,参数channel_handle指向的一个ChannelHandle对象的成员变量name描述的是一个用来创建GPU通道的UNIX Socket的名称。当这个名称不等于空的时候,就说明成功创建了一个GPU通道。在这种情况下,BrowserGpuChannelHostFactory::EstablishRequest类的成员函数OnEstablishedOnIO就会将参数channel_handle指向的一个ChannelHandle对象保存在成员变量channel_handle_中,并且将参数gpu_info指向的一个GPUInfo对象保存在成员变量gpu_info_中,该GPU对象描述的是GPU相关的信息。

       BrowserGpuChannelHostFactory::EstablishRequest类的成员函数OnEstablishedOnIO最后调用另外一个成员函数FinishOnIO根据接收到的UNIX Socket的Client端文件描述符创建一个Client端GPU通道。

       BrowserGpuChannelHostFactory::EstablishRequest类的成员函数FinishOnIO的实现如下所示:

[cpp]  view plain copy
  1. void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnIO() {  
  2.   event_.Signal();  
  3.   main_loop_->PostTask(  
  4.       FROM_HERE,  
  5.       base::Bind(&BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain,  
  6.                  this));  
  7. }  
       这个函数定义在文件external/chromium_org/content/browser/gpu/browser_gpu_channel_host_factory.cc中。

       BrowserGpuChannelHostFactory::EstablishRequest类的成员函数FinishOnIO将创建Client端GPU通道的工作将给Browser进程的主线程处理,即在Browser进程的主线程中调用BrowserGpuChannelHostFactory::EstablishRequest类的成员函数FinishOnMain进行处理。

       BrowserGpuChannelHostFactory::EstablishRequest类的成员函数FinishOnMain的实现如下所示:

[cpp]  view plain copy
  1. void BrowserGpuChannelHostFactory::EstablishRequest::FinishOnMain() {  
  2.   if (!finished_) {  
  3.     BrowserGpuChannelHostFactory* factory =  
  4.         BrowserGpuChannelHostFactory::instance();  
  5.     factory->GpuChannelEstablished();  
  6.     finished_ = true;  
  7.   }  
  8. }  
      这个函数定义在文件external/chromium_org/content/browser/gpu/browser_gpu_channel_host_factory.cc中。

      BrowserGpuChannelHostFactory::EstablishRequest类的成员函数FinishOnMain首先获得当前进程中的一个BrowserGpuChannelHostFactory单例,然后调用它的成员函数GpuChannelEstablished创建一个Client端GPU通道。

      BrowserGpuChannelHostFactory类的成员函数GpuChannelEstablished的实现如下所示:

[cpp]  view plain copy
  1. void BrowserGpuChannelHostFactory::GpuChannelEstablished() {  
  2.   ......  
  3.   if (pending_request_->channel_handle().name.empty()) {  
  4.     ......  
  5.   } else {  
  6.     ......  
  7.     gpu_channel_ = GpuChannelHost::Create(this,  
  8.                                           pending_request_->gpu_info(),  
  9.                                           pending_request_->channel_handle(),  
  10.                                           shutdown_event_.get());  
  11.   }  
  12.   ......  
  13.   pending_request_ = NULL;  
  14.   
  15.   ......  
  16. }  
       这个函数定义在文件external/chromium_org/content/browser/gpu/browser_gpu_channel_host_factory.cc中。

       前面在分析BrowserGpuChannelHostFactory类的成员函数EstablishGpuChannel时提到,BrowserGpuChannelHostFactory类的成员变量pending_request_指向的是一个EstablishRequest对象,该EstablishRequest对象描述的是一个GPU通道创建请求。前面我们已经将接收到的用来创建Client端GPU通道的UNIX Socket的名称和Client端文件描述符保存在了其成员变量channel_handle_描述的一个ChannelHandle对象中。这个ChannelHandle对象可以通过调用上述的EstablishRequest对象的成员函数channel_handle获得。

       如前所述,当前面获得的ChannelHandle对象的成员变量name描述的字符串不等于空的时候,就说明前面成功创建了一个Server端GPU通道。在这种情况下,BrowserGpuChannelHostFactory类的成员函数GpuChannelEstablished就调用GpuChannelHost类的静态成员函数Create根据上述ChannelHandle对象创建一个Client端GPU通道,即一个GpuChannelHost对象,并且保存在成员变量gpu_channel_中。

       GpuChannelHost类的静态成员函数Create的实现如下所示:

[cpp]  view plain copy
  1. scoped_refptr GpuChannelHost::Create(  
  2.     GpuChannelHostFactory* factory,  
  3.     const gpu::GPUInfo& gpu_info,  
  4.     const IPC::ChannelHandle& channel_handle,  
  5.     base::WaitableEvent* shutdown_event) {  
  6.   ......  
  7.   scoped_refptr host = new GpuChannelHost(factory, gpu_info);  
  8.   host->Connect(channel_handle, shutdown_event);  
  9.   return host;  
  10. }  
       这个函数定义在文件external/chromium_org/content/common/gpu/client/gpu_channel_host.cc中。

       GpuChannelHost类的静态成员函数Create首先是创建了一个GpuChannelHost对象,接着调用该GpuChannelHost对象的成员函数Connect根据参数channel_handle描述的ChannelHandle对象创建一个Client端GPU通道。

       GpuChannelHost类的成员函数Connect的实现如下所示:

[cpp]  view plain copy
  1. void GpuChannelHost::Connect(const IPC::ChannelHandle& channel_handle,  
  2.                              base::WaitableEvent* shutdown_event) {  
  3.   // Open a channel to the GPU process. We pass NULL as the main listener here  
  4.   // since we need to filter everything to route it to the right thread.  
  5.   scoped_refptr io_loop = factory_->GetIOLoopProxy();  
  6.   channel_ = IPC::SyncChannel::Create(channel_handle,  
  7.                                       IPC::Channel::MODE_CLIENT,  
  8.                                       NULL,  
  9.                                       io_loop.get(),  
  10.                                       true,  
  11.                                       shutdown_event);  
  12.   
  13.   ......  
  14. }  

       这个函数定义在文件external/chromium_org/content/common/gpu/client/gpu_channel_host.cc中。

       GpuChannelHost类的成员函数Connect主要就是调用IPC::SyncChannel类的成员函数Create创建一个Client端GPU通道。从前面Chromium的Render进程启动过程分析一文可以知道,IPC::SyncChannel类的成员函数Create最后会调用ChannelPosix类的成员函数CreatePipe创建前面在GPU进程或者GPU线程中创建的UNIX Socket的Client端文件描述符,如下所示:

[cpp]  view plain copy
  1. bool ChannelPosix::CreatePipe(  
  2.     const IPC::ChannelHandle& channel_handle) {  
  3.   ......  
  4.   
  5.   int local_pipe = -1;  
  6.   if (channel_handle.socket.fd != -1) {  
  7.     ......  
  8.     local_pipe = channel_handle.socket.fd;  
  9.     ......  
  10.   }   
  11.   
  12.   ......  
  13.   
  14.   pipe_ = local_pipe;  
  15.   return true;  
  16. }  
       这个函数定义在文件external/chromium_org/ipc/ipc_channel_posix.cc中。

       从前面的调用过程可以知道,参数channel_handle指向的ChannelHandle对象包含了一个UNIX Socket的Client端文件描述符,这个Client端文件描述符就保存在该ChannelHandle对象的成员变量socket描述的一个FileDescriptor对象的成员变量fd中。因此,ChannelPosix类的成员函数CreatePipe就可以直接获得该UNIX Socket的Client端文件描述符,并且保存在成员变量pipe_中,以后就可以通过它向GPU进程或者GPU线程请求执行GPU操作,也就是通过GPU通道向GPU进程或者GPU线程请求执行GPU操作。

       这一步执行完成之后,Browser进程与GPU进程或者GPU线程之间的GPU通道就创建完成了。这个GPU通道与普通的IPC通道一样,都是通过UNIX Socket进行通信的,不过前者是专门用来执行GPU操作的。

       在Chromium中,除了Browser进程之外,Render进程和Plugin进程也需要执行GPU操作。这意味着Render进程和Plugin进程也需要和GPU进程或者GPU线程建立GPU通道。接下来我们就分析Render进程与GPU进程或者GPU线程建立GPU通道的过程。等到下一系列的文章分析完成Chromium的GPU渲染机制之后,我们再回头来分析Plugin进程与GPU进程或者GPU线程建立GPU通道的过程。

       Render进程在两种情况下需要使用GPU。第一种情况下是渲染网页内容的时候,第二种情况是网页包含了一个canvas标签的时候。接下来我们就分别分析这两种情况。

       Render进程使用一个content::RenderWidget对象来描述一个网页。在使用GPU渲染网页的时候,Render进程就会调用与该网页关联的content::RenderWidget对象的成员函数CreateOutputSurface创建一个绘图表面。该绘图表面最终会由交Browser进程与Chromium的其它UI进行合成,并且显示在屏幕中。在绘图表面的创建过程中,就会为之创建一个与GPU进程或者GPU线程进行通信的GPU通道,如下所示:

[cpp]  view plain copy
  1. scoped_ptr RenderWidget::CreateOutputSurface(bool fallback) {  
  2.   ......  
  3.   
  4. #if defined(OS_ANDROID)  
  5.   if (SynchronousCompositorFactory* factory =  
  6.       SynchronousCompositorFactory::GetInstance()) {  
  7.     return factory->CreateOutputSurface(routing_id());  
  8.   }  
  9. #endif  
  10.   
  11.   const CommandLine& command_line = *CommandLine::ForCurrentProcess();  
  12.   bool use_software = fallback;  
  13.   if (command_line.HasSwitch(switches::kDisableGpuCompositing))  
  14.     use_software = true;  
  15.   
  16.   scoped_refptr context_provider;  
  17.   if (!use_software) {  
  18.     context_provider = ContextProviderCommandBuffer::Create(  
  19.         CreateGraphicsContext3D(), "RenderCompositor");  
  20.     ......  
  21.   }  
  22.   
  23.   if (!context_provider.get()) {  
  24.     scoped_ptr software_device(  
  25.         new CompositorSoftwareOutputDevice());  
  26.   
  27.     return scoped_ptr(new CompositorOutputSurface(  
  28.         routing_id(),  
  29.         output_surface_id,  
  30.         NULL,  
  31.         software_device.Pass(),  
  32.         true));  
  33.   }  
  34.   
  35.   ......  
  36.   
  37.   bool use_swap_compositor_frame_message = false;  
  38.   return scoped_ptr(  
  39.       new CompositorOutputSurface(  
  40.           routing_id(),  
  41.           output_surface_id,  
  42.           context_provider,  
  43.           scoped_ptr(),  
  44.           use_swap_compositor_frame_message));  
  45. }  

       这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。

       在WebView版的Chromium中,调用SynchronousCompositorFactory类的静态成员函数GetInstance可以获得一个SynchronousCompositorFactory对象,在这种情况下,调用获得的SynchronousCompositorFactory对象的成员函数CreateOutputSurface创建一个绘图表面。

       在非WebView版的Chromium中,SynchronousCompositorFactory类的静态成员函数GetInstance的返回值为NULL,这时候RenderWidget类的成员函数CreateOutputSurface首先是检查Chromium的命令行参数是否设置了switches::kDisableGpuCompositing选项。如果设置了,那么就需要使用软件方式来渲染网页,这时候本地变量use_software的值设置为true。否则的话,可能使用GPU来渲染网页,取决于参数fallback的值。

       在使用GPU渲染的情况下,即本地变量use_software的值等于false的情况下,RenderWidget类的成员函数CreateOutputSurface首先调用另外一个成员函数CreateGraphicsContext3D创建图1所示的一个WebGraphicsContext3DCommandBufferImpl对象。这个WebGraphicsContext3DCommandBufferImpl对象封装了一个与GPU进程或者GPU线程通信的GPU通道。接下来这个WebGraphicsContext3DCommandBufferImpl对象又通过ContextProviderCommandBuffer类的静态成员函数Create封装在一个ContextProviderCommandBuffer对象中。最后这个ContextProviderCommandBuffer对象又被封装在一个CompositorOutputSurface对象中,用来描述网页的绘图表面。以后我们分析网页的渲染过程时,再详细分析这些对象的作用。现在,我们重点是要记住Render进程是通过WebGraphicsContext3DCommandBufferImpl类与GPU进程或者GPU线程建立GPU通道的。

       在使用软件方式渲染的情况下,即本地变量use_software的值等于false的情况下,RenderWidget类的成员函数CreateOutputSurface首先创建一个软件输出设备对象,即一个SoftwareOutputDevice对象,接着再根据该SoftwareOutputDevice对象创建一个CompositorOutputSurface对象来描述网页的绘图表面。

       此外,如果前面不能成功创建一个ContextProviderCommandBuffer对象,这种情况是由于不能成功创建一个WebGraphicsContext3DCommandBufferImpl对象引发的,也就是不能成功创建一个到GPU进程或者GPU线程的GPU通道,那么也改为上述的软件渲染方式。

       接下来我们假设Render进程通过GPU来渲染网页,这时候最重要的事情就是调用RenderWidget类的成员函数CreateGraphicsContext3D创建一个到GPU进程或者GPU线程的通道,如下所示:

[cpp]  view plain copy
  1. scoped_ptr  
  2. RenderWidget::CreateGraphicsContext3D() {  
  3.   ......  
  4.   
  5.   CauseForGpuLaunch cause =  
  6.       CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE;  
  7.   scoped_refptr gpu_channel_host(  
  8.       RenderThreadImpl::current()->EstablishGpuChannelSync(cause));  
  9.   
  10.   blink::WebGraphicsContext3D::Attributes attributes;  
  11.   attributes.antialias = false;  
  12.   attributes.shareResources = true;  
  13.   attributes.noAutomaticFlushes = true;  
  14.   attributes.depth = false;  
  15.   attributes.stencil = false;  
  16.   ......  
  17.   
  18.   scoped_ptr context(  
  19.       new WebGraphicsContext3DCommandBufferImpl(surface_id(),  
  20.                                                 GetURLForGraphicsContext3D(),  
  21.                                                 gpu_channel_host.get(),  
  22.                                                 attributes,  
  23.                                                 lose_context_when_out_of_memory,  
  24.                                                 limits,  
  25.                                                 NULL));  
  26.   return context.Pass();  
  27. }  
       这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。

       RenderWidget类的成员函数CreateGraphicsContext3D首先是调用RenderThreadImpl类的静态成员函数current获得一个RenderThreadImpl对象。从前面Chromium的Render进程启动过程分析一文可以知道,这个RenderThreadImpl对象是用来描述Render进程的主线程的,或者Browser进程中的Render线程的。

       RenderWidget类的成员函数CreateGraphicsContext3D接下来调用上面获得的RenderThreadImpl对象的在成员函数EstablishGpuChannelSync创建一个GPU通道,并且将该GPU通道封装在一个WebGraphicsContext3DCommandBufferImpl对象中。在创建这个WebGraphicsContext3DCommandBufferImpl对象的时候,通过一个WebGraphicsContext3D::Attributes对象描述它的属性。其中,这个WebGraphicsContext3D::Attributes对象的成员变量shareResources的值被设置为true,表示以后通过前面创建的WebGraphicsContext3DCommandBufferImpl对象创建的OpenGL上下文是在OpenGL共享组中的。这一点我们在后面一个系列的文章中分析Chromium的GPU渲染机制时,才详细分析。

      接下来,我们继续分析RenderThreadImpl类的在成员函数EstablishGpuChannelSync的实现,以便可以了解Render进程的GPU通道的创建过程,如下所示:

[cpp]  view plain copy
  1. GpuChannelHost* RenderThreadImpl::EstablishGpuChannelSync(  
  2.     CauseForGpuLaunch cause_for_gpu_launch) {  
  3.   ......  
  4.   
  5.   // Ask the browser for the channel name.  
  6.   int client_id = 0;  
  7.   IPC::ChannelHandle channel_handle;  
  8.   gpu::GPUInfo gpu_info;  
  9.   if (!Send(new GpuHostMsg_EstablishGpuChannel(cause_for_gpu_launch,  
  10.                                                &client_id,  
  11.                                                &channel_handle,  
  12.                                                &gpu_info)) ||  
  13. #if defined(OS_POSIX)  
  14.       channel_handle.socket.fd == -1 ||  
  15. #endif  
  16.       channel_handle.name.empty()) {  
  17.     // Otherwise cancel the connection.  
  18.     return NULL;  
  19.   }  
  20.    
  21.   ......  
  22.   
  23.   // Cache some variables that are needed on the compositor thread for our  
  24.   // implementation of GpuChannelHostFactory.  
  25.   io_message_loop_proxy_ = ChildProcess::current()->io_message_loop_proxy();  
  26.   
  27.   gpu_channel_ = GpuChannelHost::Create(  
  28.       this, gpu_info, channel_handle,  
  29.       ChildProcess::current()->GetShutDownEvent());  
  30.   return gpu_channel_.get();  
  31. }  
       这个函数定义在文件external/chromium_org/content$ vi renderer/render_thread_impl.cc中。

       RenderThreadImpl类的成员函数EstablishGpuChannelSync通过向Browser进程发送一个类型为GpuHostMsg_EstablishGpuChannel的IPC消息请求创建一个到GPU进程或者GPU线程的GPU通道。注意, 类型为GpuHostMsg_EstablishGpuChannel的IPC消息是一个同步IPC消息,当从RenderThreadImpl类的成员函数Send返回的时候,本地变量channel_handle描述的一个ChannelHandle对象包含了要创建的GPU通道所使用的UNIX Socket的名称和Client端文件描述符。关于同步IPC消息的发送过程,可以参考前面Chromium的IPC消息发送、接收和分发机制分析一文。

      获得了要创建的GPU通道所使用的UNIX Socket的名称和Client端文件描述符之后,RenderThreadImpl类的成员函数EstablishGpuChannelSync就调用前面分析过的GpuChannelHost类的静态成员函数Create创建一个Client端GPU通道。

      在前面Chromium的Render进程启动过程分析一文中提到,Browser进程在启动Render进程之前,即在RenderProcessHostImpl类的成员函数Init中,会调用RenderProcessHostImpl类的成员的另外一个成员函数CreateMessageFilters注册一系列的Filter,用来过滤从其它进程发送过来的IPC消息,如下所示:

[cpp]  view plain copy
  1. void RenderProcessHostImpl::CreateMessageFilters() {  
  2.   ......  
  3.   
  4.   gpu_message_filter_ = new GpuMessageFilter(GetID(), widget_helper_.get());  
  5.   AddFilter(gpu_message_filter_);  
  6.   
  7.   ......  
  8. };  
      这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_process_host_impl.cc中。

      其中的一个Filter为GpuMessageFilter,它用来处理从Render进程发送过来的与GPU操作相关的IPC消息,如下所示:

[cpp]  view plain copy
  1. bool GpuMessageFilter::OnMessageReceived(const IPC::Message& message) {  
  2.   bool handled = true;  
  3.   IPC_BEGIN_MESSAGE_MAP(GpuMessageFilter, message)  
  4.     IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuHostMsg_EstablishGpuChannel,  
  5.                                     OnEstablishGpuChannel)  
  6.     ......  
  7.     IPC_MESSAGE_UNHANDLED(handled = false)  
  8.   IPC_END_MESSAGE_MAP()  
  9.   return handled;  
  10. }  
      这个函数定义在文件external/chromium_org/content/browser/renderer_host/gpu_message_filter.cc中。

      从这里可以看到,从Render进程发送过来的类型为GpuHostMsg_EstablishGpuChannel的IPC消息由GpuMessageFilter类的成员函数OnEstablishGpuChannel处理,如下所示:

[cpp]  view plain copy
  1. void GpuMessageFilter::OnEstablishGpuChannel(  
  2.     CauseForGpuLaunch cause_for_gpu_launch,  
  3.     IPC::Message* reply_ptr) {  
  4.   ......  
  5.   
  6.   scoped_ptr reply(reply_ptr);  
  7.   ......  
  8.   
  9.   GpuProcessHost* host = GpuProcessHost::FromID(gpu_process_id_);  
  10.   if (!host) {  
  11.     host = GpuProcessHost::Get(GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED,  
  12.                                cause_for_gpu_launch);  
  13.     ......  
  14.   
  15.     gpu_process_id_ = host->host_id();  
  16.   
  17.     ......  
  18.   }  
  19.   
  20.   bool share_contexts = true;  
  21.   host->EstablishGpuChannel(  
  22.       render_process_id_,  
  23.       share_contexts,  
  24.       base::Bind(&GpuMessageFilter::EstablishChannelCallback,  
  25.                  weak_ptr_factory_.GetWeakPtr(),  
  26.                  base::Passed(&reply)));  
  27. }  
       这个函数定义在文件external/chromium_org/content/browser/renderer_host/gpu_message_filter.cc中。

       与前面分析的Browser进程创建一个GPU通道的过程类似,Browser进程在收到Render进程发送过来的创建GPU通道的请求时,首先是调用GpuProcessHost类的静态成员函数FromID检查GPU进程是否已经启动。如果还没有启动,即GpuProcessHost类的静态成员函数FromID的返回值为NULL,那么就会调用GpuProcessHost类的静态成员函数Get启动一个类型为GpuProcessHost::GPU_PROCESS_KIND_SANDBOXED的GPU进程或者GPU线程,即一个运行在沙箱中的GPU进程或者GPU线程,并且将分配给该GPU进程或者GPU线程的ID保存在GpuMessageFilter类的成员变量gpu_process_id_中。

       一旦GPU进程启动成功,或者GPU进程之前已经启动过,那么GpuMessageFilter类的成员函数OnEstablishGpuChannel会获得一个对应的GpuProcessHost对象,调用该GpuProcessHost对象的成员函数EstablishGpuChannel即可创建一个到GPU进程或者GPU线程的GPU通道。这一点前面已经分析过。

       在调用GpuProcessHost类的成员函数EstablishGpuChannel创建一个到GPU进程或者GPU线程的GPU通道的时候,指定的第二个参数share_contexts的值为true。根据前面的分析可以知道,在这种情况下,创建的GPU通道与其它GPU通道共享同一个OpenGL组。

       在调用GpuProcessHost类的成员函数EstablishGpuChannel创建一个到GPU进程或者GPU线程的GPU通道的时候,指定的第三个参数是一个绑定了GpuMessageFilter类的成员函数EstablishChannelCallback的Task。从前面的分析可以知道,当GPU进程成功创建了一个Server端GPU通道之后,就会将该Server端GPU通道使用的UNIX Socket的名称和Client端文件描述符通过一个类型为GpuHostMsg_ChannelEstablished的IPC返回给Browser进程,这时候GpuMessageFilter类的成员函数EstablishChannelCallback就会被调用,如下所示:

[cpp]  view plain copy
  1. void GpuMessageFilter::EstablishChannelCallback(  
  2.     scoped_ptr reply,  
  3.     const IPC::ChannelHandle& channel,  
  4.     const gpu::GPUInfo& gpu_info) {  
  5.   ......  
  6.   
  7.   GpuHostMsg_EstablishGpuChannel::WriteReplyParams(  
  8.       reply.get(), render_process_id_, channel, gpu_info);  
  9.   Send(reply.release());  
  10. }  
       这个函数定义在文件external/chromium_org/content/browser/renderer_host/gpu_message_filter.cc中。

       GpuMessageFilter类的成员函数EstablishChannelCallback获得的UNIX Socket的名称和Client端文件描述符封装在参数channel描述的一个ChannelHandle对象,这个ChannelHandle对象的内容最终会被写入到参数reply描述的一个IPC消息中,该IPC消息最终会被发送给Render进程,作为前面Render进程发送的类型为GpuHostMsg_EstablishGpuChannel的IPC消息的回复,使得Render进程可以获得上述的UNIX Socket的名称和Client端文件描述符,从而创建一个Client端GPU通道。

       这样,Render进程渲染网页所要用到的GPU通道就创建完成了,以后Render进程就可以通过该GPU通道来渲染网页了。

       接下来,我们再分析当网页包含了一个canvas标签时创建GPU通道的过程。当网页包含了一个canvas标签时,我们可以通过JS获得一个3D绘图接口,然后通过GPU来渲染canvas标签的内容,如下所示:

[javascript]  view plain copy
  1. "myCanvas">  
  2.   
  3. "text/javascript">  
  4. var canvas=document.getElementById('myCanvas');  
  5. var ctx=canvas.getContext('webgl');  
  6. ctx.fillStyle='#FF0000';  
  7. ctx.fillRect(0,0,80,100);  
  8.   
       Chromium通过WebKit解析网页的内容时,会为遇到的canvas标签创建一个HTMLCanvasElement对象。当我们在JS中以参数“webgl”调用canvas标签的成员函数getContext时,就会触发HTMLCanvasElement类的成员函数getContext被调用,如下所示:

[cpp]  view plain copy
  1. CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type, CanvasContextAttributes* attrs)  
  2. {  
  3.     // A Canvas can either be "2D" or "webgl" but never both. If you request a 2D canvas and the existing  
  4.     // context is already 2D, just return that. If the existing context is WebGL, then destroy it  
  5.     // before creating a new 2D context. Vice versa when requesting a WebGL canvas. Requesting a  
  6.     // context with any other type string will destroy any existing context.  
  7.     enum ContextType {  
  8.         Context2d,  
  9.         ContextWebkit3d,  
  10.         ContextExperimentalWebgl,  
  11.         ContextWebgl,  
  12.         // Only add new items to the end and keep the order of existing items.  
  13.         ContextTypeCount,  
  14.     };  
  15.   
  16.     // FIXME - The code depends on the context not going away once created, to prevent JS from  
  17.     // seeing a dangling pointer. So for now we will disallow the context from being changed  
  18.     // once it is created.  
  19.     if (type == "2d") {  
  20.         ......  
  21.         if (!m_context) {  
  22.             ......  
  23.             m_context = CanvasRenderingContext2D::create(thisstatic_cast(attrs), document().inQuirksMode());  
  24.             ......  
  25.         }  
  26.         return m_context.get();  
  27.     }  
  28.   
  29.     // Accept the the provisional "experimental-webgl" or official "webgl" context ID.  
  30.     ContextType contextType;  
  31.     bool is3dContext = true;  
  32.     if (type == "experimental-webgl")  
  33.         contextType = ContextExperimentalWebgl;  
  34.     else if (type == "webgl")  
  35.         contextType = ContextWebgl;  
  36.     else  
  37.         is3dContext = false;  
  38.   
  39.     if (is3dContext) {  
  40.         ......  
  41.         if (!m_context) {  
  42.             ......  
  43.             m_context = WebGLRenderingContext::create(thisstatic_cast(attrs));  
  44.             ......  
  45.         }  
  46.         return m_context.get();  
  47.     }  
  48.     return 0;  
  49. }  

      这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp中。

      当参数type的值等于“2d”的时候,表示要创建的是一个2D绘图接口,这时候HTMLCanvasElement类的成员函数getContext返回一个CanvasRenderingContext2D对象来描述该2D绘图接口,这是通过调用CanvasRenderingContext2D类的静态成员函数create创建的。

      当参数type的值等于"webgl"的时候,表示要创建的是一个3D绘图接口,这时候HTMLCanvasElement类的成员函数getContext返回一个WebGLRenderingContext对象来描述该3D绘图接口,这是通过调用WebGLRenderingContext类的静态成员函数create创建的。

      接下来我们就主要分析3D绘图接口的创建过程,也就是WebGLRenderingContext类的静态成员函数create的实现,如下所示:

[cpp]  view plain copy
  1. PassOwnPtrWillBeRawPtr WebGLRenderingContext::create(HTMLCanvasElement* canvas, WebGLContextAttributes* attrs)  
  2. {  
  3.     Document& document = canvas->document();  
  4.     LocalFrame* frame = document.frame();  
  5.     ......  
  6.   
  7.     Settings* settings = frame->settings();  
  8.     ......  
  9.   
  10.     blink::WebGraphicsContext3D::Attributes attributes = attrs->attributes(document.topDocument().url().string(), settings);  
  11.     OwnPtr context = adoptPtr(blink::Platform::current()->createOffscreenGraphicsContext3D(attributes, 0));  
  12.     ......  
  13.   
  14.     OwnPtrWillBeRawPtr renderingContext = adoptPtrWillBeNoop(new WebGLRenderingContext(canvas, context.release(), attrs));  
  15.     ......  
  16.   
  17.     return renderingContext.release();  
  18. }  
       这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/html/canvas/WebGLRenderingContext.cpp中。

       blink::Platform类的静态成员函数current返回的是一个RendererWebKitPlatformSupportImpl对象,这是一个描述平台相关属性的平台,WebGLRenderingContext类的静态成员函数create主要就是通过调用它的成员函数createOffscreenGraphicsContext3D为JS创建一个3D绘图上下文,最后将它封装在一个WebGLRenderingContext对象中返回给调用者。

       在Android平台上,RendererWebKitPlatformSupportImpl类的成员函数createOffscreenGraphicsContext3D的实现如下所示:

[cpp]  view plain copy
  1. blink::WebGraphicsContext3D*  
  2. RendererWebKitPlatformSupportImpl::createOffscreenGraphicsContext3D(  
  3.     const blink::WebGraphicsContext3D::Attributes& attributes) {  
  4.   return createOffscreenGraphicsContext3D(attributes, NULL);  
  5. }  
      这个函数定义在文件external/chromium_org/content/renderer/renderer_webkitplatformsupport_impl.cc中。

      RendererWebKitPlatformSupportImpl类的成员函数createOffscreenGraphicsContext3D调用另外一个重载版本的成员函数createOffscreenGraphicsContext3D来创建一个WebGraphicsContext3D对象,如下所示:

[cpp]  view plain copy
  1. blink::WebGraphicsContext3D*  
  2. RendererWebKitPlatformSupportImpl::createOffscreenGraphicsContext3D(  
  3.     const blink::WebGraphicsContext3D::Attributes& attributes,  
  4.     blink::WebGraphicsContext3D* share_context) {  
  5.   ......  
  6.   
  7. #if defined(OS_ANDROID)  
  8.   if (SynchronousCompositorFactory* factory =  
  9.           SynchronousCompositorFactory::GetInstance()) {  
  10.     return factory->CreateOffscreenGraphicsContext3D(attributes);  
  11.   }  
  12. #endif  
  13.   
  14.   scoped_refptr gpu_channel_host(  
  15.       RenderThreadImpl::current()->EstablishGpuChannelSync(  
  16.           CAUSE_FOR_GPU_LAUNCH_WEBGRAPHICSCONTEXT3DCOMMANDBUFFERIMPL_INITIALIZE));  
  17.   
  18.   WebGraphicsContext3DCommandBufferImpl::SharedMemoryLimits limits;  
  19.   bool lose_context_when_out_of_memory = false;  
  20.   return WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext(  
  21.       gpu_channel_host.get(),  
  22.       attributes,  
  23.       lose_context_when_out_of_memory,  
  24.       GURL(attributes.topDocumentURL),  
  25.       limits,  
  26.       static_cast(share_context));  
  27. }  
       这个函数定义在文件external/chromium_org/content/renderer/renderer_webkitplatformsupport_impl.cc中。

       RendererWebKitPlatformSupportImpl类的成员函数createOffscreenGraphicsContext3D与前面分析的RenderWidget类的成员函数CreateGraphicsContext3D的实现是差不多,它们都是首先通过调用RenderThreadImpl类的成员函数EstablishGpuChannelSync请求Browser进程创建一个到GPU进程或者GPU线程的GPU通道,接着再将该GPU通道封装在一个WebGraphicsContext3DCommandBufferImpl对象中。

       不过,RendererWebKitPlatformSupportImpl类的成员函数createOffscreenGraphicsContext3D通过调用WebGraphicsContext3DCommandBufferImpl类的静态成员函数CreateOffscreenContext来创建一个WebGraphicsContext3DCommandBufferImpl对象,如下所示:

[cpp]  view plain copy
  1. WebGraphicsContext3DCommandBufferImpl*  
  2. WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext(  
  3.     GpuChannelHost* host,  
  4.     const WebGraphicsContext3D::Attributes& attributes,  
  5.     bool lose_context_when_out_of_memory,  
  6.     const GURL& active_url,  
  7.     const SharedMemoryLimits& limits,  
  8.     WebGraphicsContext3DCommandBufferImpl* share_context) {  
  9.   ......  
  10.   
  11.   return new WebGraphicsContext3DCommandBufferImpl(  
  12.       0,  
  13.       active_url,  
  14.       host,  
  15.       attributes,  
  16.       lose_context_when_out_of_memory,  
  17.       limits,  
  18.       share_context);  
  19. }  
       这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。

       WebGraphicsContext3DCommandBufferImpl类的静态成员函数CreateOffscreenContext与RenderWidget类的成员函数CreateGraphicsContext3D的主要不同之处在于,前者
创建的WebGraphicsContext3DCommandBufferImpl对象关联的Surface ID值为0,而后者创建的WebGraphicsContext3DCommandBufferImpl对象关联的Surface ID值不为0。Surface ID描述的是一个OpenGL上下文所对应的绘图表面的,这个绘图表面绑定在一个EGLSurface中。在接下来一个系列的文章中分析Chromium的GPU渲染机制时,我们再详细分析OpenGL上下文相关的概念。

       这样,JS在使用3D绘图接口时所要用到的GPU通道就创建完成了,以后JS就可以通过该GPU通道来绘制相对标签的内容了。

       至此,我们就分析完成GPU进程或者GPU线程的启动过程,以及Browser进程和Render进程与GPU进程或者GPU线程建立GPU通道的过程了。事实上,Plugin进程在需要使用3D绘图接口的时候,也会像Render进程一样,请求Browser进程为其创建一个到GPU进程或者GPU线程的GPU通道。在接下来的一篇文章中,我们就继续分析Plugin进程的启动过程,等下一个系列的文章分析完成Chromium的GPU渲染机制之后,我们再回过头来分析Plugin进程创建到GPU进程或者GPU线程的GPU通道的过程,以及Plugin进程其于上述GPU通道使用GPU渲染UI的过程,敬请关注!更多的信息也可以关注老罗的新浪微博:http://weibo.com/shengyangluo。

你可能感兴趣的:(WebKit)