三、OpenGL ES 2.0之渲染器设置源码分析

503 阅读3分钟

本文是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中。