Chromium的GPU进程启动过程分析_第1页
Chromium的GPU进程启动过程分析_第2页
Chromium的GPU进程启动过程分析_第3页
Chromium的GPU进程启动过程分析_第4页
Chromium的GPU进程启动过程分析_第5页
已阅读5页,还剩55页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

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

2、ser进程会与它建立两个IPC通道。一个IPC通道用来传输普通的IPC消息,另一个IPC通道专门用来执行GPU操作,称为GPU通道。类似地,Render进程需要执行GPU操作时,也会通过Browser进程与GPU进程建立一个专门用来执行GPU操作的IPC通道。Render进程之所以要通过Browser进程间接地与GPU进程建立GPU通道,是因为GPU进程是由Browser进程启动的,Render进程对它一无所知。 以上描述的Browser进程、Render进程和GPU进程的关系可以通过图1概括,如下所示:在图1中,Browser进程与Render进程的IPC通道的建立过程可以参考前面一文,本文

3、只分析以下三部分内容: 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渲染网页的时候,会通过之前

4、与Browser进程建立的IPC通道请求Browser进程为它创建一个GPU通道,并且将该GPU通道封装在一个WebGraphicsContext3DCommandBufferImpl对象,以后就可以通过该WebGraphicsContext3DCommandBufferImpl对象向GPU进程请求执行GPU操作了。 GPU进程在启动的过程中,也会像Browser进程和Render进程一样,启动一个IO线程,专门用来执行IPC。以后每当GPU进程通过上述IPC通道接收到一个创建GPU通道的请求的时候,都会在内部创建一个OpenGL上下文。这个OpenGL上下文通过一个GLContext对象描述

5、。这样在GPU进程中,就会存在若干个OpenGL上下文。这些OpenGL上下文都运行在同一个线程中,这个线程称为GPU Child Thread。这样就会涉及到一个OpenGL上下文调度问题,即每当GPU进程接收到一个GPU操作请求时,都要先切换到请求的GPU操作所在的OpenGL上下文,然后才能执行请求的GPU操作。关于GPU进程的OpenGL上下文调度问题,我们在下一系列的文章中再详细分析。 接下来,我们就先分析Browser进程启动GPU进程的过程。这个过程主要是涉及到Browser进程和GPU进程的IPC通道的建立过程。 在前面一文中,我们提到,Browser进程,也就是Chromiu

