Chrome内核解析 -- 绘制引擎基础篇:绘图上下文(RenderingContext, GraphicsContext)

来源:互联网 发布:js number() 编辑:程序博客网 时间:2024/06/06 03:54

转载请注明出处:http://blog.csdn.net/yunchao_he/article/details/41698169


多个图形上下文(GraphicsContext,也称为3D Context, GL Context)

众所周知,使用OpenGL/D3D绘图,或者GDI, GTK/Cario, QT, X11, Skia等与绘图相关的库时,有图形上下文(GraphicsContext)这个概念。它和进程上下文有类似之处,只不过它保存绘图有关的信息,比如当前申请的Buffer, Texture, FBO等对象,以及这些对象的属性,如Texture的tile mode(repeat, mirror),以及变换矩阵,投影矩阵,光照信息,线宽,线的颜色,裁剪区大小,alpha混合模式和参数,等等众多相关属性。在Chromium里,由于其多进程构架以及Web内容的复杂性, 它可能包含以下多个GraphicsContext:

首先,网页中的一些特殊元素或组件在Chromium里往往有自己的GraphicsContext,这些特殊元素是WebGL,Canvas2D, Video, Pepper3D等。页面中每出现一个特殊元素,Chromium都会创建一个独立的GraphicsContext。另外,包含其它所有元素的Base Layer(也称Root Layer)也有自己的GraphicsContext。它们都是绘制到off-screen的FBO, 比如PixelBuffe或者Texture上。

其次,RenderCompositor将上述的各个Layer合成,合成过程中也会创建自己的GraphicsContext,它也是绘制到off-screen的FBO。

最终,BrowserCompositor把网页内容和UI合成,也会创建GrapicsContext,这一次合成的结果将绘制到屏幕上。它对应着on-screen Framebuffer。对于最常见的双buffer系统,它会绘制到back buffer, 然后通过swap buffer在屏幕上显示。


虚拟上下文:Virtual Context

对于GPU硬件上不支持多个GraphicsContexts(或者虽然支持,但性能很差)的设备,Chromium会创建虚拟上下文。每个虚拟上下文都维护自己的绘图状态,比如所绑定的FBO, texture,buffer等,以及一些绘制状态和属性。多个虚拟上下文对应一个真实的GraphicsContext。切换虚拟上下文时,则需要重新绑定这些buffer, 以及恢复绘制状态。

所以,虚拟上下文的切换只是逻辑上的切换,并没有真正切换GraphicsContext。这种方式可以在不支持多个图形上下文的设备上,通过虚拟上下文来模拟多个图形上下文。


RenderingContext 

实际上,Chromium中为了管理GraphicsContext, 在创建Command Buffer时不仅创建了GraphicsContext和Virtual Context, 还创建了和绘制相关的其它重要的类,比如TransferBufferManager, CommandBufferService, GLES2DecoderImpl, GpuScheduler, GLES2Implementation, GLES2CmdHelper, TransferBuffer等等。这些类和GraphicsContext, Virtual Context一起,构成了Chromium的绘图上下文(RenderingContext)。特别是GLES2DecoderImpl, 它负责解析command buffer客户端发来的Gpu请求,并真正发起GL操作。它的初始化函数gpu::gles2::GLES2DecoderImpl::Initialize里,会创建很多重要的基础设施,比如FBO, 以及需要attach到FBO中的color buffer(Texture 或者RenderBuffer), depth buffer, stencil buffer等等。

这些类和Command Buffer紧密相关,将在下一节详细介绍。读者需要了解的是,Chromium中为了支持多个GraphicsContext,引入了命令缓冲区(command buffer)机制。它需要在多个图形上下文之间进行切换,同步。所以,除了GraphicsContext本身,Chromium的RenderingContext还包含很多其它的基础设施,来实现这一机制。

(注意:GraphicContext和RenderingContext都可以翻译为绘图上下文,在Chromium里,RenderingContext包含GraphicsContext, 还包括VirtualContext, GpuScheduler, GLES2Decoder, GLES2Implementation等等。为避免混淆,除了将GraphicsContext翻译为图形上下文,RenderingContext翻译为绘图上下文以外,将尽量使用英文原文)


Chromium中的具体实现:

1. 创建RenderingContext, GraphicsContext, VirtualContext


