本文是OpenGL ES 2.0源码分析的第三篇,关于设置OpenGL ES的Renderer,也是最重要最复杂的。
还是先列出我们使用OpenGL ES 2.0的过程:
mGLSurfaceView.setEGLContextClientVersion(2);
mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
mGLSurfaceView.setRenderer(new TexRenderer(this));
mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
直接进入setRenderer的源码:
public void setRenderer(Renderer renderer) {
// 上两篇文章我们已经熟悉这个了,这里说明我们不能对一个GLSurfaceView设置多
// 次Renderer,也就是这个方法有且只能被调用一次
checkRenderThreadState();
// 我们上篇文章有分析过,这里不再赘述
if (mEGLConfigChooser == null) {
mEGLConfigChooser = new SimpleEGLConfigChooser(true);
}
// 上下文工厂,第一篇文章我们有分析过,主要是提供创建和销毁上下文的功能
// 如果想设置自己的EGLContextFactory可以在setRender之前通过
// setEGLContextFactory去设置自己的,否则就使用默认
if (mEGLContextFactory == null) {
mEGLContextFactory = new DefaultContextFactory();
}
// 跟上面EGLContextFactory类似,只不过这个是提供surface的创建和销毁
if (mEGLWindowSurfaceFactory == null) {
mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
}
mRenderer = renderer;
// new 一个GL线程,并吧当前的GLSurfaceView通过弱引用的方式传入GL线程
mGLThread = new GLThread(mThisWeakRef);
// 启动GL线程
mGLThread.start();
}
我们先看下使用到mRenderer的地方,然后在具体分析GLThread,使用到mRenderer的地方只有一个:
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// 当View从Window分离又重新依附的时候要重新创建和启动GL线程
if (mDetached && (mRenderer != null)) {
int renderMode = RENDERMODE_CONTINUOUSLY;
if (mGLThread != null) {
renderMode = mGLThread.getRenderMode();
}
mGLThread = new GLThread(mThisWeakRef);
if (renderMode != RENDERMODE_CONTINUOUSLY) {
mGLThread.setRenderMode(renderMode);
}
mGLThread.start();
}
mDetached = false;
}
我们知道用到的地方就好,这些都不是我们关注的重点,我们今天的重点在于GLThread。
GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) {
super();
mWidth = 0;
mHeight = 0;
mRequestRender = true;
mRenderMode = RENDERMODE_CONTINUOUSLY;
mWantRenderNotification = false;
mGLSurfaceViewWeakRef = glSurfaceViewWeakRef;
}
构造函数很简单,就是一些很简单的初始化,我们接着分析run()方法:
@Override
public void run() {
// 设置线程名
setName("GLThread " + getId());
try {
guardedRun();
} catch (InterruptedException e) {
// fall thru and exit normally
} finally {
sGLThreadManager.threadExiting(this);
}
}
越跟越重要,现在我们要去guardedRun()一探究竟。
private void guardedRun() throws InterruptedException {
mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
...
while (true) {
synchronized (sGLThreadManager) {
while (true) {
// 如果请求退出就结束循环
if (mShouldExit) {
return;
}
// 如果事件队列还有事件,就取出事件,并退出此循环
if (! mEventQueue.isEmpty()) {
event = mEventQueue.remove(0);
break;
}
// 更新暂停状态
...
// 是否需要放弃EGL上下文,如果需要就释放EGL surface和context
···
// 是否已经丢失了EGL上下文,如果是就释放EGL surface和context
...
// 当前如果是暂停中,就释放EGL surface
...
// 当前如果是暂停中,就选择性释放EGL context
...
// 如果我们丢失了SurfaceView surface就释放EGL surface和更新状态
...
// 我们是否已经获取了SurfaceView surface,是就更新状态
...
// 如果有执行渲染通知就更新状态并通知执行
...
if (readyToDraw()) {
// 没有GL Context就创建一个Context
...
// 有GL Surface就更新一些数据和状态
...
} else {
// 如果有finishDrawingRunnable就执行
...
}
}
} // 同步结束
// 如果内层循环有事件就执行event
...
// 如果要创建EGL Surface就创建
...
// 如果没有GL10对象,就创建GL10对象
...
// 如果有 EGL Context 就调用 onSurfaceCreated
if (createEglContext) {
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
}
createEglContext = false;
}
// 如果 size 改变了就调用 onSurfaceChanged
if (sizeChanged) {
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
view.mRenderer.onSurfaceChanged(gl, w, h);
}
sizeChanged = false;
}
// 调用 onDrawFrame 方法
GLSurfaceView view = mGLSurfaceViewWeakRef.get();
if (view != null) {
view.mRenderer.onDrawFrame(gl);
// 如果 finishDrawingRunnable 不为空,就执行
...
}
// 交换EGL Display 和 Surface 的缓冲区,这里才是真正的绘制
// 具体交换代码:mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)
int swapError = mEglHelper.swap();
...
}
}
通过上面的源代码,我们知道了为什么我们Renderer里面三个方法的调用顺序,还知道了真正绘制的部分不是发生在onDrawFrame中。