Chromium硬件加速渲染的OpenGL上下文绘图表面创建过程分析

1991次阅读  |  发布于5年以前

GPU命令需要在OpenGL上下文中执行。每一个OpenGL上下文都关联有一个绘图表面,GPU命令就是作用在绘图表面上的。不同用途的OpenGL上下文关联的绘图表面不一样,例如用于离屏渲染的OpenGL上下文关联的绘图表面可以用Pbuffer描述,而用于屏幕渲染的OpenGL上下文的绘图表面要用本地窗口描述。本文接下来就分析Chromium硬件加速渲染涉及到的OpenGL上下文的绘图表面创建过程。

从前面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
          --SurfaceView

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上下文的绘图表面。

  1. 调用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接着又创建了一个SurfaceInfo对象,该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类的成员变量surfaceid,描述的是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的成员变量frametree,用来管理网页中的所有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类有一个类型为scopedptr的成员变量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的成员变量rendermanager,它负责在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接着又创建了一个SurfaceInfo对象,该SurfaceInfo对象包含了参数renderer_id和render_widget_id的值,以及一个空的窗口,即gfx::kNullAcceleratedWidget,,和一个空的绘图表面句柄,即gfx::GLSurfaceHandle(),并且以前面获得的ID值作为键值保存在成员变量surface_map_描述的一个std::map中。

注意,参数renderer_id描述的是一个Render进程的ID,而参数render_widget_id描述的是在Render进程renderer_id中加载的一个网页的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/renderer_host/render_widget_host_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(&params);
      else
        render_view = new RenderViewImpl(&params);

      render_view->Initialize(&params);
      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

Copyright© 2013-2019

京ICP备2023019179号-2