以WebGL为例,当JavaScript调用getContext("webgl")时,则会创建GraphicsContext和VirtualContext。具体调用过程是,Render进程的主线程中,当V8执行到

getContext("webgl");  // 或者getContext("experimental-webgl");

时,会通过JS binding到Blink, 调用Blink中相应的API: blink::HTMLCanvasElement::getContext, 它会创建WebGLRenderingContext,后者调用Chromium的content模块,创建RenderingContext, 其中包括GraphicsContext和Virtual Context,其主要的调用序列如下:

blink::HTMLCanvasElement::getContext blink::WebGLRenderingContext::create content::RendererBlinkPlatformImpl::createOffscreenGraphicContext3D content::WebGraphicsContext3DCommandBufferImpl::InitializeOnCurrentThread content::WebGrapicsContext3DCommandBufferImpl::CreateContext content::CommandBufferProxyImpl::Initialize

当然,实际创建GraphicsContext需要发起GL操作,比如eglCreateContext。而Chromium里有一个简单原则:凡是GL操作,都是Browser进程的Gpu线程负责。同样地,上述代码本身并没有完成创建RenderingContext的过程,它只是向Browser进程发送创建请求(发送GpuCommandBufferMsg_Initialize消息),Browser进程收到这个请求后,由

content::GpuCommandBufferStub::OnInitialize
负责初始化command buffer, 而GraphicsContext就是在初始化command buffer过程中创建的。具体实现是,通过调用gfx::GLSurface::CreateOffscreenGLSurface或者gfx::GLSurface::CreateViewSurface来创建GLSurface, 然后通过调用gfx::GLContext::CreateGLContext来创建图形上下文。而虚拟上下文则调用gpu::GLContextVirtual::GLContextVirtual来创建。其它和RenderingContext相关的类,也是在Command Buffer初始化时创建的,比如GpuScheduler, GLES2Implementation, GLES2DecoderImpl等等,特别重要的是gpu::gles2::GLES2DecoderImpl::Initialize中申请了off-screen GraphicsContext的FBO, 以及FBO所需要的texture和render buffer。它们位于Browser进程的主线程。

当然,GLSurface和GLContext都是平台相关的,它会调用具体的库来实现,比如egl, CGL等等。它们位于Gpu线程。以Chromium on Android为例,GraphicsContext是通过/src/ui/gl_context_android.cc中的工厂函数CLContext::CreateGLContext,最终调用GLContextEGL::eglCreateContext来创建(/src/ui/gl/gl_context_egl.cc)。而虚拟上下文的创建其实和平台无关,它在Browser进程的主线程就可完成。


整个过程如下:



2. 上下文切换

上下文切换,实际上就是让目标Context成为当前Context。在Chromium里,则是调用gpu::GLContext::MakeCurrent。无论目标Context是平台相关的真实3D Context(具体实现有GLContextEGL, GLContextWGL, GLContextGLX等等), 还是平台无关的Virtual Context, 它们都是GLContext的子类。通过调用gpu::GLContext::MakeCurrent都能动态绑定到具体的类中。具体分析如下:

如果切换的目标Context是真实的GraphicsContext, 则会调用平台相关的MakeCurrent函数,比如GLContextEGL::MakeCurrent。最终调用平台相关的GL操作,比如eglMakeCurrent。真实的3D Context的切换操作比较耗时。具体代码位于/src/ui/gl目录下的相关文件中。

而对于VirtualContext, 则调用GLContextVirtual。它会查看当前的真实3D Context与目标Context是否相同。如果相同,则无需切换真实的GraphicsContext。对于不支持多个GraphicsContext的设备,除第一次调用MakeCurrent会切换到真实的GraphicsContext, 其它情况下都不必切换真实GraphicsContext。切换虚拟上下文的函数是/src/ui/gl/gl_gl_api_implementation.cc中的gfx::VirtualGLApi::MakeCurrent。从GLContextVirtual::MakeCurrent到该函数的调用序列为:

gpu::GLContextVirtual::MakeCurrentgfx::GLContext::MakeVirtuallyCurrentgfx::VirtualGLApi::MakeCurrent

如前所述,切换VirtualContext主要是重新绑定buffer,以及通过显式设置状态参数以恢复相关的绘制状态。通常情况下,虚拟上下文的切换是轻量级的。


 


1 0