在Android平台上,虽说系统自带的GLSurfaceView这个控件中已经帮我们创建好EGL环境了,一般来说使用GLSurfaceView就可以满足我们的需求了。
而当我们用不着GLSurfaceView的时候,那又如何自己创建EGL环境呢?因此就来探究下 EGL环境的创建。
在Java层,EGL封装了两套框架
区别如下:
EGL14是在Android 4.2(API 17)引入的,所以API 17以下的版本不支持EGL14。
EGL10不支持OpenGL ES 2.x,因此在EGL10中某些相关常量参数只能用手写硬编码代替,例如EGL14.EGL_CONTEXT_CLIENT_VERSION以及EGL14.EGL_OPENGL_ES2_BIT等等。
流程如下:
所谓显示设备,也就是我们OpenGL渲染的目标,也可以说就是我们的屏幕,而EGLDisplay是一个封装了物理屏幕的数据类型。
一般来说我们都需要对获取的结果进行验证,如果没获取到抛出异常即可。
//获取默认的显示设备,渲染的目标
mEglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEglDisplay == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("egl no display!");
}
//初始化显示设备,主次版本号
int[] version = new int[2];
if (!EGL14.eglInitialize(mEglDisplay, version, 0, version, 1)){
mEglDisplay = null;
throw new RuntimeException("eglInitialize error");
}
当获取到EGLDisplay后,我们需要指定一些配置项,例如色彩格式、像素格式、RGBA的表示以及SurfaceType等,实际上也就是指FrameBuffer的配置参数。
EGL的参数配置在数组中以 id,value 依次存放,对于个别的属性可以只有 id 没有 value ,结尾需要用EGL_NONE标识。
eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size,EGLint *num_config)
attrib_list:配置参数列表
configs:返回输出的EGLConfigs数据,可能有多个;
config_size:表示最多需要输出多少个EGLConfig;
num_config:表示满足配置参数的EGLConfig的个数。
//配置输出的格式 也就是制定FrameBuffer的配置参数
int[] attrList = {
EGL14.EGL_RED_SIZE,8,//颜色缓冲区中红色的位数
EGL14.EGL_GREEN_SIZE,8,
EGL14.EGL_BLUE_SIZE,8,
EGL14.EGL_ALPHA_SIZE,8,
EGL14.EGL_RENDERABLE_TYPE,EGL14.EGL_OPENGL_ES2_BIT,//渲染窗口支持的布局组成
EGL14.EGL_SURFACE_TYPE,EGL14.EGL_PBUFFER_BIT, //egl支持的窗口类型
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
if (!EGL14.eglChooseConfig(mEglDisplay, attrList, 0, configs, 0, configs.length, numConfigs, 0)) {
throw new RuntimeException("unable to find RGB888 ES2 EGL config");
}
一个线程对应一个EGLContext,如果需要共享资源,则可在创建的时候设置即可。
int[] attriList = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
//第三个参数即为需要共享的上下文对象,资源包括纹理、FrameBuffer以及其他的Buffer等资源。
mEglContext = EGL14.eglCreateContext(mEglDisplay, configs[0], EGL14.EGL_NO_CONTEXT, attriList, 0);
checkEglError("eglCreateContext");
if (mEglContext == null) {
throw new RuntimeException("context is null");
}
最后则需要创建一个GLSurface,它的作用就是把我们的EGL和我们渲染的目标连接起来。
而我们的 EGLSurface也就是要给FrameBuffer,可以通过如下俩种方式创建。
EGL14.eglCreatePbufferSurface:创建离屏的Surface
EGL14.eglCreateWindowSurface:创建可以实际显示的Surface
//创建surface 离屏
int[] surfaceAttribs = {
EGL14.EGL_WIDTH, mWidth,
EGL14.EGL_HEIGHT, mHeight,
EGL14.EGL_NONE
};
mEglSurface = EGL14.eglCreatePbufferSurface(mEglDisplay, configs[0], surfaceAttribs, 0);
checkEglError("eglCreatePbufferSurface");
if (mEglSurface == null) {
throw new RuntimeException("surface is null");
}
//非离屏 需要surface承载
mEglSurfaceEncoder = EGL14.eglCreateWindowSurface(mEglDisplay, configEncoder,surface,new int[]{EGL14.EGL_NONE}, 0);
checkEglError("eglCreatePbufferSurface");
if (mEglSurface == null) {
throw new RuntimeException("surface is null");
}
EGL在初始化时默认设置的是双缓冲模式,也就是两份FrameBuffer;
一个用于绘制图像,一个用于显示图像,每次绘制完一帧都需要交换一次缓冲。
因此我们需要在OpenGL ES绘制完毕后,调用如下进行交换。
EGL14.eglSwapBuffers(mEglDisplay, mEglSurfaceEncoder);