首页 > 代码库 > Chromium Graphics: 3D上下文及其虚拟化 - Part II
Chromium Graphics: 3D上下文及其虚拟化 - Part II
Part I介绍了OpenGL上下文和绘制表面等基本概念,以及Chromium为什么需要多个3D上下文。本文将继续这个话题,探讨Chromium为什么引入虚拟3D上下文,以及虚拟上下文之间切换有何不同。
多个3D上下文的限制
Chromium需要使用多个3D上下文,并且这些上下文能够共享资源。然而,目前图形驱动对多个3D上下文的支持存在不同程度的问题,尤其是在移动设备上,例如(参见http://crbug.com/155557 ):
有些驱动一旦使用多个上下文就会暴露这样或那样的问题;
有些驱动在共享资源的多个上下文之间切换时很慢;
有些驱动限制了多个上下文数量;
有些驱动在资源共享方面存在问题,例如在一个上下文中更新了一个资源,但另一个上下文却得不到这个更新。
虚拟的3D上下文
对存在上述问题的GPU设备,Chromium引入了虚拟上下文的概念,其核心思路是尽可能少地创建实际的3D上下文,使得多个虚拟上下文可共享一个实际的上下文,而虚拟上下文的切换不一定不会导致实际上下文的切换,如此不仅减少了实际的上下文数量,而且有效地避免了上下文切换的性能开销。
Chromium只会对存在问题的GPU设备采用创建虚拟上下文的方法,也就说,对于多上下文支持良好的GPU设备,是不会创建虚拟上下文的。文件gpu/config/gpu_driver_bug_list_json.cc列举所有可能有问题的GPU驱动,包括上述多上下文支持的问题,Chromium运行时读取这个列表并与当前系统的GPU驱动进行匹配,如果匹配成功并且包含特性use_virtualized_gl_contexts时,那么Chromium将会使用虚拟上下文的方法。
GPU线程收到来自客户端(可能是Browser进程,也可能是Renderer进程)的上下文创建请求后,会请求GPU驱动创建上下文实例:
如果GPU驱动能够支持多上下文时,GPU线程会为每个请求都创建一个真实的上下文;
如果GPU驱动不能支持多上下文时, GPU线程则会为每个请求创建一个虚拟的上下文;
对于情况1,GPU线程直接创建平台相关的上下文。需要特别说明的是,GPU线程中所有创建的上下文都属于同一个共享组(sharegroup)。同一共享组中所有上下文之间资源是可以共享的,共享意味着它们使用的是同一个名字空间,任何一个上下文创建的Texture都可以被其他上下文所使用。可共享的资源包括Texture,Buffer对象和GLSL对象等。因此,Browser端的Compositor可直接访问WebGL上下文中的Texture,用来最终的屏幕合成。
对于情况2,GPU线程创建的是虚拟上下文,并且所有的虚拟上下文都将共享同一个真实的上下文。
不论是真实上下文,还是虚拟上下文,必须与一个与其兼容的渲染表面(Drawing Surface)才能被切换成为“当前”上下文,启动OpenGL的渲染流水线。
渲染表面与上下文切换的关系
GLX和EGL规范中都明确定义了,要让GLXContext或EGLContext成为“当前”的上下文还需要指定渲染表面。Chromium定义了两类渲染表面:
屏上(onscreen)渲染表面:每个onscreen的渲染表面都是窗口系统的一个窗口,根据窗口ID或者句柄创建而来,具有前后双缓冲区,通过交换缓冲区将渲染结果呈现在屏幕上的窗口中。Chromium只会为Browser进程端的Compositor创建这个渲染表面作为合成表面(compositingsurface);
离屏(offscreen)渲染表面:只有后缓冲区,内容在屏幕不可见。以WebGL为例,Chromium通过Framebuffer对象将WebGL渲染到一个Texture,而WebGL上下文所使用的渲染表面正是一个构建在PBuffer上的offscreen渲染表面。
有了渲染表面,便可以将一个上下文切换成为“当前”上下文。真实上下文之间的切换,实际是OpenGL状态机之间的切换,而虚拟上下文的切换,Chromium是不会触发真实上下文的切换,尽管渲染表面是从onscreen类型切换到offscreen类型的。
前面提到,GPU线程收到上下文创建请求后,创建虚拟上下文之前,会首先尝试从共享组(share group)获取一个真实上下文,如果没有,则需要创建这样一个上下文并添加到共享组中,再创建一个构建在这个真实上下文之上的虚拟上下文,然后以渲染表面初始化这个虚拟上下文,这里再强调一下,上下文需要一个与其兼容的渲染表面才能成为能够执行GL命令的“当前”上下文。
如果GPU线程收来自Browser进程端的请求,要为Compositor创建虚拟上下文,那么这个渲染表面是一个onscreen渲染表面(从窗口ID创建而来);
如果GPU线程收到来自Render进程端的请求,要为WebGL创建虚拟上下文,那么这个渲染表面是一个offscreen渲染表面(Pbuffer)。
创建上下文之后,GPU线程将交替执行不同上下文中的GL命令,这就意味着GPU线程能够在虚拟上下文之间进行切换。那么虚拟上下文的切换有什么不同呢?
第一,因为只有真实上下文才会拥有一个OpenGL状态机,所以在使用虚拟上下文的情况下,GPU线程只有一个OpenGL状态机,而这个状态机被不同目的的虚拟上下文(如Compositor,WebGL等)所复用。为了确保来回切换时能够恢复上一次的OpenGL状态,Chromium为每个虚拟上下文都提供了一个状态恢复器用以OpenGL状态的恢复。OpenGL的状态包括,
Framebuffer对象的绑定
所有Texture单元的绑定
顶点属性
Buffer对象和RenderBuffer的绑定
OpenGL全局状态
第二,虚拟上下文的切换只是逻辑上的上下文切换,并不会导致真实上下文的切换(因为只有一个真实上下文),尽管Compositor和WebGL上下文使用的是不同的渲染表面,但仍然不会因渲染表面的不同而导致上下文的切换。具体的来说,假如当前GPU线程已经工作在Compositor的虚拟上下文中,使用的是onscreen渲染表面,此时GPU线程需要切换到WebGL上下文去执行GL命令,而WebGL虚拟上下文时使用的offscreen渲染表面(Pbuffer),即便是两者具有不同类型的渲染表面,也不会导致真实上下文的切换。因为对于WebGL的离屏渲染方式来说,它是通过Framebuffer对象将内容渲染到一个Texture上,所以指定的渲染表面既可以是onscreen,也可以是offscreen的,只是对于onscreen的情况来说,使用的仅仅是它的后缓冲区,最终的内容还是在Texture中,所以不用关心用的是哪类渲染表面。
那么,在哪些情况下虚拟上下文的切换才会导致真实上下文的切换呢?
第一次切换到Browser进程端的Compositor虚拟上下文时,此时整个GPU线程还没有绑定一个有效的“当前”上下文,需要执行真实上下文的切换,保证GPU线程有个共享的OpenGL状态机;
当两个不在同一共享组的虚拟上下文切换时,表面这两个虚拟上下文具有对应的真实上下文。
当两个虚拟上下文同时需要在两个不同的onscreen表面切换时,不论是否在同一个共享组中。
目前Chromium的实现中,实际上只有情况1才会发生,而情况2和3只是从代码逻辑中的推理而得出来的。
未完待续...下节将从Chromium源码解读上下文以及虚拟化是如何实现的。
本文出自 “洪波的技术专栏” 博客,请务必保留此出处http://hongbo.blog.51cto.com/4699661/1548515
Chromium Graphics: 3D上下文及其虚拟化 - Part II