标签:android chromium opengl上下文 绘图表面
GPU命令需要在OpenGL上下文中执行。每一个OpenGL上下文都关联有一个绘图表面,GPU命令就是作用在绘图表面上的。不同用途的OpenGL上下文关联的绘图表面不一样,例如用于离屏渲染的OpenGL上下文关联的绘图表面可以用Pbuffer描述,而用于屏幕渲染的OpenGL上下文的绘图表面要用本地窗口描述。本文分析Chromium硬件加速渲染涉及到的OpenGL上下文及其联的绘图表面的关联过程。
老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!
从前面Chromium的GPU进程启动过程分析一文可以知道,在Chromium中,所有的GPU命令均在一个GPU线程中执行。这个GPU线程有可能是位于Browser进程中,也有可能是位于GPU进程中。需要执行GPU命令的有WebGL、Render进程和Browser进程,为了方便描述,我们称之为WebGL端、Render端和Browser端,意为它们是Chromium的GPU线程的Client端。
WebGL端和Render端执行的离屏渲染,而Browser端执行的屏幕渲染,因此它们需要的绘图表面是不一样的,如图1所示:
图1 Chromium的真实OpenGL上下文的绘图表面
从图1可以看到,WebGL端、Render端和Browser端的OpenGL上下文均是通过GLContextEGL描述,不过它们关联的绘图表面却是不一样的。GLContextEGL描述的是一个真实的OpenGL上下文,这是相对于我们后面要描述的虚拟OpenGL上下文而言的。
WebGL端和Render端的OpenGL上下文关联的绘图表面是GPU线程中的默认离屏绘图表面,这个离屏绘图表面用一个Pbuffer来描述。但是如果底层的GPU支持无绘图表面的OpenGL上下文,那么GPU线程的默认离屏绘图表面使用一个Surfaceless描述。
Browser端的OpenGL上下文关联的绘图表面是一个本地窗口,因为它负责将WebGL端、Render端和浏览器窗口其它的UI渲染在屏幕上。在Android平台上,SurfaceView描述的就是一个本地窗口,因此,Chromium将它作为Browser端的OpenGL上下文的绘图表面。
在前面Chromium硬件加速渲染机制基础知识简要介绍和学习计划一文提到,有些平台的GPU不能很好地同时支持创建多个OpenGL上下文。对于这些平台,Chromium为WebGL端、Render端和Browser端创建的是虚拟OpenGL上下文,如图2所示:
图2 Chromium的虚拟OpenGL上下文的绘图表面
虚拟OpenGL上下文使用GLContextVirtual来描述。由于它们是虚拟的,因此在激活的时候,必须切换到真实的OpenGL上下文去。这意味着虚拟OpenGL上下文必须要对应有真实OpenGL上下文。
Chromium在不能很好支持同时创建多个OpenGL上下文的平台上,只创建一个真实OpenGL上下文,并且WebGL端、Render端和Browser端的虚拟OpenGL上下文均与该真实OpenGL上下文对应。上述真实OpenGL上下文为WebGL端和Render端所用时,关联的是绘图表面是GPU线程的默认离屏绘图表面,但是为Browser端所用时,关联的绘图表面是本地窗口。在Android平台上,这个本地窗口即为一个SurfaceView。从这一点我们可以看到,OpenGL上下文的绘图表面是可以动态修改的。
从图1和图2还可以看到,不管WebGL端、Render端和Browser端的OpenGL上下文是真实的,还是虚拟的,它们都是位于一个共享组中。这使得Browser端的OpenGL上下文可以直接访问在WebGL端和Render端的OpenGL上下文中创建的资源,从而可以高效地完成合成和渲染操作。接下来,我们就分别分析WebGL端、Render端和Browser端的OpenGL上下文的绘图表面的创建过程。理解OpenGL上下文的绘图表面的创建过程对理解OpenGL上下文的创建过程是至关重要的,因此我们专用用一篇文章来分析它们。
我们首先分析WebGL端的OpenGL上下文的绘图表面的创建过程。
从前面Chromium的GPU进程启动过程分析一文可以知道,WebGL端是通过一个WebGraphicsContext3DCommandBufferImpl对象向GPU线程请求执行GPU操作的。这个WebGraphicsContext3DCommandBufferImpl对象是通过调用WebGraphicsContext3DCommandBufferImpl类的静态成员函数CreateOffscreenContext创建的,它的实现如下所示:
WebGraphicsContext3DCommandBufferImpl* WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext( GpuChannelHost* host, const WebGraphicsContext3D::Attributes& attributes, bool lose_context_when_out_of_memory, const GURL& active_url, const SharedMemoryLimits& limits, WebGraphicsContext3DCommandBufferImpl* share_context) { ...... return new WebGraphicsContext3DCommandBufferImpl( 0, active_url, host, attributes, lose_context_when_out_of_memory, limits, share_context); }这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。
我们注意到,在调用WebGraphicsContext3DCommandBufferImpl类的构造函数创建一个WebGraphicsContext3DCommandBufferImpl对象的时候,第一个参数指定0,这表示WebGL端的OpenGL上下文使用的绘图表面的ID为0。
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) : ......, surface_id_(surface_id), ...... { ...... }这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。
这意味着WebGL端使用的WebGraphicsContext3DCommandBufferImpl对象的成员变量surface_id_的值等于0。Render进程在为WebGL端创建OpenGL上下文时,会将该成员变量的值传递到GPU线程,这时候GPU线程就知道要为要创建的OpenGL上下文关联的一个离屏绘图表面。这个过程我们在接下来一篇文章中分析OpenGL上下文的创建过程时再分析,此时我们只需要记住WebGL端的OpenGL上下文关联的绘图表面的ID等于0。
我们接下来分析Browser端的OpenGL上下文的绘图表面的创建过程。这要从Browser进程的启动过程说起。
Chromium自带了一个Shell APK,用来说明如何通过Content API来创建一个基于Chromium的浏览器。Chromium的Content API封装了Chromium的多进程架构以及WebKit等,通过它们可以很容易地实现一个与Chrome类似的浏览器。Shell APK的源码位于目录external/chromium_org/content/shell/android/shell_apk中。
Shell APK的Main Activity是ContentShellActivity,它运行在Shell APK的主进程中,也就是Browser进程。当Shell APK启动时,ContentShellActivity类的成员函数onCreate就会被调用,它的执行过程如下所示:
public class ContentShellActivity extends Activity { ...... protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); ...... setContentView(R.layout.content_shell_activity); mShellManager = (ShellManager) findViewById(R.id.shell_container); mWindowAndroid = new ActivityWindowAndroid(this); ...... mShellManager.setWindow(mWindowAndroid); String startupUrl = getUrlFromIntent(getIntent()); if (!TextUtils.isEmpty(startupUrl)) { mShellManager.setStartupUrl(Shell.sanitizeUrl(startupUrl)); } if (CommandLine.getInstance().hasSwitch(ContentSwitches.DUMP_RENDER_TREE)) { try { BrowserStartupController.get(this).startBrowserProcessesSync( BrowserStartupController.MAX_RENDERERS_LIMIT); } catch (ProcessInitException e) { ...... } } else { try { BrowserStartupController.get(this).startBrowserProcessesAsync( new BrowserStartupController.StartupCallback() { @Override public void onSuccess(boolean alreadyStarted) { finishInitialization(savedInstanceState); } ...... }); } catch (ProcessInitException e) { ...... } } } ...... }这个函数定义在文件external/chromium_org/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java中。
ContentShellActivity类的成员函数onCreate主要做了以下几件事情:
1. 将R.layout.content_shell_activity布局文件设置为浏览器窗口UI。
2. 在上述布局文件中找到一个id为shell_container的控件,该控件是一个自定义控件,类型为ShellManager,保存在ContentShellActivity类的成员变量mShellManager中。
3. 创建一个ActivityWindowAndroid对象,保存在ContentShellActivity类的成员变量mWindowAndroid中,并且调用ShellManager类的成员函数setWindow将其设置到上述ShellManager控件内部去。ActivityWindowAndroid对象封装了对Shell APK的主Activity的一些操作。
4. 检查启动Shell APK的主Activity的Intent是否设置有启动URL。如果有,那么就通过调用ShellManager类的成员函数setStartupUrl将其设置到上述ShellManager控件内部去。
5. 检查Shell APK的命令行参数是否设置了ContentSwitches.DUMP_RENDER_TREE选项。如果设置了,那么就调用Browser进程的BrowserStartupController单例的成员函数startBrowserProcessesSync同步启动和初始化Content模块,相当于是在Native层对Browser进程执行初始化工作。如果没有设置,那么就调用Browser进程的BrowserStartupController单例的成员函数startBrowserProcessesAsync异步启动和初始化Content模块。对于异步方式,当Content模块启动和初始完成后,会调用ContentShellActivity类的成员函数finishInitialization继续执行启动Shell APK的工作。
接下来,我们主要分析第2、3和5个步骤的相关操作,其中,假设第5步使用异步方式启动和初始化Content模块。
第2步主要是创建了一个类型为ShellManager的控件,这将会导致ShellManager类的构造函数被调用,如下所示:
public class ShellManager extends FrameLayout { ...... public ShellManager(final Context context, AttributeSet attrs) { super(context, attrs); nativeInit(this); ...... } ...... }这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/ShellManager.java中。
ShellManager类的构造函数主要是调用了另外一个成员函数nativeInit执行一些初始化工作。
ShellManager类的成员变量nativeInit是一个JNI函数,它由Native层的函数Init实现,如下所示:
struct GlobalState { GlobalState() {} base::android::ScopedJavaGlobalRef<jobject> j_shell_manager; }; base::LazyInstance<GlobalState> g_global_state = LAZY_INSTANCE_INITIALIZER; ...... static void Init(JNIEnv* env, jclass clazz, jobject obj) { g_global_state.Get().j_shell_manager.Reset( base::android::ScopedJavaLocalRef<jobject>(env, obj)); }这个函数定义在文件external/chromium_org/content/shell/android/shell_manager.cc中。
从前面的调用过程可以知道,参数obj指向的是一个Java层的ShellManager对象,函数Init将它保存在全局变量g_global_state描述的一个GlobalState对象的成员变量j_shell_manager中。
这一步执行完成之后,回到ContentShellActivity类的成员函数onCreate中,它的第3步主要是调用ShellManager类的成员函数setWindow设置一个ActivityWindowAndroid对象到Shell APK的主Activity窗口UI的ShellManager控件的内部去,它的实现如下所示:
public class ShellManager extends FrameLayout { ...... public void setWindow(WindowAndroid window) { assert window != null; mWindow = window; mContentViewRenderView = new ContentViewRenderView(getContext()) { @Override protected void onReadyToRender() { if (sStartup) { mActiveShell.loadUrl(mStartupUrl); sStartup = false; } } }; mContentViewRenderView.onNativeLibraryLoaded(window); } ...... }
这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/ShellManager.java
ShellManager类的成员函数setWindow首先是将参数window描述的一个ActivityWindowAndroid对象保存在成员变量mWindow中,接着创建了一个ContentViewRenderView对象,并且保存在成员变量mContentViewRenderView中。当Shell APK启动和初始化完毕,上面创建的ContentViewRenderView对象的成员函数onReadyRender就会被调用。在调用的过程中,就会调用ShellManager类的成员变量mActivityShell描述的一个Shell对象的成员函数loadUrl加载由另外一个成员变量mStartupUrl指定的一个URL。这个URL是设置在用来启动Shell APK的Intent中的。ShellManager类的成员函数setWindow最后调用前面创建的ContentViewRenderView对象的成员函数onNativeLibraryLoaded对其进行初始化。
接下来,我们首先分析ContentViewRenderView对象的创建过程,即ContentViewRenderView类的构造函数的实现,接着再分析ContentViewRenderView对象的初始化过程,即ContentViewRenderView类的成员函数onNativeLibraryLoaded的实现。
ContentViewRenderView类的构造函数的实现如下所示:
public class ContentViewRenderView extends FrameLayout { ...... private final SurfaceView mSurfaceView; ...... public ContentViewRenderView(Context context) { super(context); mSurfaceView = createSurfaceView(getContext()); ...... addView(mSurfaceView, new FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); ...... } ...... }这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java中。
从这里可以看到,ContentViewRenderView类描述的是一个FrameLayout,它的构造函数主要是通过调用另外一个成员函数createSurfaceView创建了一个SurfaceView,保存在成员变量mSurfaceView中,并且作为当前正在创建的ContentViewRenderView的子控件。
这一步执行完成后,回到ShellManager类的成员函数setWindow中,它接下来调用ContentViewRenderView类的成员函数onNativeLibraryLoaded对前面创建的ContentViewRenderView进行初始化,如下所示:
public class ContentViewRenderView extends FrameLayout { // The native side of this object. private long mNativeContentViewRenderView; private SurfaceHolder.Callback mSurfaceCallback; ...... public void onNativeLibraryLoaded(WindowAndroid rootWindow) { ...... mNativeContentViewRenderView = nativeInit(rootWindow.getNativePointer()); ...... mSurfaceCallback = new SurfaceHolder.Callback() { @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { ...... nativeSurfaceChanged(mNativeContentViewRenderView, format, width, height, holder.getSurface()); ...... } ...... }; mSurfaceView.getHolder().addCallback(mSurfaceCallback); ...... } ...... }这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java中。
从前面的调用过程可以知道,参数rootWindow指向的是一个ActivityWindowAndroid对象,ContentViewRenderView类的成员函数onNativeLibraryLoaded首先调用该ActivityWindowAndroid对象的成员函数getNativePointer获得一个Native层的WindowAndroid对象,然后再以该Native层的WindowAndroid对象为参数,调用ContentViewRenderView类的成员函数nativeInit对当前正在创建的ContentViewRenderView进行初始化。
ContentViewRenderView类的成员函数onNativeLibraryLoaded还创建了一个SurfaceHolder.Callback对象,保存在成员变量mSurfaceCallback中,并且使用该SurfaceHolder.Callback对象获得成员变量mSurfaceView描述的一个SurfaceView底层所使用的绘图表面的变化。也就是说,当ContentViewRenderView类的成员变量mSurfaceView描述的一个SurfaceView底层所使用的绘图表面发生变化时,上述SurfaceHolder.Callback对象的成员函数surfaceChanged就会被调用,然后又会调用ContentViewRenderView类的成员函数nativeSurfaceChanged执行相关的操作。
接下来,我们首先分析ActivityWindowAndroid类的成员函数getNativePointer的实现,接着再分析ContentViewRenderView类的成员函数nativeInit的实现。
ActivityWindowAndroid类继承于WindowAndroid类,它的成员函数getNativePointer也是从父类WindowAndroid继承下来的,因此接下来我们分析WindowAndroid的成员函数getNativePointer的实现,如下所示:
public class WindowAndroid { ...... private long mNativeWindowAndroid = 0; ...... public long getNativePointer() { if (mNativeWindowAndroid == 0) { mNativeWindowAndroid = nativeInit(mVSyncMonitor.getVSyncPeriodInMicroseconds()); } return mNativeWindowAndroid; } ...... }这个函数定义在文件external/chromium_org/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java中。
WindowAndroid类的成员函数getNativePointer返回的是成员变量mNativeWindowAndroid描述的一个Native层的WindowAndroid对象。不过。当这个成员变量的值等于0的时候,就表示Native层的WindowAndroid对象还没有创建,这时候就需要调用另外一个成员函数nativeInit进行创建。
WindowAndroid类的成员函数nativeInit是一个JNI函数,由Native层的函数Init实现,如下所示:
jlong Init(JNIEnv* env, jobject obj, jlong vsync_period) { WindowAndroid* window = new WindowAndroid(env, obj, vsync_period); return reinterpret_cast<intptr_t>(window); }这个函数定义在文件external/chromium_org/ui/base/android/window_android.cc中。
函数Init主要是创建了一个Native层的WindowAndroid对象,并且将它的地址返回给调用者。
这一步执行完成后,回到ContentViewRenderView类的成员函数onNativeLibraryLoaded中,它接下来调用ContentViewRenderView类的成员函数nativeInit对正在创建的ContentViewRenderView进行初始化。
ContentViewRenderView类的成员函数nativeInit是一个JNI函数,由Native层的函数Init实现,如下所示:
static jlong Init(JNIEnv* env, jobject obj, jlong native_root_window) { gfx::NativeWindow root_window = reinterpret_cast<gfx::NativeWindow>(native_root_window); ContentViewRenderView* content_view_render_view = new ContentViewRenderView(env, obj, root_window); return reinterpret_cast<intptr_t>(content_view_render_view); }这个函数定义在文件external/chromium_org/content/browser/android/content_view_render_view.cc中。
从前面的调用过程可以知道,参数native_root_window指向的是一个Native层的WindowAndroid对象,函数Init将它封装在一个新创建的Native层的ContentViewRenderView对象中,最后将新创建的Native层的ContentViewRenderView对象的地址返回给调用者。
这一步执行完成后,就在Java层创建了一个SurfaceView,并且在Native层创建了一个WindowAndroid对象和一个ContentViewRenderView对象,回到ContentShellActivity类的成员函数onCreate中,它的第5步是调用Browser进程的BrowserStartupController单例的成员函数startBrowserProcessesAsync异步启动和初始化Content模块。当Content模块启动和初始完成后,会调用ContentShellActivity类的成员函数finishInitialization继续执行启动Shell APK的工作,如下所示:
public class ContentShellActivity extends Activity { ...... private void finishInitialization(Bundle savedInstanceState) { String shellUrl = ShellManager.DEFAULT_SHELL_URL; if (savedInstanceState != null && savedInstanceState.containsKey(ACTIVE_SHELL_URL_KEY)) { shellUrl = savedInstanceState.getString(ACTIVE_SHELL_URL_KEY); } mShellManager.launchShell(shellUrl); } ...... }这个函数定义在文件external/chromium_org/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java中。
ContentShellActivity类的成员函数finishInitialization主要是调用了成员变量mShellManager指向的一个ShellManager对象的成员函数launchShell启动一个Shell,同时为该Shell指定了一个URL。该URL的默认值为ShellManager.DEFAULT_SHELL_URL,但是如果上次启动Shell APK时,保存有最后使用的Shell URL,那么就会使用最后使用的Shell URL。
ShellManager类的成员函数launchShell的实现如下所示:
public class ShellManager extends FrameLayout { ...... public void launchShell(String url) { ...... nativeLaunchShell(url); ...... } ...... }这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/ShellManager.java中。
ShellManager类的成员函数launchShell主要是调用了另外一个成员函数nativeLaunchShell在Native层启动一个Shell。
ShellManager类的成员函数nativeLaunchShell是一个JNI函数,由Native层的函数LaunchShell实现,如下所示:
void LaunchShell(JNIEnv* env, jclass clazz, jstring jurl) { ShellBrowserContext* browserContext = ShellContentBrowserClient::Get()->browser_context(); GURL url(base::android::ConvertJavaStringToUTF8(env, jurl)); Shell::CreateNewWindow(browserContext, url, NULL, MSG_ROUTING_NONE, gfx::Size()); }
这个函数定义在文件external/chromium_org/content/shell/android/shell_manager.cc中。
函数LaunchShell主要是调用另外一个函数Shell::CreateNewWindow在Native层启动一个Shell。注意,在调用函数Shell::CreateNewWindow时,第四个参数的值指定为MSG_ROUTING_NONE,后面我们分析Render端的OpenGL上下文的绘图表面的创建过程时,将会用到该参数。
函数Shell::CreateNewWindow的实现如下所示:
Shell* Shell::CreateNewWindow(BrowserContext* browser_context, const GURL& url, SiteInstance* site_instance, int routing_id, const gfx::Size& initial_size) { WebContents::CreateParams create_params(browser_context, site_instance); create_params.routing_id = routing_id; create_params.initial_size = AdjustWindowSize(initial_size); WebContents* web_contents = WebContents::Create(create_params); Shell* shell = CreateShell(web_contents, create_params.initial_size); if (!url.is_empty()) shell->LoadURL(url); return shell; }这个函数定义在文件external/chromium_org/content/shell/browser/shell.cc中。
函数Shell::CreateNewWindow首先调用WebContents类的静态成员函数Create创建了一个WebContentsImpl对象,接着再调用函数CreateShell创建了一个Shell,最后在参数url的值不等于空的情况下,调用前面创建的Shell的成员函数LoadUrl加载参数url描述的网址。
后面我们分析Render端的OpenGL上下文的绘图表面的创建过程时,我们再分析WebContents类的静态成员函数Create创建WebContentsImpl对象的过程,接下来我们继续分析函数CreateShell创建Shell的过程,如下所示:
Shell* Shell::CreateShell(WebContents* web_contents, const gfx::Size& initial_size) { Shell* shell = new Shell(web_contents); shell->PlatformCreateWindow(initial_size.width(), initial_size.height()); ...... return shell; }这个函数定义在文件external/chromium_org/content/shell/browser/shell.cc中。
函数CreateShell首先是创建了一个Shell对象,接着调用这个Shell对象的成员函数PlatformCreateWindow创建一个Shell窗口,如下所示:
void Shell::PlatformCreateWindow(int width, int height) { java_object_.Reset(AttachCurrentThread(), CreateShellView(this)); }这个函数定义在文件external/chromium_org/content/shell/browser/shell_android.cc。
Shell类的成员函数PlatformCreateWindow主要是调用另外一个函数CreateShellView创建一个Java层的Shell对象,并且保存在全局变量java_object_中。
函数CreateShellView的实现如下所示:
jobject CreateShellView(Shell* shell) { JNIEnv* env = base::android::AttachCurrentThread(); jobject j_shell_manager = g_global_state.Get().j_shell_manager.obj(); return Java_ShellManager_createShell( env, j_shell_manager, reinterpret_cast<intptr_t>(shell)).Release(); }这个函数定义在文件external/chromium_org/content/shell/android/shell_manager.cc中。
从前面的分析可以知道,全局变量g_global_state指向的一个GlobalState对象的成员变量j_shell_manager描述的是一个Java层的ShellManager对象,这里调用函数Java_ShellManager_createShell调用它的成员函数createShell,用来创建一个Java层的Shell对象。
Java层的ShellManager类的成员函数createShell的实现如下所示:
public class ShellManager extends FrameLayout { ...... private Object createShell(long nativeShellPtr) { ...... LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); Shell shellView = (Shell) inflater.inflate(R.layout.shell_view, null); shellView.initialize(nativeShellPtr, mWindow, mContentViewClient); ...... showShell(shellView); return shellView; } ...... }这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/ShellManager.java中。
ShellManager类的成员函数createShell首先是根据R.layout.shell_view布局文件创建了一个Shell控件,接着Shell类的成员函数initialize对该控件进行初始化,最后调用另外一个成员函数showShell显示该控件,如下所示:
public class ShellManager extends FrameLayout { ...... private void showShell(Shell shellView) { shellView.setContentViewRenderView(mContentViewRenderView); addView(shellView, new FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); mActiveShell = shellView; ContentViewCore contentViewCore = mActiveShell.getContentViewCore(); if (contentViewCore != null) { mContentViewRenderView.setCurrentContentViewCore(contentViewCore); ...... } } ...... }这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/ShellManager.java中。
从前面的分析可以知道,ShellManager类的成员变量mContentViewRenderView描述的是一个ContentViewRenderView控件,该ContentViewRenderView控件包含有一个SurfaceView,ShellManager类的成员函数showShell又将该ContentViewRenderView控件作为参数shellView描述的一个Shell控件的子控件,最后参数shellView描述的Shell控件又作为当前正在处理的ShellManager控件的一个子控件。从这里我们就看到Browser窗口的UI层次结构大致为:
ShellManager --Shell --ContentViewRenderView --SurfaceeView
ShellManager类的成员函数showShell接下来将参数shellView描述的Shell控件保存在成员变量mActiveShell中,并且调用该Shell控件的成员函数getContentViewCore获得一个ContentViewCore对象。有了这个ContentViewCore对象,就将它设置到ShellManager类的成员变量mContentViewRenderView描述的ContentViewRenderView控件的内部去。这是通过调用ContentViewRenderView类的成员函数setCurrentContentViewCore实现的。
ContentViewRenderView类的成员函数setCurrentContentViewCore的实现如下所示:
public class ContentViewRenderView extends FrameLayout { ...... public void setCurrentContentViewCore(ContentViewCore contentViewCore) { ...... mContentViewCore = contentViewCore; if (mContentViewCore != null) { ...... nativeSetCurrentContentViewCore(mNativeContentViewRenderView, mContentViewCore.getNativeContentViewCore()); } ...... } ...... }这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java中。
ContentViewRenderView类的成员函数setCurrentContentViewCore首先将参数contentViewCore描述的一个ContentViewCore对象保存在成员变量mContentViewCore中,接着调用该ContentViewCore对象的成员函数getNativeContentViewCore获得它在Native层对应的一个ContentViewCoreImpl对象。
从前面的分析可以知道,ContentViewRenderView类的成员变量mNativeContentViewRenderView描述的是Native层的一个ContentViewRenderView对象,ContentViewRenderView类的成员函数setCurrentContentViewCore最后调用另外一个成员函数nativeSetCurrentContentViewCore将该Native层的ContentViewRenderView对象,以及前面获得的Native层的ContentViewCoreImpl对象设置到Native层的Chromium中去。
ContentViewRenderView类的成员函数nativeSetCurrentContentViewCore是一个JNI函数,由Native层的函数Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSetCurrentContentViewCore实现,如下所示:
__attribute__((visibility("default"))) void Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSetCurrentContentViewCore(JNIEnv* env, jobject jcaller, jlong nativeContentViewRenderView, jlong nativeContentViewCore) { ContentViewRenderView* native = reinterpret_cast<ContentViewRenderView*>(nativeContentViewRenderView); CHECK_NATIVE_PTR(env, jcaller, native, "SetCurrentContentViewCore"); return native->SetCurrentContentViewCore(env, jcaller, nativeContentViewCore); }这个函数定义在文件out/target/product/generic/obj/GYP/shared_intermediates/content/jni/ContentViewRenderView_jni.h中。
函数Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSetCurrentContentViewCore首先是将参数nativeContentViewRenderView转换为一个Native层的ContentViewRenderView对象,接着将参数nativeContentViewCore描述的一个Native层的ContentViewCoreImpl对象设置到其内部去,这是通过调用Native层的ContentViewRenderView类的成员函数SetCurrentContentViewCore实现的。
Native层的ContentViewRenderView类的成员函数SetCurrentContentViewCore的实现如下所示:
void ContentViewRenderView::SetCurrentContentViewCore( JNIEnv* env, jobject obj, jlong native_content_view_core) { InitCompositor(); ContentViewCoreImpl* content_view_core = reinterpret_cast<ContentViewCoreImpl*>(native_content_view_core); compositor_->SetRootLayer(content_view_core ? layer_tree_build_helper_->GetLayerTree( content_view_core->GetLayer()) : scoped_refptr<cc::Layer>()); }这个函数定义在文件external/chromium_org/content/browser/android/content_view_render_view.cc中。
ContentViewRenderView类的成员函数SetCurrentContentViewCore首先调用另外一个成员函数InitCompositor创建一个在Browser进程使用的UI合成器,这个UI合成器保存在成员变量compositor_中,它负责合成Render端负责渲染的网页UI等。
ContentViewRenderView类的成员函数SetCurrentContentViewCore接着将参数native_content_view_core转化为一个Native层的ContentViewCoreImpl对象,并且将该ContentViewCoreImpl对象内部包含的一个Layer作为Browser进程的UI合成器的Root Layer,以后Browser进程的UI合成器通过遍历该Root Layer描述的Layer Tree,就可以将Render端负责渲染的网页UI合成到浏览器窗口来。
接下来,我们继续分析ContentViewRenderView类的成员函数InitCompositor的实现,以及可以了解Browser进程的UI合成器的创建过程,如下所示:
void ContentViewRenderView::InitCompositor() { if (!compositor_) compositor_.reset(Compositor::Create(this, root_window_)); }这个函数定义在文件external/chromium_org/content/browser/android/content_view_render_view.cc中。
从这里可以看到,Browser进程的UI合成器是通过调用Compositor类的静态成员函数Create创建的,它的实现如下所示:
Compositor* Compositor::Create(CompositorClient* client, gfx::NativeWindow root_window) { return client ? new CompositorImpl(client, root_window) : NULL; }这个函数定义在文件external/chromium_org/content/browser/renderer_host/compositor_impl_android.cc中。
从这里可以看到,Browser进程的UI合成器通过CompositorImpl来描述,即它是一个CompositorImpl对象。
这一步执行完成后,Browser进程在Native层中创建了一个Shell窗口和一个UI合成器,为以后合成Render端负责的网页UI做好了准备。从前面的分析可以知道,Browser进程的Shell窗口包含有一个SurfaceView。在前面分析的ContentViewRenderView类的成员函数onNativeLibraryLoaded中又提到,当上述SurfaceView底层使用的绘图表面发生变化(包括第一次创建时),ContentViewRenderView类的成员函数nativeSurfaceChanged会被调用。
ContentViewRenderView类的成员函数nativeSurfaceChanged是一个JNI函数,由Native层的函数Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSurfaceChanged实现,如下所示:
__attribute__((visibility("default"))) void Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSurfaceChanged(JNIEnv* env, jobject jcaller, jlong nativeContentViewRenderView, jint format, jint width, jint height, jobject surface) { ContentViewRenderView* native = reinterpret_cast<ContentViewRenderView*>(nativeContentViewRenderView); CHECK_NATIVE_PTR(env, jcaller, native, "SurfaceChanged"); return native->SurfaceChanged(env, jcaller, format, width, height, surface); }这个函数定义在文件out/target/product/generic/obj/GYP/shared_intermediates/content/jni/ContentViewRenderView_jni.h中。
函数Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSurfaceChanged首先将参数nativeContentViewRenderView转换为一个Native层的ContentViewRenderView对象,接着调用该ContentViewRenderView对象的成员函数SurfaceChanged通知Browser进程用来显示网页UI的SurfaceView发生了变化。
ContentViewRenderView类的成员函数SurfaceChanged的实现如下所示:
void ContentViewRenderView::SurfaceChanged(JNIEnv* env, jobject obj, jint format, jint width, jint height, jobject surface) { if (current_surface_format_ != format) { current_surface_format_ = format; compositor_->SetSurface(surface); } ...... }这个函数定义在文件external/chromium_org/content/browser/android/content_view_render_view.cc中。
ContentViewRenderView类的成员变量current_surface_format_保存的是Browser进程的SurfaceView底层使用的绘图表面的颜色格式,它的值被初始化为0。因此,当Browser进程的SurfaceView第一次创建,以及它底层使用的绘图表面的颜色格式发生变化时,ContentViewRenderView类的成员函数SurfaceChanged就会调用成员变量compositor_指向的一个CompositorImpl对象的成员函数SetSurface来重新设置Browser端的OpenGL上下文的绘图表面。
CompositorImpl类的成员函数SetSurface的实现如下所示:
void CompositorImpl::SetSurface(jobject surface) { JNIEnv* env = base::android::AttachCurrentThread(); ...... ANativeWindow* window = NULL; if (surface) { ...... window = ANativeWindow_fromSurface(env, surface); } if (window) { SetWindowSurface(window); ...... } }这个函数定义在文件external/chromium_org/content/browser/renderer_host/compositor_impl_android.cc中。
CompositorImpl类的成员函数SetSurface首先调用函数ANativeWindow_fromSurface从参数surface描述的一个Java层的Surface对象获得一个关联的Native层的ANativeWindow对象,这个ANativeWindow对象描述的就是一个OS本地窗口。
从前面的调用过程可以知道,参数surface描述的Java层的Surface对象描述的即为Browser进程的Shell窗口的SurfaceView底层所使用的绘图表面,因此,前面获得的ANativeWindow对象描述的OS本地窗口即为Browser进程的Shell窗口的SurfaceView。
最后,CompositorImpl类的成员函数SetSurface调用另外一个成员函数SetWindowSurface将前面获得的ANativeWindow对象设置为Browser端的OpenGL上下文的绘图表面,它的实现如下所示:
void CompositorImpl::SetWindowSurface(ANativeWindow* window) { GpuSurfaceTracker* tracker = GpuSurfaceTracker::Get(); ...... if (window) { window_ = window; ...... surface_id_ = tracker->AddSurfaceForNativeWidget(window); tracker->SetSurfaceHandle( surface_id_, gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_DIRECT)); ...... } }这个函数定义在文件external/chromium_org/content/browser/renderer_host/compositor_impl_android.cc中。
CompositorImpl类的成员函数主要完成两件事情:
1. 调用Browser进程的GpuSurfaceTracker单例的成员函数AddSurfaceForNativeWidget为Browser端的OpenGL上下文创建一个绘图表面,这个绘图表面即为参数window描述的OS本地窗口。GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget的返回值为一个Surface ID,保存在CompositorImpl类的成员变量surface_id_中,以后Browser端通过该Surface ID就可以在Browser进程的GpuSurfaceTracker单例中找到Browser端的OpenGL上下文的绘图表面。
2. 调用Browser进程的GpuSurfaceTracker单例的成员函数SetSurfaceHandle设置Browser端的OpenGL上下文的绘图表面句柄的类型为gfx::NATIVE_DIRECT,表示Browser端的OpenGL上下文的绘图表面可以直接渲染在屏幕上。
接下来,我们就继续分析GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget和SetSurfaceHandle的实现。
GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget的实现如下所示:
int GpuSurfaceTracker::AddSurfaceForNativeWidget( gfx::AcceleratedWidget widget) { base::AutoLock lock(lock_); int surface_id = next_surface_id_++; surface_map_[surface_id] = SurfaceInfo(0, 0, widget, gfx::GLSurfaceHandle(), NULL); return surface_id; }这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_surface_tracker.cc中。
GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget首先将成员变量next_surface_id_的当前值作为新增加的绘图表面的ID,并且将它的值增加1,作为下一次增加的绘图表面的ID。
GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget接着又创建了一个SurfceInfo对象,该SurfaceInfo对象包含了参数widget描述的一个OS本地窗口,以及一个空的绘图表面句柄,并且以前面获得的ID值作为键值保存在成员变量surface_map_描述的一个std::map中。
GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget最后将分配给新增加的绘图表面的ID返回给调用者。
GpuSurfaceTracker类的成员函数SetSurfaceHandle的实现如下所示:
void GpuSurfaceTracker::SetSurfaceHandle(int surface_id, const gfx::GLSurfaceHandle& handle) { base::AutoLock lock(lock_); DCHECK(surface_map_.find(surface_id) != surface_map_.end()); SurfaceInfo& info = surface_map_[surface_id]; info.handle = handle; }这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_surface_tracker.cc中。
GpuSurfaceTracker类的成员函数SetSurfaceHandle首先根据参数surface_id描述的绘图表面的ID在成员变量surface_map_描述的一个std::map中得到一个对应的SurfaceInfo对象,并且将该SurfaceInfo对象包含的绘图表面句柄修改为参数handle描述的图表面句柄。从前面的调用过程可以知道,参数handle描述的图表面句柄的类型设置为gfx::NATIVE_DIRECT,在接下来一篇文章分析Browser端的OpenGL上下文的创建过程时将会使用到该类型值。
Browser端和WebGL端、Render端一样,所有的GPU操作都是要通过GPU线程执行的,因此它也像WebGL端、Render端一样,需要一个WebGraphicsContext3DCommandBufferImpl对象与GPU线程进行通信。
Browser端的UI合成器将Render端负责渲染的网页UI合成在一个Output Surface上。这个Output Surface对应的就是Browser进程的Shell窗口的SurfaceView,它是通过调用Browser端的UI合成器的成员函数CreateOutputSurface创建的,即调用CompositorImpl类的成员函数CreateOutputSurface创建的。
CompositorImpl类的成员函数CreateOutputSurface在执行的过程中,就会创建一个WebGraphicsContext3DCommandBufferImpl对象,以便以后可以用来与GPU线程进行通信,即请求GPU线程执行指定的GPU操作。
CompositorImpl类的成员函数CreateOutputSurface的实现如下所示:
scoped_ptr<cc::OutputSurface> CompositorImpl::CreateOutputSurface( bool fallback) { ...... 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首先是通过Browser进程的BrowserGpuChannelHostFactory单例的成员函数GetGpuChannel获得一个GPU通道。这个GPU通道是用来在Browser进程和GPU进程之间进行通信的,它的创建过程可以参考前面Chromium的GPU进程启动过程分析一文。
有了上述GPU通道之后,CompositorImpl类的成员函数CreateOutputSurface接着调用一个全局函数CreateGpuProcessViewContext创建一个WebGraphicsContext3DCommandBufferImpl对象,如下所示:
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中。
这里我们重点关注的是调用WebGraphicsContext3DCommandBufferImpl类的构造函数创建一个WebGraphicsContext3DCommandBufferImpl对象时指定的第一个参数surface_id,它来自于CompositorImpl类的成员变量surface_id_,描述的是Browser端的OpenGL上下文的绘图表面的ID,后面请求GPU线程为Browser端创建OpenGL上下文时,将会使用到该ID。
函数CreateGpuProcessViewContext最后将创建出来的WebGraphicsContext3DCommandBufferImpl对象返回给CompositorImpl类的成员函数CreateOutputSurface,后者接下来调用ContextProviderCommandBuffer类的静态成员函数Create将获得的WebGraphicsContext3DCommandBufferImpl对象封装在一个ContextProviderCommandBuffer对象,最后该ContextProviderCommandBuffer对象又会被封装在一个OutputSurfaceWithoutParent对象中,作为Browser进程的UI合成器的Output Surface。
这样,我们就分析完成了Browser端的OpenGL上下文的绘图表面的创建过程,这个绘图表面是通过一个SurfaceView描述的,并且描述该绘图表面的句柄(Surface Handle)的类型被设置为gfx::NATIVE_DIRECT。在接下来的一篇文章分析Browser端的OpenGL上下文的创建过程,我们就会看到这些信息是如何使用的。
我们最后分析Render端的OpenGL上下文的绘图表面的创建过程。
同样从前面Chromium的GPU进程启动过程分析一文可以知道,Render端也是通过一个WebGraphicsContext3DCommandBufferImpl对象向GPU线程请求执行GPU操作的。这个WebGraphicsContext3DCommandBufferImpl对象是通过调用RenderWidget类的成员函数CreateGraphicsContext3D创建的,它的实现如下所示:
scoped_ptr<WebGraphicsContext3DCommandBufferImpl> RenderWidget::CreateGraphicsContext3D() { ...... 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中。
从这里可以看到,RenderWidget类的成员函数CreateGraphicsContext3D在调用WebGraphicsContext3DCommandBufferImpl类的构造函数创建一个WebGraphicsContext3DCommandBufferImpl对象的时候,指定的绘图表面的ID是通过调用RenderWidget类的成员函数surface_id获得的,它的实现如下所示:
class CONTENT_EXPORT RenderWidget : public IPC::Listener, public IPC::Sender, NON_EXPORTED_BASE(virtual public blink::WebWidgetClient), public base::RefCounted<RenderWidget> { public: ...... int32 surface_id() const { return surface_id_; } ...... protected: ...... int32 surface_id_; ...... };
这个函数定义在文件external/chromium_org/content/renderer/render_widget.h中。
RenderWidget类的成员函数surface_id返回的是成员变量surface_id_的值,那么这个成员变量是什么时候初始化的呢?这也得从Browser进程的启动过程说起。
从前面的分析可以知道,Browser进程在启动的过程中,会调用Native层的Shell类的静态成员函数CreateNewWindow创建一个Shell窗口,如下所示:
void LaunchShell(JNIEnv* env, jclass clazz, jstring jurl) { ShellBrowserContext* browserContext = ShellContentBrowserClient::Get()->browser_context(); GURL url(base::android::ConvertJavaStringToUTF8(env, jurl)); Shell::CreateNewWindow(browserContext, url, NULL, MSG_ROUTING_NONE, gfx::Size()); }
这个函数定义在文件external/chromium_org/content/shell/android/shell_manager.cc中。
Shell类的静态成员函数CreateNewWindow在创建Shell窗口之前,会调用WebContents类的静态成员函数Create创建一个WebContentsImpl对象,如下所示:Shell* Shell::CreateNewWindow(BrowserContext* browser_context, const GURL& url, SiteInstance* site_instance, int routing_id, const gfx::Size& initial_size) { WebContents::CreateParams create_params(browser_context, site_instance); create_params.routing_id = routing_id; create_params.initial_size = AdjustWindowSize(initial_size); WebContents* web_contents = WebContents::Create(create_params); ...... }这个函数定义在文件external/chromium_org/content/shell/browser/shell.cc中。
注意参数routing_id的值等于MSG_ROUTING_NONE,它被封装在一个WebContents::CreateParams对象传递给WebContents类的静态成员函数Create。
WebContents类的静态成员函数Create的实现如下所示:
WebContents* WebContents::Create(const WebContents::CreateParams& params) { return WebContentsImpl::CreateWithOpener( params, static_cast<WebContentsImpl*>(params.opener)); }这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。
WebContents类的静态成员函数Create调用WebContentsImpl类的静态成员函数CreateWithOpener创建一个WebContentsImpl对象,后者的实现如下所示:
WebContentsImpl* WebContentsImpl::CreateWithOpener( const WebContents::CreateParams& params, WebContentsImpl* opener) { ...... WebContentsImpl* new_contents = new WebContentsImpl( params.browser_context, params.opener_suppressed ? NULL : opener); ...... new_contents->Init(params); return new_contents; }这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。
WebContentsImpl类的静态成员函数CreateWithOpener首先是创建一个WebContentsImpl对象,接着调用该WebContentsImpl对象的成员函数Init对其进行初始化,最后将该WebContentsImpl对象返回给调用者。
在调用WebContentsImpl类的成员函数Init对创建的WebContentsImpl对象进行初始化的时候,就会为Render端创建一个绘图表面。在分析WebContentsImpl类的成员函数Init的实现之前,我们先分析WebContentsImpl对象的创建过程,即WebContentsImpl类的构造函数的实现,如下所示:
WebContentsImpl::WebContentsImpl( BrowserContext* browser_context, WebContentsImpl* opener) : ......, frame_tree_(new NavigatorImpl(&controller_, this), this, this, this, this), ...... { ...... }这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。
WebContentsImpl类有一个类型为FrameTree的成员变量frame_tree,用来管理网页中的所有iframe标签,也就是网页的内容在第一级别上是按照frame来管理的,每一个frame又按照其内部的标签进行第二级别上的组织。
我们继续分析WebContentsImpl类的成员变量frame_tree描述的FrameTree对象的创建过程,即FrameTree类的构造函数的实现,如下所示:
FrameTree::FrameTree(Navigator* navigator, RenderFrameHostDelegate* render_frame_delegate, RenderViewHostDelegate* render_view_delegate, RenderWidgetHostDelegate* render_widget_delegate, RenderFrameHostManager::Delegate* manager_delegate) : ......, root_(new FrameTreeNode(this, navigator, render_frame_delegate, render_view_delegate, render_widget_delegate, manager_delegate, std::string())), ...... { }这个函数定义在文件external/chromium_org/content/browser/frame_host/frame_tree.cc中。
FrameTree类有一个类型为scoped_ptr<FrameTreeNode>的成员变量root_,它指向的是一个FrameTreeNode对象,这个FrameTreeNode对象描述的是网页的frame tree的根结点,它的创建过程如下所示:
FrameTreeNode::FrameTreeNode(FrameTree* frame_tree, Navigator* navigator, RenderFrameHostDelegate* render_frame_delegate, RenderViewHostDelegate* render_view_delegate, RenderWidgetHostDelegate* render_widget_delegate, RenderFrameHostManager::Delegate* manager_delegate, const std::string& name) : ......, render_manager_(this, render_frame_delegate, render_view_delegate, render_widget_delegate, manager_delegate), ...... {}这个函数定义在文件external/chromium_org/content/browser/frame_host/frame_tree_node.cc中。
FrameTreeNode类有一个类型为RenderFrameHostManager的成员变量render_manager_,它负责在Browser进程中创建一个RenderViewHostImpl对象与负责渲染网页的Render进程进行通信。
从这里我们可以知道,Browser进程为每一个网页创建一个WebContentImpl对象,这个WebContentImpl对象将网页看作是一个frame tree,这个frame tree由frame tree node组成,每一个frame tree node都包含有一个RenderFrameHostManager对象,这个RenderFrameHostManager对象负责与渲染网页的Render进程进行通信。通过这种frame tree的组织,Browser进程可以将一个网页的不同frame放在不同的Render进程进行渲染,从而起到域隔离作用,保证安全性,因为不同的frame加载的一般是不同域的网页。
这一步执行完成后,回到WebContentsImpl类的静态成员函数CreateWithOpener中,它接下来对前面创建的WebContentImpl对象进行初始化,这是通过调用WebContentImpl类的成员函数Init实现的,如下所示:
void WebContentsImpl::Init(const WebContents::CreateParams& params) { ...... GetRenderManager()->Init( params.browser_context, params.site_instance, params.routing_id, params.main_frame_routing_id); ...... if (browser_plugin_guest_) { ...... } else { // Regular WebContentsView. view_.reset(CreateWebContentsView( this, delegate, &render_view_host_delegate_view_)); } ...... }这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。
WebContentImpl类的成员函数Init首先调用另外一个成员函数GetRenderManager获得一个RenderFrameHostManager对象,后者的实现如下所示:
RenderFrameHostManager* WebContentsImpl::GetRenderManager() const { return frame_tree_.root()->render_manager(); }这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。
从这里可以看到,WebContentImpl类的成员函数GetRenderManager返回的是网页的frame tree的根结点的RenderFrameHostManager对象,WebContentImpl类的成员函数Init接下来就调用它的成员函数Init继续执行初始化工作。
WebContentImpl类的成员函数Init调用RenderFrameHostManager类的成员函数Init执行初始化工作完毕之后,会判断成员变量browser_plugin_guest_的值是否为NULL。当该成员变量的值不等于NULL的时候,表示当前正在处理的WebContentImpl对象是为一个Browser Plugin创建的。关于Browser Plugin,我们在前面Chromium的Plugin进程启动过程分析一文中提涉及到,它的作用类似于iframe标签。我们假设当前正在处理的WebContentImpl对象不是为一个Browser Plugin创建的,即这时候当前正在处理的WebContentImpl对象的成员变量browser_plugin_guest_的值是为NULL,那么WebContentImpl类的成员函数Init接下来会调用另外一个函数CreateWebContentsView创建一个WebContentsViewAndroid对象,如下所示:
WebContentsView* CreateWebContentsView( WebContentsImpl* web_contents, WebContentsViewDelegate* delegate, RenderViewHostDelegateView** render_view_host_delegate_view) { WebContentsViewAndroid* rv = new WebContentsViewAndroid( web_contents, delegate); *render_view_host_delegate_view = rv; return rv; }这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_view_android.cc中。
函数CreateWebContentsView创建的WebContentsViewAndroid对象返回给WebContentsImpl类的成员函数Init之后,会保存在成员变量view_中。
回到WebContentsImpl类的成员函数Init中,前面提到,它会进一步调用获得的一个RenderFrameHostManager对象的成员函数Init执行初始化工作,如下所示:
void RenderFrameHostManager::Init(BrowserContext* browser_context, SiteInstance* site_instance, int view_routing_id, int frame_routing_id) { ...... SetRenderFrameHost(CreateRenderFrameHost(site_instance, view_routing_id, frame_routing_id, false, delegate_->IsHidden())); ...... }这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_manager.cc中。
RenderFrameHostManager类的成员函数Init首先调用另外一个成员函数CreateRenderFrameHost创建一个RenderFrameHostImpl对象,用来在Browser进程中描述一个网页的一个frame,接着又调用成员函数SetRenderFrameHost将创建出来的RenderFrameHostImpl对象保存在WebContentImpl类的成员变量render_frame_host_中。
接下来我们继续分析RenderFrameHostManager类的成员函数CreateRenderFrameHost的实现,以及可以了解Render端的OpenGL上下文的绘图表面的创建过程,如下所示:
scoped_ptr<RenderFrameHostImpl> RenderFrameHostManager::CreateRenderFrameHost( SiteInstance* site_instance, int view_routing_id, int frame_routing_id, bool swapped_out, bool hidden) { if (frame_routing_id == MSG_ROUTING_NONE) frame_routing_id = site_instance->GetProcess()->GetNextRoutingID(); // Create a RVH for main frames, or find the existing one for subframes. FrameTree* frame_tree = frame_tree_node_->frame_tree(); RenderViewHostImpl* render_view_host = NULL; if (frame_tree_node_->IsMainFrame()) { render_view_host = frame_tree->CreateRenderViewHostForMainFrame( site_instance, view_routing_id, frame_routing_id, swapped_out, hidden); } ...... // TODO(creis): Pass hidden to RFH. scoped_ptr<RenderFrameHostImpl> render_frame_host = make_scoped_ptr(RenderFrameHostFactory::Create(render_view_host, render_frame_delegate_, frame_tree, frame_tree_node_, frame_routing_id, swapped_out).release()); return render_frame_host.Pass(); }
这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_manager.cc中。
RenderFrameHostManager类的成员函数CreateRenderFrameHost首先检查参数frame_routing_id的值是否等于MSG_ROUTING_NONE。如果是的话,就先生成一个Routing ID给它。
RenderFrameHostManager类的成员函数CreateRenderFrameHost接下来检查当前正在处理的RenderFrameHostManager对象管理的frame tree node描述的是否是一个main frame。如果是的话,那么就调用它所属的frame tree的成员函数CreateRenderViewHostForMainFrame为其创建一个RenderViewHostImpl对象。
我们假设当前正在处理的RenderFrameHostManager对象管理的frame tree node描述的是一个main frame,那么前面创建出来的RenderViewHostImpl对象最后通过RenderFrameHostFactory类的静态成员函数Create封装在一个RenderFrameHostImpl对象中返回给调用者。
接下来我们继续分析FrameTree类的成员函数CreateRenderViewHostForMainFrame创建RenderViewHostImpl对象的过程,以便可以了解Render端的OpenGL上下文的绘图表面的创建过程,如下所示:
RenderViewHostImpl* FrameTree::CreateRenderViewHostForMainFrame( SiteInstance* site_instance, int routing_id, int main_frame_routing_id, bool swapped_out, bool hidden) { ...... RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( RenderViewHostFactory::Create(site_instance, render_view_delegate_, render_widget_delegate_, routing_id, main_frame_routing_id, swapped_out, hidden)); render_view_host_map_[site_instance->GetId()] = rvh; return rvh; }这个函数定义在文件external/chromium_org/content/browser/frame_host/frame_tree.cc中。
FrameTree类的成员函数CreateRenderViewHostForMainFrame主要是为参数site_instance描述的一个站点创建一个RenderViewHostImpl对象,这是通过调用RenderViewHostFactory类的静态成员函数Create实现的。创建出来的RenderViewHostImpl对象以参数site_instance描述的站点的ID为键值保存在FrameTree类的成员变量render_view_host_map_描述的是一个Hash Map中。
接下来我们继续分析RenderViewHostFactory类的静态成员函数Create的实现,如下所示:
RenderViewHost* RenderViewHostFactory::Create( SiteInstance* instance, RenderViewHostDelegate* delegate, RenderWidgetHostDelegate* widget_delegate, int routing_id, int main_frame_routing_id, bool swapped_out, bool hidden) { ...... return new RenderViewHostImpl(instance, delegate, widget_delegate, routing_id, main_frame_routing_id, swapped_out, hidden); }这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_view_host_factory.cc中。
RenderViewHostFactory类的静态成员函数Create创建了一个RenderViewHostImpl对象,并且返回给调用者。
RenderViewHostImpl类是从RenderWidgetHostImpl类继承下来的,如下所示:
class CONTENT_EXPORT RenderViewHostImpl : public RenderViewHost, public RenderWidgetHostImpl { ...... };这个类定义在文件external/chromium_org/content/browser/renderer_host/render_view_host_impl.h中。
因此,当创建一个RenderViewHostImpl对象的时候,RenderWidgetHostImpl类的构造函数会被调用,在调用过程将会为Render端的OpenGL上下文创建一个绘图表面,如下所示:
RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, RenderProcessHost* process, int routing_id, bool hidden) : ...... { ...... if (routing_id_ == MSG_ROUTING_NONE) { routing_id_ = process_->GetNextRoutingID(); surface_id_ = GpuSurfaceTracker::Get()->AddSurfaceForRenderer( process_->GetID(), routing_id_); } ...... }这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc中。
参数routing_id是从前面分析的函数LaunchShell传递下来的,它的值等于MSG_ROUTING_NONE,因此,RenderWidgetHostImpl类的构造函数首先是调用参数process指向的一个RenderProcessHost对象的成员函数GetNextRoutingID生成一个Routing ID,然后再调用Browser进程中的GpuSurfaceTracker单例的成员函数AddSurfaceForRender为Render端创建一个绘图表面,如下所示:
int GpuSurfaceTracker::AddSurfaceForRenderer(int renderer_id, int render_widget_id) { base::AutoLock lock(lock_); int surface_id = next_surface_id_++; surface_map_[surface_id] = SurfaceInfo(renderer_id, render_widget_id, gfx::kNullAcceleratedWidget, gfx::GLSurfaceHandle(), NULL); return surface_id; }这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_surface_tracker.cc中。
GpuSurfaceTracker类的成员函数AddSurfaceForRender首先将成员变量next_surface_id_的当前值作为新增加的绘图表面的ID,并且将它的值增加1,作为下一次增加的绘图表面的ID。
GpuSurfaceTracker类的成员函数AddSurfaceForRender接着又创建了一个SurfceInfo对象,该SurfaceInfo对象包含了参数renderer_id和render_widget_id的值,以及一个空的窗口,即gfx::kNullAcceleratedWidget,,和一个空的绘图表面句柄,即gfx::GLSurfaceHandle(),并且以前面获得的ID值作为键值保存在成员变量surface_map_描述的一个std::map中。
注意,参数renderer_id描述的是一个Render进程的ID,而参数render_widget_id描述的是在参数参数renderer_id描述的Render进程中加载的一个网页的Routing ID。这两个参数共同确定了在一个Render进程中加载的一个网页。
GpuSurfaceTracker类的成员函数AddSurfaceForRender最后将分配给新增加的绘图表面的ID返回给调用者,即RenderWidgetHostImpl类的构造函数,后者将获得的绘图表面ID保存在成员变量surface_id_中。
这一步执行完成之后,一个Render端的OpenGL上下文的绘图表面就创建完成了。注意,这时候创建出来的绘图表面的句柄是空的,并且不像前面分析的Browser端的OpenGL上下文的绘图表面关联有一个OS本地窗口。这一点是Render端和Browser端的OpenGL上下文的绘图表面的主要区别之一。
Browser进程获得了要加载的网页的URL之后,最后会通过在前面分析的WebContentsImpl类的静态成员函数CreateWithOpener中创建的一个WebContentsImpl对象的成员函数CreateRenderViewForRenderManager为要加载的网页创建一个Render View。这一点以后我们在分析Chromium加载网页URL的过程时将会看到。现在我们主要分析WebContentsImpl类的成员函数CreateRenderViewForRenderManager的实现,以便可以了解Render端的OpenGL上下文的绘图表面句柄初始化过程,如下所示:
bool WebContentsImpl::CreateRenderViewForRenderManager( RenderViewHost* render_view_host, int opener_route_id, int proxy_routing_id, bool for_main_frame) { ...... RenderWidgetHostViewBase* rwh_view; ...... if (!for_main_frame) { ...... } else { rwh_view = view_->CreateViewForWidget(render_view_host); } ...... if (!static_cast<RenderViewHostImpl*>( render_view_host)->CreateRenderView(base::string16(), opener_route_id, proxy_routing_id, max_page_id, created_with_opener_)) { return false; } ...... return true; }
这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。
前面分析WebContentImpl类的成员函数Init的实现时提到,WebContentImpl类的成员变量view_指向的是一个WebContentsViewAndroid对象。
这里参数for_main_frame的值等于true,表示要为正在加载网页的主Frame在Browser进程中创建一个Render View,这是通过调用WebContentsImpl类的成员变量view_指向的WebContentsViewAndroid对象的成员函数CreateViewForWidget实现的。
另外一个参数render_view_host指向的便是在RenderFrameHostManager类的成员函数CreateRenderFrameHost中创建的RenderViewHostImpl对象,WebContentsImpl类的成员函数CreateRenderViewForRenderManager为正在加载网的主Frame在Browser进程中创建了一个Render View之后,再通过上述RenderViewHostImpl对象的成员函数CreateRenderView为其在Render进程中创建一个对应的Render View。
接下来我们先分析在Browser进程中创建Render View的过程,即WebContentsViewAndroid类的成员函数CreateViewForWidget的实现,然后再分析在Render进程中创建对应的Render View的过程,即RenderViewHostImpl类的成员函数CreateRenderView的实现。
WebContentsViewAndroid类的成员函数CreateViewForWidget的实现如下所示:
RenderWidgetHostViewBase* WebContentsViewAndroid::CreateViewForWidget( RenderWidgetHost* render_widget_host) { if (render_widget_host->GetView()) { // During testing, the view will already be set up in most cases to the // test view, so we don‘t want to clobber it with a real one. To verify that // this actually is happening (and somebody isn‘t accidentally creating the // view twice), we check for the RVH Factory, which will be set when we‘re // making special ones (which go along with the special views). DCHECK(RenderViewHostFactory::has_factory()); return static_cast<RenderWidgetHostViewBase*>( render_widget_host->GetView()); } // Note that while this instructs the render widget host to reference // |native_view_|, this has no effect without also instructing the // native view (i.e. ContentView) how to obtain a reference to this widget in // order to paint it. See ContentView::GetRenderWidgetHostViewAndroid for an // example of how this is achieved for InterstitialPages. RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(render_widget_host); return new RenderWidgetHostViewAndroid(rwhi, content_view_core_); }这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_view_android.cc中。
参数render_widget_host指向的是一个RenderViewHostImpl对象。由于RenderViewHostImpl类是从RenderWidgetHost类继承下来的,因此参数render_widget_host可以是RenderWidgetHost指针。
WebContentsViewAndroid类的成员函数CreateViewForWidget首无是调用参数render_widget_host指向的一个RenderViewHostImpl对象的成员函数GetView检查是否已经为它创建过Render View了。如果已经创建过,那么再通过它的成员函数GetView获得该RenderView,并且返回给调用者。如果还没有创建过,那么接下来通过调用RenderWidgetHostImpl静态成员函数From将参数render_widget_host转换为一个RenderWidgetHostImpl对象,并且以得到的RenderWidgetHostImpl对象为参数创建一个RenderWidgetHostViewAndroid对象返回给调用者。
前面在分析RenderViewHostFactory类的静态成员函数Create的实现时提到,RenderViewHostImpl类是从RenderWidgetHostImpl类继承下来的, 因此这里可以将参数render_widget_host转换为一个RenderWidgetHostImpl对象。
从这里我们就可以看到,在Android平台上,在Browser进程为正在加载网页的主Frame创建的Render View实际上是一个RenderWidgetHostViewAndroid对象,接下来我们继续分析这个RenderWidgetHostViewAndroid对象的创建过程,即RenderWidgetHostViewAndroid类的构造函数的实现,如下所示:
RenderWidgetHostViewAndroid::RenderWidgetHostViewAndroid( RenderWidgetHostImpl* widget_host, ContentViewCoreImpl* content_view_core) : host_(widget_host), ...... { host_->SetView(this); ...... }这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc 中。
RenderWidgetHostViewAndroid类的构造函数将参数widget_host指向的一个RenderWidgetHostImpl对象保存在成员变量host_中,接着再调用该RenderWidgetHostImpl对象的成员函数SetView将当前正在创建的RenderWidgetHostViewAndroid对象作为它的Render View。
RenderWidgetHostImpl类的成员函数SetView的实现如下所示:
void RenderWidgetHostImpl::SetView(RenderWidgetHostViewBase* view) { view_ = view; GpuSurfaceTracker::Get()->SetSurfaceHandle( surface_id_, GetCompositingSurface()); ...... }这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc 中。
RenderWidgetHostImpl类的成员函数SetView首先将参数view指向的RenderWidgetHostViewAndroid对象保存在成员变量view_,接着调用另外一个成员函数GetCompositingSurface获得一个绘图表面句柄,最后通过调用前面分析过的GpuSurfaceTracker类的成员函数SetSurfaceHandle将该绘图表面句柄 设置为成员变量surface_id_描述的绘图表面的句柄。
RenderWidgetHostImpl类的成员变量surface_id_描述的绘图表面是在RenderWidgetHostImpl类的构造函数中创建的,该绘图表面即Render端的OpenGL上下文的绘图表面,这个创建过程我们在前面已经分析过。这里有一点需要注意的是,在RenderWidgetHostImpl类的构造函数中创建的绘图表面的句柄是空的,因此这里需要对它进行初始化。
接下来我们继续分析RenderWidgetHostImpl类的成员函数GetCompositingSurface的实现,如下所示:
gfx::GLSurfaceHandle RenderWidgetHostImpl::GetCompositingSurface() { if (view_) return view_->GetCompositingSurface(); return gfx::GLSurfaceHandle(); }这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc 中。
从前面的调用过程可以知道,RenderWidgetHostImpl类的成员变量view_的值不为NULL,它指向了一个RenderWidgetHostViewAndroid对象,RenderWidgetHostImpl类的成员函数GetCompositingSurface调用该RenderWidgetHostViewAndroid对象的成员函数GetCompositingSurface获得一个绘图表面句柄 。
RenderWidgetHostViewAndroid类的成员函数GetCompositingSurface的实现如下所示:
gfx::GLSurfaceHandle RenderWidgetHostViewAndroid::GetCompositingSurface() { gfx::GLSurfaceHandle handle = gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_TRANSPORT); ...... return handle; }这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_view_android.cc中。
RenderWidgetHostViewAndroid类的成员函数GetCompositingSurface返回的绘图表面句柄的类型为gfx::NATIVE_TRANSPORT,也就是Render端的OpenGL上下文使用的绘图表面的类型为gfx::NATIVE_TRANSPORT。我们需要记住这一点,在后面一篇文章中分析Render端的OpenGL上下文的创建过程时会使用到。
回到WebContentsImpl类的成员函数CreateRenderViewForRenderManager中,接下来我们继续分析RenderViewHostImpl类的成员函数CreateRenderView,以便了解在Render进程中为正在加载网页的主Frame创建Render View的过程。
RenderViewHostImpl类的成员函数CreateRenderView的实现如下所示:
bool RenderViewHostImpl::CreateRenderView( const base::string16& frame_name, int opener_route_id, int proxy_route_id, int32 max_page_id, bool window_was_created_with_opener) { ...... ViewMsg_New_Params params; ...... params.view_id = GetRoutingID(); params.main_frame_routing_id = main_frame_routing_id_; params.surface_id = surface_id(); ...... params.frame_name = frame_name; // Ensure the RenderView sets its opener correctly. ...... Send(new ViewMsg_New(params)); ...... return true; }这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_view_host_impl.cc。
RenderViewHostImpl类的成员函数CreateRenderView向Render进程发送一个类型为ViewMsg_New的IPC消息,该IPC消息携带了在Browser进程为正在加载网页的主Frame创建的Render View的各种信息,例如View ID、Main Frame Routing ID、Surface ID以及Frame Name等。其中,Surface ID是通过调用成员函数surface_id获得的。
前面提到,RenderViewHostImpl类是从RenderWidgetHostImpl类继承下来的,它的成员函数surface_id也是从RenderWidgetHostImpl类继承下来的,而RenderWidgetHostImpl类的成员函数surface_id返回的Surface ID描述的即为在其构造函数中为Render端的OpenGL上下文创建的绘图表面。
类型为ViewMsg_New的IPC消息在Render进程是通过RenderThreadImpl类的成员函数OnControlMessageReceived接收的,如下所示:
bool RenderThreadImpl::OnControlMessageReceived(const IPC::Message& msg) { ...... bool handled = true; IPC_BEGIN_MESSAGE_MAP(RenderThreadImpl, msg) ...... IPC_MESSAGE_HANDLER(ViewMsg_New, OnCreateNewView) ...... IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; }这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。
从这里可以看到,RenderThreadImpl类的成员函数OnControlMessageReceived将类型为ViewMsg_New的IPC消息分发给成员函数OnCreateNewView处理。
RenderThreadImpl类的成员函数OnCreateNewView的实现如下所示:
void RenderThreadImpl::OnCreateNewView(const ViewMsg_New_Params& params) { EnsureWebKitInitialized(); // When bringing in render_view, also bring in webkit‘s glue and jsbindings. RenderViewImpl::Create(params.opener_route_id, params.window_was_created_with_opener, params.renderer_preferences, params.web_preferences, params.view_id, params.main_frame_routing_id, params.surface_id, params.session_storage_namespace_id, params.frame_name, false, params.swapped_out, params.proxy_routing_id, params.hidden, params.never_visible, params.next_page_id, params.screen_info, params.accessibility_mode); }这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。
RenderThreadImpl类的成员函数OnCreateNewView首先调用另外一个成员函数EnsureWebKitInitialized保存在Render进程要使用的WebKit已经初始化,接着调用RenderViewImpl类的静态成员函数Create为正在加载网页的主Frame创建一个Render View。
RenderViewImpl类的静态成员函数Create的实现如下所示:
RenderViewImpl* RenderViewImpl::Create( int32 opener_id, bool window_was_created_with_opener, const RendererPreferences& renderer_prefs, const WebPreferences& webkit_prefs, int32 routing_id, int32 main_frame_routing_id, int32 surface_id, int64 session_storage_namespace_id, const base::string16& frame_name, bool is_renderer_created, bool swapped_out, int32 proxy_routing_id, bool hidden, bool never_visible, int32 next_page_id, const blink::WebScreenInfo& screen_info, AccessibilityMode accessibility_mode) { DCHECK(routing_id != MSG_ROUTING_NONE); RenderViewImplParams params(opener_id, window_was_created_with_opener, renderer_prefs, webkit_prefs, routing_id, main_frame_routing_id, surface_id, session_storage_namespace_id, frame_name, is_renderer_created, swapped_out, proxy_routing_id, hidden, never_visible, next_page_id, screen_info, accessibility_mode); RenderViewImpl* render_view = NULL; if (g_create_render_view_impl) render_view = g_create_render_view_impl(?ms); else render_view = new RenderViewImpl(?ms); render_view->Initialize(?ms); return render_view; }这个函数定义在文件external/chromium_org/content/renderer/render_view_impl.cc中。
RenderViewImpl类的静态成员函数Create首先是创建一个RenderViewImpl对象,接着再调用RenderViewImpl类的成员函数Initialize对该RenderViewImpl对象进行初始化。
从这里可以看到,在Render进程中为正在加载网页的主Frame创建的Render View实际上是一个RenderViewImpl对象,在Android平台上,它与Browser进程中的RenderWidgetHostViewAndroid对象对应。
RenderViewImpl类的成员函数Initialize的实现如下所示:
void RenderViewImpl::Initialize(RenderViewImplParams* params) { ...... surface_id_ = params->surface_id; ...... }这个函数定义在文件external/chromium_org/content/renderer/render_view_impl.cc中。
从前面的分析可以知道,封装在参数params中的surface_id是从Browser进程中传递过来的,它描述的是Render端的OpenGL上下文的绘图表面的ID,这个ID值保存在RenderViewImpl类的成员变量surface_id_中。
前面提到,Render端用来与GPU进程执行IPC的WebGraphicsContext3DCommandBufferImpl对象是通过RenderWidget类的成员函数CreateGraphicsContext3D创建的,如下所示:
scoped_ptr<WebGraphicsContext3DCommandBufferImpl> RenderWidget::CreateGraphicsContext3D() { ...... 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中。
前面也提到,RenderWidget类的成员函数CreateGraphicsContext3D调用另外一个成员函数surface_id获得一个Surface ID,用作Render端的OpenGL上下文的绘图表面的ID。RenderWidget类的成员函数surface_id返回的是成员变量surface_id_的值。
RenderWidget类的成员变量surface_id_的访问权限为protected,前面分析的RenderViewImpl类是从RenderWidget类继承下来的,并且RenderWidget类的成员变量surface_id_是在RenderViewImpl类的成员函数Initialize进行设置的,这样RenderWidget类的成员函数CreateGraphicsContext3D在为Render端创建WebGraphicsContext3DCommandBufferImpl对象时,就可以获得一个不为0的Surface ID了。
至此,我们就分析完成WebGL、Browser和Render三端的OpenGL上下文的绘图表面的创建过程了,这些绘图表面都是在Browser进程创建的,并且通过一个Surface ID进行引用,这个Surface ID在为WebGL、Browser和Render三端创建OpenGL上下文时将会用到。
通过上面的分析,我们可以知道WebGL、Browser和Render三端的OpenGL上下文的绘图表面的特点,如下所示:
1. WebGL端的OpenGL上下文的绘图表面的ID设置为0,表示它不需要Browser进程为其创建一个绘图表面。
2. Render端的OpenGL上下文的绘图表面的ID不为0,它关联的绘图表面句柄(SurfaceHandle)的类型为gfx::NATIVE_TRANSPORT,但是它没有关联一个OS本地窗口。
3. Browser端的OpenGL上下文的绘图表面的ID不为0,它关联的绘图表面句柄(SurfaceHandle)的类型为gfx::NATIVE_DIRECT,并且关联有一个OS本地窗口。在Android平台上,这个OS本地窗口即为一个SurfaceView。
我们记住这些结论,在接下来一篇文章中分析WebGL、Browser和Render三端的OpenGL上下文的创建过程时,将会使用到,敬请关注!更多的信息,也可以关注老罗的新浪微博:http://weibo.com/shengyangluo。
版权声明:本文为博主原创文章,未经博主允许不得转载。
Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析
标签:android chromium opengl上下文 绘图表面
原文地址:http://blog.csdn.net/luoshengyang/article/details/48947685