GPU命令需要在OpenGL上下文中执行。每一个OpenGL上下文都关联有一个绘图表面,GPU命令就是作用在绘图表面上的。不同用途的OpenGL上下文关联的绘图表面不一样,例如用于离屏渲染的OpenGL上下文关联的绘图表面可以用Pbuffer描述,而用于屏幕渲染的OpenGL上下文的绘图表面要用本地窗口描述。本文接下来就分析Chromium硬件加速渲染涉及到的OpenGL上下文的绘图表面创建过程。
从前面Chromium的GPU进程启动过程分析一文可以知道,在Chromium中,所有的GPU命令均在一个GPU线程中执行。这个GPU线程有可能是位于Browser进程中,也有可能是位于GPU进程中。需要执行GPU命令的有WebGL、Render进程和Browser进程,为了方便描述,我们称之为WebGL端、Render端和Browser端,意为它们是Chromium的GPU线程的Client端。
WebGL端和Render端执行的离屏渲染,而Browser端执行的屏幕渲染,因此它们需要的绘图表面是不一样的,如图1所示:
图1 Chromium的真实OpenGL上下文的绘图表面
从图1可以看到,WebGL端、Render端和Browser端的OpenGL上下文均是通过GLContextEGL描述,不过它们关联的绘图表面却是不一样的。GLContextEGL描述的是一个真实的OpenGL上下文,这是相对于我们后面要描述的虚拟OpenGL上下文而言的。
WebGL端和Render端的OpenGL上下文关联的绘图表面是GPU线程中的默认离屏绘图表面,这个离屏绘图表面用一个Pbuffer来描述。但是如果底层的GPU支持无绘图表面的OpenGL上下文,那么GPU线程的默认离屏绘图表面使用一个Surfaceless描述。
Browser端的OpenGL上下文关联的绘图表面是一个本地窗口,因为它负责将WebGL端、Render端和浏览器窗口其它的UI渲染在屏幕上。在Android平台上,SurfaceView描述的就是一个本地窗口,因此,Chromium将它作为Browser端的OpenGL上下文的绘图表面。
在前面Chromium硬件加速渲染机制基础知识简要介绍和学习计划一文提到,有些平台的GPU不能很好地同时支持创建多个OpenGL上下文。对于这些平台,Chromium为WebGL端、Render端和Browser端创建的是虚拟OpenGL上下文,如图2所示:
图2 Chromium的虚拟OpenGL上下文的绘图表面
虚拟OpenGL上下文使用GLContextVirtual来描述。由于它们是虚拟的,因此在激活的时候,必须切换到真实的OpenGL上下文去。这意味着虚拟OpenGL上下文必须要对应有真实OpenGL上下文。
Chromium在不能很好支持同时创建多个OpenGL上下文的平台上,只创建一个真实OpenGL上下文,并且WebGL端、Render端和Browser端的虚拟OpenGL上下文均与该真实OpenGL上下文对应。上述真实OpenGL上下文为WebGL端和Render端所用时,关联的绘图表面是GPU线程的默认离屏绘图表面,但是为Browser端所用时,关联的绘图表面是本地窗口。在Android平台上,这个本地窗口即为一个SurfaceView。从这一点我们可以看到,OpenGL上下文的绘图表面是可以动态修改的。
从图1和图2还可以看到,不管WebGL端、Render端和Browser端的OpenGL上下文是真实的,还是虚拟的,它们都是位于一个共享组中。这使得Browser端的OpenGL上下文可以直接访问在WebGL端和Render端的OpenGL上下文中创建的资源,从而可以高效地完成合成和渲染操作。接下来,我们就分别分析WebGL端、Render端和Browser端的OpenGL上下文的绘图表面的创建过程。理解OpenGL上下文的绘图表面的创建过程对理解OpenGL上下文的创建过程是至关重要的,因此我们专门用一篇文章来分析它们。
我们首先分析WebGL端的OpenGL上下文的绘图表面的创建过程。
从前面Chromium的GPU进程启动过程分析一文可以知道,WebGL端是通过一个WebGraphicsContext3DCommandBufferImpl对象向GPU线程请求执行GPU操作的。这个WebGraphicsContext3DCommandBufferImpl对象是通过调用WebGraphicsContext3DCommandBufferImpl类的静态成员函数CreateOffscreenContext创建的,它的实现如下所示:
WebGraphicsContext3DCommandBufferImpl*
WebGraphicsContext3DCommandBufferImpl::CreateOffscreenContext(
GpuChannelHost* host,
const WebGraphicsContext3D::Attributes& attributes,
bool lose_context_when_out_of_memory,
const GURL& active_url,
const SharedMemoryLimits& limits,
WebGraphicsContext3DCommandBufferImpl* share_context) {
......
return new WebGraphicsContext3DCommandBufferImpl(
0,
active_url,
host,
attributes,
lose_context_when_out_of_memory,
limits,
share_context);
}
这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。
我们注意到,在调用WebGraphicsContext3DCommandBufferImpl类的构造函数创建一个WebGraphicsContext3DCommandBufferImpl对象的时候,第一个参数指定0,这表示WebGL端的OpenGL上下文使用的绘图表面的ID为0。
WebGraphicsContext3DCommandBufferImpl类的构造函数的实现如下所示:
WebGraphicsContext3DCommandBufferImpl::WebGraphicsContext3DCommandBufferImpl(
int surface_id,
const GURL& active_url,
GpuChannelHost* host,
const Attributes& attributes,
bool lose_context_when_out_of_memory,
const SharedMemoryLimits& limits,
WebGraphicsContext3DCommandBufferImpl* share_context)
: ......,
surface_id_(surface_id),
...... {
......
}
这个函数定义在文件external/chromium_org/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc中。
这意味着WebGL端使用的WebGraphicsContext3DCommandBufferImpl对象的成员变量surface_id_的值等于0。Render进程在为WebGL端创建OpenGL上下文时,会将该成员变量的值传递到GPU线程,这时候GPU线程就知道要为正在创建的OpenGL上下文关联的一个离屏绘图表面。这个过程我们在接下来一篇文章中分析OpenGL上下文的创建过程时再分析,此时我们只需要记住WebGL端的OpenGL上下文关联的绘图表面的ID等于0。
我们接下来分析Browser端的OpenGL上下文的绘图表面的创建过程。这要从Browser进程的启动过程说起。
Chromium自带了一个Shell APK,用来说明如何通过Content API来创建一个基于Chromium的浏览器。Chromium的Content API封装了Chromium的多进程架构以及WebKit等,通过它们可以很容易地实现一个与Chrome类似的浏览器。Shell APK的源码位于目录external/chromium_org/content/shell/android/shell_apk中。
Shell APK的Main Activity是ContentShellActivity,它运行在Shell APK的主进程中,也就是Browser进程。当Shell APK启动时,ContentShellActivity类的成员函数onCreate就会被调用,它的执行过程如下所示:
public class ContentShellActivity extends Activity {
......
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......
setContentView(R.layout.content_shell_activity);
mShellManager = (ShellManager) findViewById(R.id.shell_container);
mWindowAndroid = new ActivityWindowAndroid(this);
......
mShellManager.setWindow(mWindowAndroid);
String startupUrl = getUrlFromIntent(getIntent());
if (!TextUtils.isEmpty(startupUrl)) {
mShellManager.setStartupUrl(Shell.sanitizeUrl(startupUrl));
}
if (CommandLine.getInstance().hasSwitch(ContentSwitches.DUMP_RENDER_TREE)) {
try {
BrowserStartupController.get(this).startBrowserProcessesSync(
BrowserStartupController.MAX_RENDERERS_LIMIT);
} catch (ProcessInitException e) {
......
}
} else {
try {
BrowserStartupController.get(this).startBrowserProcessesAsync(
new BrowserStartupController.StartupCallback() {
@Override
public void onSuccess(boolean alreadyStarted) {
finishInitialization(savedInstanceState);
}
......
});
} catch (ProcessInitException e) {
......
}
}
}
......
}
这个函数定义在文件external/chromium_org/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java中。
ContentShellActivity类的成员函数onCreate主要做了以下几件事情:
1. 将R.layout.content_shell_activity布局文件设置为浏览器窗口UI。
2. 在上述布局文件中找到一个id为shell_container的控件,该控件是一个自定义控件,类型为ShellManager,保存在ContentShellActivity类的成员变量mShellManager中。
3. 创建一个ActivityWindowAndroid对象,保存在ContentShellActivity类的成员变量mWindowAndroid中,并且调用ShellManager类的成员函数setWindow将其设置到上述ShellManager控件内部去。ActivityWindowAndroid对象封装了对Shell APK的主Activity的一些操作。
4. 检查启动Shell APK的主Activity的Intent是否设置有启动URL。如果有,那么就通过调用ShellManager类的成员函数setStartupUrl将其设置到上述ShellManager控件内部去。
5. 检查Shell APK的命令行参数是否设置了ContentSwitches.DUMP_RENDER_TREE选项。如果设置了,那么就调用Browser进程的BrowserStartupController单例的成员函数startBrowserProcessesSync同步启动和初始化Content模块,相当于是在Native层对Browser进程执行初始化工作。如果没有设置,那么就调用Browser进程的BrowserStartupController单例的成员函数startBrowserProcessesAsync异步启动和初始化Content模块。对于异步方式,当Content模块启动和初始完成后,会调用ContentShellActivity类的成员函数finishInitialization继续执行启动Shell APK的工作。
接下来,我们主要分析第2、3和5个步骤的相关操作,其中,假设第5步使用异步方式启动和初始化Content模块。
第2步主要是创建了一个类型为ShellManager的控件,这将会导致ShellManager类的构造函数被调用,如下所示:
public class ShellManager extends FrameLayout {
......
public ShellManager(final Context context, AttributeSet attrs) {
super(context, attrs);
nativeInit(this);
......
}
......
}
这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/ShellManager.java中。
ShellManager类的构造函数主要是调用了另外一个成员函数nativeInit执行一些初始化工作。
ShellManager类的成员变量nativeInit是一个JNI函数,它由Native层的函数Init实现,如下所示:
struct GlobalState {
GlobalState() {}
base::android::ScopedJavaGlobalRef<jobject> j_shell_manager;
};
base::LazyInstance<GlobalState> g_global_state = LAZY_INSTANCE_INITIALIZER;
......
static void Init(JNIEnv* env, jclass clazz, jobject obj) {
g_global_state.Get().j_shell_manager.Reset(
base::android::ScopedJavaLocalRef<jobject>(env, obj));
}
这个函数定义在文件external/chromium_org/content/shell/android/shell_manager.cc中。
从前面的调用过程可以知道,参数obj指向的是一个Java层的ShellManager对象,函数Init将它保存在全局变量g_global_state描述的一个GlobalState对象的成员变量j_shell_manager中。
这一步执行完成之后,回到ContentShellActivity类的成员函数onCreate中,它的第3步主要是调用ShellManager类的成员函数setWindow设置一个ActivityWindowAndroid对象到Shell APK的主Activity窗口UI的ShellManager控件的内部去,它的实现如下所示:
public class ShellManager extends FrameLayout {
......
public void setWindow(WindowAndroid window) {
assert window != null;
mWindow = window;
mContentViewRenderView = new ContentViewRenderView(getContext()) {
@Override
protected void onReadyToRender() {
if (sStartup) {
mActiveShell.loadUrl(mStartupUrl);
sStartup = false;
}
}
};
mContentViewRenderView.onNativeLibraryLoaded(window);
}
......
}
这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/ShellManager.java
ShellManager类的成员函数setWindow首先是将参数window描述的一个ActivityWindowAndroid对象保存在成员变量mWindow中,接着创建了一个ContentViewRenderView对象,并且保存在成员变量mContentViewRenderView中。当Shell APK启动和初始化完毕,上面创建的ContentViewRenderView对象的成员函数onReadyRender就会被调用。在调用的过程中,就会调用ShellManager类的成员变量mActivityShell描述的一个Shell对象的成员函数loadUrl加载由另外一个成员变量mStartupUrl指定的一个URL。这个URL是设置在用来启动Shell APK的Intent中的。ShellManager类的成员函数setWindow最后调用前面创建的ContentViewRenderView对象的成员函数onNativeLibraryLoaded对其进行初始化。
接下来,我们首先分析ContentViewRenderView对象的创建过程,即ContentViewRenderView类的构造函数的实现,接着再分析ContentViewRenderView对象的初始化过程,即ContentViewRenderView类的成员函数onNativeLibraryLoaded的实现。
ContentViewRenderView类的构造函数的实现如下所示:
public class ContentViewRenderView extends FrameLayout {
......
private final SurfaceView mSurfaceView;
......
public ContentViewRenderView(Context context) {
super(context);
mSurfaceView = createSurfaceView(getContext());
......
addView(mSurfaceView,
new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
......
}
......
}
这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java中。
从这里可以看到,ContentViewRenderView类描述的是一个FrameLayout,它的构造函数主要是通过调用另外一个成员函数createSurfaceView创建了一个SurfaceView,保存在成员变量mSurfaceView中,并且作为当前正在创建的ContentViewRenderView的子控件。
这一步执行完成后,回到ShellManager类的成员函数setWindow中,它接下来调用ContentViewRenderView类的成员函数onNativeLibraryLoaded对前面创建的ContentViewRenderView进行初始化,如下所示:
public class ContentViewRenderView extends FrameLayout {
// The native side of this object.
private long mNativeContentViewRenderView;
private SurfaceHolder.Callback mSurfaceCallback;
......
public void onNativeLibraryLoaded(WindowAndroid rootWindow) {
......
mNativeContentViewRenderView = nativeInit(rootWindow.getNativePointer());
......
mSurfaceCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
......
nativeSurfaceChanged(mNativeContentViewRenderView,
format, width, height, holder.getSurface());
......
}
......
};
mSurfaceView.getHolder().addCallback(mSurfaceCallback);
......
}
......
}
这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java中。
从前面的调用过程可以知道,参数rootWindow指向的是一个ActivityWindowAndroid对象,ContentViewRenderView类的成员函数onNativeLibraryLoaded首先调用该ActivityWindowAndroid对象的成员函数getNativePointer获得一个Native层的WindowAndroid对象,然后再以该Native层的WindowAndroid对象为参数,调用ContentViewRenderView类的成员函数nativeInit对当前正在创建的ContentViewRenderView进行初始化。
ContentViewRenderView类的成员函数onNativeLibraryLoaded还创建了一个SurfaceHolder.Callback对象,保存在成员变量mSurfaceCallback中,并且使用该SurfaceHolder.Callback对象获得成员变量mSurfaceView描述的一个SurfaceView底层所使用的绘图表面的变化。也就是说,当ContentViewRenderView类的成员变量mSurfaceView描述的一个SurfaceView底层所使用的绘图表面发生变化时,上述SurfaceHolder.Callback对象的成员函数surfaceChanged就会被调用,然后又会调用ContentViewRenderView类的成员函数nativeSurfaceChanged执行相关的操作。
接下来,我们首先分析ActivityWindowAndroid类的成员函数getNativePointer的实现,接着再分析ContentViewRenderView类的成员函数nativeInit的实现。
ActivityWindowAndroid类继承于WindowAndroid类,它的成员函数getNativePointer也是从父类WindowAndroid继承下来的,因此接下来我们分析WindowAndroid的成员函数getNativePointer的实现,如下所示:
public class WindowAndroid {
......
private long mNativeWindowAndroid = 0;
......
public long getNativePointer() {
if (mNativeWindowAndroid == 0) {
mNativeWindowAndroid = nativeInit(mVSyncMonitor.getVSyncPeriodInMicroseconds());
}
return mNativeWindowAndroid;
}
......
}
这个函数定义在文件external/chromium_org/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java中。
WindowAndroid类的成员函数getNativePointer返回的是成员变量mNativeWindowAndroid描述的一个Native层的WindowAndroid对象。不过,当这个成员变量的值等于0的时候,就表示Native层的WindowAndroid对象还没有创建,这时候就需要调用另外一个成员函数nativeInit进行创建。
WindowAndroid类的成员函数nativeInit是一个JNI函数,由Native层的函数Init实现,如下所示:
jlong Init(JNIEnv* env, jobject obj, jlong vsync_period) {
WindowAndroid* window = new WindowAndroid(env, obj, vsync_period);
return reinterpret_cast<intptr_t>(window);
}
这个函数定义在文件external/chromium_org/ui/base/android/window_android.cc中。
函数Init主要是创建了一个Native层的WindowAndroid对象,并且将它的地址返回给调用者。
这一步执行完成后,回到ContentViewRenderView类的成员函数onNativeLibraryLoaded中,它接下来调用ContentViewRenderView类的成员函数nativeInit对正在创建的ContentViewRenderView进行初始化。
ContentViewRenderView类的成员函数nativeInit是一个JNI函数,由Native层的函数Init实现,如下所示:
static jlong Init(JNIEnv* env,
jobject obj,
jlong native_root_window) {
gfx::NativeWindow root_window =
reinterpret_cast<gfx::NativeWindow>(native_root_window);
ContentViewRenderView* content_view_render_view =
new ContentViewRenderView(env, obj, root_window);
return reinterpret_cast<intptr_t>(content_view_render_view);
}
这个函数定义在文件external/chromium_org/content/browser/android/content_view_render_view.cc中。
从前面的调用过程可以知道,参数native_root_window指向的是一个Native层的WindowAndroid对象,函数Init将它封装在一个新创建的Native层的ContentViewRenderView对象中,最后将新创建的Native层的ContentViewRenderView对象的地址返回给调用者。
这一步执行完成后,就在Java层创建了一个SurfaceView,并且在Native层创建了一个WindowAndroid对象和一个ContentViewRenderView对象,回到ContentShellActivity类的成员函数onCreate中,它的第5步是调用Browser进程的BrowserStartupController单例的成员函数startBrowserProcessesAsync异步启动和初始化Content模块。当Content模块启动和初始完成后,会调用ContentShellActivity类的成员函数finishInitialization继续执行启动Shell APK的工作,如下所示:
public class ContentShellActivity extends Activity {
......
private void finishInitialization(Bundle savedInstanceState) {
String shellUrl = ShellManager.DEFAULT_SHELL_URL;
if (savedInstanceState != null
&& savedInstanceState.containsKey(ACTIVE_SHELL_URL_KEY)) {
shellUrl = savedInstanceState.getString(ACTIVE_SHELL_URL_KEY);
}
mShellManager.launchShell(shellUrl);
}
......
}
这个函数定义在文件external/chromium_org/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java中。
ContentShellActivity类的成员函数finishInitialization主要是调用了成员变量mShellManager指向的一个ShellManager对象的成员函数launchShell启动一个Shell,同时为该Shell指定了一个URL。该URL的默认值为ShellManager.DEFAULT_SHELL_URL,但是如果上次启动Shell APK时,保存有最后使用的Shell URL,那么就会使用最后使用的Shell URL。
ShellManager类的成员函数launchShell的实现如下所示:
public class ShellManager extends FrameLayout {
......
public void launchShell(String url) {
......
nativeLaunchShell(url);
......
}
......
}
这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/ShellManager.java中。
ShellManager类的成员函数launchShell主要是调用了另外一个成员函数nativeLaunchShell在Native层启动一个Shell。
ShellManager类的成员函数nativeLaunchShell是一个JNI函数,由Native层的函数LaunchShell实现,如下所示:
void LaunchShell(JNIEnv* env, jclass clazz, jstring jurl) {
ShellBrowserContext* browserContext =
ShellContentBrowserClient::Get()->browser_context();
GURL url(base::android::ConvertJavaStringToUTF8(env, jurl));
Shell::CreateNewWindow(browserContext,
url,
NULL,
MSG_ROUTING_NONE,
gfx::Size());
}
这个函数定义在文件external/chromium_org/content/shell/android/shell_manager.cc中。
函数LaunchShell主要是调用另外一个函数Shell::CreateNewWindow在Native层启动一个Shell。注意,在调用函数Shell::CreateNewWindow时,第四个参数的值指定为MSG_ROUTING_NONE,后面我们分析Render端的OpenGL上下文的绘图表面的创建过程时,将会用到该参数。
函数Shell::CreateNewWindow的实现如下所示:
Shell* Shell::CreateNewWindow(BrowserContext* browser_context,
const GURL& url,
SiteInstance* site_instance,
int routing_id,
const gfx::Size& initial_size) {
WebContents::CreateParams create_params(browser_context, site_instance);
create_params.routing_id = routing_id;
create_params.initial_size = AdjustWindowSize(initial_size);
WebContents* web_contents = WebContents::Create(create_params);
Shell* shell = CreateShell(web_contents, create_params.initial_size);
if (!url.is_empty())
shell->LoadURL(url);
return shell;
}
这个函数定义在文件external/chromium_org/content/shell/browser/shell.cc中。
函数Shell::CreateNewWindow首先调用WebContents类的静态成员函数Create创建了一个WebContentsImpl对象,接着再调用函数CreateShell创建了一个Shell,最后在参数url的值不等于空的情况下,调用前面创建的Shell的成员函数LoadUrl加载参数url描述的网址。
后面我们分析Render端的OpenGL上下文的绘图表面的创建过程时,我们再分析WebContents类的静态成员函数Create创建WebContentsImpl对象的过程,接下来我们继续分析函数CreateShell创建Shell的过程,如下所示:
Shell* Shell::CreateShell(WebContents* web_contents,
const gfx::Size& initial_size) {
Shell* shell = new Shell(web_contents);
shell->PlatformCreateWindow(initial_size.width(), initial_size.height());
......
return shell;
}
这个函数定义在文件external/chromium_org/content/shell/browser/shell.cc中。
函数CreateShell首先是创建了一个Shell对象,接着调用这个Shell对象的成员函数PlatformCreateWindow创建一个Shell窗口,如下所示:
void Shell::PlatformCreateWindow(int width, int height) {
java_object_.Reset(AttachCurrentThread(), CreateShellView(this));
}
这个函数定义在文件external/chromium_org/content/shell/browser/shell_android.cc。
Shell类的成员函数PlatformCreateWindow主要是调用另外一个函数CreateShellView创建一个Java层的Shell对象,并且保存在成员变量java_object_中。
函数CreateShellView的实现如下所示:
jobject CreateShellView(Shell* shell) {
JNIEnv* env = base::android::AttachCurrentThread();
jobject j_shell_manager = g_global_state.Get().j_shell_manager.obj();
return Java_ShellManager_createShell(
env,
j_shell_manager,
reinterpret_cast<intptr_t>(shell)).Release();
}
这个函数定义在文件external/chromium_org/content/shell/android/shell_manager.cc中。
从前面的分析可以知道,全局变量g_global_state指向的一个GlobalState对象的成员变量j_shell_manager描述的是一个Java层的ShellManager对象,这里调用函数Java_ShellManager_createShell调用它的成员函数createShell,用来创建一个Java层的Shell对象。
Java层的ShellManager类的成员函数createShell的实现如下所示:
public class ShellManager extends FrameLayout {
......
private Object createShell(long nativeShellPtr) {
......
LayoutInflater inflater =
(LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Shell shellView = (Shell) inflater.inflate(R.layout.shell_view, null);
shellView.initialize(nativeShellPtr, mWindow, mContentViewClient);
......
showShell(shellView);
return shellView;
}
......
}
这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/ShellManager.java中。
ShellManager类的成员函数createShell首先是根据R.layout.shell_view布局文件创建了一个Shell控件,接着Shell类的成员函数initialize对该控件进行初始化,最后调用另外一个成员函数showShell显示该控件,如下所示:
public class ShellManager extends FrameLayout {
......
private void showShell(Shell shellView) {
shellView.setContentViewRenderView(mContentViewRenderView);
addView(shellView, new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
mActiveShell = shellView;
ContentViewCore contentViewCore = mActiveShell.getContentViewCore();
if (contentViewCore != null) {
mContentViewRenderView.setCurrentContentViewCore(contentViewCore);
......
}
}
......
}
这个函数定义在文件external/chromium_org/content/shell/android/java/src/org/chromium/content_shell/ShellManager.java中。
从前面的分析可以知道,ShellManager类的成员变量mContentViewRenderView描述的是一个ContentViewRenderView控件,该ContentViewRenderView控件包含有一个SurfaceView,ShellManager类的成员函数showShell又将该ContentViewRenderView控件作为参数shellView描述的一个Shell控件的子控件,最后参数shellView描述的Shell控件又作为当前正在处理的ShellManager控件的一个子控件。从这里我们就看到Browser窗口的UI层次结构大致为:
ShellManager
--Shell
--ContentViewRenderView
--SurfaceView
ShellManager类的成员函数showShell接下来将参数shellView描述的Shell控件保存在成员变量mActiveShell中,并且调用该Shell控件的成员函数getContentViewCore获得一个ContentViewCore对象。有了这个ContentViewCore对象,就将它设置到ShellManager类的成员变量mContentViewRenderView描述的ContentViewRenderView控件的内部去。这是通过调用ContentViewRenderView类的成员函数setCurrentContentViewCore实现的。
ContentViewRenderView类的成员函数setCurrentContentViewCore的实现如下所示:
public class ContentViewRenderView extends FrameLayout {
......
public void setCurrentContentViewCore(ContentViewCore contentViewCore) {
......
mContentViewCore = contentViewCore;
if (mContentViewCore != null) {
......
nativeSetCurrentContentViewCore(mNativeContentViewRenderView,
mContentViewCore.getNativeContentViewCore());
}
......
}
......
}
这个函数定义在文件external/chromium_org/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java中。
ContentViewRenderView类的成员函数setCurrentContentViewCore首先将参数contentViewCore描述的一个ContentViewCore对象保存在成员变量mContentViewCore中,接着调用该ContentViewCore对象的成员函数getNativeContentViewCore获得它在Native层对应的一个ContentViewCoreImpl对象。
从前面的分析可以知道,ContentViewRenderView类的成员变量mNativeContentViewRenderView描述的是Native层的一个ContentViewRenderView对象,ContentViewRenderView类的成员函数setCurrentContentViewCore最后调用另外一个成员函数nativeSetCurrentContentViewCore将该Native层的ContentViewRenderView对象,以及前面获得的Native层的ContentViewCoreImpl对象设置到Native层的Chromium中去。
ContentViewRenderView类的成员函数nativeSetCurrentContentViewCore是一个JNI函数,由Native层的函数Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSetCurrentContentViewCore实现,如下所示:
__attribute__((visibility("default")))
void
Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSetCurrentContentViewCore(JNIEnv*
env,
jobject jcaller,
jlong nativeContentViewRenderView,
jlong nativeContentViewCore) {
ContentViewRenderView* native =
reinterpret_cast<ContentViewRenderView*>(nativeContentViewRenderView);
CHECK_NATIVE_PTR(env, jcaller, native, "SetCurrentContentViewCore");
return native->SetCurrentContentViewCore(env, jcaller, nativeContentViewCore);
}
这个函数定义在文件out/target/product/generic/obj/GYP/shared_intermediates/content/jni/ContentViewRenderView_jni.h中。
函数Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSetCurrentContentViewCore首先是将参数nativeContentViewRenderView转换为一个Native层的ContentViewRenderView对象,接着将参数nativeContentViewCore描述的一个Native层的ContentViewCoreImpl对象设置到其内部去,这是通过调用Native层的ContentViewRenderView类的成员函数SetCurrentContentViewCore实现的。
Native层的ContentViewRenderView类的成员函数SetCurrentContentViewCore的实现如下所示:
void ContentViewRenderView::SetCurrentContentViewCore(
JNIEnv* env, jobject obj, jlong native_content_view_core) {
InitCompositor();
ContentViewCoreImpl* content_view_core =
reinterpret_cast<ContentViewCoreImpl*>(native_content_view_core);
compositor_->SetRootLayer(content_view_core
? layer_tree_build_helper_->GetLayerTree(
content_view_core->GetLayer())
: scoped_refptr<cc::Layer>());
}
这个函数定义在文件external/chromium_org/content/browser/android/content_view_render_view.cc中。
ContentViewRenderView类的成员函数SetCurrentContentViewCore首先调用另外一个成员函数InitCompositor创建一个在Browser进程使用的UI合成器,这个UI合成器保存在成员变量compositor_中,它负责合成Render端负责渲染的网页UI等。
ContentViewRenderView类的成员函数SetCurrentContentViewCore接着将参数native_content_view_core转化为一个Native层的ContentViewCoreImpl对象,并且将该ContentViewCoreImpl对象内部包含的一个Layer作为Browser进程的UI合成器的Root Layer,以后Browser进程的UI合成器通过遍历该Root Layer描述的Layer Tree,就可以将Render端负责渲染的网页UI合成到浏览器窗口来。
接下来,我们继续分析ContentViewRenderView类的成员函数InitCompositor的实现,以及可以了解Browser进程的UI合成器的创建过程,如下所示:
void ContentViewRenderView::InitCompositor() {
if (!compositor_)
compositor_.reset(Compositor::Create(this, root_window_));
}
这个函数定义在文件external/chromium_org/content/browser/android/content_view_render_view.cc中。
从这里可以看到,Browser进程的UI合成器是通过调用Compositor类的静态成员函数Create创建的,它的实现如下所示:
Compositor* Compositor::Create(CompositorClient* client,
gfx::NativeWindow root_window) {
return client ? new CompositorImpl(client, root_window) : NULL;
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/compositor_impl_android.cc中。
从这里可以看到,Browser进程的UI合成器通过CompositorImpl来描述,即它是一个CompositorImpl对象。
这一步执行完成后,Browser进程在Native层中创建了一个Shell窗口和一个UI合成器,为以后合成Render端负责的网页UI做好了准备。从前面的分析可以知道,Browser进程的Shell窗口包含有一个SurfaceView。在前面分析的ContentViewRenderView类的成员函数onNativeLibraryLoaded中又提到,当上述SurfaceView底层使用的绘图表面发生变化(包括第一次创建时),ContentViewRenderView类的成员函数nativeSurfaceChanged会被调用。
ContentViewRenderView类的成员函数nativeSurfaceChanged是一个JNI函数,由Native层的函数Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSurfaceChanged实现,如下所示:
__attribute__((visibility("default")))
void
Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSurfaceChanged(JNIEnv*
env,
jobject jcaller,
jlong nativeContentViewRenderView,
jint format,
jint width,
jint height,
jobject surface) {
ContentViewRenderView* native =
reinterpret_cast<ContentViewRenderView*>(nativeContentViewRenderView);
CHECK_NATIVE_PTR(env, jcaller, native, "SurfaceChanged");
return native->SurfaceChanged(env, jcaller, format, width, height, surface);
}
这个函数定义在文件out/target/product/generic/obj/GYP/shared_intermediates/content/jni/ContentViewRenderView_jni.h中。
函数Java_com_android_org_chromium_content_browser_ContentViewRenderView_nativeSurfaceChanged首先将参数nativeContentViewRenderView转换为一个Native层的ContentViewRenderView对象,接着调用该ContentViewRenderView对象的成员函数SurfaceChanged通知Browser进程用来显示网页UI的SurfaceView发生了变化。
ContentViewRenderView类的成员函数SurfaceChanged的实现如下所示:
void ContentViewRenderView::SurfaceChanged(JNIEnv* env, jobject obj,
jint format, jint width, jint height, jobject surface) {
if (current_surface_format_ != format) {
current_surface_format_ = format;
compositor_->SetSurface(surface);
}
......
}
这个函数定义在文件external/chromium_org/content/browser/android/content_view_render_view.cc中。
ContentViewRenderView类的成员变量current_surface_format_保存的是Browser进程的SurfaceView底层使用的绘图表面的颜色格式,它的值被初始化为0。因此,当Browser进程的SurfaceView第一次创建,以及它底层使用的绘图表面的颜色格式发生变化时,ContentViewRenderView类的成员函数SurfaceChanged就会调用成员变量compositor_指向的一个CompositorImpl对象的成员函数SetSurface来重新设置Browser端的OpenGL上下文的绘图表面。
CompositorImpl类的成员函数SetSurface的实现如下所示:
void CompositorImpl::SetSurface(jobject surface) {
JNIEnv* env = base::android::AttachCurrentThread();
......
ANativeWindow* window = NULL;
if (surface) {
......
window = ANativeWindow_fromSurface(env, surface);
}
if (window) {
SetWindowSurface(window);
......
}
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/compositor_impl_android.cc中。
CompositorImpl类的成员函数SetSurface首先调用函数ANativeWindow_fromSurface从参数surface描述的一个Java层的Surface对象获得一个关联的Native层的ANativeWindow对象,这个ANativeWindow对象描述的就是一个OS本地窗口。
从前面的调用过程可以知道,参数surface描述的Java层的Surface对象描述的即为Browser进程的Shell窗口的SurfaceView底层所使用的绘图表面,因此,前面获得的ANativeWindow对象描述的OS本地窗口即为Browser进程的Shell窗口的SurfaceView。
最后,CompositorImpl类的成员函数SetSurface调用另外一个成员函数SetWindowSurface将前面获得的ANativeWindow对象设置为Browser端的OpenGL上下文的绘图表面,它的实现如下所示:
void CompositorImpl::SetWindowSurface(ANativeWindow* window) {
GpuSurfaceTracker* tracker = GpuSurfaceTracker::Get();
......
if (window) {
window_ = window;
......
surface_id_ = tracker->AddSurfaceForNativeWidget(window);
tracker->SetSurfaceHandle(
surface_id_,
gfx::GLSurfaceHandle(gfx::kNullPluginWindow, gfx::NATIVE_DIRECT));
......
}
}
这个函数定义在文件external/chromium_org/content/browser/renderer_host/compositor_impl_android.cc中。
CompositorImpl类的成员函数主要完成两件事情:
1. 调用Browser进程的GpuSurfaceTracker单例的成员函数AddSurfaceForNativeWidget为Browser端的OpenGL上下文创建一个绘图表面,这个绘图表面即为参数window描述的OS本地窗口。GpuSurfaceTracker类的成员函数AddSurfaceForNativeWidget的返回值为一个Surface ID,保存在CompositorImpl类的成员变量surface_id_中,以后Browser端通过该Surface ID就可以在Browser进程的GpuSurfaceTracker单例中找到Browser端的OpenGL上下文的绘图表面。
接下来,我们就继续分析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
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(¶ms);
else
render_view = new RenderViewImpl(¶ms);
render_view->Initialize(¶ms);
return render_view;
}
这个函数定义在文件external/chromium_org/content/renderer/render_view_impl.cc中。
RenderViewImpl类的静态成员函数Create首先是创建一个RenderViewImpl对象,接着再调用RenderViewImpl类的成员函数Initialize对该RenderViewImpl对象进行初始化。
从这里可以看到,在Render进程中为正在加载网页的主Frame创建的Render View实际上是一个RenderViewImpl对象,在Android平台上,它与Browser进程中的RenderWidgetHostViewAndroid对象对应。
RenderViewImpl类的成员函数Initialize的实现如下所示:
void RenderViewImpl::Initialize(RenderViewImplParams* params) {
......
surface_id_ = params->surface_id;
......
}
这个函数定义在文件external/chromium_org/content/renderer/render_view_impl.cc中。
从前面的分析可以知道,封装在参数params中的surface_id是从Browser进程中传递过来的,它描述的是Render端的OpenGL上下文的绘图表面的ID,这个ID值保存在RenderViewImpl类的成员变量surface_id_中。
前面提到,Render端用来与GPU进程执行IPC的WebGraphicsContext3DCommandBufferImpl对象是通过RenderWidget类的成员函数CreateGraphicsContext3D创建的,如下所示:
scoped_ptr<WebGraphicsContext3DCommandBufferImpl>
RenderWidget::CreateGraphicsContext3D() {
......
scoped_ptr<WebGraphicsContext3DCommandBufferImpl> context(
new WebGraphicsContext3DCommandBufferImpl(surface_id(),
GetURLForGraphicsContext3D(),
gpu_channel_host.get(),
attributes,
lose_context_when_out_of_memory,
limits,
NULL));
return context.Pass();
}
这个函数定义在文件external/chromium_org/content/renderer/render_widget.cc中。
前面也提到,RenderWidget类的成员函数CreateGraphicsContext3D调用另外一个成员函数surface_id获得一个Surface ID,用作Render端的OpenGL上下文的绘图表面的ID。RenderWidget类的成员函数surface_id返回的是成员变量surface_id_的值。
RenderWidget类的成员变量surface_id_的访问权限为protected,前面分析的RenderViewImpl类是从RenderWidget类继承下来的,并且RenderWidget类的成员变量surface_id_是在RenderViewImpl类的成员函数Initialize进行设置的,这样RenderWidget类的成员函数CreateGraphicsContext3D在为Render端创建WebGraphicsContext3DCommandBufferImpl对象时,就可以获得一个不为0的Surface ID了。
至此,我们就分析完成WebGL、Browser和Render三端的OpenGL上下文的绘图表面的创建过程了,这些绘图表面都是在Browser进程创建的,并且通过一个Surface ID进行引用,这个Surface ID在为WebGL、Browser和Render三端创建OpenGL上下文时将会用到。
通过上面的分析,我们可以知道WebGL、Browser和Render三端的OpenGL上下文的绘图表面的特点,如下所示:
WebGL端的OpenGL上下文的绘图表面的ID设置为0,表示它不需要Browser进程为其创建一个绘图表面。
3. Browser端的OpenGL上下文的绘图表面的ID不为0,它关联的绘图表面句柄(SurfaceHandle)的类型为gfx::NATIVE_DIRECT,并且关联有一个OS本地窗口。在Android平台上,这个OS本地窗口即为一个SurfaceView。
我们记住这些结论,在接下来一篇文章中分析WebGL、Browser和Render三端的OpenGL上下文的创建过程时,将会使用到,敬请关注!更多的信息,也可以关注老罗的新浪微博:http://weibo.com/shengyangluo。
扫一扫
在手机上阅读