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

发表于 5年以前  | 总阅读数:1992 次

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

《Android系统源代码情景分析》一书正在进击的程序员网(http://0xcc0xcd.com)中连载,点击进入!

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

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 目录