前言
这篇文章就带着大家简单过一下Android的GLSurfaceView
源码的一些主要的处理流程。
GLSurfaceView怎么用
在开始分析源码前,非常有必要先看看GLSurfaceView的基本使用方法:
mGLView= (GLSurfaceView) findViewById(R.id.gl_view);
mGLView.setEGLContextClientVersion(2);
//在setRenderer之前,可以调用以下方法来进行EGL设置
//mGLView.setEGLConfigChooser(); //颜色、深度、模板等等设置
//mGLView.setEGLWindowSurfaceFactory(); //窗口设置
//mGLView.setEGLContextFactory(); //EGLContext设置
//设置渲染器,渲染主要就是由渲染器来决定
mGLView.setRenderer(new GLSurfaceView.Renderer(){
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//todo surface被创建后需要做的处理
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//todo 渲染窗口大小发生改变的处理
}
@Override
public void onDrawFrame(GL10 gl) {
//todo 执行渲染工作
}
});
/*渲染方式,RENDERMODE_WHEN_DIRTY表示被动渲染,RENDERMODE_CONTINUOUSLY表示持续渲染*/
mGLView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
源码分析
入口方法
我们先从setRenderer(Renderer renderer)
这个入口开始。
它主要做了两件事:
1、检查环境和变量同步配置
//检测环境
checkRenderThreadState();
//同步配置项,如果没有设置取默认项(懒加载模式)
if (mEGLConfigChooser == null) {
mEGLConfigChooser = new SimpleEGLConfigChooser(true);
}
if (mEGLContextFactory == null) {
mEGLContextFactory = new DefaultContextFactory();
}
if (mEGLWindowSurfaceFactory == null) {
mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
}
mRenderer = renderer;
函数checkRenderThreadState()
检查了mRenderer是否已经存在,存在则抛出异常;换句话说,我们不能在同一个GLSurfaceView
调用多次setRenderer(Renderer renderer)
,会挂!
mEGLConfigChooser
、mEGLContextFactory
、mEGLWindowSurfaceFactory
是用户在setRenderer之前,可以调用相关方法来进行EGL设置,如果没有设置则采用默认实现。
mEGLConfigChooser
主要是指定了OpenGL ES一些颜色深度、缓存区深度的一些设定。
mEGLContextFactory
主要是提供了创建和销毁EGL Context上下文的一些处理。
mEGLWindowSurfaceFactory
主要是提供了创建和销毁EGLSurface的一些处理。
2、启动一个GL线程
mGLThread = new GLThread(mThisWeakRef);
mGLThread.start();
GLThread
就是我们一直所说的GL线程,主要是用于与OpenGL ES环境进行交互的线程。
入参mThisWeakRef
是一个弱引用,指向了GLSurfaceView
本身。
GL线程
接下来我们需要分析一下GLThread
这个GL线程,跟进到GLThread
的run()
方法。
我们发现run()
里面有一个方法guardedRun()
,这个也就是GL线程的主要逻辑函数,由两个while(true)
循环组成。
public void run() {
try {
//最最最重要的逻辑
guardedRun();
}
catch (InterruptedException e) {
}
finally {
}
}
转进去查看guardedRun()
的代码。
我们先来查看初始化创建相关的代码。
在代码开头创建了一个EglHelper
,EglHelper
是一个封装了一些EGL通用操作的工具类。
mEglHelper = new EglHelper(mGLSurfaceViewWeakRef);
首次循环,逻辑会走到这个位置:
if (! mHaveEglContext) {
if (askedToReleaseEglContext) {
askedToReleaseEglContext = false;
} else {
try {
//进行OpenGL ES环境初始化
mEglHelper.start();
} catch (RuntimeException t) {
}
mHaveEglContext = true;
createEglContext = true;
}
}
流程很明显,将会调用EglHelper.start()
进行OpenGL ES环境的初始化。
然后将标示量createEglContext
设置为true。
查看一下EglHelper.start()
的实现:
public void start() {
//获取一个EGL实例
mEgl = (EGL10) EGLContext.getEGL();
//获取显示设备
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
//检测EglDisplay是否正常
if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
throw new RuntimeException("eglGetDisplay failed");
}
//初始化EGL的内部数据结构,返回EGL实现的主版本号和次版本号。
int[] version = new int[2];
if(!mEgl.eglInitialize(mEglDisplay, version)) {
throw new RuntimeException