6、m应用程序的主进程,在启动的时候,会调用BrowserMainLoop类的成员函数CreateStartupTasks。BrowserMainLoop类的成员函数CreateStartupTasks会请求启动一个GPU进程,相关的代码如下所示:cpp view plain copyvoid BrowserMainLoop:CreateStartupTasks() . / First time through, we really want to create all the tasks if (!startup_task_runner_.get() startup_task_runner_ =

7、 make_scoped_ptr(new StartupTaskRunner( base:Bind(&BrowserStartupComplete), base:MessageLoop:current()-message_loop_proxy(); . StartupTask browser_thread_started = base:Bind( &BrowserMainLoop:BrowserThreadsStarted, base:Unretained(this); startup_task_runner_-AddTask(browser_thread_started); . if (Br

8、owserMayStartAsynchronously() startup_task_runner_-StartRunningTasksAsync(); if (!BrowserMayStartAsynchronously() . startup_task_runner_-RunAllTasksNow(); 这个函数定义在文件external/chromium_org/content/browser/browser_main_loop.cc中。 BrowserMainLoop类的成员函数CreateStartupTasks首先是会创建一个StartupTaskRunner对象,并且保存在成员变

9、量startup_task_runner_中。这个StartupTaskRunner对象封装了当前线程的一个消息循环,因此通过它可以向当前线程的消息队列发送消息。当前线程即为Browser进程的主线程,因此有了这个StartupTaskRunner对象之后,接下来可以向其主线程的消息队列发送消息。 BrowserMainLoop类的成员函数CreateStartupTasks接下来创建了一个StartupTask,这个StartupTask绑定的函数为BrowserMainLoop类的成员函数BrowserThreadsStarted,用来执行一个Browser线程启动完毕任务,并且会保存在前

10、面创建的一个StartupTaskRunner对象的内部等待执行。 最后,取决于Browser进程使用同步还是异步方式启动,BrowserMainLoop类的成员函数CreateStartupTasks使用不同的方式来执行保存在成员变量startup_task_runner_指向的一个StartupTaskRunner对象中的StartupTask: 1. 如果是使用同步方式启动,那么就调用上述StartupTaskRunner对象的成员函数RunAllTasksNow立即执行保存在它里面的各个StartupTask对象所描述的任务。 2. 如果是使用异步方式启动,那么就调用上述Startup

11、TaskRunner对象的成员函数StartRunningTasksAsync向主线程的消息队列发送一个消息,当该消息被处理时,再执行保存在上述StartupTaskRunner对象里面的各个StartupTask对象所描述的任务。 无论是同步方式,还是异步方式,最终都会在主线程中调用BrowserMainLoop类的成员函数BrowserThreadsStarted,与GPU进程启动相关的代码如下所示:cpp view plain copyint BrowserMainLoop:BrowserThreadsStarted() . bool initialize_gpu_data_manage

12、r = true; #if defined(OS_ANDROID) / On Android, GLSurface:InitializeOneOff() must be called before initalizing / the GpuDataManagerImpl as it uses the GL bindings. / if (!gfx:GLSurface:InitializeOneOff() . initialize_gpu_data_manager = false; #endif if (initialize_gpu_data_manager) GpuDataM

13、anagerImpl:GetInstance()-Initialize(); bool always_uses_gpu = true; bool established_gpu_channel = false; #if defined(USE_AURA) | defined(OS_MACOSX) if (ShouldInitializeBrowserGpuChannelAndTransportSurface() established_gpu_channel = true; if (!GpuDataManagerImpl:GetInstance()-CanUseGpuBrowserCompos

14、itor() established_gpu_channel = always_uses_gpu = false; BrowserGpuChannelHostFactory:Initialize(established_gpu_channel); . #elif defined(OS_ANDROID) established_gpu_channel = true; BrowserGpuChannelHostFactory:Initialize(established_gpu_channel); #endif . 这个函数定义在文件external/chromium_org/content/br

15、owser/browser_main_loop.cc中。 在Android平台上,BrowserMainLoop类的成员函数BrowserThreadsStarted首先调用gfx:GLSurface类的静态成员函数InitializeOneOff在当进程中加载合适的OpenGL库,以及创建一个EGLDisplay。这样做有两个原因,一是后面调用GpuDataManagerImpl类的成员函数Initialize时,在Android平台上需要通过加载的OpenGL库来获取GPU信息,二是Android平台的Chromium实际上并没有独立的GPU进程,而是在Browser进程中创建一个GPU线

16、程,不过这个GPU线程起到的作用与GPU进程是一样的。上述第二个原因要求Browser进程要做一些GPU相关的初始化工作,即加载合适的OpenGL库,以及创建一个EGLDisplay,以后创建OpenGL上下文时需要使用到这个EGLDisplay。对于独立GPU进程的情况,上述的GPU初始化也是需要做的。后面我们就会看到,GPU进程在启动的时候,会调用gfx:GLSurface类的静态成员函数InitializeOneOff。 只有在gfx:GLSurface类的静态成员函数InitializeOneOff的返回值为true,即在当进程中成功加载了合适的OpenGL库之后,BrowserMai

17、nLoop类的成员函数Initialize才会被调用,负责检查当前设备使用的GPU及其相关的驱动是否在黑名单中。如果在黑名单中,那么Chromium就不会采用GPU对网页进行硬件加速渲染。这是由于不是所有的GPU都能够很好地支持Chromium进行硬件加速渲染,因此就需要设置一个黑名单,避免在渲染网页的过程中出现错误。一旦不能使用GPU对网页进行硬件加速渲染,那么Chromium就会退而求其次,使用CPU进行渲染。 如果Chromium在编译时定义了宏USE_AURA,那么就表示要使用GPU对网页进行硬件加速渲染,这时候就可能需要启动GPU进程。AURA是Chromium 35引入的一个窗口管

18、理框架,通过GPU来实现界面上的像按钮、滚动条和对话框等界面控件。但是由于GPU黑名单的存在,因此就不一定能够如愿地使用GPU对网页进行硬件加速渲染。这时候就需要进一步调用函数ShouldInitializeBrowserGpuChannelAndTransportSurface以及GpuDataManagerImpl类的成员函数CanUseGpuBrowserCompositor进行判断。如果最终确定不能够使用GPU对网页进行硬件加速渲染,那么接下来在调用BrowserGpuChannelHostFactory类的静态成员函数Initialize的时候,传递进去的参数就会等于false。对于

19、Mac OS X平台,也会进行相同的处理。 如果Chromium在编译时没有定义宏USE_AURA,但是当前平台是Android,那么BrowserMainLoop类的成员函数BrowserThreadsStarted就直接将本地变量established_gpu_channel设置为true,并且以其为参数,调用BrowserGpuChannelHostFactory类的静态成员函数Initialize启动一个GPU进程。这表明在Android平台上,要求GPU能够支持Chromium对网页进行硬件加速渲染。 为了更好地理解上面分析的内容,接下来我们在分析GPU进程的启动过程,也就是Brow

20、serGpuChannelHostFactory类的静态成员函数Initialize之前,先分析以下几个函数: 1. gfx:GLSurface:InitializeOneOff 2. GpuDataManagerImpl:Initialize 3. ShouldInitializeBrowserGpuChannelAndTransportSurface 4. GpuDataManagerImpl:CanUseGpuBrowserCompositor gfx:GLSurface类的静态成员函数InitializeOneOff负责在当前进程中加载合适的OpenGL库,它的实现如下所示:cpp v

21、iew plain copybool GLSurface:InitializeOneOff() . std:vector allowed_impls; GetAllowedGLImplementations(&allowed_impls); . CommandLine* cmd = CommandLine:ForCurrentProcess(); / The default implementation is always the first one in list. GLImplementation impl = allowed_impls0; bool fallback_to_osmesa

22、 = false; if (cmd-HasSwitch(switches:kOverrideUseGLWithOSMesaForTests) impl = kGLImplementationOSMesaGL; else if (cmd-HasSwitch(switches:kUseGL) std:string requested_implementation_name = cmd-GetSwitchValueASCII(switches:kUseGL); if (requested_implementation_name = any) fallback_to_osmesa = true; el

23、se if (requested_implementation_name = swiftshader) impl = kGLImplementationEGLGLES2; else impl = GetNamedGLImplementation(requested_implementation_name); if (std:find(allowed_impls.begin(), allowed_impls.end(), impl) = allowed_impls.end() . return false; . return InitializeOneOffImplementation( imp

24、l, fallback_to_osmesa, gpu_service_logging, disable_gl_drawing); 这个函数定义在文件external/chromium_org/ui/gl/gl_surface.cc中。 gfx:GLSurface类的静态成员函数InitializeOneOff首先是调用另外一个函数GetAllowedGLImplementations获得当前平台所支持的OpenGL实现版本列表。对于Android平台,它的实现如下所示:cpp view plain copyvoid GetAllowedGLImplementations(std:vector*

25、 impls) impls-push_back(kGLImplementationEGLGLES2); impls-push_back(kGLImplementationOSMesaGL); 这个函数定义在文件external/chromium_org/ui/gl/gl_implementation_android.cc中。 从这里可以看到,在Android平台上,Chromium支持两个版本的OpenGL实现,其中一个是kGLImplementationEGLGLES2,另一个是kGLImplementationOSMesaGL。kGLImplementationEGLGLES2描述的Ope

26、nGL即为Android系统本身提供的OpenGL实现,这个就是由底层的GPU实现的OpenGL库。kGLImplementationOSMesaGL描述的OpenGL是由Mesa实现的OpenGL库。Mesa是一个开源的OpenGL实现框架,它可以以软件方式模拟GPU硬件加速渲染,也可以通过底层真实的GPU来实现硬件加速渲染。 回到gfx:GLSurface类的静态成员函数InitializeOneOff中,它获得当前平台所支持的OpenGL实现版本列表之后,取出列表中的第一个版本作为默认版本。从前面的分析就可以知道,对于Android平台,这个默认的OpenGL实现版本就是kGLImple

27、mentationEGLGLES2描述的版本。 接下来,gfx:GLSurface类的静态成员函数InitializeOneOff再根据命令行参数选择最终使用的OpenGL实现版本: 1. 如果设置了switches:kOverrideUseGLWithOSMesaForTests选项,那么就表示要使用kGLImplementationOSMesaGL描述的OpenGL版本,方便用来测试。 2. 如果设置了switches:kUseGL选项,那么就根据这个选项的值选择指定的OpenGL实现。不过有两种特殊情况。一是当该选项值等于any时,默认使用之前选择的OpenGL实现版本,但是如果不能成功

28、加载该版本的库,那么就改为使用kGLImplementationOSMesaGL描述的OpenGL版本。二是当该选项的值等于swiftshader时,使用kGLImplementationEGLGLES2描述的OpenGL版本。SwiftShader是一件纯软件实现的3D渲染引擎工具,由TransGaming公司实现,宣称支持所有的Pixel和Vertex Shader DX9特效,并且可以获得比微软D3D的REF设备(reference rasterizer)快50倍的速度。在Android平台上,没有提供SwiftShader,因此用kGLImplementationEGLGLES2描述的

29、OpenGL版本替代。 最后,gfx:GLSurface类的静态成员函数InitializeOneOff调用另外一个成员函数InitializeOneOffImplementation在当前进程中加载前面所选择的OpenGL实现版本,它的实现如下所示:cpp view plain copybool GLSurface:InitializeOneOffImplementation(GLImplementation impl, bool fallback_to_osmesa, bool gpu_service_logging, bool disable_gl_drawing) bool initi

30、alized = InitializeStaticGLBindings(impl) & InitializeOneOffInternal(); if (!initialized & fallback_to_osmesa) ClearGLBindings(); initialized = InitializeStaticGLBindings(kGLImplementationOSMesaGL) & InitializeOneOffInternal(); if (!initialized) ClearGLBindings(); . return initialized; 这个函数定义在文件exte

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

32、但是不能成功创建一个EGLDisplay,并且参数fallback_to_osmesa的值为true,那么就如前所述,改为使用kGLImplementationOSMesaGL描述的OpenGL实现版本,也就是由Mesa实现的OpenGL库。 接下来,我们先分析函数InitializeStaticGLBindings的实现,接着再分析函数InitializeOneOffInternal的实现。 函数InitializeStaticGLBindings的实现如下所示:cpp view plain copybool InitializeStaticGLBindings(GLImplementati

33、on implementation) . switch (implementation) case kGLImplementationEGLGLES2: base:NativeLibrary gles_library = LoadLibraryAndPrintError(libGLESv2.so); . base:NativeLibrary egl_library = LoadLibraryAndPrintError(libEGL.so); . GLGetProcAddressProc get_proc_address = reinterpret_cast( base:GetFunctionP

34、ointerFromNativeLibrary( egl_library, eglGetProcAddress); . SetGLGetProcAddressProc(get_proc_address); AddGLNativeLibrary(egl_library); AddGLNativeLibrary(gles_library); SetGLImplementation(kGLImplementationEGLGLES2); InitializeStaticGLBindingsGL(); InitializeStaticGLBindingsEGL(); . break; . return

35、 true; 这个函数定义在文件external/chromium_org/ui/gl/gl_implementation_android.cc中。 从这里就可以看到,与kGLImplementationEGLGLES2描述的OpenGL实现版本相关的库有两个,分别为libGLESv2.so和libEGL.so,前者描述的是OpenGL实现,后者描述的是EGL实现。调用函数LoadLibraryAndPrintError加载了这两个库之后,最后分别调用了函数InitializeStaticGLBindingsGL和InitializeStaticGLBindingsEGL创建了一个RealGL

36、Api接口和一个RealEGLApi接口,这样以后就可以通过这两个接口调用由前面加载的libGLESv2.so和libEGL.so所导出的gl*和egl*函数。 这一步执行完成之后,回到前面分析的gfx:GLSurface类的静态成员函数InitializeOneOffImplementation中,它接下来调用函数InitializeOneOffInternal创建一个EGLDisplay,以验证前面加载的OpenGL相关的库的正确性。 函数InitializeOneOffInternal的实现如下所示:cpp view plain copybool GLSurface:Initialize

37、OneOffInternal() switch (GetGLImplementation() case kGLImplementationEGLGLES2: if (!GLSurfaceEGL:InitializeOneOff() LOG(ERROR) Initialize(); 这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_data_manager_impl.cc中。 GpuDataManagerImpl类的成员变量private_指向的是一个GpuDataManagerImplPrivate对象,这里调用它的成员函数Initi

38、alize检查设备配置的GPU是否在黑名单列表中。 GpuDataManagerImplPrivate类的成员函数Initialize的实现如下所示:cpp view plain copyvoid GpuDataManagerImplPrivate:Initialize() . gpu:GPUInfo gpu_info; if (command_line-GetSwitchValueASCII( switches:kUseGL) = gfx:kGLImplementationOSMesaName) / If using the OSMesa GL implementation, use fak

39、e vendor and device ids to / make sure it never gets blacklisted. This is better than simply / cancelling GPUInfo gathering as it allows us to proceed with loading the / blacklist below which may have non-device specific entries we want to / apply anyways (e.g., OS version blacklisting). gpu_info.gp

40、u.vendor_id = 0xffff; gpu_info.gpu.device_id = 0xffff; / Also declare the driver_vendor to be osmesa to be able to specify / exceptions based on driver_vendor=osmesa for some blacklist rules. gpu_info.driver_vendor = gfx:kGLImplementationOSMesaName; else . gpu:CollectBasicGraphicsInfo(&gpu_info); st

41、d:string gpu_blacklist_string; std:string gpu_driver_bug_list_string; if (!command_line-HasSwitch(switches:kIgnoreGpuBlacklist) & !command_line-HasSwitch(switches:kUseGpuInTests) gpu_blacklist_string = gpu:kSoftwareRenderingListJson; if (!command_line-HasSwitch(switches:kDisableGpuDriverBugWorkaroun

42、ds) gpu_driver_bug_list_string = gpu:kGpuDriverBugListJson; InitializeImpl(gpu_blacklist_string, gpu_driver_bug_list_string, gpu_info); 这个函数定义在文件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,以及

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论