在Chromium中,WebGL端、Render端和Browser端的GPU命令都是通过GPU进程中的一个GPU线程来执行的。这三端的GPU命令是独立执行的,不能相互发生影响。为了达到这个目的,GPU线程分别为它们创建不同的OpenGL上下文,并且使得它们的GPU命令在各自的OpenGL上下文中执行。本文接下来就详细分析WebGL端、Render端和Browser端的OpenGL上下文的创建过程。
在前面Chromium硬件加速渲染机制基础知识简要介绍和学习计划一文中,我们提到,Chromium将WebGL端、Render端和Browser端看作是GPU线程的Client端,而将GPU线程看作是Server端,也称为Service端。每一个Client端都有一个对应的OpenGL上下文,这个OpenGL上下文通过WebGraphicsContext3DCommandBufferImpl类来描述。同时,每一个Client端在Service端也有一个对应的OpenGL上下文,这个OpenGL上下文通过CommandBufferStub类来描述。对于Service端来说,Client端的OpenGL上下文按照共享组的形式进行管理,如图1所示:
图1 OpenGL上下文
通常,在一个OpenGL上下文创建的资源只能在该OpenGL上下文中访问。但是在同一个共享组中的不同OpenGL上下文是可以共享OpenGL资源的,例如Buffer、纹理、FBO、RBO和Program等。我们将这些共享组称为资源共享组。通过共享资源,可以解决一些OpenGL性能问题。例如,把纹理数据从CPU上传到GPU是比较耗时的(glTexImage2D)。如果我们在一个OpenGL上下文以同步方式上传一个很大的纹理数据,那么就会影响到OpenGL的性能。同样,把GPU数据读回到CPU也是比较耗时的(glReadPixels)。如果我们在一个OpenGL上下文以同步方式从GPU读回数据到CPU,那么也会面临性能问题。有了资源共享组之后,就可以实现异步纹理上传和异步读取GPU数据。简单来说,就是在另外一个线程中创建另外一个在同一个资源共享组中的OpenGL上下文,然后在该OpenGL上下文实现纹理上传和异步读取GPU数据。关于OpenGL资源共享的更多信息,可以参考https://www.khronos.org/webgl/wiki/SharedResouces和https://www.khronos.org/registry/webgl/extensions/WEBGL_shared_resources。
此外,在同一个资源共享组中的每一个OpenGL上下文,它们可能有各自的GPU通道,也可能共享同一个GPU通道。Client端就是通过这些GPU通道请求Service端执行GPU命令的。WebGL端、Render端和Browser端与GPU进程建立GPU通道的过程,可以参考前面Chromium的GPU进程启动过程分析一文。
在Client端,资源共享组称为Share Group,但是在Service端,资源共享组称为Context Group,它们表达的意思是一样的。在Service端,之所以将资源共享组称为Context Group,是因为Share Group另有它用,它用来描述一个OpenGL上下文共享组。OpenGL上下文共享组是OpenGL本身的概念。在Chromium中,所有的Client端的OpenGL上下文都隐式地放在一个OpenGL上下文共享组中。也就是说,所有的 WebGL端、Render端和Browser端OpenGL上下文都是位于同一个OpenGL上下文共享组中的。这样做的目的是使得Browser端的OpenGL上下文可以直接使用WebGL端和Render端的OpenGL上下文的资源,从而高效地完成UI合成操作。
从字面上看,我们前面提到的资源共享组和OpenGL上下文共享组的作用都是一样的。的确如此,并且资源共享组也是在OpenGL上下文共享组的基础上实现的。但是在使用上,它们是有区别的。首先,所有的WebGL端、Render端和Browser端OpenGL上下文都在同一个OpenGL上下文共享组是隐含的,而要将两个不同的OpenGL上下文放在同一个资源共享组中都是需要显式指定的。其次,位于同一个资源共享组的OpenGL上下文都是在同一个进程中的,否则无法指定在同一个资源共享组中,而位于不同进程中的OpenGL上下文是可以在同一个OpenGL上下文共享组中的,这得益于所有的WebGL端、Render端和Browser端OpenGL上下文都是在GPU进程中创建的。事实上,整个Chromium就只有一个OpenGL上下文共享组,而资源共享组可以在不同的进程中分别创建若干个。关于资源共享组和OpenGL上下文共享组,在这篇文章以及下一篇文章,我们会结合源代码详细分析。
接下来,我们就结合源代码分析WebGL端、Render端和Browser端的OpenGL上下文的创建过程。在分析的过程中,我们将会看到这些OpenGL上下文是如何划分资源共享组和OpenGL上下文共享组的。
在前面Chromium的GPU进程启动过程分析一文中,我们提到,Render端的OpenGL上下文是通过RenderWidget类的成员函数CreateGraphicsContext3D创建的,如下所示:
scoped_ptr<WebGraphicsContext3DCommandBufferImpl>
RenderWidget::CreateGraphicsContext3D() {
......
blink::WebGraphicsContext3D::Attributes attributes;
......
attributes.shareResources = true;
......
scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context(
new WebGraphicsContext3DCommandBufferImpl(surface_id(),
GetURLForGraphicsContext3D(),
gpu_channel_host.get(),
attributes,
lose_context_when_out_of_memory,
limits,
NULL));
return context.Pass();
}
这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。
我们注意到,第四个参数attributes描述的一个Attributes对象的成员变量shareResources的值等于true,并且最后一个参数的值为NULL。
RenderWidget类的成员函数CreateGraphicsContext3D创建了一个WebGraphicsContext3DCommandBufferImpl对象来描述一个Render端的OpenGL上下文,这个WebGraphicsContext3DCommandBufferImpl对象的创建过程如下所示:
WebGraphicsContext3DCommandBufferImpl::WebGraphicsContext3DCommandBufferImpl(
int surface_id,
const GURL& active_url,
GpuChannelHost* host,
const Attributes& attributes,
bool lose_context_when_out_of_memory,
const SharedMemoryLimits& limits,
WebGraphicsContext3DCommandBufferImpl* share_context)
: lose_context_when_out_of_memory_(lose_context_when_out_of_memory),
attributes_(attributes),
visible_(false),
host_(host),
surface_id_(surface_id),
active_url_(active_url),
gpu_preference_(attributes.preferDiscreteGPU ? gfx::PreferDiscreteGpu
: gfx::PreferIntegratedGpu),
weak_ptr_factory_(this),
mem_limits_(limits) {
if (share_context) {
DCHECK(!attributes_.shareResources);
share_group_ = share_context->share_group_;
} else {
share_group_ = attributes_.shareResources
? GetDefaultShareGroupForHost(host)
: scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>(
new ShareGroup());
}
}
这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。
我们注意到,当最后一个参数share_context的值不等于NULL时,它指向的是一个WebGraphicsContext3DCommandBufferImpl对象。这表示当前正在创建的WebGraphicsContext3DCommandBufferImpl对象与参数share_context指向的WebGraphicsContext3DCommandBufferImpl对象位于同一个资源共享组中。这个资源共享组使用一个WebGraphicsContext3DCommandBufferImpl::ShareGroup对象描述,并且保存在WebGraphicsContext3DCommandBufferImpl类的成员变量share_group_中。
因此,当参数share_context的值不等于NULL时,WebGraphicsContext3DCommandBufferImpl类的构造函数就将share_context指向的WebGraphicsContext3DCommandBufferImpl对象的成员变量share_group_描述的WebGraphicsContext3DCommandBufferImpl::ShareGroup对象取出来,并且保存在当前正在创建的WebGraphicsContext3DCommandBufferImpl对象的成员变量share_group_中。
当参数share_context的值等于NULL时,WebGraphicsContext3DCommandBufferImpl类的构造函数检查参数attributes描述的一个Attributes对象的成员变量shareResources的值。当它的值等于true的时候,就表示当前正在创建的WebGraphicsContext3DCommandBufferImpl对象,使用与参数host描述的一个GpuChannelHost对象关联的一个WebGraphicsContext3DCommandBufferImpl::ShareGroup对象,作为自已的资源共享组。这个WebGraphicsContext3DCommandBufferImpl::ShareGroup对象是通过调用全局函数GetDefaultShareGroupForHost获得的,它的实现如下所示:
static base::LazyInstance<base::Lock>::Leaky
g_default_share_groups_lock = LAZY_INSTANCE_INITIALIZER;
typedef std::map<GpuChannelHost*,
scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup> >
ShareGroupMap;
static base::LazyInstance<ShareGroupMap> g_default_share_groups =
LAZY_INSTANCE_INITIALIZER;
scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup>
GetDefaultShareGroupForHost(GpuChannelHost* host) {
base::AutoLock lock(g_default_share_groups_lock.Get());
ShareGroupMap& share_groups = g_default_share_groups.Get();
ShareGroupMap::iterator it = share_groups.find(host);
if (it == share_groups.end()) {
scoped_refptr<WebGraphicsContext3DCommandBufferImpl::ShareGroup> group =
new WebGraphicsContext3DCommandBufferImpl::ShareGroup();
share_groups[host] = group;
return group;
}
return it->second;
}
这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。
全局变量g_default_share_groups描述的是一个std::map。这个std::map以GpuChannelHost指针为键值,保存不同的GpuChannelHost对象所对应的WebGraphicsContext3DCommandBufferImpl::ShareGroup对象。从前面Chromium的GPU进程启动过程分析一文可以知道,一个GpuChannelHost对象描述的是一个GPU通道。
函数GetDefaultShareGroupForHost根据参数host的值在全局变量g_default_share_groups的std::map中检查是否已经存在一个对应的WebGraphicsContext3DCommandBufferImpl::ShareGroup对象。如果已经存在,那么就将它返回给调用者。否则的话,就创建一个新的WebGraphicsContext3DCommandBufferImpl::ShareGroup对象,并且保存在全局变量g_default_share_groups的std::map中,最后再将该WebGraphicsContext3DCommandBufferImpl::ShareGroup对象返回给调用者。
回到WebGraphicsContext3DCommandBufferImpl类的构造函数中,如果参数share_context的值等于NULL,并且检查参数attributes描述的一个Attributes对象的成员变量shareResources的值等于false,那么就表示正在创建的WebGraphicsContext3DCommandBufferImpl对象位于一个新创建的独立资源共享组中。
从WebGraphicsContext3DCommandBufferImpl类的构造函数和函数GetDefaultShareGroupForHost的实现,以及图1所示的内容,就可以看出:
1. 如果参数share_context的值不等于NULL,那么当前正在创建的WebGraphicsContext3DCommandBufferImpl对象和它描述的WebGraphicsContext3DCommandBufferImpl对象位于同一个资源共享组中,并且这两个WebGraphicsContext3DCommandBufferImpl对象可能使用不同的GPU通道与GPU进程执行IPC。这个资源共享组就相当于图1所示的最上面的Share Group。
2. 如果参数share_context的值等于NULL,并且参数attributes描述的一个Attributes对象的成员变量shareResources的值等于true,那么当前正在创建的WebGraphicsContext3DCommandBufferImpl对象所在的资源共享组由参数host指定。这意味着在该资源共享组中所有的WebGraphicsContext3DCommandBufferImpl对象,都通过同一个GPU通道与GPU进程执行IPC。这个资源共享组就相当于图1所示的中间的Share Group。
3. 如果参数share_context的值等于NULL,并且参数attributes描述的一个Attributes对象的成员变量shareResources的值等于false,那么当前正在创建的WebGraphicsContext3DCommandBufferImpl对象位于一个新创建的独立资源共享组中。这个资源共享组就相当于图1所示的最下面的Share Group。
从前面的分析可以知道,Render端的OpenGL上下文的创建过程符合上述的第2种情况,这意味着Render端的OpenGL上下文可以与使用相同GPU通道的其它OpenGL上下文位于同一个资源共享组中。
我们再来看Browser端的OpenGL上下文的创建过程。从前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文可以知道,Browser端的OpenGL上下文是通过函数CreateGpuProcessViewContext创建的,如下所示:
static scoped_ptr<WebGraphicsContext3DCommandBufferImpl>
CreateGpuProcessViewContext(
const scoped_refptr<GpuChannelHost>& gpu_channel_host,
const blink::WebGraphicsContext3D::Attributes attributes,
int surface_id) {
......
return make_scoped_ptr(
new WebGraphicsContext3DCommandBufferImpl(surface_id,
url,
gpu_channel_host.get(),
attributes,
lose_context_when_out_of_memory,
limits,
NULL));
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/compositor_impl_android.cc中。
我们注意到,函数CreateGpuProcessViewContext在调用WebGraphicsContext3DCommandBufferImpl类的构造函数创建一个WebGraphicsContext3DCommandBufferImpl对象时,最后一个参数指定为NULL,这意味着Browser端的OpenGL上下文所在的资源共享组由参数attributes描述的一个Attributes对象的成员变量shareResources的值决定。从前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文可以知道,这个Attributes对象是从CompositorImpl类的成员函数CreateOutputSurface传递下来的,如下所示:
scoped_ptr<cc::OutputSurface> CompositorImpl::CreateOutputSurface(
bool fallback) {
blink::WebGraphicsContext3D::Attributes attrs;
attrs.shareResources = true;
......
scoped_refptr<ContextProviderCommandBuffer> context_provider;
BrowserGpuChannelHostFactory* factory =
BrowserGpuChannelHostFactory::instance();
scoped_refptr<GpuChannelHost> gpu_channel_host = factory->GetGpuChannel();
if (gpu_channel_host && !gpu_channel_host->IsLost()) {
context_provider = ContextProviderCommandBuffer::Create(
CreateGpuProcessViewContext(gpu_channel_host, attrs, surface_id_),
"BrowserCompositor");
}
......
return scoped_ptr<cc::OutputSurface>(
new OutputSurfaceWithoutParent(context_provider));
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/compositor_impl_android.cc中。
从这里可以看到,从CompositorImpl类的成员函数CreateOutputSurface传递下来的Attributes对象的成员变量shareResources的值等于true,这意味着Browser端的OpenGL上下文与Render端的OpenGL上下文一样,可以与使用相同GPU通道的其它OpenGL上下文位于同一个资源共享组中。
我们最后看WebGL端的OpenGL上下文的创建过程。从前面Chromium的GPU进程启动过程分析一文可以知道,WebGL端的OpenGL上下文是通过RendererWebKitPlatformSupportImpl类的成员函数createOffscreenGraphicsContext3D创建的,如下所示:
blink::WebGraphicsContext3D*
RendererWebKitPlatformSupportImpl::createOffscreenGraphicsContext3D(
const blink::WebGraphicsContext3D::Attributes& attributes) {
return createOffscreenGraphicsContext3D(attributes, NULL);
}
这个函数定义在文件external/chromium_org/content/renderer/renderer_webkitplatformsupport_impl.cc中。
RendererWebKitPlatformSupportImpl类的成员函数createOffscreenGraphicsContext3D调用另外一个重载版本的成员函数createOffscreenGraphicsContext3D创建一个WebGraphicsContext3DCommandBufferImpl对象,如下所示:
blink::WebGraphicsContext3D*
RendererWebKitPlatformSupportImpl::createOffscreenGraphicsContext3D(
const blink::WebGraphicsContext3D::Attributes& attributes,
blink::WebGraphicsContext3D* share_context) {
......
return WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext(
gpu_channel_host.get(),
attributes,
lose_context_when_out_of_memory,
GURL(attributes.topDocumentURL),
limits,
static_cast<WebGraphicsContext3DCommandBufferImpl*>(share_context));
}
这个函数定义在文件external/chromium_org/content/renderer/renderer_webkitplatformsupport_impl.cc中。
从前面的调用过程可以知道,参数share_context的值等于NULL,这意味着WebGL端的OpenGL上下文所在的资源共享组由参数attributes描述的一个Attributes对象的成员变量shareResources的值决定。从前面Chromium的GPU进程启动过程分析一文可以知道,这个Attributes对象是从WebGLRenderingContext类的成员函数create传递下来的,如下所示:
PassOwnPtrWillBeRawPtr<WebGLRenderingContext> WebGLRenderingContext::create(HTMLCanvasElement* canvas, WebGLContextAttributes* attrs)
{
......
blink::WebGraphicsContext3D::Attributes attributes = attrs->attributes(document.topDocument().url().string(), settings);
OwnPtr<blink::WebGraphicsContext3D> context = adoptPtr(blink::Platform::current()->createOffscreenGraphicsContext3D(attributes, 0));
......
return renderingContext.release();
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/html/canvas/WebGLRenderingContext.cpp中。
从这里可以看到,传递给RendererWebKitPlatformSupportImpl类的成员函数createOffscreenGraphicsContext3D的Attributes对象是通过调用参数attrs指向的一个WebGLContextAttributes对象的成员函数attributes创建的,它的实现如下所示:
blink::WebGraphicsContext3D::Attributes WebGLContextAttributes::attributes(
const blink::WebString& topDocumentURL, Settings* settings) const
{
blink::WebGraphicsContext3D::Attributes attrs;
......
attrs.shareResources = false;
......
return attrs;
}
这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/html/canvas/WebGLContextAttributes.cpp中。
从这里可以看到,WebGLContextAttributes类的成员函数attributes返回的Attributes对象的成员变量shareResources的值等于false,这意味着WebGL端的OpenGL上下文不与其它OpenGL上下文共享一个资源组,即它位于一个独立的资源共享组中。
了解了WebGL端、Render端和Browser端的OpenGL上下文所在的资源共享组之后,接下来我们再分析它们在Service端,即在GPU线程中的OpenGL上下文的创建过程。
WebGL端、Render端和Browser端在请求GPU线程执行GPU命令之前,会调用WebGraphicsContext3DCommandBufferImpl类的成员函数makeContextCurrent将之前为它们所创建的WebGraphicsContext3DCommandBufferImpl对象设置当前使用的OpenGL上下文,如下所示:
bool WebGraphicsContext3DCommandBufferImpl::makeContextCurrent() {
if (!MaybeInitializeGL()) {
......
return false;
}
gles2::SetGLContext(GetGLInterface());
......
return true;
}
这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。
WebGraphicsContext3DCommandBufferImpl类的成员函数makeContextCurrent首先调用另外一个成员函数MaybeInitializeGL检查当前使用的OpenGL上下文是否已经初始化过。如果还没有初始化,那么WebGraphicsContext3DCommandBufferImpl类的成员函数MaybeInitializeGL就会对其进行初始化,并且在初始化完成之后,创建一个OpenGL调用接口。WebGraphicsContext3DCommandBufferImpl类的成员函数makeContextCurrent接下来通过调用成员函数GetGLInterface获得上述OpenGL调用接口,并且调用函数gles2::SetGLContext将其设置为当前线程的OpenGL调用接口。关于Client端的OpenGL调用接口,我们在接下来一篇文章中再详细分析。
接下来我们主要分析OpenGL上下文的初始化过程,即WebGraphicsContext3DCommandBufferImpl类的成员函数MaybeInitializeGL的实现,如下所示:
bool WebGraphicsContext3DCommandBufferImpl::MaybeInitializeGL() {
if (initialized_)
return true;
......
if (!CreateContext(surface_id_ != 0)) {
......
return false;
}
......
initialized_ = true;
return true;
}
这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。
WebGraphicsContext3DCommandBufferImpl类的成员函数MaybeInitializeGL首先检查成员变量initialized_的值是否等于true。如果等于true,那么就说明当前使用的OpenGL上下文已经初始化过了。否则的话,就调用另外一个成员函数CreateContext进行初始化,并且在初始化成功后,将成员变量initialized_的值设置为true。
WebGraphicsContext3DCommandBufferImpl类的成员函数CreateContext的实现如下所示:
bool WebGraphicsContext3DCommandBufferImpl::CreateContext(bool onscreen) {
......
scoped_refptr<gpu::gles2::ShareGroup> gles2_share_group;
scoped_ptr<base::AutoLock> share_group_lock;
bool add_to_share_group = false;
if (!command_buffer_) {
WebGraphicsContext3DCommandBufferImpl* share_context = NULL;
share_group_lock.reset(new base::AutoLock(share_group_->lock()));
share_context = share_group_->GetAnyContextLocked();
if (!InitializeCommandBuffer(onscreen, share_context)) {
......
return false;
}
if (share_context)
gles2_share_group = share_context->GetImplementation()->share_group();
add_to_share_group = true;
}
// Create the GLES2 helper, which writes the command buffer protocol.
gles2_helper_.reset(new gpu::gles2::GLES2CmdHelper(command_buffer_.get()));
if (!gles2_helper_->Initialize(mem_limits_.command_buffer_size)) {
......
return false;
}
......
transfer_buffer_ .reset(new gpu::TransferBuffer(gles2_helper_.get()));
......
// Create the object exposing the OpenGL API.
bool bind_generates_resources = false;
real_gl_.reset(
new gpu::gles2::GLES2Implementation(gles2_helper_.get(),
gles2_share_group,
transfer_buffer_.get(),
bind_generates_resources,
lose_context_when_out_of_memory_,
command_buffer_.get()));
setGLInterface(real_gl_.get());
if (!real_gl_->Initialize(
mem_limits_.start_transfer_buffer_size,
mem_limits_.min_transfer_buffer_size,
mem_limits_.max_transfer_buffer_size,
mem_limits_.mapped_memory_reclaim_limit)) {
......
return false;
}
if (add_to_share_group)
share_group_->AddContextLocked(this);
......
return true;
}
这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。
WebGraphicsContext3DCommandBufferImpl类的成员函数CreateContext主要是做两件事情。
第一件事情是创建一个OpenGL函数调用代理对象。这个OpenGL函数调用代理对象也称为一个OpenGL命令缓冲区代理对象,保存在WebGraphicsContext3DCommandBufferImpl类的成员变量command_buffer_中,用来将Client端的OpenGL调用命令发送给GPU进程执行。
前面提到,Client端的OpenGL上下文是按照资源共享组的形式组织的,这个资源共享组使用一个WebGraphicsContext3DCommandBufferImpl::ShareGroup对象描述,保存在WebGraphicsContext3DCommandBufferImpl类的成员变量share_group_中。在调用WebGraphicsContext3DCommandBufferImpl类的成员函数InitializeCommandBuffer创建OpenGL命令缓冲区代理对象的时候,需要用到上述资源共享组。
WebGraphicsContext3DCommandBufferImpl::ShareGroup在内部维护了在同一个资源共享组中的所有OpenGL上下文,即一系列的WebGraphicsContext3DCommandBufferImpl对象。通过WebGraphicsContext3DCommandBufferImpl::ShareGroup类的成员函数GetAnyContextLocked可以获取在同一个资源共享组的其它任意一个OpenGL上下文。有了这个OpenGL上下文之后,就可以用来初始化当前使用的OpenGL上下文。
第二件事情是创建一个OpenGL实现。一个OpenGL实现通过一个gpu::gles2::GLES2Implementation对象描述,它负责将Client端的OpenGL函数调用转化为OpenGL调用命令。创建一个OpenGL实现需要四个重要参数:
1. 一个OpenGL命令缓冲区代理对象(commandbuffer),OpenGL实现需要通过它来和GPU进程通信。
2. 一个gpu::gles2::GLES2CmdHelper对象(gles2helper),OpenGL实现需要通过它来将Client端的OpenGL函数调用转化为OpenGL命令,并且保存在一个命令缓冲区中。
3. 一个gpu::TransferBuffer对象(transferbuffer),OpenGL实现需要通过它来将OpenGL命令附带的数据传递给GPU进程。
4. 一个gpu::gles2::ShareGroup对象(gles2_share_group),表示OpenGL实现所属的资源共享组。
每一个Client端的OpenGL上下文都对应有一个OpenGL实现,也就是每一个WebGraphicsContext3DCommandBufferImpl对象都有一个对应的gpu::gles2::GLES2Implementation对象,保存在其成员变量real_gl_中。WebGraphicsContext3DCommandBufferImpl对象按照资源共享组进行组织,其对应的gpu::gles2::GLES2Implementation对象,也是按照资源共享组进行组织的。不过前一个资源共享组通过WebGraphicsContext3DCommandBufferImpl::ShareGroup类描述,而后一个资源共享组使用gpu::gles2::ShareGroup类描述。
给出一个WebGraphicsContext3DCommandBufferImpl对象,可以通过它的成员函数GetImplementation获得它的OpenGL实现,即一个gpu::gles2::GLES2Implementation对象。给出一个gpu::gles2::GLES2Implementation对象,可以通过它的成员函数share_group获得它所属的资源共享组,即一个gpu::gles2::ShareGroup对象。因此,知道在同一个资源共享组的其它任意一个WebGraphicsContext3DCommandBufferImpl对象之后,就可以获得它对应的OpenGL实现所属的资源共享组,进而使用该资源共享组为当前正在初始化的WebGraphicsContext3DCommandBufferImpl对象创建一个对应的OpenGL实现。
为当前正在初始化的WebGraphicsContext3DCommandBufferImpl对象创建一个对应的OpenGL实现之后,还需要做两件事情。第一件事情是调用WebGraphicsContext3DCommandBufferImpl类的成员函数setGLInterface将该OpenGL实现作为一个OpenGL调用接口保存在内部,以便后面在需要的时候,可以通过WebGraphicsContext3DCommandBufferImpl类的成员函数GetGLInterface获得该OpenGL调用接口。第二件事情是调用gpu::gles2::GLES2Implementation类的成员函数Initialize对该OpenGL实现进行初始化。
关于OpenGL实现的创建和初始化,我们在接下来一篇文章中再详细分析。接下来我们主要分析OpenGL命令缓冲区代理对象的创建过程,即WebGraphicsContext3DCommandBufferImpl类的成员函数InitializeCommandBuffer的实现,如下所示:
bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer(
bool onscreen, WebGraphicsContext3DCommandBufferImpl* share_context) {
......
CommandBufferProxyImpl* share_group_command_buffer = NULL;
if (share_context) {
share_group_command_buffer = share_context->command_buffer_.get();
}
std::vector<int32> attribs;
......
// Create a proxy to a command buffer in the GPU process.
if (onscreen) {
command_buffer_.reset(host_->CreateViewCommandBuffer(
surface_id_,
share_group_command_buffer,
attribs,
active_url_,
gpu_preference_));
} else {
command_buffer_.reset(host_->CreateOffscreenCommandBuffer(
gfx::Size(1, 1),
share_group_command_buffer,
attribs,
active_url_,
gpu_preference_));
}
......
// Initialize the command buffer.
bool result = command_buffer_->Initialize();
......
return result;
}
这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。
当参数share_context的值不等于NULL。表示它描述的Client端OpenGL上下文与当前正在创建的Client端OpenGL上下文在同一个资源共享组中,这时候需要将它内部使用的一个OpenGL命令缓冲区代理对象取出来,保存在本地变量share_group_command_buffer中,接下来为当前正在创建的Client端OpenGL内部使用的OpenGL命令缓冲区代理对象时会使用到的。
当前正在创建的Client端OpenGL上下文内部使用的OpenGL命令缓冲区代理对象保存在WebGraphicsContext3DCommandBufferImpl类的成员变量command_buffer_中。当这个成员变量的值等于NULL时,就意味着当前正在创建的Client端OpenGL内部使用的OpenGL命令缓冲区代理对象还没有创建,这时候WebGraphicsContext3DCommandBufferImpl类的成员函数InitializeCommandBuffer就会调用成员变量host_描述的一个GpuChannelHost对象的成员函数CreateViewCommandBuffer或者CreateOffscreenCommandBuffer进行创建,取决于参数onscreen的值。从前面Chromium的GPU进程启动过程分析一文可以知道,GpuChannelHost是用来描述一个GPU通道的。
从前面的调用过程可以知道,WebGraphicsContext3DCommandBufferImpl类的成员变量surface_id_的值不等于0的时候,参数onscreen的值就等于true,这时候WebGraphicsContext3DCommandBufferImpl类的成员函数InitializeCommandBuffer调用成员变量host_描述的一个GpuChannelHost对象的成员函数CreateViewCommandBuffer创建一个OpenGL命令缓冲区代理对象,否则的话就调用成员变量host_描述的一个GpuChannelHost对象的成员函数CreateOffscreenCommandBuffer创建一个OpenGL命令缓冲区代理对象。
从前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文可以知道,当WebGraphicsContext3DCommandBufferImpl类的成员变量surface_id_的值不等于0的时候,就表示当前正在创建的Client端OpenGL上下文关联有一个绘图表面。从前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文还可以知道,Browser端和Render端关联有绘图表面,而WebGL端的OpenGL上下文没有关联绘图表面。因此,Browser端和Render端的OpenGL上下文内部使用的OpenGL命令缓冲区代理对象是通过GpuChannelHost类的成员函数CreateViewCommandBuffer创建的,而WebGL端的OpenGL上下文内部使用的OpenGL命令缓冲区代理对象是通过GpuChannelHost类的成员函数CreateOffscreenCommandBuffer的。
WebGraphicsContext3DCommandBufferImpl类的成员函数InitializeCommandBuffer调用GpuChannelHost类的成员函数CreateViewCommandBuffer或者CreateOffscreenCommandBuffer创建了一个OpenGL命令缓冲区代理对象之后,接下来还会调用该OpenGL命令缓冲区代理对象的成员函数Initialize执行初始化工作。
接下来我们先分析GpuChannelHost类的成员函数CreateViewCommandBuffer或者CreateOffscreenCommandBuffer的实现,然后再分析它们所创建的OpenGL命令缓冲区代理对象的成员函数Initialize的实现。
GpuChannelHost类的成员函数CreateViewCommandBuffer的实现如下所示:
CommandBufferProxyImpl* GpuChannelHost::CreateViewCommandBuffer(
int32 surface_id,
CommandBufferProxyImpl* share_group,
const std::vector<int32>& attribs,
const GURL& active_url,
gfx::GpuPreference gpu_preference) {
......
GPUCreateCommandBufferConfig init_params;
init_params.share_group_id =
share_group ? share_group->GetRouteID() : MSG_ROUTING_NONE;
......
int32 route_id = GenerateRouteID();
CreateCommandBufferResult result = factory_->CreateViewCommandBuffer(
surface_id, init_params, route_id);
......
CommandBufferProxyImpl* command_buffer =
new CommandBufferProxyImpl(this, route_id);
AddRoute(route_id, command_buffer->AsWeakPtr());
......
return command_buffer;
}
这个函数定义在文件external/chromium_org/content/common/gpu/client/gpu_channel_host.cc中。
GpuChannelHost类的成员函数CreateViewCommandBuffer首先调用成员变量factory_描述的一个GpuChannelHostFactory对象的成员函数CreateViewCommandBuffer请求GPU进程创建一个OpenGL命令缓冲区服务对象,以便用来与接下来创建的OpenGL命令缓冲区代理对象进行通信。
GpuChannelHost类的成员函数CreateViewCommandBuffer在请求GPU进程创建一个OpenGL命令缓冲区服务对象时,传递了两个重要的参数,一个是资源共享组ID,另一个是通信路由ID。其中,当参数share_group的值不等于NULL时,资源共享组ID由它指向的一个OpenGL命令缓冲区代理对象使用的通信路由ID指定,用来表示当前请求GPU进程创建OpenGL命令缓冲区服务对象与它指向的OpenGL命令缓冲区代理对象在GPU进程中对应的OpenGL命令缓冲区代理对象在同一个资源共享组中。当参数share_group的值等于NULL时,表示当前请求GPU进程创建OpenGL命令缓冲区服务对象目前不与其它OpenGL命令缓冲区服务对象在同一个资源共享组中。
Client端OpenGL上下文通过OpenGL命令缓冲区代理对象向GPU进程请求执行GPU命令时,需要指定一个路由ID。GPU进程获得这个路由ID后,就会找到对应的OpenGL命令缓冲区服务对象执行指定的GPU命令。也就是说,一个OpenGL命令缓冲区代理对象和一个OpenGL命令缓冲区服务对象是通过路由ID建立关联的。因此,GpuChannelHost类的成员函数CreateViewCommandBuffer会为每一个它创建的OpenGL命令缓冲区代理对象分配一个路由ID,这是通过调用另外一个成员函数GenerateRouteID来分配的。这个路由ID同时也会传递在GPU进程中创建的OpenGL命令缓冲区服务对象使用。
GpuChannelHost类的成员函数CreateViewCommandBuffer调用成员变量factory_描述的一个GpuChannelHostFactory对象的成员函数CreateViewCommandBuffer请求GPU进程创建了一个OpenGL命令缓冲区服务对象之后,接下来就会创建一个OpenGL命令缓冲区代理对象,也就是一个CommandBufferProxyImpl对象,并且调用成员函数AddRoute将该OpenGL命令缓冲区代理对象与前面为其分配的路由ID关联起来,以便可以用来接收其对应的OpenGL命令缓冲区服务对象发送过来的IPC消息。关联IPC消息的发送和接收过程中的路由机制,可以参考前面Chromium的IPC消息发送、接收和分发机制分析一文。
从前面Chromium的GPU进程启动过程分析一文可以知道,用来描述Render端使用的GPU通道的GpuChannelHost对象的成员变量factory_指向的实际上是一个RenderThreadImpl对象,而用来Browser端使用的GPU通道的GpuChannelHost对象成员变量factory_指向的实际上是一个BrowserGpuChannelHostFactory对象,它们都是从GpuChannelHostFactory类继承下来的。因此,GpuChannelHost类的成员函数CreateViewCommandBuffer通过调用RenderThreadImpl类的成员函数CreateViewCommandBuffer为Render端在GPU进程中创建一个OpenGL命令缓冲区服务对象,而调用BrowserGpuChannelHostFactory类的成员函数CreateViewCommandBuffer为Browser端在GPU进程中创建一个OpenGL命令缓冲区服务对象。
接下来我们先分析RenderThreadImpl类的成员函数CreateViewCommandBuffer的实现,接着再分析BrowserGpuChannelHostFactory类的成员函数CreateViewCommandBuffer的实现。
RenderThreadImpl类的成员函数CreateViewCommandBuffer的实现如下所示:
CreateCommandBufferResult RenderThreadImpl::CreateViewCommandBuffer(
int32 surface_id,
const GPUCreateCommandBufferConfig& init_params,
int32 route_id) {
......
CreateCommandBufferResult result = CREATE_COMMAND_BUFFER_FAILED;
IPC::Message* message = new GpuHostMsg_CreateViewCommandBuffer(
surface_id,
init_params,
route_id,
&result);
// Allow calling this from the compositor thread.
thread_safe_sender()->Send(message);
return result;
}
这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。
RenderThreadImpl类的成员函数CreateViewCommandBuffer执行的操作是向Browser进程发送一个类型为GpuHostMsg_CreateViewCommandBuffer的IPC消息。
在前面Chromium的GPU进程启动过程分析一文中提到,Browser进程是通过GpuMessageFilter类的成员函数OnMessageReceived接收GPU操作相关的IPC消息的,这意味上述类型为GpuHostMsg_CreateViewCommandBuffer的IPC消息由GpuMessageFilter类的成员函数OnMessageReceived接收,如下所示:
bool GpuMessageFilter::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(GpuMessageFilter, message)
......
IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuHostMsg_CreateViewCommandBuffer,
OnCreateViewCommandBuffer)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/gpu_message_filter.cc中。
从这里可以看到,GpuMessageFilter类的成员函数OnMessageReceived将接收到的类型为GpuHostMsg_CreateViewCommandBuffer的IPC消息交给另外一个成员函数OnCreateViewCommandBuffer处理,如下所示:
void GpuMessageFilter::OnCreateViewCommandBuffer(
int32 surface_id,
const GPUCreateCommandBufferConfig& init_params,
int32 route_id,
IPC::Message* reply_ptr) {
......
scoped_ptr<IPC::Message> reply(reply_ptr);
GpuSurfaceTracker* surface_tracker = GpuSurfaceTracker::Get();
gfx::GLSurfaceHandle compositing_surface;
int renderer_id = 0;
int render_widget_id = 0;
bool result = surface_tracker->GetRenderWidgetIDForSurface(
surface_id, &renderer_id, &render_widget_id);
if (result && renderer_id == render_process_id_) {
compositing_surface = surface_tracker->GetSurfaceHandle(surface_id);
}
......
GpuProcessHost* host = GpuProcessHost::FromID(gpu_process_id_);
......
host->CreateViewCommandBuffer(
compositing_surface,
surface_id,
render_process_id_,
init_params,
route_id,
base::Bind(&GpuMessageFilter::CreateCommandBufferCallback,
weak_ptr_factory_.GetWeakPtr(),
base::Passed(&reply)));
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/gpu_message_filter.cc中。
从前面的分析可以知道,参数surface_id描述的Render端OpenGL上下文关联的绘图表面的ID,GpuMessageFilter类的成员函数OnCreateViewCommandBuffer首先调用Browser进程中的GpuSurfaceTracker单例的成员函数GetRenderWidgetIDForSurface验证参数surface_id的有效性,即它描述的确实是一个Render端OpenGL上下文关联的绘图表面的ID。
当Browser进程中的GpuSurfaceTracker单例的成员函数GetRenderWidgetIDForSurface的返回值等于true,并且输出参数renderer_id描述的Render端ID等于当前正在处理的GpuMessageFilter对象的成员变量render_process_id_的值时,就表示参数surface_id是有效性的。Browser进程为每一个Render端都创建了一个GpuMessageFilter对象,分别用来接收和处理各个Render端发送过来的GPU操作相关的IPC消息,每一个GpuMessageFilter对象的成员变量render_process_id_描述的便是对应的Render端的ID。
当参数surface_id有效时,GpuMessageFilter类的成员函数OnCreateViewCommandBuffer继续调用Browser进程中的GpuSurfaceTracker单例的成员函数GetSurfaceHandle获得一个与参数surface_id对应的绘图表面句柄,即一个gfx::GLSurfaceHandle对象。从前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文可以知道,Render端OpenGL上下文关联的绘图表面的句柄的类型为gfx::NATIVE_TRANSPORT,并且没有关联OS本地窗口。
接下来,GpuMessageFilter类的成员函数OnCreateViewCommandBuffer根据成员变量gpu_process_id_获得一个GpuProcessHost对象,这个GpuProcessHost对象描述了Browser进程之前所启动的GPU进程。
最后,GpuMessageFilter类的成员函数OnCreateViewCommandBuffer通过前面找到的GpuProcessHost对象的成员函数CreateViewCommandBuffer请求GPU进程创建一个OpenGL命令缓冲区服务对象。
GpuProcessHost类的成员函数CreateViewCommandBuffer的实现如下所示:
void GpuProcessHost::CreateViewCommandBuffer(
const gfx::GLSurfaceHandle& compositing_surface,
int surface_id,
int client_id,
const GPUCreateCommandBufferConfig& init_params,
int route_id,
const CreateCommandBufferCallback& callback) {
......
if (!compositing_surface.is_null() &&
Send(new GpuMsg_CreateViewCommandBuffer(
compositing_surface, surface_id, client_id, init_params, route_id))) {
......
}
......
}
这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_process_host.cc中。
GpuProcessHost类的成员函数CreateViewCommandBuffer主要是向GPU进程发送了一个类型为GpuMsg_CreateViewCommandBuffer的IPC消息,该IPC消息封装参数surface_id和compositing_surface描述的绘图表面ID和绘图表面句柄等信息。
在分析GPU进程接收和处理类型为GpuMsg_CreateViewCommandBuffer的IPC消息之前,我们回到前面分析的GpuChannelHost类的成员函数CreateViewCommandBuffer中,分析Browser端OpenGL上下文在GPU进程中对应的OpenGL命令缓冲区服务对象的创建过程,即BrowserGpuChannelHostFactory类的成员函数CreateViewCommandBuffer的实现。
BrowserGpuChannelHostFactory类的成员函数CreateViewCommandBuffer的实现如下所示:
CreateCommandBufferResult BrowserGpuChannelHostFactory::CreateViewCommandBuffer(
int32 surface_id,
const GPUCreateCommandBufferConfig& init_params,
int32 route_id) {
CreateRequest request;
request.route_id = route_id;
GetIOLoopProxy()->PostTask(FROM_HERE, base::Bind(
&BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO,
base::Unretained(this),
&request,
surface_id,
init_params));
// We're blocking the UI thread, which is generally undesirable.
// In this case we need to wait for this before we can show any UI /anyway/,
// so it won't cause additional jank.
// TODO(piman): Make this asynchronous (http://crbug.com/125248).
TRACE_EVENT0("browser",
"BrowserGpuChannelHostFactory::CreateViewCommandBuffer");
base::ThreadRestrictions::ScopedAllowWait allow_wait;
request.event.Wait();
return request.result;
}
这个函数定义在文件external/chromium_org/content/browser/gpu/browser_gpu_channel_host_factory.cc中。
BrowserGpuChannelHostFactory类的成员函数CreateViewCommandBuffer往Browser进程的IO线程的消息队列发送一个Task,该Task绑定的函数为BrowserGpuChannelHostFactory类的成员函数CreateViewCommandBufferOnIO。因此,接下来BrowserGpuChannelHostFactory类的成员函数CreateViewCommandBufferOnIO会在Browser进程的IO线程中执行,如下所示:
void BrowserGpuChannelHostFactory::CreateViewCommandBufferOnIO(
CreateRequest* request,
int32 surface_id,
const GPUCreateCommandBufferConfig& init_params) {
GpuProcessHost* host = GpuProcessHost::FromID(gpu_host_id_);
......
gfx::GLSurfaceHandle surface =
GpuSurfaceTracker::Get()->GetSurfaceHandle(surface_id);
host->CreateViewCommandBuffer(
surface,
surface_id,
gpu_client_id_,
init_params,
request->route_id,
base::Bind(&BrowserGpuChannelHostFactory::CommandBufferCreatedOnIO,
request));
}
这个函数定义在文件external/chromium_org/content/browser/gpu/browser_gpu_channel_host_factory.cc中。
BrowserGpuChannelHostFactory类的成员函数CreateViewCommandBuffer的实现与前面分析的GpuMessageFilter类的成员函数OnCreateViewCommandBuffer类似,它首先通过成员变量gpu_host_id_找到一个GpuProcessHost对象,该GpuProcessHost描述的也是Browser进程之前所启动的GPU进程,接着通过Browser进程中的GpuSurfaceTracker单例找到参数surface_id描述的一个绘图表面的句柄 ,最后调用前面找到的GpuProcessHost对象的成员函数CreateViewCommandBuffer请求GPU进程为Browser进程创建一个OpenGL命令缓冲区服务对象。
从前面的分析可以知道,参数surface_id描述的是Browser端OpenGL上下文关联的绘图表面的ID。通过Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文又可以知道,Browser端OpenGL上下文关联的绘图表面的句柄的类型为gfx::NATIVE_DIRECT,并且关联有一个OS本地窗口。在Android平台,该OS本地窗口实际上就是一个SurfaceView。
从这里就可以看到,Render端和Browser端OpenGL上下文都是通过GpuProcessHost类的成员函数CreateViewCommandBuffer请求GPU进程为Browser进程创建OpenGL命令缓冲区服务对象,也就是都是通过向GPU进程发送一个类型GpuMsg_CreateViewCommandBuffer的IPC消息实现的。
在分析GPU进程接收和处理类型GpuMsg_CreateViewCommandBuffer的IPC消息之前,我们继续回到WebGraphicsContext3DCommandBufferImpl类的成员函数InitializeCommandBuffer中,分析WebGL端OpenGL上下文内部使用的OpenGL命令缓冲区代理对象的创建过程,即GpuChannelHost类的成员函数CreateOffscreenCommandBuffer的实现,如下所示:
CommandBufferProxyImpl* GpuChannelHost::CreateOffscreenCommandBuffer(
const gfx::Size& size,
CommandBufferProxyImpl* share_group,
const std::vector<int32>& attribs,
const GURL& active_url,
gfx::GpuPreference gpu_preference) {
......
GPUCreateCommandBufferConfig init_params;
init_params.share_group_id =
share_group ? share_group->GetRouteID() : MSG_ROUTING_NONE;
......
int32 route_id = GenerateRouteID();
......
if (!Send(new GpuChannelMsg_CreateOffscreenCommandBuffer(size,
init_params,
route_id,
&succeeded))) {
......
return NULL;
}
......
CommandBufferProxyImpl* command_buffer =
new CommandBufferProxyImpl(this, route_id);
AddRoute(route_id, command_buffer->AsWeakPtr());
......
return command_buffer;
}
这个函数定义在文件external/chromium_org/content/common/gpu/client/gpu_channel_host.cc中。
GpuChannelHost类的成员函数CreateOffscreenCommandBuffer与前面分析的GpuChannelHost类的成员函数CreateViewCommandBuffer的实现几乎是一样的,只不过前者通过直接向GPU进程发送一个类型为GpuChannelMsg_CreateOffscreenCommandBuffer的IPC消息请求在GPU进程中创建一个OpenGL命令缓冲区服务对象,然后再为WebGL端OpenGL上下文创建一个OpenGL命令缓冲区代理对象,即一个CommandBufferProxyImpl对象。
但是有一点需要注意的,对于WebGL端OpenGL上下文来说,参数share_group的值为NULL,也就是说,WebGL端OpenGL上下文不与其它Client端OpenGL上下文共享资源,也就是不与其它Client端OpenGL上下文在同一个资源共享组中。
通过前面的分析就可以知道,Render端、Browser端和WebGL端OpenGL上下文在内部都有一个OpenGL命令缓冲区代理对象,即一个CommandBufferProxyImpl对象,这些CommandBufferProxyImpl对象在GPU进程中都分别对应有一个OpenGL命令缓冲区服务对象。其中,Render端和Browser端OpenGL上下文在GPU进程中的OpenGL命令缓冲区服务对象是Render进程通过Browser进程间接向GPU进程发送一个类型为GpuMsg_CreateViewCommandBuffer的IPC消息请求创建的,而WebGL端OpenGL上下文在GPU进程中的OpenGL命令缓冲区服务对象是Render进程直接向GPU进程发送一个类型为GpuChannelMsg_CreateOffscreenCommandBuffer的IPC消息请求创建的。
因此,接下来我们就分别分析GPU进程接收和处理类型为GpuMsg_CreateViewCommandBuffer和GpuChannelMsg_CreateOffscreenCommandBuffer的IPC消息的过程,以便可以了解Render端、Browser端和WebGL端OpenGL上下文在GPU进程中的OpenGL命令缓冲区服务对象的创建过程。
GPU进程通过GpuChannelManager类的成员函数OnMessageReceived接收和处理类型为GpuMsg_CreateViewCommandBuffer的IPC消息,如下所示:
bool GpuChannelManager::OnMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(GpuChannelManager, msg)
......
IPC_MESSAGE_HANDLER(GpuMsg_CreateViewCommandBuffer,
OnCreateViewCommandBuffer)
......
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel_manager.cc中。
从这里可以看到,GpuChannelManager类的成员函数OnMessageReceived将类型为GpuMsg_CreateViewCommandBuffer的IPC消息分发给成员函数OnCreateViewCommandBuffer处理。
GpuChannelManager类的成员函数OnCreateViewCommandBuffer的实现如下所示:
void GpuChannelManager::OnCreateViewCommandBuffer(
const gfx::GLSurfaceHandle& window,
int32 surface_id,
int32 client_id,
const GPUCreateCommandBufferConfig& init_params,
int32 route_id) {
......
CreateCommandBufferResult result = CREATE_COMMAND_BUFFER_FAILED;
GpuChannelMap::const_iterator iter = gpu_channels_.find(client_id);
if (iter != gpu_channels_.end()) {
result = iter->second->CreateViewCommandBuffer(
window, surface_id, init_params, route_id);
}
Send(new GpuHostMsg_CommandBufferCreated(result));
}
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel_manager.cc中。
参数client_id描述的是一个GPU进程的Client端的ID。从前面的分析可以知道,这个Client端要么是Browser端,要么是Render端。从前面Chromium的GPU进程启动过程分析一文可以知道,Browser端和Render端与GPU进程建立时GPU通道时,GPU进程会分别为它们创建一个GpuChannel对象描述建立的GPU通道。并且以Browser端和Render端的ID为键值将上述GpuChannel对象保存在GpuChannelManager类的成员变量gpu_channels_描述的一个Hash Map中。
GpuChannelManager类的成员函数OnCreateViewCommandBuffer根据参数client_id的值在成员变量gpu_channels_描述的Hash Map中找到一个对应的GpuChannel对象,并且调用该GpuChannel对象的成员函数CreateViewCommandBuffer为参数client_id和route_id描述的一个Client端OpenGL上下文创建一个OpenGL命令缓冲区服务对象,最后发送一个类型为GpuHostMsg_CommandBufferCreated的IPC消息给Client端,作为类型为GpuMsg_CreateViewCommandBuffer的IPC消息的回复消息。
接下来我们继续分析GpuChannel类的成员函数CreateViewCommandBuffer的实现,以便了解OpenGL命令缓冲区服务对象的创建过程,如下所示:
CreateCommandBufferResult GpuChannel::CreateViewCommandBuffer(
const gfx::GLSurfaceHandle& window,
int32 surface_id,
const GPUCreateCommandBufferConfig& init_params,
int32 route_id) {
......
GpuCommandBufferStub* share_group = stubs_.Lookup(init_params.share_group_id);
// Virtualize compositor contexts on OS X to prevent performance regressions
// when enabling FCM.
// http://crbug.com/180463
bool use_virtualized_gl_context = false;
#if defined(OS_MACOSX)
use_virtualized_gl_context = true;
#endif
scoped_ptr<GpuCommandBufferStub> stub(
new GpuCommandBufferStub(this,
share_group,
window,
mailbox_manager_.get(),
image_manager_.get(),
gfx::Size(),
disallowed_features_,
init_params.attribs,
init_params.gpu_preference,
use_virtualized_gl_context,
route_id,
surface_id,
watchdog_,
software_,
init_params.active_url));
......
if (!router_.AddRoute(route_id, stub.get())) {
......
return CREATE_COMMAND_BUFFER_FAILED_AND_CHANNEL_LOST;
}
stubs_.AddWithID(stub.release(), route_id);
return CREATE_COMMAND_BUFFER_SUCCEEDED;
}
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel.cc中。
在GPU进程,OpenGL命令缓冲区服务对象通过一个GpuCommandBufferStub对象描述,因此,GpuChannel类的成员函数CreateViewCommandBuffer为参数route_id描述的Client端OpenGL上下文内部使用的OpenGL命令缓冲区代理对象创建了一个GpuCommandBufferStub对象。
创建出来的GpuCommandBufferStub对象会作为一个路由添加到GPU进程的IO线程中去负责接收以后其对应的OpenGL命令缓冲区代理对象,即一个CommandBufferProxyImpl对象,发送过来的IPC消息,并且也会保存在GpuChannel类的成员变量stubs_描述的一个Map中。前面提到,一个GPU通道可能被多个OpenGL上下文同时使用,因此,GpuChannel类通过成员变量stubs_来保存共用同一个GPU通道的OpenGL上下文内部使用的OpenGL命令缓冲区服务对象。
与运行在Client端的OpenGL命令缓冲区代理对象一样,运行在GPU进程中的OpenGL命令缓冲区服务对象也是按照资源共享组的形式进行组织的,因此在创建一个GpuCommandBufferStub对象时,需要知道它与哪些GpuCommandBufferStub对象在同一个资源共享组中。从前面的调用过程可以知道,参数init_params描述的一个GPUCreateCommandBufferConfig对象的成员变量share_group_id描述了在Client端正在创建的Client端的OpenGL命令缓冲区代理对象所属的资源共享组ID。如果通过这个资源共享组ID在GpuChannel类通过成员变量stubs_描述的Map可以找到一个对应的GpuCommandBufferStub对象,那么就意味着找到的GpuCommandBufferStub对象与正在创建的GpuCommandBufferStub对象在同一个资源共享组中。这同时也意味着在Client端属于同一个资源共享组的OpenGL命令缓冲区代理对象对应的OpenGL命令缓冲区服务对象在GPU进程中也是属于同一个资源共享组中。
GpuChannel类的成员函数CreateViewCommandBuffer在创建GpuCommandBufferStub对象的时候,还用到了两个较为重要的参数。
一个参数是use_virtualized_gl_context,当它的值等于true的时候,表示以后为现在正在创建的GpuCommandBufferStub对象创建Service端OpenGL上下文时,默认要创建的是一个虚拟OpenGL上下文,否则的话,要创建的是一个真实OpenGL上下文。从这里可以看到,除了Mac OS X平台,其余的平台在创建Service端OpenGL上下文时,默认创建的都是真实OpenGL上下文。后面我们分析Service端OpenGL上下文的创建过程时,就会看到这一点。
另外一个参数是GpuChannel类的成员变量mailboxmanager,它指向的是一个MailboxManager对象。从前面Chromium的GPU进程启动过程分析一文可以知道,在Chromium中,所有的GPU通道共用了同一个MailboxManager对象,也就是说,在GPU进程中,所有的GpuChannel对象的成员变量mailbox_manager_指向的都是同一个MailboxManager对象。这个MailboxManager对象是用来在不同的OpenGL上下文中传递和共享OpenGL纹理的。这种OpenGL纹理传递和共享机制称为Mailbox机制。正是通过这种Mailbox机制,WebGL端可以将自己渲染的UI以OpenGL纹理的方式交给Browser端合成到浏览器窗口中去。
接下来我们继续分析OpenGL命令缓冲区服务对象的创建过程,即GpuCommandBufferStub类的构造函数的实现,如下所示:
GpuCommandBufferStub::GpuCommandBufferStub(
GpuChannel* channel,
GpuCommandBufferStub* share_group,
const gfx::GLSurfaceHandle& handle,
gpu::gles2::MailboxManager* mailbox_manager,
gpu::gles2::ImageManager* image_manager,
const gfx::Size& size,
const gpu::gles2::DisallowedFeatures& disallowed_features,
const std::vector<int32>& attribs,
gfx::GpuPreference gpu_preference,
bool use_virtualized_gl_context,
int32 route_id,
int32 surface_id,
GpuWatchdog* watchdog,
bool software,
const GURL& active_url)
: channel_(channel),
handle_(handle),
......,
use_virtualized_gl_context_(use_virtualized_gl_context),
...... {
......
if (share_group) {
context_group_ = share_group->context_group_;
......
} else {
context_group_ = new gpu::gles2::ContextGroup(
mailbox_manager,
image_manager,
new GpuCommandBufferMemoryTracker(channel),
channel_->gpu_channel_manager()->shader_translator_cache(),
NULL,
attrib_parser.bind_generates_resource_);
}
use_virtualized_gl_context_ |=
context_group_->feature_info()->workarounds().use_virtualized_gl_contexts;
}
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_command_buffer_stub.cc中。
GpuCommandBufferStub类的构造函数将传递进程的参数分别保存在对应的成员变量中,其中,有三个较为重要的参数:
1. channel,指向的是一个GpuChannel对象,保存在成员变量channel_中,描述的是当前正在创建的GpuCommandBufferStub对象所使用的GPU通道。
2. handle,指向的是一个gfx::GLSurfaceHandle对象,保存在成员变量handle_,描述的是当前正在创建的GpuCommandBufferStub对象对应的Service端OpenGL端上下文所关联的绘图表面句柄 。从前面的分析可以知道,从Render端传递过来的gfx::GLSurfaceHandle对象的类型为gfx::NATIVE_TRANSPORT,并且没有关联OS本地窗口,而从Browser端传递过来的gfx::GLSurfaceHandle对象的类型为gfx::NATIVE_DIRECT,并且关联有一个OS本地窗口。在Android平台,该OS本地窗口实际上就是一个SurfaceView。
GpuCommandBufferStub类的构造函数还做了另外一件事情,就是初始化成员变量contextgroup,使得它指向一个gpu::gles2::ContextGroup对象,描述当前正在创建的GpuCommandBufferStub对象所属的资源共享组。当参数share_group的值不等于NULL的时候,那么就表示它指向的一个GpuCommandBufferStub对象与正在创建的GpuCommandBufferStub对象在同一个资源共享组中。在这种情况下,使用share_group指向的一个GpuCommandBufferStub对象的成员变量context_group_初始化当前正在创建的GpuCommandBufferStub对象的成员变量context_group_即可。另一方面,如果参数share_group的的值等于NULL,那么就表示正在创建的GpuCommandBufferStub对象目前不与其它GpuCommandBufferStub对象位于同一个资源共享组中。在这种情况下,使用一个新创建的gpu::gles2::ContextGroup对象作为当前正在创建的GpuCommandBufferStub对象所属的资源共享组。
最后,GpuCommandBufferStub类的构造函数通过检查成员变量context_group_描述的资源共享组,获知是否要为属于该资源共享组的Service端上下文创建虚拟OpenGL上下文。如果是的话,那么就会将成员变量use_virtualized_gl_context_设置为true。
这样,我们就分析了GPU进程接收和处理类型为GpuMsg_CreateViewCommandBuffer的IPC消息的过程,也就是在GPU进程中为Render端和Browser端OpenGL上下文创建OpenGL命令缓冲区服务对象的过程。接下来我们继续分析在GPU进程中为WebGL端OpenGL上下文创建OpenGL命令缓冲区服务对象的过程,也就是GPU进程接收和处理类型为GpuChannelMsg_CreateOffscreenCommandBuffer的IPC消息的过程。
前面提到,类型为GpuChannelMsg_CreateOffscreenCommandBuffer的IPC消息是一个控制类型的IPC消息,并且是直接从Render进程为WebGL端创建的GPU通道发送给GPU进程的,因此这个IPC消息在GPU进程中由描述该GPU通道的一个GpuChannel对象的成员函数OnControlMessageReceived接收和处理,如下所示:
bool GpuChannel::OnControlMessageReceived(const IPC::Message& msg) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(GpuChannel, msg)
IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateOffscreenCommandBuffer,
OnCreateOffscreenCommandBuffer)
......
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
DCHECK(handled) << msg.type();
return handled;
}
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel.cc中。
从这里可以看到,GpuChannel类的成员函数OnControlMessageReceived将类型为GpuChannelMsg_CreateOffscreenCommandBuffer的IPC消息分发给成员函数OnCreateOffscreenCommandBuffer处理。
GpuChannel类的成员函数OnCreateOffscreenCommandBuffer的实现如下所示:
void GpuChannel::OnCreateOffscreenCommandBuffer(
const gfx::Size& size,
const GPUCreateCommandBufferConfig& init_params,
int32 route_id,
bool* succeeded) {
......
GpuCommandBufferStub* share_group = stubs_.Lookup(init_params.share_group_id);
scoped_ptr<GpuCommandBufferStub> stub(new GpuCommandBufferStub(
this,
share_group,
gfx::GLSurfaceHandle(),
mailbox_manager_.get(),
image_manager_.get(),
size,
disallowed_features_,
init_params.attribs,
init_params.gpu_preference,
false,
route_id,
0,
watchdog_,
software_,
init_params.active_url));
......
if (!router_.AddRoute(route_id, stub.get())) {
......
*succeeded = false;
return;
}
stubs_.AddWithID(stub.release(), route_id);
......
*succeeded = true;
}
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel.cc中。
GpuChannel类的成员函数OnCreateOffscreenCommandBuffer为WebGL端OpenGL上下文创建OpenGL命令缓冲区服务对象的过程与前面分析的GpuChannel类的成员函数CreateViewCommandBuffer为Render端和Browser端OpenGL上下文创建OpenGL命令缓冲区服务对象的过程是相似的,因此这里不再进行详细分析。
不过有两点需要注意。第一点是WebGL端OpenGL上下文不与其它的Client端OpenGL上下文共享资源组,也就是通过参数init_params描述的一个GPUCreateCommandBufferConfig对象的成员变量share_group_id_不能在GpuChannel类的成员变量stubs_描述的Map中找到一个对应的GpuCommandBufferStub对象,即接下来传递GpuCommandBufferStub类的构造函数的参数share_group的值为NULL。第二点是传递给GpuCommandBufferStub类的构造函数的第三个参数,是一个默认构造的gfx::GLSurfaceHandle对象,这意味着WebGL端OpenGL上下文没有关联绘图表面。
这一步执行完成后, Render端、Browser端和WebGL端OpenGL上下文在GPU进程中使用的OpenGL命令缓冲区服务对象的创建过程就分析完毕,回到WebGraphicsContext3DCommandBufferImpl类的成员函数InitializeCommandBuffer中,它接下来会对Render端、Browser端和WebGL端OpenGL上下文使用的OpenGL命令缓冲区代理对象进行初始化,这是通过调用CommandBufferProxyImpl类的成员函数Initialize实现的,如下所示:
bool CommandBufferProxyImpl::Initialize() {
TRACE_EVENT0("gpu", "CommandBufferProxyImpl::Initialize");
shared_state_shm_.reset(channel_->factory()->AllocateSharedMemory(
sizeof(*shared_state())).release());
......
if (!shared_state_shm_->Map(sizeof(*shared_state())))
return false;
......
base::SharedMemoryHandle handle =
channel_->ShareToGpuProcess(shared_state_shm_->handle());
......
bool result;
if (!Send(new GpuCommandBufferMsg_Initialize(
route_id_, handle, &result, &capabilities_))) {
......
return false;
}
......
return true;
}
这个函数定义在文件external/chromium_org/content/common/gpu/client/command_buffer_proxy_impl.cc中。
CommandBufferProxyImpl类的成员函数Initialize首先是创建了一块共享内存,用来与GPU进程共享当前正在初始化的OpenGL命令缓冲区代理对象的状态。创建出来的共享内存通过一个SharedMemoryHandle句柄封装在一个类型为GpuCommandBufferMsg_Initialize的IPC消息中发送给GPU进程。
注意,CommandBufferProxyImpl类的成员函数Initialize在发送IPC消息的时候,指定了路由ID。这个路由ID保存在成员变量route_id_中,用来指定将IPC消息发送给运行在GPU进程中的与当前正在初始化的OpenGL命令缓冲区代理对象对应的OpenGL命令缓冲区服务对象处理,即发送给对应的GpuCommandBufferStub对象处理。
GpuCommandBufferStub类通过成员函数OnMessageReceived接收OpenGL命令缓冲区代理对象发送过来的IPC消息,如下所示:
bool GpuCommandBufferStub::OnMessageReceived(const IPC::Message& message) {
......
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(GpuCommandBufferStub, message)
IPC_MESSAGE_HANDLER_DELAY_REPLY(GpuCommandBufferMsg_Initialize,
OnInitialize);
......
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
......
return handled;
}
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_command_buffer_stub.cc中。
从这里可以看到,GpuCommandBufferStub类的成员函数OnMessageReceived将类型为GpuCommandBufferMsg_Initialize的IPC消息分发给成员函数OnInitialize处理。
GpuCommandBufferStub类的成员函数OnInitialize的实现如下所示:
void GpuCommandBufferStub::OnInitialize(
base::SharedMemoryHandle shared_state_handle,
IPC::Message* reply_message) {
......
command_buffer_.reset(new gpu::CommandBufferService(
context_group_->transfer_buffer_manager()));
......
decoder_.reset(::gpu::gles2::GLES2Decoder::Create(context_group_.get()));
scheduler_.reset(new gpu::GpuScheduler(command_buffer_.get(),
decoder_.get(),
decoder_.get()));
......
decoder_->set_engine(scheduler_.get());
if (!handle_.is_null()) {
......
surface_ = ImageTransportSurface::CreateSurface(
channel_->gpu_channel_manager(),
this,
handle_);
} else {
GpuChannelManager* manager = channel_->gpu_channel_manager();
surface_ = manager->GetDefaultOffscreenSurface();
}
scoped_refptr<gfx::GLContext> context;
if (use_virtualized_gl_context_ && channel_->share_group()) {
context = channel_->share_group()->GetSharedContext();
if (!context.get()) {
context = gfx::GLContext::CreateGLContext(
channel_->share_group(),
channel_->gpu_channel_manager()->GetDefaultOffscreenSurface(),
gpu_preference_);
......
channel_->share_group()->SetSharedContext(context.get());
}
......
context = new gpu::GLContextVirtual(
channel_->share_group(), context.get(), decoder_->AsWeakPtr());
if (!context->Initialize(surface_.get(), gpu_preference_)) {
......
}
}
if (!context.get()) {
context = gfx::GLContext::CreateGLContext(
channel_->share_group(), surface_.get(), gpu_preference_);
}
......
if (!decoder_->Initialize(surface_,
context,
!surface_id(),
initial_size_,
disallowed_features_,
requested_attribs_)) {
......
}
gpu_control_service_.reset(
new gpu::GpuControlService(context_group_->image_manager(), NULL));
......
GpuCommandBufferMsg_Initialize::WriteReplyParams(
reply_message, true, decoder_->GetCapabilities());
Send(reply_message);
......
}
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_command_buffer_stub.cc中。
GpuCommandBufferStub类的成员函数OnInitialize的执行过程如下所示:
1. 创建一个CommandBufferService对象,保存在成员变量command_buffer_中,用来向Client端的OpenGL命令缓冲区代理对象提供GPU相关的服务。
2. 调用GLES2Decoder类的静态成员函数Create创建一个GLES2DecoderImpl对象,保存在成员变量decoder_中,用来解析Client端发送过来的GPU命令。
3. 创建一个GpuScheduler对象,保存在成员变量scheduler_中,作为上面创建的GLES2DecoderImpl对象的调度器,用来调度执行Client端发送过来的GPU命令。
4. 当成员变量handle_描述的绘图表面句柄不为空时,调用ImageTransportSurface类的静态成员函数CreateSurface创建一个绘图表面。否则的话,就先获得一个GpuChannelManager对象,再调用该GpuChannelManager对象的成员函数GetDefaultOffscreenSurface创建一个离屏类型的绘图表面。从前面的分析可以知道,Render端和Browser端对应的GpuCommandBufferStub对象的成员变量handle_描述的绘图表面句柄不为空,而WebGL端对应的GpuCommandBufferStub对象的成员变量handle_描述的绘图表面句柄为空,因此,在GPU进程中,Render端和Browser端的OpenGL上下文关联的绘图表面是通过ImageTransportSurface类的静态成员函数CreateSurface创建的,而WebGL端的OpenGL上下文关联的绘图表面是通过GpuChannelManager类的成员函数GetDefaultOffscreenSurface创建的。创建出来的绘图表面保存在成员变量surface_中。
5. 当成员变量use_virtualized_gl_context_的值等于true,并且当前正在处理的GpuCommandBufferStub对象位于一个OpenGL上下文共享组时,就为其创建一个虚拟OpenGL上下文。这个虚拟OpenGL上下文使用一个GLContextVirtual对象描述。当这个虚拟OpenGL上下文创建出来后,还会调用GLContextVirtual类的成员函数Initialize对其进行初始化。虚拟OpenGL上下文在使用时,必须要切换到一个真实OpenGL上下文中去,因此每一个虚拟OpenGL上下文都对应有一个真实OpenGL上下文。实际上,位于同一个OpenGL上下文共享组中的虚拟OpenGL上下文对应的真实OpenGL上下文都是同一个。这个真实OpenGL上下文保存在OpenGL上下文共享组中,因此,如果还没有为OpenGL上下文共享组创建真实OpenGL上下文,那么就需要调用GLContext类的静态成员函数CreateGLContext创建一个。创建出来的真实OpenGL上下文使用一个GLContextEGL对象描述。
7. 调用GLES2DecoderImpl类的成员函数Initialize对前面第2步创建的GLES2DecoderImpl对象进行初始化,主要是将前面第4步创建的绘图表面和第5步或者第6步创建的OpenGL上下文保存在它的内部,以便以后可以使用。
8. 对接收到的类型为GpuCommandBufferMsg_Initialize的IPC消息进行回复。
接下来,我们主要分析Render端、Browser端和WebGL端的OpenGL上下文关联的绘图表面的创建过程,即上述第4步的执行过程,以及Render端、Browser端和WebGL端的OpenGL上下文的创建过程,即第5步和第6步的执行过程。至于其它过程创建的CommandBufferService对象、GLES2DecoderImpl对象和GpuScheduler对象的具体作用,我们在接下来一篇文章中分析Client端执行GPU命令的过程时再详细分析。
从前面的分析可以知道,Render端和Browser端OpenGL上下文关联的绘图表面是通过ImageTransportSurface类的静态成员函数CreateSurface创建的,它的实现如下所示:
scoped_refptr<gfx::GLSurface> ImageTransportSurface::CreateSurface(
GpuChannelManager* manager,
GpuCommandBufferStub* stub,
const gfx::GLSurfaceHandle& handle) {
scoped_refptr<gfx::GLSurface> surface;
if (handle.transport_type == gfx::TEXTURE_TRANSPORT)
surface = new TextureImageTransportSurface(manager, stub, handle);
else
surface = CreateNativeSurface(manager, stub, handle);
if (!surface.get() || !surface->Initialize())
return NULL;
return surface;
}
这个函数定义在文件external/chromium_org/content/common/gpu/image_transport_surface.cc中。
参数manager指向一个GpuChannelManager对象。从前面的调用过程可以知道,这个GpuChannelManager对象是从负责接收和处理IPC消息的GPU通道获得的。从前面Chromium的GPU进程启动过程分析一文又可以知道,在GPU进程中,所有的GPU通道关联的GpuChannelManager对象都是同一个GpuChannelManager对象。
参数stub指向一个GpuCommandBufferStub对象,ImageTransportSurface类的静态成员函数CreateSurface要的做的事情就是为它对应的OpenGL上下文创建一个绘图表面,这个绘图表面是基于参数handle描述的绘图表面句柄创建的。
参数handle指定的绘图表面句柄要么是Render端的,要么是Browser端的。前面提到,Render端和Browser端的绘图表面句柄的类型分别为gfx::NATIVE_TRANSPORT和gfx::NATIVE_DIRECT,均不是gfx::TEXTURE_TRANSPORT,因此,ImageTransportSurface类的静态成员函数CreateSurface接下来会调用另外一个静态成员函数CreateNativeSurface来为Render端和Browser端OpenGL上下文创建绘图表面。
这里顺便提一下,类型为gfx::TEXTURE_TRANSPORT的绘图表面句柄表示基于它创建的绘图表面是通过纹理的形式传给其它OpenGL上下文使用的。例如,在以前的Chromium版本中,Render端OpenGL上下文使用的绘图表面的句柄类型就为gfx::TEXTURE_TRANSPORT,这时候Render端就会把网页的UI渲染在一个纹理中,然后再将这个纹理传递给Browser端的OpenGL上下文进行合成。在后来的Chromium版本中,不再使用这种形式,而是直接将渲染Render端网页UI的命令和资源传递给Browser端,然后由Browser端在自己的OpenGL上下文执行这些命令获得Render端的UI。这样就可以省去中间生成一个纹理的过程。
接下来我们继续分析ImageTransportSurface类的静态成员函数CreateNativeSurface的实现,如下所示:
scoped_refptr<gfx::GLSurface> ImageTransportSurface::CreateNativeSurface(
GpuChannelManager* manager,
GpuCommandBufferStub* stub,
const gfx::GLSurfaceHandle& handle) {
if (handle.transport_type == gfx::NATIVE_TRANSPORT) {
return scoped_refptr<gfx::GLSurface>(
new ImageTransportSurfaceAndroid(manager,
stub,
manager->GetDefaultOffscreenSurface(),
handle.parent_client_id));
}
......
ANativeWindow* window =
GpuSurfaceLookup::GetInstance()->AcquireNativeWidget(
stub->surface_id());
scoped_refptr<gfx::GLSurface> surface =
new gfx::NativeViewGLSurfaceEGL(window);
bool initialize_success = surface->Initialize();
if (window)
ANativeWindow_release(window);
......
return scoped_refptr<gfx::GLSurface>(
new DirectSurfaceAndroid(manager, stub, surface.get()));
}
这个函数定义在文件external/chromium_org/content/common/gpu/image_transport_surface_android.cc中。
前面提到,Render端OpenGL上下文使用的绘图表面的句柄的类型为gfx::NATIVE_TRANSPORT,也就是参数handle描述的绘图表面句柄的类型为gfx::NATIVE_TRANSPORT,这时候ImageTransportSurface类的静态成员函数CreateNativeSurface就会为其创建一个类型为ImageTransportSurfaceAndroid的绘图表面。
在创建一个类型为ImageTransportSurfaceAndroid的绘图表面的时候,也就是在调用ImageTransportSurfaceAndroid类的构造函数的时候,传递进去的第三个参数是一个gfx::GLSurface对象,这个gfx::GLSurface对象是通过调用参数manager指向的一个GpuChannelManager对象的成员函数GetDefaultOffscreenSurface获得的,它的实现如下所示:
gfx::GLSurface* GpuChannelManager::GetDefaultOffscreenSurface() {
if (!default_offscreen_surface_.get()) {
default_offscreen_surface_ =
gfx::GLSurface::CreateOffscreenGLSurface(gfx::Size());
}
return default_offscreen_surface_.get();
}
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel_manager.cc中。
GpuChannelManager对象的成员函数GetDefaultOffscreenSurface返回的是成员变量default_offscreen_surface_描述的一个离屏类型的绘图表面。这个离屏类型的绘图表面是通过调用gfx::GLSurface类的静态成员函数CreateOffscreenGLSurface创建的,如下所示:
scoped_refptr<GLSurface> GLSurface::CreateOffscreenGLSurface(
const gfx::Size& size) {
CHECK_NE(kGLImplementationNone, GetGLImplementation());
switch (GetGLImplementation()) {
case kGLImplementationOSMesaGL: {
scoped_refptr<GLSurface> surface(new GLSurfaceOSMesa(1, size));
if (!surface->Initialize())
return NULL;
return surface;
}
case kGLImplementationEGLGLES2: {
scoped_refptr<GLSurface> surface;
if (GLSurfaceEGL::IsEGLSurfacelessContextSupported() &&
(size.width() == 0 && size.height() == 0)) {
surface = new SurfacelessEGL(size);
} else {
surface = new PbufferGLSurfaceEGL(size);
}
if (!surface->Initialize())
return NULL;
return surface;
}
default:
NOTREACHED();
return NULL;
}
}
这个函数定义在文件external/chromium_org/ui/gl/gl_surface_android.cc中。
在前面Chromium的GPU进程启动过程分析一文提到,Android平台的GPU进程加载的OpenGL实现是kGLImplementationEGLGLES2描述的版本中,gfx::GLSurface类的静态成员函数CreateOffscreenGLSurface实际创建的绘图表面要么是一个SurfacelessEGL对象,要么是一个PbufferGLSurfaceEGL对象。如果平台支持创建无绘图表面的OpenGL上下文,那么创建的绘图表面就是一个SurfacelessEGL对象。否则的话,创建的绘图表面就是一个PbufferGLSurfaceEGL对象,也就是一个基于Pbuffer实现的绘图表面。
我们假设创建的是PbufferGLSurfaceEGL对象,gfx::GLSurface类的静态成员函数CreateOffscreenGLSurface接下来还会调用它的成员函数Initialize进行初始化,如下所示:
bool PbufferGLSurfaceEGL::Initialize() {
EGLSurface old_surface = surface_;
EGLDisplay display = GetDisplay();
......
const EGLint pbuffer_attribs[] = {
EGL_WIDTH, size_.width(),
EGL_HEIGHT, size_.height(),
EGL_NONE
};
EGLSurface new_surface = eglCreatePbufferSurface(display,
GetConfig(),
pbuffer_attribs);
......
surface_ = new_surface;
return true;
}
这个函数定义在文件external/chromium_org/ui/gl/gl_surface_egl.cc中。
PbufferGLSurfaceEGL类的成员函数Initialize主要就是调用eglCreatePbufferSurface函数创建了一个基于Pbuffer的绘图表面,并且保存在成员变量surface_中。
回到ImageTransportSurface类的静态成员函数CreateNativeSurface,我们就可以知道,所有的Render端的OpenGL上下文使用的绘图表面都是相同的,这个绘图表面是一个离屏类型的绘图表面,保存在GPU进程中一个GpuChannelManager对象,它要么是一个SurfacelessEGL对象,要么是一个PbufferGLSurfaceEGL对象。有了这个离屏类型的绘图表面之后,ImageTransportSurface类的静态成员函数CreateNativeSurface再将其封装在一个ImageTransportSurfaceAndroid对象中,作为Render端OpenGL上下文的绘图表面了。
ImageTransportSurfaceAndroid对象的创建过程如下所示:
ImageTransportSurfaceAndroid::ImageTransportSurfaceAndroid(
GpuChannelManager* manager,
GpuCommandBufferStub* stub,
gfx::GLSurface* surface,
uint32 parent_client_id)
: PassThroughImageTransportSurface(manager, stub, surface),
parent_client_id_(parent_client_id) {}
这个函数定义在文件external/chromium_org/content/common/gpu/image_transport_surface_android.cc中。
从这里可以看到,ImageTransportSurfaceAndroid类的构造函数将参数surface描述的绘图表面传递给其父类PassThroughImageTransportSurface的构造函数处理,如下所示:
PassThroughImageTransportSurface::PassThroughImageTransportSurface(
GpuChannelManager* manager,
GpuCommandBufferStub* stub,
gfx::GLSurface* surface)
: GLSurfaceAdapter(surface),
did_set_swap_interval_(false) {
......
}
这个函数定义在文件external/chromium_org/content/common/gpu/image_transport_surface.cc中。
从这里可以看到,PassThroughImageTransportSurface类的构造函数又将参数surface描述的绘图表面传递给其父类GLSurfaceAdapter的构造函数处理,如下所示:
GLSurfaceAdapter::GLSurfaceAdapter(GLSurface* surface) : surface_(surface) {}
这个函数定义在文件external/chromium_org/ui/gl/gl_surface.cc中。
GLSurfaceAdapter的构造函数最终将参数surface描述的绘图表面保存在成员变量surface_中。
这样,Render端OpenGL上下文使用的绘图表面就创建完毕。回到ImageTransportSurface类的静态成员函数CreateNativeSurface中,接下来我们继续分析Browser端OpenGL上下文使用的绘图表面的创建过程。
Browser端OpenGL上下文使用的绘图表面的句柄的类型为gfx::NATIVE_DIRECT,不等于gfx::NATIVE_TRANSPORT,因此ImageTransportSurface类的静态成员函数CreateNativeSurface为Browser端OpenGL上下文创建的绘图表面实际上是一个DirectSurfaceAndroid对象。在创建这个DirectSurfaceAndroid对象的时候,也就是在调用DirectSurfaceAndroid的构造函数的时候,传递进去的第三个参数是一个gfx::GLSurface指针。这个gfx::GLSurface指针指向的是一个gfx::NativeViewGLSurfaceEGL对象。这个gfx::NativeViewGLSurfaceEGL对象又关联有一个ANativeWindow。上述gfx::NativeViewGLSurfaceEGL对象的获取过程如下所示:
1. 调用参数stub指向的一个GpuCommandBufferStub对象的成员函数surface_id获得一个Surface ID,这个Surface ID是Browser端通过类型为GpuMsg_CreateViewCommandBuffer的IPC消息传递过来的。
2. 根据前面获得的Surface ID,调用GpuSurfaceLookup类的成员函数AcquireNativeWidget获得一个ANativeWindow。从前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文可以知道,这个ANativeWindow实际上就是在Browser进程中创建的一个SurfaceView,这个SurfaceView是用来显示网页UI的。
3. 根据前面获得的ANativeWindow创建一个NativeViewGLSurfaceEGL对象,并且调用它的成员函数Initialize进行初始化。
接下来我们分析NativeViewGLSurfaceEGL类的构造函数的实现,如下所示:
NativeViewGLSurfaceEGL::NativeViewGLSurfaceEGL(EGLNativeWindowType window)
: window_(window),
...... {
......
}
这个函数定义在文件external/chromium_org/ui/gl/gl_surface_egl.cc中。
NativeViewGLSurfaceEGL类的构造函数主要就是将参数window描述的一个ANativeWindow对象保存在成员变量window_中。
NativeViewGLSurfaceEGL类的成员函数Initialize的实现如下所示:
bool NativeViewGLSurfaceEGL::Initialize() {
return Initialize(scoped_ptr<VSyncProvider>());
}
这个函数定义在文件external/chromium_org/ui/gl/gl_surface_egl.cc中。
NativeViewGLSurfaceEGL类的成员函数Initialize调用另外一个重载版本的成员函数Initialize执行初始化工作,如下所示:
bool NativeViewGLSurfaceEGL::Initialize(
scoped_ptr<VSyncProvider> sync_provider) {
......
std::vector<EGLint> egl_window_attributes;
......
// Create a surface for the native window.
surface_ = eglCreateWindowSurface(
GetDisplay(), GetConfig(), window_, &egl_window_attributes[0]);
......
return true;
}
这个函数定义在文件external/chromium_org/ui/gl/gl_surface_egl.cc中。
重载版本的NativeViewGLSurfaceEGL类的成员函数Initialize主要就是调用eglCreateWindowSurface创建了一个基于ANativeWindow的绘图表面,并且保存在成员变量surface_中。
回到ImageTransportSurface类的静态成员函数CreateNativeSurface中,接下来我们继续分析DirectSurfaceAndroid对象的创建过程,即DirectSurfaceAndroid类的构造函数的实现,如下所示:
DirectSurfaceAndroid::DirectSurfaceAndroid(GpuChannelManager* manager,
GpuCommandBufferStub* stub,
gfx::GLSurface* surface)
: PassThroughImageTransportSurface(manager, stub, surface) {}
这个函数定义在external/chromium_org/content/common/gpu/image_transport_surface_android.cc中。
DirectSurfaceAndroid类的构造函数与前面分析的ImageTransportSurfaceAndroid类的构造函数一样,都是将参数surface描述的绘图表面传递给父类PassThroughImageTransportSurface的构造函数处理。PassThroughImageTransportSurface类的构造函数最终会将参数surface描述的绘图表面保存在父类GLSurfaceAdapter的成员变量surface_中。
这样,Browser端OpenGL上下文使用的绘图表面也创建完毕。回到GpuCommandBufferStub类的成员函数OnInitialize中,接下来我们继续分析WebGL端OpenGL上下文使用的绘图表面的创建过程,如下所示:
1. 从负责接收和处理IPC消息的GPU通道获得一个GpuChannelManager对象。前面提到,这个GpuChannelManager对象对所有的GPU通道来说,都是相同的。
2. 调用前面获得的GpuChannelManager对象的成员函数GetDefaultOffscreenSurface获得一个绘图表面。
上述GpuChannelManager对象的成员函数GetDefaultOffscreenSurface我们在前面已经分析过,它返回的是其成员变量default_offscreen_surface_描述的一个离屏类型的绘图表面。这个离屏类型的绘图表面要么通过一个SurfacelessEGL对象描述,要么通过一个PbufferGLSurfaceEGL对象描述。由此可知,不但所有的WebGL端OpenGL上下文使用的是同一个绘图表面,而且所有的WebGL端和Render端OpenGL上下文使用的也都是同一个绘图表面。
有了绘图表面之后,GpuCommandBufferStub类的成员函数OnInitialize接下来就可以为Render端、Browser端和WebGL端在GPU进程中创建OpenGL上下文了。前面提到,这个OpenGL上下文可能是一个虚拟OpenGL上下文,也有可能是一个真实OpenGL上下文。
我们首先分析真实OpenGL上下文的创建过程。前面提到,真实OpenGL上下文是GpuCommandBufferStub类的成员函数OnInitialize通过调用gfx::GLContext类的静态成员函数CreateGLContext创建的,如下代码片段所示:
context = gfx::GLContext::CreateGLContext(
channel_->share_group(), surface_.get(), gpu_preference_);
创建真实OpenGL上下文主要使用到两个参数。第一个参数指定一个OpenGL上下文共享组。第二个参数指定要关联的绘图表面。其中,OpenGL上下文共享组通过调用GpuCommandBufferStub类的成员变量channel_指向的一个GpuChannel对象的成员函数share_group获得,而绘图表面由GpuCommandBufferStub类的成员变量surface_指定,即为前面通过ImageTransportSurface类的静态成员函数CreateSurface或者GpuChannelManager类的成员函数GetDefaultOffscreenSurface创建的绘图表面。
GpuChannel类的成员函数share_group的实现如下所示:
class GpuChannel : public IPC::Listener, public IPC::Sender {
public:
......
gfx::GLShareGroup* share_group() const { return share_group_.get(); }
......
private:
......
scoped_refptr<gfx::GLShareGroup> share_group_
......
};
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel.h中。
GpuChannel类的成员函数share_group返回的是成员变量share_group_描述的一个gfx::GLShareGroup对象。那么GpuChannel类的成员变量share_group_是什么时候初始化的呢?
从前面Chromium的GPU进程启动过程分析一文可以知道,GPU进程接收到类型为GpuMsg_EstablishChannel的IPC消息时,就会调用进程内的一个GpuChannelManager对象的成员函数OnEstablishChannel创建一个GPU通道,即一个GpuChannel对象,如下所示:
void GpuChannelManager::OnEstablishChannel(int client_id, bool share_context) {
......
gfx::GLShareGroup* share_group = NULL;
gpu::gles2::MailboxManager* mailbox_manager = NULL;
if (share_context) {
if (!share_group_.get()) {
share_group_ = new gfx::GLShareGroup;
DCHECK(!mailbox_manager_.get());
mailbox_manager_ = new gpu::gles2::MailboxManager;
}
share_group = share_group_.get();
mailbox_manager = mailbox_manager_.get();
}
scoped_ptr<GpuChannel> channel(new GpuChannel(
this, watchdog_, share_group, mailbox_manager, client_id, false));
......
}
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel_manager.cc中。
参数share_context是从Render端、Browser端或者WebGL端传递过来的,它们的值均为true。这意味着在GPU进程中创建的所有GpuChannel对象都是使用同一个GpuChannelManager对象的成员变量share_group_指向的一个gfx::GLShareGroup对象来创建的。这个gfx::GLShareGroup对象最终就保存在GpuChannel类的成员变量share_group_中,如下所示:
GpuChannel::GpuChannel(GpuChannelManager* gpu_channel_manager,
GpuWatchdog* watchdog,
gfx::GLShareGroup* share_group,
gpu::gles2::MailboxManager* mailbox,
int client_id,
bool software)
: ......,
share_group_(share_group ? share_group : new gfx::GLShareGroup),
...... {
......
}
这个函数定义在文件external/chromium_org/content/common/gpu/gpu_channel.cc中。
这意味着在GPU进程中,所有的真实OpenGL上下文都是位于同一个OpenGL上下文共享组中,而这个OpenGL上下文共享组就是保存在GPU进程中的一个GpuChannelManager对象的成员变量share_group_中的。
了解了创建真实OpenGL上下文所需要的OpenGL上下文共享组和绘图表面的来龙去脉之后,接下来我们就可以分析真实OpenGL上下文的创建过程了,即gfx::GLContext类的静态成员函数CreateGLContext的实现,如下所示:
scoped_refptr<GLContext> GLContext::CreateGLContext(
GLShareGroup* share_group,
GLSurface* compatible_surface,
GpuPreference gpu_preference) {
scoped_refptr<GLContext> context;
switch (GetGLImplementation()) {
case kGLImplementationMockGL:
return scoped_refptr<GLContext>(new GLContextStub());
case kGLImplementationOSMesaGL:
context = new GLContextOSMesa(share_group);
break;
default:
if (compatible_surface->GetHandle())
context = new GLContextEGL(share_group);
else
context = new GLNonOwnedContext(share_group);
break;
}
if (!context->Initialize(compatible_surface, gpu_preference))
return NULL;
return context;
}
这个函数定义在文件external/chromium_org/ui/gl/gl_context_android.cc中。
前面提到,Android平台使用的OpenGL实现为kGLImplementationEGLGLES2,因此gfx::GLContext类的静态成员函数CreateGLContext最后会调用default子句的代码创建一个真实OpenGL上下文。
当调用参数compatible_surface指向的一个GLSurface对象的成员函数GetHandle得到的返回值不为NULL的时候,gfx::GLContext类的静态成员函数CreateGLContext创建的真实OpenGL上下文是一个GLContextEGL对象,否则的话就是一个GLNonOwnedContext对象。
从前面的分析可以知道,对于Browser端来说,参数compatible_surface指向的实际上是一个DirectSurfaceAndroid对象,它的成员函数GetHandle是从其父类GLSurfaceAdapter继承下来的,实现如下所示:
void* GLSurfaceAdapter::GetHandle() {
return surface_->GetHandle();
}
这个函数定义在文件external/chromium_org/ui/gl/gl_surface.cc中。
从前面的分析可以知道,对于Browser端来说,GLSurfaceAdapter类的成员变量surface_指向的是一个NativeViewGLSurfaceEGL对象,GLSurfaceAdapter类的成员函数GetHandle调用了它的成员函数GetHandle。
NativeViewGLSurfaceEGL类的成员函数GetHandle的实现如下所示:
EGLSurface NativeViewGLSurfaceEGL::GetHandle() {
return surface_;
}
这个函数定义在文件external/chromium_org/ui/gl/gl_surface_egl.cc中。
NativeViewGLSurfaceEGL类的成员函数GetHandle返回的是成员变量surface_的值,这个成员变量是在前面分析的NativeViewGLSurfaceEGL类的成员函数Initialize中初始化的,它的值不等于NULL。回到gfx::GLContext类的静态成员函数CreateGLContext中,这意味着对于Browser端来说,gfx::GLContext类的静态成员函数CreateGLContext为它创建的真实OpenGL上下文是一个GLContextEGL对象。
再次回到gfx::GLContext类的静态成员函数CreateGLContext中,对于Render端来说,参数compatible_surface指向的实际上是一个ImageTransportSurfaceAndroid对象,它的成员函数GetHandle也是从其父类GLSurfaceAdapter继承下来的,不过这时候GLSurfaceAdapter类的成员变量surface_指向的是一个PbufferGLSurfaceEGL对象。根据前面的分析,这时候GLSurfaceAdapter类的成员函数GetHandle调用了PbufferGLSurfaceEGL类的成员函数GetHandle,后者的实现如下所示:
EGLSurface PbufferGLSurfaceEGL::GetHandle() {
return surface_;
}
这个函数定义在文件external/chromium_org/ui/gl/gl_surface_egl.cc中。
PbufferGLSurfaceEGL类的成员函数GetHandle返回的是成员变量surface_的值,这个成员变量是在前面分析的PbufferGLSurfaceEGL类的成员函数Initialize中初始化的,它的值不等于NULL。回到gfx::GLContext类的静态成员函数CreateGLContext中,这意味着对于Render端来说,gfx::GLContext类的静态成员函数CreateGLContext为它创建的真实OpenGL上下文也是一个GLContextEGL对象。
再次回到gfx::GLContext类的静态成员函数CreateGLContext中,对于WebGL端来说,参数compatible_surface指向的实际上是一个PbufferGLSurfaceEGL对象,因此它的成员函数GetHandle的返回址也不为NULL,这意味着对于WebGL端来说,gfx::GLContext类的静态成员函数CreateGLContext为它创建的真实OpenGL上下文也是一个GLContextEGL对象。
这样,GPU进程为WebGL端、Render端和Browser端创建真实OpenGL上下文的过程就分析完成了。这些真实OpenGL上下文可以用图2来描述,如下所示:
图2 WebGL端、Render端和Browser端的真实OpenGL上下文
回到GpuCommandBufferStub类的成员函数OnInitialize中,接下来我们分析它为Render端、Browser端和WebGL端创建虚拟OpenGL上下文的过程,如下代码片段所示:
context = channel_->share_group()->GetSharedContext();
if (!context.get()) {
context = gfx::GLContext::CreateGLContext(
channel_->share_group(),
channel_->gpu_channel_manager()->GetDefaultOffscreenSurface(),
gpu_preference_);
......
channel_->share_group()->SetSharedContext(context.get());
}
......
context = new gpu::GLContextVirtual(
channel_->share_group(), context.get(), decoder_->AsWeakPtr());
从这里可以看到,不管是WebGL端、Render端还是Browser端,它们的虚拟OpenGL上下文都通过一个GLContextVirtual对象来描述,并且这些虚拟OpenGL上下文都是对应同一个真实OpenGL上下文,这个真实OpenGL上下文也是前面分析的gfx::GLContext类的静态成员函数CreateGLContext创建的。
在调用gfx::GLContext类的静态成员函数CreateGLContext的时候,传递进去的第三个参数是通过调用GPU进程负责管理所有的GPU通道的一个GpuChannelManager对象的成员函数GetDefaultOffscreenSurface获得的。这意味着传递给gfx::GLContext类的静态成员函数CreateGLContext的第三个参数指向的是一个PbufferGLSurfaceEGL对象。根据前面对gfx::GLContext类的静态成员函数CreateGLContext的分析,我们可以知道,为WebGL端、Render端和Browser端创建的虚拟OpenGL上下文对应的真实OpenGL上下文也是一个GLContextEGL对象。
接下来,我们就分析虚拟OpenGL上下文的创建过程,即GLContextVirtual类的构造函数的实现,如下所示:
GLContextVirtual::GLContextVirtual(
gfx::GLShareGroup* share_group,
gfx::GLContext* shared_context,
base::WeakPtr<gles2::GLES2Decoder> decoder)
: GLContext(share_group),
shared_context_(shared_context),
......,
decoder_(decoder) {
}
这个函数定义在文件external/chromium_org/gpu/command_buffer/service/gl_context_virtual.cc中。
GLContextVirtual类的构造函数主要将参数shared_context_描述的一个真实OpenGL上下文和参数decoder描述的一个GLES2DecoderImpl对象分别保存在成员变量shared_context_和decoder_中,并且将另外一个参数share_group描述的一个gfx::GLShareGroup对象交给父类GLContext的构造函数处理。
这样,GPU进程为WebGL端、Render端和Browser端创建虚拟OpenGL上下文的过程就分析完成了。这些虚拟OpenGL上下文可以用图3来描述,如下所示:
图3 WebGL端、Render端和Browser端的虚拟OpenGL上下文
关于图2和图3的详细分析,可以参考前面Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析一文,这里不再复述。
至此,我们就分析完成WebGL端、Render端和Browser端OpenGL上下文的创建过程了。从这些创建过程可以知道:
1. 在Client端,即Render进程或者Browser进程,WebGL端、Render端和Browser端OpenGL上下文都是通过一个WebGraphicsContext3DCommandBufferImpl对象描述,这个WebGraphicsContext3DCommandBufferImpl对象内部通过一个CommandBufferProxyImpl对象与Service端对应的OpenGL上下文通信。
2. 在Service端,即GPU进程,WebGL端、Render端和Browser端OpenGL上下文都对应有一个真实OpenGL上下文或者一个虚拟OpenGL上下文,这些真实OpenGL上下文或者虚拟OpenGL上下文通过一个GpuCommandBufferStub对象与Client端OpenGL上下文进行通信。
3. 在Service端,不管WebGL端、Render端和Browser端使用的是真实OpenGL上下文还是虚拟OpenGL上下文,它们都是位于同一个OpenGL上下文共享组中,这使得它们可以共享OpenGL资源。这也意味着在整个Chromium中,有且仅有一个OpenGL上下文共享组。
关于第1点、第2点和第4点,在接下来一篇文章中分析WebGL端、Render端和Browser端执行GPU命令的过程时再详细分析。关于第3点,在最后两篇文章中分析WebGL端、Render端和Browser端OpenGL上下文的调度和资源同步过程时也会进一步分析。敬请关注!更多的信息也可以关注老罗的新浪微博:http://weibo.com/shengyangluo/home。
扫一扫
在手机上阅读