频道栏目
首页 > 资讯 > 其他综合 > 正文

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

15-10-20        来源:[db:作者]  
收藏   我要投稿

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 j_shell_manager;
};

base::LazyInstance 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(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(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(native_root_window);
  ContentViewRenderView* content_view_render_view =
      new ContentViewRenderView(env, obj, root_window);
  return reinterpret_cast(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(shell)).Release();
}
这个函数定义在文件external/chromium_org/content/shell/android/shell_manager.cc中。

 

从前面的分析可以知道,全局变量g_global_state指向的一个GlobalState对象的成员变量j_shell_manager描述的是一个Java层的ShellManager对象,这里调用函数Java_ShellManager_createShell调用它的成员函数createShell,用来创建一个Java层的Shell对象。

Java层的ShellManager类的成员函数createShell的实现如下所示:

 

public class ShellManager extends FrameLayout {
    ......

    private Object createShell(long nativeShellPtr) {
        ......
        LayoutInflater inflater =
                (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        Shell shellView = (Shell) inflater.inflate(R.layout.shell_view, null);
        shellView.initialize(nativeShellPtr, mWindow, mContentViewClient);

        ......

        showShell(shellView);
        return shellView;
    }

    ......
}
这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/ShellManager.java中。

 

ShellManager类的成员函数createShell首先是根据R.layout.shell_view布局文件创建了一个Shell控件,接着Shell类的成员函数initialize对该控件进行初始化,最后调用另外一个成员函数showShell显示该控件,如下所示:

 

public class ShellManager extends FrameLayout {
    ......

    private void showShell(Shell shellView) {
        shellView.setContentViewRenderView(mContentViewRenderView);
        addView(shellView, new FrameLayout.LayoutParams(
                FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
        mActiveShell = shellView;
        ContentViewCore contentViewCore = mActiveShell.getContentViewCore();
        if (contentViewCore != null) {
            mContentViewRenderView.setCurrentContentViewCore(contentViewCore);
            ......
        }
    }

    ......
}
这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/ShellManager.java中。

 

从前面的分析可以知道,ShellManager类的成员变量mContentViewRenderView描述的是一个ContentViewRenderView控件,该ContentViewRenderView控件包含有一个SurfaceView,ShellManager类的成员函数showShell又将该ContentViewRenderView控件作为参数shellView描述的一个Shell控件的子控件,最后参数shellView描述的Shell控件又作为当前正在处理的ShellManager控件的一个子控件。从这里我们就看到Browser窗口的UI层次结构大致为:

 

ShellManager
--Shell
   --ContentViewRenderView
      --SurfaceeView

ShellManager类的成员函数showShell接下来将参数shellView描述的Shell控件保存在成员变量mActiveShell中,并且调用该Shell控件的成员函数getContentViewCore获得一个ContentViewCore对象。有了这个ContentViewCore对象,就将它设置到ShellManager类的成员变量mContentViewRenderView描述的ContentViewRenderView控件的内部去。这是通过调用ContentViewRenderView类的成员函数setCurrentContentViewCore实现的。

ContentViewRenderView类的成员函数setCurrentContentViewCore的实现如下所示:

 

public class ContentViewRenderView extends FrameLayout {
    ......

    public void setCurrentContentViewCore(ContentViewCore contentViewCore) {
        ......
        mContentViewCore = contentViewCore;

        if (mContentViewCore != null) {
            ......
            nativeSetCurrentContentViewCore(mNativeContentViewRenderView,
                                            mContentViewCore.getNativeContentViewCore());
        } 
        ......
    }

    ......
}
这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java中。

 

ContentViewRenderView类的成员函数setCurrentContentViewCore首先将参数contentViewCore描述的一个ContentViewCore对象保存在成员变量mContentViewCore中,接着调用该ContentViewCore对象的成员函数getNativeContentViewCore获得它在Native层对应的一个ContentViewCoreImpl对象。

从前面的分析可以知道,ContentViewRenderView类的成员变量mNativeContentViewRenderView描述的是Native层的一个ContentViewRenderView对象,ContentViewRenderView类的成员函数setCurrentContentViewCore最后调用另外一个成员函数nativeSetCurrentContentViewCore将该Native层的ContentViewRenderView对象,以及前面获得的Native层的ContentViewCoreImpl对象设置到Native层的Chromium中去。

ContentViewRenderView类的成员函数nativeSetCurrentContentViewCore是一个JNI函数,由Native层的函数Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSetCurrentContentViewCore实现,如下所示:

 

__attribute__((visibility(default)))
void
    Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSetCurrentContentViewCore(JNIEnv*
    env,
    jobject jcaller,
    jlong nativeContentViewRenderView,
    jlong nativeContentViewCore) {
  ContentViewRenderView* native =
      reinterpret_cast(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(native_content_view_core);
  compositor_->SetRootLayer(content_view_core
                                ? layer_tree_build_helper_->GetLayerTree(
                                      content_view_core->GetLayer())
                                : scoped_refptr());
}
这个函数定义在文件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(nativeContentViewRenderView);
  CHECK_NATIVE_PTR(env, jcaller, native, SurfaceChanged);
  return native->SurfaceChanged(env, jcaller, format, width, height, surface);
}
这个函数定义在文件out/target/product/generic/obj/GYP/shared_intermediates/content/jni/ContentViewRenderView_jni.h中。

 

函数Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSurfaceChanged首先将参数nativeContentViewRenderView转换为一个Native层的ContentViewRenderView对象,接着调用该ContentViewRenderView对象的成员函数SurfaceChanged通知Browser进程用来显示网页UI的SurfaceView发生了变化。

ContentViewRenderView类的成员函数SurfaceChanged的实现如下所示:

 

void ContentViewRenderView::SurfaceChanged(JNIEnv* env, jobject obj,
    jint format, jint width, jint height, jobject surface) {
  if (current_surface_format_ != format) {
    current_surface_format_ = format;
    compositor_->SetSurface(surface);
  }
  ......
}
这个函数定义在文件external/chromium_org/content/browser/android/content_view_render_view.cc中。

 

ContentViewRenderView类的成员变量current_surface_format_保存的是Browser进程的SurfaceView底层使用的绘图表面的颜色格式,它的值被初始化为0。因此,当Browser进程的SurfaceView第一次创建,以及它底层使用的绘图表面的颜色格式发生变化时,ContentViewRenderView类的成员函数SurfaceChanged就会调用成员变量compositor_指向的一个CompositorImpl对象的成员函数SetSurface来重新设置Browser端的OpenGL上下文的绘图表面。

CompositorImpl类的成员函数SetSurface的实现如下所示:

 

void CompositorImpl::SetSurface(jobject surface) {
  JNIEnv* env = base::android::AttachCurrentThread();
  ......

  ANativeWindow* window = NULL;
  if (surface) {
    ......
    window = ANativeWindow_fromSurface(env, surface);
  }
  if (window) {
    SetWindowSurface(window);
    ......
  }
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/compositor_impl_android.cc中。

 

CompositorImpl类的成员函数SetSurface首先调用函数ANativeWindow_fromSurface从参数surface描述的一个Java层的Surface对象获得一个关联的Native层的ANativeWindow对象,这个ANativeWindow对象描述的就是一个OS本地窗口。

从前面的调用过程可以知道,参数surface描述的Java层的Surface对象描述的即为Browser进程的Shell窗口的SurfaceView底层所使用的绘图表面,因此,前面获得的ANativeWindow对象描述的OS本地窗口即为Browser进程的Shell窗口的SurfaceView。

最后,CompositorImpl类的成员函数SetSurface调用另外一个成员函数SetWindowSurface将前面获得的ANativeWindow对象设置为Browser端的OpenGL上下文的绘图表面,它的实现如下所示:

 

void CompositorImpl::SetWindowSurface(ANativeWindow* window) {
  GpuSurfaceTracker* tracker = GpuSurfaceTracker::Get();
  ......

  if (window) {
    window_ = window;
    ......
    surface_id_ = tracker->AddSurfaceForNativeWidget(window);
    tracker->SetSurfaceHandle(
        surface_id_,
        gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_DIRECT));
    ......
  }
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/compositor_impl_android.cc中。

 

CompositorImpl类的成员函数主要完成两件事情:

1. 调用Browser进程的GpuSurfaceTracker单例的成员函数AddSurfaceForNativeWidget为Browser端的OpenGL上下文创建一个绘图表面,这个绘图表面即为参数window描述的OS本地窗口。GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget的返回值为一个Surface ID,保存在CompositorImpl类的成员变量surface_id_中,以后Browser端通过该Surface ID就可以在Browser进程的GpuSurfaceTracker单例中找到Browser端的OpenGL上下文的绘图表面。

2. 调用Browser进程的GpuSurfaceTracker单例的成员函数SetSurfaceHandle设置Browser端的OpenGL上下文的绘图表面句柄的类型为gfx::NATIVE_DIRECT,表示Browser端的OpenGL上下文的绘图表面可以直接渲染在屏幕上。

接下来,我们就继续分析GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget和SetSurfaceHandle的实现。

GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget的实现如下所示:

 

int GpuSurfaceTracker::AddSurfaceForNativeWidget(
    gfx::AcceleratedWidget widget) {
  base::AutoLock lock(lock_);
  int surface_id = next_surface_id_++;
  surface_map_[surface_id] =
      SurfaceInfo(0, 0, widget, gfx::GLSurfaceHandle(), NULL);
  return surface_id;
}
这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_surface_tracker.cc中。

 

GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget首先将成员变量next_surface_id_的当前值作为新增加的绘图表面的ID,并且将它的值增加1,作为下一次增加的绘图表面的ID。

GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget接着又创建了一个SurfceInfo对象,该SurfaceInfo对象包含了参数widget描述的一个OS本地窗口,以及一个空的绘图表面句柄,并且以前面获得的ID值作为键值保存在成员变量surface_map_描述的一个std::map中。

GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget最后将分配给新增加的绘图表面的ID返回给调用者。

GpuSurfaceTracker类的成员函数SetSurfaceHandle的实现如下所示:

 

void GpuSurfaceTracker::SetSurfaceHandle(int surface_id,
                                         const gfx::GLSurfaceHandle& handle) {
  base::AutoLock lock(lock_);
  DCHECK(surface_map_.find(surface_id) != surface_map_.end());
  SurfaceInfo& info = surface_map_[surface_id];
  info.handle = handle;
}
这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_surface_tracker.cc中。

 

GpuSurfaceTracker类的成员函数SetSurfaceHandle首先根据参数surface_id描述的绘图表面的ID在成员变量surface_map_描述的一个std::map中得到一个对应的SurfaceInfo对象,并且将该SurfaceInfo对象包含的绘图表面句柄修改为参数handle描述的图表面句柄。从前面的调用过程可以知道,参数handle描述的图表面句柄的类型设置为gfx::NATIVE_DIRECT,在接下来一篇文章分析Browser端的OpenGL上下文的创建过程时将会使用到该类型值。

Browser端和WebGL端、Render端一样,所有的GPU操作都是要通过GPU线程执行的,因此它也像WebGL端、Render端一样,需要一个WebGraphicsContext3DCommandBufferImpl对象与GPU线程进行通信。

Browser端的UI合成器将Render端负责渲染的网页UI合成在一个Output Surface上。这个Output Surface对应的就是Browser进程的Shell窗口的SurfaceView,它是通过调用Browser端的UI合成器的成员函数CreateOutputSurface创建的,即调用CompositorImpl类的成员函数CreateOutputSurface创建的。

CompositorImpl类的成员函数CreateOutputSurface在执行的过程中,就会创建一个WebGraphicsContext3DCommandBufferImpl对象,以便以后可以用来与GPU线程进行通信,即请求GPU线程执行指定的GPU操作。

CompositorImpl类的成员函数CreateOutputSurface的实现如下所示:

 

scoped_ptr CompositorImpl::CreateOutputSurface(
    bool fallback) {
  ......

  scoped_refptr context_provider;
  BrowserGpuChannelHostFactory* factory =
      BrowserGpuChannelHostFactory::instance();
  scoped_refptr 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(
      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
CreateGpuProcessViewContext(
    const scoped_refptr& gpu_channel_host,
    const blink::WebGraphicsContext3D::Attributes attributes,
    int surface_id) {
  ......

  return make_scoped_ptr(
      new WebGraphicsContext3DCommandBufferImpl(surface_id,
                                                url,
                                                gpu_channel_host.get(),
                                                attributes,
                                                lose_context_when_out_of_memory,
                                                limits,
                                                NULL));
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/compositor_impl_android.cc中。

 

这里我们重点关注的是调用WebGraphicsContext3DCommandBufferImpl类的构造函数创建一个WebGraphicsContext3DCommandBufferImpl对象时指定的第一个参数surface_id,它来自于CompositorImpl类的成员变量surface_id_,描述的是Browser端的OpenGL上下文的绘图表面的ID,后面请求GPU线程为Browser端创建OpenGL上下文时,将会使用到该ID。

函数CreateGpuProcessViewContext最后将创建出来的WebGraphicsContext3DCommandBufferImpl对象返回给CompositorImpl类的成员函数CreateOutputSurface,后者接下来调用ContextProviderCommandBuffer类的静态成员函数Create将获得的WebGraphicsContext3DCommandBufferImpl对象封装在一个ContextProviderCommandBuffer对象,最后该ContextProviderCommandBuffer对象又会被封装在一个OutputSurfaceWithoutParent对象中,作为Browser进程的UI合成器的Output Surface。

这样,我们就分析完成了Browser端的OpenGL上下文的绘图表面的创建过程,这个绘图表面是通过一个SurfaceView描述的,并且描述该绘图表面的句柄(Surface Handle)的类型被设置为gfx::NATIVE_DIRECT。在接下来的一篇文章分析Browser端的OpenGL上下文的创建过程,我们就会看到这些信息是如何使用的。

我们最后分析Render端的OpenGL上下文的绘图表面的创建过程。

同样从前面Chromium的GPU进程启动过程分析一文可以知道,Render端也是通过一个WebGraphicsContext3DCommandBufferImpl对象向GPU线程请求执行GPU操作的。这个WebGraphicsContext3DCommandBufferImpl对象是通过调用RenderWidget类的成员函数CreateGraphicsContext3D创建的,它的实现如下所示:

 

scoped_ptr  
RenderWidget::CreateGraphicsContext3D() {  
  ......  
  
  scoped_ptr 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 {
 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(params.opener));
}
这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

WebContents类的静态成员函数Create调用WebContentsImpl类的静态成员函数CreateWithOpener创建一个WebContentsImpl对象,后者的实现如下所示:

 

WebContentsImpl* WebContentsImpl::CreateWithOpener(
    const WebContents::CreateParams& params,
    WebContentsImpl* opener) {
  ......
  WebContentsImpl* new_contents = new WebContentsImpl(
      params.browser_context, params.opener_suppressed ? NULL : opener);

  ......

  new_contents->Init(params);
  return new_contents;
}
这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

 

WebContentsImpl类的静态成员函数CreateWithOpener首先是创建一个WebContentsImpl对象,接着调用该WebContentsImpl对象的成员函数Init对其进行初始化,最后将该WebContentsImpl对象返回给调用者。

在调用WebContentsImpl类的成员函数Init对创建的WebContentsImpl对象进行初始化的时候,就会为Render端创建一个绘图表面。在分析WebContentsImpl类的成员函数Init的实现之前,我们先分析WebContentsImpl对象的创建过程,即WebContentsImpl类的构造函数的实现,如下所示:

 

WebContentsImpl::WebContentsImpl(
    BrowserContext* browser_context,
    WebContentsImpl* opener)
    : ......,
      frame_tree_(new NavigatorImpl(&controller_, this),
                  this, this, this, this),
      ...... {
  ......
}
这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

 

WebContentsImpl类有一个类型为FrameTree的成员变量frame_tree,用来管理网页中的所有iframe标签,也就是网页的内容在第一级别上是按照frame来管理的,每一个frame又按照其内部的标签进行第二级别上的组织。

我们继续分析WebContentsImpl类的成员变量frame_tree描述的FrameTree对象的创建过程,即FrameTree类的构造函数的实现,如下所示:

 

FrameTree::FrameTree(Navigator* navigator,
                     RenderFrameHostDelegate* render_frame_delegate,
                     RenderViewHostDelegate* render_view_delegate,
                     RenderWidgetHostDelegate* render_widget_delegate,
                     RenderFrameHostManager::Delegate* manager_delegate)
    : ......,
      root_(new FrameTreeNode(this,
                              navigator,
                              render_frame_delegate,
                              render_view_delegate,
                              render_widget_delegate,
                              manager_delegate,
                              std::string())),
      ...... {
}
这个函数定义在文件external/chromium_org/content/browser/frame_host/frame_tree.cc中。

 

FrameTree类有一个类型为scoped_ptr<frametreenode>的成员变量root_,它指向的是一个FrameTreeNode对象,这个FrameTreeNode对象描述的是网页的frame tree的根结点,它的创建过程如下所示:</frametreenode>

 

FrameTreeNode::FrameTreeNode(FrameTree* frame_tree,
                             Navigator* navigator,
                             RenderFrameHostDelegate* render_frame_delegate,
                             RenderViewHostDelegate* render_view_delegate,
                             RenderWidgetHostDelegate* render_widget_delegate,
                             RenderFrameHostManager::Delegate* manager_delegate,
                             const std::string& name)
    : ......,
      render_manager_(this,
                      render_frame_delegate,
                      render_view_delegate,
                      render_widget_delegate,
                      manager_delegate),
      ...... {}
这个函数定义在文件external/chromium_org/content/browser/frame_host/frame_tree_node.cc中。

 

FrameTreeNode类有一个类型为RenderFrameHostManager的成员变量render_manager_,它负责在Browser进程中创建一个RenderViewHostImpl对象与负责渲染网页的Render进程进行通信。

从这里我们可以知道,Browser进程为每一个网页创建一个WebContentImpl对象,这个WebContentImpl对象将网页看作是一个frame tree,这个frame tree由frame tree node组成,每一个frame tree node都包含有一个RenderFrameHostManager对象,这个RenderFrameHostManager对象负责与渲染网页的Render进程进行通信。通过这种frame tree的组织,Browser进程可以将一个网页的不同frame放在不同的Render进程进行渲染,从而起到域隔离作用,保证安全性,因为不同的frame加载的一般是不同域的网页。

这一步执行完成后,回到WebContentsImpl类的静态成员函数CreateWithOpener中,它接下来对前面创建的WebContentImpl对象进行初始化,这是通过调用WebContentImpl类的成员函数Init实现的,如下所示:

 

void WebContentsImpl::Init(const WebContents::CreateParams& params) {
  ......

  GetRenderManager()->Init(
      params.browser_context, params.site_instance, params.routing_id,
      params.main_frame_routing_id);

  ......

  if (browser_plugin_guest_) {
    ......
  } else {
    // Regular WebContentsView.
    view_.reset(CreateWebContentsView(
        this, delegate, &render_view_host_delegate_view_));
  }

  ......
}

 

这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

WebContentImpl类的成员函数Init首先调用另外一个成员函数GetRenderManager获得一个RenderFrameHostManager对象,后者的实现如下所示:

 

RenderFrameHostManager* WebContentsImpl::GetRenderManager() const {
  return frame_tree_.root()->render_manager();
}
这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_impl.cc中。

从这里可以看到,WebContentImpl类的成员函数GetRenderManager返回的是网页的frame tree的根结点的RenderFrameHostManager对象,WebContentImpl类的成员函数Init接下来就调用它的成员函数Init继续执行初始化工作。

WebContentImpl类的成员函数Init调用RenderFrameHostManager类的成员函数Init执行初始化工作完毕之后,会判断成员变量browser_plugin_guest_的值是否为NULL。当该成员变量的值不等于NULL的时候,表示当前正在处理的WebContentImpl对象是为一个Browser Plugin创建的。关于Browser Plugin,我们在前面Chromium的Plugin进程启动过程分析一文中提涉及到,它的作用类似于iframe标签。我们假设当前正在处理的WebContentImpl对象不是为一个Browser Plugin创建的,即这时候当前正在处理的WebContentImpl对象的成员变量browser_plugin_guest_的值是为NULL,那么WebContentImpl类的成员函数Init接下来会调用另外一个函数CreateWebContentsView创建一个WebContentsViewAndroid对象,如下所示:

 

WebContentsView* CreateWebContentsView(
    WebContentsImpl* web_contents,
    WebContentsViewDelegate* delegate,
    RenderViewHostDelegateView** render_view_host_delegate_view) {
  WebContentsViewAndroid* rv = new WebContentsViewAndroid(
      web_contents, delegate);
  *render_view_host_delegate_view = rv;
  return rv;
}
这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_view_android.cc中。

 

函数CreateWebContentsView创建的WebContentsViewAndroid对象返回给WebContentsImpl类的成员函数Init之后,会保存在成员变量view_中。

回到WebContentsImpl类的成员函数Init中,前面提到,它会进一步调用获得的一个RenderFrameHostManager对象的成员函数Init执行初始化工作,如下所示:

 

void RenderFrameHostManager::Init(BrowserContext* browser_context,
                                  SiteInstance* site_instance,
                                  int view_routing_id,
                                  int frame_routing_id) {
  ......

  SetRenderFrameHost(CreateRenderFrameHost(site_instance,
                                           view_routing_id,
                                           frame_routing_id,
                                           false,
                                           delegate_->IsHidden()));

  ......
}
这个函数定义在文件external/chromium_org/content/browser/frame_host/render_frame_host_manager.cc中。

 

RenderFrameHostManager类的成员函数Init首先调用另外一个成员函数CreateRenderFrameHost创建一个RenderFrameHostImpl对象,用来在Browser进程中描述一个网页的一个frame,接着又调用成员函数SetRenderFrameHost将创建出来的RenderFrameHostImpl对象保存在WebContentImpl类的成员变量render_frame_host_中。

接下来我们继续分析RenderFrameHostManager类的成员函数CreateRenderFrameHost的实现,以及可以了解Render端的OpenGL上下文的绘图表面的创建过程,如下所示:

 

scoped_ptr 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 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(
      RenderViewHostFactory::Create(site_instance,
                                    render_view_delegate_,
                                    render_widget_delegate_,
                                    routing_id,
                                    main_frame_routing_id,
                                    swapped_out,
                                    hidden));

  render_view_host_map_[site_instance->GetId()] = rvh;
  return rvh;
}
这个函数定义在文件external/chromium_org/content/browser/frame_host/frame_tree.cc中。

 

FrameTree类的成员函数CreateRenderViewHostForMainFrame主要是为参数site_instance描述的一个站点创建一个RenderViewHostImpl对象,这是通过调用RenderViewHostFactory类的静态成员函数Create实现的。创建出来的RenderViewHostImpl对象以参数site_instance描述的站点的ID为键值保存在FrameTree类的成员变量render_view_host_map_描述的是一个Hash Map中。

接下来我们继续分析RenderViewHostFactory类的静态成员函数Create的实现,如下所示:

 

RenderViewHost* RenderViewHostFactory::Create(
    SiteInstance* instance,
    RenderViewHostDelegate* delegate,
    RenderWidgetHostDelegate* widget_delegate,
    int routing_id,
    int main_frame_routing_id,
    bool swapped_out,
    bool hidden) {
  ......

  return new RenderViewHostImpl(instance, delegate, widget_delegate, routing_id,
                                main_frame_routing_id, swapped_out, hidden);
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_view_host_factory.cc中。

 

RenderViewHostFactory类的静态成员函数Create创建了一个RenderViewHostImpl对象,并且返回给调用者。

RenderViewHostImpl类是从RenderWidgetHostImpl类继承下来的,如下所示:

 

class CONTENT_EXPORT RenderViewHostImpl
    : public RenderViewHost,
      public RenderWidgetHostImpl {
  ......
};
这个类定义在文件external/chromium_org/content/browser/renderer_host/render_view_host_impl.h中。

 

因此,当创建一个RenderViewHostImpl对象的时候,RenderWidgetHostImpl类的构造函数会被调用,在调用过程将会为Render端的OpenGL上下文创建一个绘图表面,如下所示:

 

RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate,
                                           RenderProcessHost* process,
                                           int routing_id,
                                           bool hidden)
    : ...... {
  ......

  if (routing_id_ == MSG_ROUTING_NONE) {
    routing_id_ = process_->GetNextRoutingID();
    surface_id_ = GpuSurfaceTracker::Get()->AddSurfaceForRenderer(
        process_->GetID(),
        routing_id_);
  } 

  ......
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc中。

 

参数routing_id是从前面分析的函数LaunchShell传递下来的,它的值等于MSG_ROUTING_NONE,因此,RenderWidgetHostImpl类的构造函数首先是调用参数process指向的一个RenderProcessHost对象的成员函数GetNextRoutingID生成一个Routing ID,然后再调用Browser进程中的GpuSurfaceTracker单例的成员函数AddSurfaceForRender为Render端创建一个绘图表面,如下所示:

 

int GpuSurfaceTracker::AddSurfaceForRenderer(int renderer_id,
                                             int render_widget_id) {
  base::AutoLock lock(lock_);
  int surface_id = next_surface_id_++;
  surface_map_[surface_id] =
      SurfaceInfo(renderer_id, render_widget_id, gfx::kNullAcceleratedWidget,
                  gfx::GLSurfaceHandle(), NULL);
  return surface_id;
}
这个函数定义在文件external/chromium_org/content/browser/gpu/gpu_surface_tracker.cc中。

 

GpuSurfaceTracker类的成员函数AddSurfaceForRender首先将成员变量next_surface_id_的当前值作为新增加的绘图表面的ID,并且将它的值增加1,作为下一次增加的绘图表面的ID。

 

GpuSurfaceTracker类的成员函数AddSurfaceForRender接着又创建了一个SurfceInfo对象,该SurfaceInfo对象包含了参数renderer_id和render_widget_id的值,以及一个空的窗口,即gfx::kNullAcceleratedWidget,,和一个空的绘图表面句柄,即gfx::GLSurfaceHandle(),并且以前面获得的ID值作为键值保存在成员变量surface_map_描述的一个std::map中。

注意,参数renderer_id描述的是一个Render进程的ID,而参数render_widget_id描述的是在参数参数renderer_id描述的Render进程中加载的一个网页的Routing ID。这两个参数共同确定了在一个Render进程中加载的一个网页。

GpuSurfaceTracker类的成员函数AddSurfaceForRender最后将分配给新增加的绘图表面的ID返回给调用者,即RenderWidgetHostImpl类的构造函数,后者将获得的绘图表面ID保存在成员变量surface_id_中。

这一步执行完成之后,一个Render端的OpenGL上下文的绘图表面就创建完成了。注意,这时候创建出来的绘图表面的句柄是空的,并且不像前面分析的Browser端的OpenGL上下文的绘图表面关联有一个OS本地窗口。这一点是Render端和Browser端的OpenGL上下文的绘图表面的主要区别之一。

Browser进程获得了要加载的网页的URL之后,最后会通过在前面分析的WebContentsImpl类的静态成员函数CreateWithOpener中创建的一个WebContentsImpl对象的成员函数CreateRenderViewForRenderManager为要加载的网页创建一个Render View。这一点以后我们在分析Chromium加载网页URL的过程时将会看到。现在我们主要分析WebContentsImpl类的成员函数CreateRenderViewForRenderManager的实现,以便可以了解Render端的OpenGL上下文的绘图表面句柄初始化过程,如下所示:

 

bool WebContentsImpl::CreateRenderViewForRenderManager(
    RenderViewHost* render_view_host,
    int opener_route_id,
    int proxy_routing_id,
    bool for_main_frame) {
  ......
  RenderWidgetHostViewBase* rwh_view;
  ......
  if (!for_main_frame) {
    ......
  } else {
    rwh_view = view_->CreateViewForWidget(render_view_host);
  }

  ......

  if (!static_cast(
          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(
        render_widget_host->GetView());
  }
  // Note that while this instructs the render widget host to reference
  // |native_view_|, this has no effect without also instructing the
  // native view (i.e. ContentView) how to obtain a reference to this widget in
  // order to paint it. See ContentView::GetRenderWidgetHostViewAndroid for an
  // example of how this is achieved for InterstitialPages.
  RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(render_widget_host);
  return new RenderWidgetHostViewAndroid(rwhi, content_view_core_);
}
这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_view_android.cc中。

 

参数render_widget_host指向的是一个RenderViewHostImpl对象。由于RenderViewHostImpl类是从RenderWidgetHost类继承下来的,因此参数render_widget_host可以是RenderWidgetHost指针。

WebContentsViewAndroid类的成员函数CreateViewForWidget首无是调用参数render_widget_host指向的一个RenderViewHostImpl对象的成员函数GetView检查是否已经为它创建过Render View了。如果已经创建过,那么再通过它的成员函数GetView获得该RenderView,并且返回给调用者。如果还没有创建过,那么接下来通过调用RenderWidgetHostImpl静态成员函数From将参数render_widget_host转换为一个RenderWidgetHostImpl对象,并且以得到的RenderWidgetHostImpl对象为参数创建一个RenderWidgetHostViewAndroid对象返回给调用者。

前面在分析RenderViewHostFactory类的静态成员函数Create的实现时提到,RenderViewHostImpl类是从RenderWidgetHostImpl类继承下来的, 因此这里可以将参数render_widget_host转换为一个RenderWidgetHostImpl对象。

从这里我们就可以看到,在Android平台上,在Browser进程为正在加载网页的主Frame创建的Render View实际上是一个RenderWidgetHostViewAndroid对象,接下来我们继续分析这个RenderWidgetHostViewAndroid对象的创建过程,即RenderWidgetHostViewAndroid类的构造函数的实现,如下所示:

 

RenderWidgetHostViewAndroid::RenderWidgetHostViewAndroid(
    RenderWidgetHostImpl* widget_host,
    ContentViewCoreImpl* content_view_core)
    : host_(widget_host),
      ...... {
  host_->SetView(this);
  ......
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_view_android.cc 中。

 

RenderWidgetHostViewAndroid类的构造函数将参数widget_host指向的一个RenderWidgetHostImpl对象保存在成员变量host_中,接着再调用该RenderWidgetHostImpl对象的成员函数SetView将当前正在创建的RenderWidgetHostViewAndroid对象作为它的Render View。

RenderWidgetHostImpl类的成员函数SetView的实现如下所示:

 

void RenderWidgetHostImpl::SetView(RenderWidgetHostViewBase* view) {
  view_ = view;

  GpuSurfaceTracker::Get()->SetSurfaceHandle(
      surface_id_, GetCompositingSurface());

  ......
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc 中。

 

RenderWidgetHostImpl类的成员函数SetView首先将参数view指向的RenderWidgetHostViewAndroid对象保存在成员变量view_,接着调用另外一个成员函数GetCompositingSurface获得一个绘图表面句柄,最后通过调用前面分析过的GpuSurfaceTracker类的成员函数SetSurfaceHandle将该绘图表面句柄 设置为成员变量surface_id_描述的绘图表面的句柄。

RenderWidgetHostImpl类的成员变量surface_id_描述的绘图表面是在RenderWidgetHostImpl类的构造函数中创建的,该绘图表面即Render端的OpenGL上下文的绘图表面,这个创建过程我们在前面已经分析过。这里有一点需要注意的是,在RenderWidgetHostImpl类的构造函数中创建的绘图表面的句柄是空的,因此这里需要对它进行初始化。

接下来我们继续分析RenderWidgetHostImpl类的成员函数GetCompositingSurface的实现,如下所示:

 

gfx::GLSurfaceHandle RenderWidgetHostImpl::GetCompositingSurface() {
  if (view_)
    return view_->GetCompositingSurface();
  return gfx::GLSurfaceHandle();
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_widget_host_impl.cc 中。

 

从前面的调用过程可以知道,RenderWidgetHostImpl类的成员变量view_的值不为NULL,它指向了一个RenderWidgetHostViewAndroid对象,RenderWidgetHostImpl类的成员函数GetCompositingSurface调用该RenderWidgetHostViewAndroid对象的成员函数GetCompositingSurface获得一个绘图表面句柄 。

RenderWidgetHostViewAndroid类的成员函数GetCompositingSurface的实现如下所示:

 

gfx::GLSurfaceHandle RenderWidgetHostViewAndroid::GetCompositingSurface() {
  gfx::GLSurfaceHandle handle =
      gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_TRANSPORT);
  ......
  return handle;
}
这个函数定义在文件external/chromium_org/content/browser/web_contents/web_contents_view_android.cc中。

 

RenderWidgetHostViewAndroid类的成员函数GetCompositingSurface返回的绘图表面句柄的类型为gfx::NATIVE_TRANSPORT,也就是Render端的OpenGL上下文使用的绘图表面的类型为gfx::NATIVE_TRANSPORT。我们需要记住这一点,在后面一篇文章中分析Render端的OpenGL上下文的创建过程时会使用到。

回到WebContentsImpl类的成员函数CreateRenderViewForRenderManager中,接下来我们继续分析RenderViewHostImpl类的成员函数CreateRenderView,以便了解在Render进程中为正在加载网页的主Frame创建Render View的过程。

RenderViewHostImpl类的成员函数CreateRenderView的实现如下所示:

 

bool RenderViewHostImpl::CreateRenderView(
    const base::string16& frame_name,
    int opener_route_id,
    int proxy_route_id,
    int32 max_page_id,
    bool window_was_created_with_opener) {
  ......

  ViewMsg_New_Params params;
  ......
  params.view_id = GetRoutingID();
  params.main_frame_routing_id = main_frame_routing_id_;
  params.surface_id = surface_id();
  ......
  params.frame_name = frame_name;
  // Ensure the RenderView sets its opener correctly.
  ......

  Send(new ViewMsg_New(params));

  ......

  return true;
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/render_view_host_impl.cc。

 

RenderViewHostImpl类的成员函数CreateRenderView向Render进程发送一个类型为ViewMsg_New的IPC消息,该IPC消息携带了在Browser进程为正在加载网页的主Frame创建的Render View的各种信息,例如View ID、Main Frame Routing ID、Surface ID以及Frame Name等。其中,Surface ID是通过调用成员函数surface_id获得的。

前面提到,RenderViewHostImpl类是从RenderWidgetHostImpl类继承下来的,它的成员函数surface_id也是从RenderWidgetHostImpl类继承下来的,而RenderWidgetHostImpl类的成员函数surface_id返回的Surface ID描述的即为在其构造函数中为Render端的OpenGL上下文创建的绘图表面。

类型为ViewMsg_New的IPC消息在Render进程是通过RenderThreadImpl类的成员函数OnControlMessageReceived接收的,如下所示:

 

bool RenderThreadImpl::OnControlMessageReceived(const IPC::Message& msg) {
  ......

  bool handled = true;
  IPC_BEGIN_MESSAGE_MAP(RenderThreadImpl, msg)
    ......
    IPC_MESSAGE_HANDLER(ViewMsg_New, OnCreateNewView)
    ......
    IPC_MESSAGE_UNHANDLED(handled = false)
  IPC_END_MESSAGE_MAP()
  return handled;
}
这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。

 

从这里可以看到,RenderThreadImpl类的成员函数OnControlMessageReceived将类型为ViewMsg_New的IPC消息分发给成员函数OnCreateNewView处理。

RenderThreadImpl类的成员函数OnCreateNewView的实现如下所示:

 

void RenderThreadImpl::OnCreateNewView(const ViewMsg_New_Params& params) {
  EnsureWebKitInitialized();
  // When bringing in render_view, also bring in webkit's glue and jsbindings.
  RenderViewImpl::Create(params.opener_route_id,
                         params.window_was_created_with_opener,
                         params.renderer_preferences,
                         params.web_preferences,
                         params.view_id,
                         params.main_frame_routing_id,
                         params.surface_id,
                         params.session_storage_namespace_id,
                         params.frame_name,
                         false,
                         params.swapped_out,
                         params.proxy_routing_id,
                         params.hidden,
                         params.never_visible,
                         params.next_page_id,
                         params.screen_info,
                         params.accessibility_mode);
}
这个函数定义在文件external/chromium_org/content/renderer/render_thread_impl.cc中。

 

RenderThreadImpl类的成员函数OnCreateNewView首先调用另外一个成员函数EnsureWebKitInitialized保存在Render进程要使用的WebKit已经初始化,接着调用RenderViewImpl类的静态成员函数Create为正在加载网页的主Frame创建一个Render View。

RenderViewImpl类的静态成员函数Create的实现如下所示:

 

RenderViewImpl* RenderViewImpl::Create(
    int32 opener_id,
    bool window_was_created_with_opener,
    const RendererPreferences& renderer_prefs,
    const WebPreferences& webkit_prefs,
    int32 routing_id,
    int32 main_frame_routing_id,
    int32 surface_id,
    int64 session_storage_namespace_id,
    const base::string16& frame_name,
    bool is_renderer_created,
    bool swapped_out,
    int32 proxy_routing_id,
    bool hidden,
    bool never_visible,
    int32 next_page_id,
    const blink::WebScreenInfo& screen_info,
    AccessibilityMode accessibility_mode) {
  DCHECK(routing_id != MSG_ROUTING_NONE);
  RenderViewImplParams params(opener_id,
                              window_was_created_with_opener,
                              renderer_prefs,
                              webkit_prefs,
                              routing_id,
                              main_frame_routing_id,
                              surface_id,
                              session_storage_namespace_id,
                              frame_name,
                              is_renderer_created,
                              swapped_out,
                              proxy_routing_id,
                              hidden,
                              never_visible,
                              next_page_id,
                              screen_info,
                              accessibility_mode);
  RenderViewImpl* render_view = NULL;
  if (g_create_render_view_impl)
    render_view = g_create_render_view_impl(¶ms);
  else
    render_view = new RenderViewImpl(¶ms);

  render_view->Initialize(¶ms);
  return render_view;
}
这个函数定义在文件external/chromium_org/content/renderer/render_view_impl.cc中。

 

RenderViewImpl类的静态成员函数Create首先是创建一个RenderViewImpl对象,接着再调用RenderViewImpl类的成员函数Initialize对该RenderViewImpl对象进行初始化。

从这里可以看到,在Render进程中为正在加载网页的主Frame创建的Render View实际上是一个RenderViewImpl对象,在Android平台上,它与Browser进程中的RenderWidgetHostViewAndroid对象对应。

RenderViewImpl类的成员函数Initialize的实现如下所示:

 

void RenderViewImpl::Initialize(RenderViewImplParams* params) {
  ......
  surface_id_ = params->surface_id;
  ......
}
这个函数定义在文件external/chromium_org/content/renderer/render_view_impl.cc中。

 

从前面的分析可以知道,封装在参数params中的surface_id是从Browser进程中传递过来的,它描述的是Render端的OpenGL上下文的绘图表面的ID,这个ID值保存在RenderViewImpl类的成员变量surface_id_中。

前面提到,Render端用来与GPU进程执行IPC的WebGraphicsContext3DCommandBufferImpl对象是通过RenderWidget类的成员函数CreateGraphicsContext3D创建的,如下所示:

 

scoped_ptr  
RenderWidget::CreateGraphicsContext3D() {  
  ......  
  
  scoped_ptr 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。

 

相关TAG标签
上一篇:Universal Windows Platform 第一弹 使用剪切板
下一篇:Android基础入门教程——10.1 TelephonyManager(电话管理器)
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站