在页面渲染过程中,Chromium需要创建多个3D上下文的实例,这些上下文实例彼此之间不但需要共享资源,并且必须在同一个线程中执行GL操作,这就要求Chromium能够有效地处理多个上下文之间的切换。然而,并不是所有的GPU设备都能够很好的支持多个3D,虚拟化3D上下文(Context Virtualization)就是为了解决多这个问题而引入的,核心思想是通过虚拟化3D上下文,使得多个虚拟的上下文可以共享同一个真实的3D上下文,虚拟上下文的切换并不一定导致真实上下文的切换,从而减少真实上下文的个数,避免不必要的切换。本文介绍OpenGL上下文等基本概念,以及Chromium需要创建多个3D上下文的原因。
OpenGL上下文和渲染表面
OpenGL上下文是对OpenGL运行时渲染状态的抽象。OpenGL运行时的每个实例都可以看做是一台“状态机”,GL命令实际上是在操纵这台状态机,更新其内部的渲染状态。
每个GL命令只会作用于“当前”(current)上下文,而绑定当前上下文的工作则需要通过窗口系统的OpenGL扩展接口完成的,OpenGLAPI本身没有提供这样的接口。不同的窗口系统为支持OpenGL都会定义一套扩展接口标准,例如X11Windows系统的GLX标准,其首要目的就是要定义渲染上下文(renderingcontext),以及如何创建OpenGL上下文和绑定当前上下文。为了存储OpenGL的渲染结果,这些标准还定义了特定于窗口系统的渲染表面(drawingsurface)。一般至少有两种类型的渲染表面可供使用:
- 屏上(onscreen)渲染表面,具有前后双缓冲区,前缓冲区用于显示,后缓冲区用于GL渲染,执行缓冲区交换(SwapBuffer)可以将在后缓冲区中渲染的内容显示在屏幕的窗口上。窗口系统中每个窗口都可以创建一个与之关联的onscreen渲染表面,将GL的渲染结果显示在这个窗口中。
- 离屏(offscreen)渲染表面,如PBuffer渲染表面,只有后缓冲区,GL命令只能将内容渲染到后缓冲区,在屏幕上是不可见的, 窗口系统无关的Framebuffer 对象通常会使用这个表面将内容渲染到Texture中。
以EGL为例,标准中定义了eglCreateContext接口用以创建一个封装了OpenGL上下文的EGLContext,然后指定一个与之兼容(具有相同类型和深度的颜色缓冲区)的渲染表面,通过eglMakeCurrent将这个EGLContext和绑定为当前的上下文,拥有一个OpenGL状态机,为GL命令提供执行环境。
Chromium为什么需要多个3D上下文
Chromium图形栈是一个复杂的系统,所解决的问题也很复杂:
- 普通页面的GPU渲染和加速合成;
- 对硬件加速Canvas 2D、视频解码以及WebGL的支持;
- Native Client应用的3D渲染;
- 将页面的合成工作和浏览器界面的合成工作合二为一;
- 进程外(out of process)Iframe的独立渲染;
- …
显然,Chromium运行时需要创建多个OpenGL上下文。以Android平台上Chromium渲染一个简单的WebGL页面http://get.webgl.org 为例,在委托渲染器模式(--enable-delegated-renderer)开启的情况下,Chromium至少需要创建两个3D上下文,分别用于browser进程端的合成器(Compositor)和Renderer进程端的WebGL渲染,简单的来说,
- Browser进程端的Compositor需要通过GPU将浏览器界面元素(例如地址栏等)和页面内容元素(包括普通内容的Tile块,WebGL等,这些元素信息由Renderer进程生成,然后通过IPC以资源的形式转交给Browser进程)进行合成
- Renderer进程端的WebGL需要执行GL命令并通过Framebuffer对象将内容渲染到Texture(这个Texture会通过Mailbox方式转交给Browser进程用于最终的屏幕合成)
显然,上述两者需要两个独立的OpenGL上下文,而且这两个上下文之间还必须能够共享资源,因为Browser端的Compositor的上下文需要能够直接访问WebGL上下文中用于存放离屏渲染内容的Texture对象,用于最终的合成操作。
从Chromium进程架构来看,出于安全和稳定性的考虑,所有GPU操作都在同一个线程(GPU线程)中执行,并通过应用层的命令缓冲区(CommandBuffer)将GPU操作设计成Client/Server模式,Client端(可能是Browser进程,也可能是Renderer进程)发起GPU操作请求,Server端GPU进程收到请求后向图形驱动器请求执行实际的GL操作。GPU进程收到的GL操作请求可能来自不同进程,这些操作请求都会根据消息到达时间戳按顺序执行(确切地说,是根据IPC消息GpuCommandBufferMsg_OnAsyncFlush送达GPU进程的时间决定的),因此每次执行GL操作之前, GPU进程必须确保已经切换正确的上下文。
这种请求/响应的异步模式,导致GPU线程中3D上下文之间频繁切换,因此 Chromium必须能够准确、有效地处理多个上下文切换的情况。
未完待续...下节将分析为什么Chromium会引入虚拟化上下文(Context Virtualization),敬请关注。