EGL介绍
EGL 是 OpenGL ES 和底层 Native 平台视窗系统之间的接口。OpenGL ES 本质上是一个图形渲染管线的状态机,而 EGL 则是用于监控这些状态以及维护 Frame buffer 和其他渲染 Surface 的外部层。EGL提供如下机制:
- 与设备的原生窗口系统通信
- 查询绘图表面的可用类型和配置
- 创建绘图表面
- 在OpenGL ES 和其他图形渲染API之间同步渲染
- 管理纹理贴图等渲染资源
OpenGL ES的javax.microedition.khronos.openges包定义了平台无关的GL绘制指令,EGL(javax.microedition.khronos.egl)则定义了控制dispays,contexts以及surfaces的统一的平台接口.
![[8.Attachments/image/95ecf7e0c1fdc7edc11479a3a5cf8544_MD5.gif]]
EGL 是 OpenGL ES 渲染 API 和本地窗口系统(native platform window system)之间的一个中间接口层,它主要由系统制造商实现。
为了让OpenGL ES能够绘制在当前设备上,我们需要EGL作为OpenGL ES与设备的桥梁。
![[8.Attachments/image/134d0f8a32e58360190215c7ae7b789e_MD5.jpg]]
- Display(EGLDisplay)是对实际显示设备的抽象
- Surface(EGLSurface)是对用来存储图像的内存区域FrameBuffer的抽象,包括Color Buffer, Stencil Buffer, Depth Buffer.
- Context(EGLContext)存储OpenGLES绘图的一些状态信息
使用EGL绘制的一般步骤:
- 获取EGLDisplay对象
- 初始化与EGLDisplay之间的连接
- 获取EGLConfig实例
- 创建EGLContext实例
- 创建EGLSurface实例
- 连接EGLContext和EGLSurface
- 使用GL指令绘制图形
- 断开并释放与EGLSurface关联的EGLContext对象
- 删除EGLSurface对象
- 删除EGLContext对象
终止与EGLDisplay之间的连接
EGL类型
EGLBoolean
EGL中的布尔类型。 typedef unsigned int EGLBoolean;
EGLDisplay
不透明类型,封装了与底层系统的交互,用于充当与原生窗口之间的接口。 typedef void * EGLDisplay;
EGLint
EGL整数类型。 typedef int32_t EGLint;
EGLNativeDisplayType
用于匹配原生窗口系统的显示类型。 typedef void * EGLNativeDisplayType;
EGL常量
布尔值
- EGL_FALSE:条件为假
EGL_TRUE:条件为真
创建窗口的属性
- EGL_RENDER_BUFFER:指定渲染所用的缓冲区
错误代码
- EGL_SUCCESS:没有错误
- EGL_NOT_INITIALIZED:没有初始化
- EGL_BAD_ACCESS:数据访问失败
- EGL_BAD_ALLOC:内存分配失败
- EGL_BAD_ATTRIBUTE:错误的属性
- EGL_BAD_CONFIG:错误的配置
- EGL_BAD_CONTEXT:错误的上下文
- EGL_BAD_CURRENT_SURFACE:当前Surface对象错误
- EGL_BAD_DISPLAY:错误的设备对象
- EGL_BAD_MATCH:无法匹配
- EGL_BAD_NATIVE_PIXMAP:错误的像素图
- EGL_BAD_NATIVE_WINDOW:错误的本地窗口对象
- EGL_BAD_PARAMETER:错误的参数
- EGL_BAD_SURFACE:错误的Surface对象
EGL_CONTEXT_LOST:上下文丢失
配置属性
- EGL_ALPHA_SIZE:颜色缓冲区中的透明度分量的位数
- EGL_ALPHA_MASK_SIZE:透明度掩码位数
- EGL_BUFFER_SIZE:颜色缓冲区中颜色分量的位数
- EGL_BLUE_SIZE:颜色缓冲区中的蓝色分量的位数
- EGL_BIND_TO_TEXTURE_RGB:是否可以绑定RGB纹理
- EGL_BIND_TO_TEXTURE_RGBA:是否可以绑定EGBA纹理
- EGL_CONFIG_CAVEAT:注意事项
- EGL_COLOR_BUFFER_TYPE:颜色缓冲区类型
- EGL_CONFORMANT:创建的上下文是否兼容
- EGL_CONFIG_ID:配置信息ID
- EGL_DEPTH_SIZE:深度缓冲区位数
- EGL_GREEN_SIZE:颜色缓冲区中的绿色分量的位数
- EGL_LEVEL:帧缓冲区级别
- EGL_LUMINANCE_SIZE:颜色缓冲区亮度位数
- EGL_MAX_PBUFFER_HEIGHT:Pbuffer的最大高度
- EGL_MAX_PBUFFER_PIXELS:Pbuffer的最大尺寸
- EGL_MAX_PBUFFER_WIDTH:Pbuffer的最大宽度
- EGL_MATCH_NATIVE_PIXMAP
- EGL_MIN_SWAP_INTERVAL:最小缓冲区交换间隔
- EGL_MAX_SWAP_INTERVAL:最大缓冲区交换间隔
- EGL_NATIVE_RENDERABLE:是否可用原生渲染库渲染
- EGL_NONE
- EGL_NATIVE_VISUAL_ID:原生窗口系统的可视ID
- EGL_NATIVE_VISUAL_TYPE:原生窗口系统的可视类型
- EGL_RED_SIZE:颜色缓冲区中的红色分量的位数
- EGL_RENDERABLE_TYPE:可渲染接口类型
- EGL_STENCIL_SIZE:模板缓冲区位数
- EGL_SAMPLES:每个像素的样本数量
- EGL_SAMPLE_BUFFERS:可用多重采样缓冲区数量
- EGL_SURFACE_TYPE:EGL表面类型
- EGL_TRANSPARENT_TYPE:透明度类型
- EGL_TRANSPARENT_BLUE_VALUE:透明的蓝色值
- EGL_TRANSPARENT_GREEN_VALUE:透明的绿色值
EGL_TRANSPARENT_RED_VALUE:透明的红色值
显示设备对象
EGL_NO_DISPLAY:当前无可用设备
显示设备类型
- EGL_DEFAULT_DISPLAY:默认为当前使用设备
EGL函数
eglChooseConfig
在初始化EGL时,我们需要列出并让EGL选择最合适的配置。
EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config);
- dpy:已连接的设备
- attrib_list:传入的配置信息数组
- configs:保存返回的配置信息的数组
- config_size:传入的配置数组的长度
num_config:保存返回的配置信息的数组的长度
EGLConfig config; EGLint numConfigs = 0; EGLint attribList[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 16, EGL_NONE }; if (!eglChooseConfig(context->eglDisplay, attribList, &config, 1, &numConfigs)) { return GL_FALSE; }
eglCreateContext
渲染上下文是OpenGL ES的内部数据结构,包含操作所需的所有状态信息。例如程序中使用的顶点着色器或者片元着色器的引用。OpenGL ES必须有一个可用的上下文才能绘图。使用下面的函数可以创建一个上下文:
EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, const EGLint *attribList);
- display:指定显示连接
- config:指定配置对象
- shareContext:允许多个EGL上下文共享特定的数据,EGL_NO_CONTEXT参数表示没有共享
- attribList:指定创建上下文使用的属性列表
return:创建的上下文对象
EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE}; context->eglContext = eglCreateContext(context->eglDisplay, config, EGL_NO_CONTEXT, contextAttribs); if (context->eglContext == EGL_NO_CONTEXT) { return GL_FALSE; }
eglCreateWindowSurface
一旦我们有了符合渲染需求的EGLConfig,就为窗口创建做好了准备。调用如下函数可以创建一个窗口。
EGLSurface eglCreateWindowSurface(EGLDisplay display, EGLConfig config, EGLNativeWindowType window,const EGLint *attribList);
- display:已连接的设备对象
- config:指定的配置对象
- window:指定原生窗口对象
- attriList:指定窗口的属性列表
return:EGL渲染区域对象
这个函数以我们到原生显示管理器的连接和前一步获得的EGLConfig为参数。此外,它需要原生窗口系统事先创建一个窗口。因为EGL是许多不同窗口系统和OpenGL ES之间的软件接口层。最后这个函数需要一个属性列表;但是,这个列表中的属性与参数属性不完全相同,并且额外使用到了创建窗口的属性。该函数在多种情况下都有可能失败。context->eglSurface = eglCreateWindowSurface(context->eglDisplay, config, context->nativeWindow, NULL); if (context->eglSurface == EGL_NO_SURFACE) { EGLint error; while((error = eglGetError()) != EGL_SUCCESS){ switch(error) { case EGL_BAD_MATCH:{ //提供的原生窗口不匹配或者不支持渲染 } case EGL_BAD_CONFIG:{ //系统不支持该配置 } case EGL_BAD_NATIVE_WINDOW:{ //提供的原生窗口无效 } case EGL_BAD_ALLOC:{ //无法为新的EGL分配资源或者该窗口已经被关联 } } } }
eglGetConfigAttrib
如果我们获获取了一个EGL配置对象,我们可以通过下列函数查询该对象中指定属性的值。
EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value);
- dpy:已连接的设备对象
- config:待查询的配置对象
- attribute:需要查询的参数属性
- value:返回的查询结果
- return:查询结果,如果attribute不是有效属性,则产生一个EGL_BAD_ATTRIBUTE错误。
eglGetConfigs
在初始化EGL之后,我们需要给EGL选择一组配置。
EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config);
- dpy:已连接设备对象
- configs:保存配置信息的列表
- size:configs的长度
- num_config:EGL返回的配置信息数量
- return:查询结果状态
通常情况下,我们有两种方式使用该函数。首先,我们指定configs参数为NULL,此时EGL会查询所有可用的EGLConfigs数量并赋值给num_config,但此时不会有任何其他信息返回。
另外,我们也可以创建一个未初始化的EGLConfig,并作为函数的参数传入。此时,EGL将会查询不超过config_size数量的配置信息存入到configs,并通过num_config返回保存数据的数量。
eglGetDisplay
获得并与可用设备进行连接。
EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);
- display_id:当前需要连接的设备类型
return:已经连接上的设备对象
EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglGetError
EGL中大部分函数在成功时都会返回EGL_TRUE,否则返回EGL_FALSE。但是,我们仅从这个返回值上并不能看出错误原因是什么。如果想要明确的知道EGL的错误代码,应该调用下列函数。
EGLint eglGetError(void);
return:见 [EGL常量-错误代码]
eglInitialize
一般在成功打开设备连接之后需要初始化EGL。初始化过程将会对EGL内部的数据结构进行设置,然后返回EGL的主次版本号。
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);
- dpy:指定EGL设备对象。
- major:设备主版本号。
minor:设备次版本号。
GLint majorVersion; GLint minorVersion; if (!eglInitialize(eglDisplay, &majorVersion, &minorVersion)) { return EGL_FALSE; }
eglMakeCurrent
因为一个应用程序可能创建多个EGLContext用作不同的用途,所以我们需要指定关联特定的EGLContext和渲染表面——这一过程被称为“指定当前上下文”。
EGLBoolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context);
- display:指定EGL显示设备
- draw:指定EGL绘图表面
- read:指定EGL读取表面
- context:指定连接到该表面的渲染上下文
- return:函数时候执行成功
EGL的基本使用步骤
1.首先我们需要知道绘制内容的目标在哪里,EGLDisplayer是一个封装系统屏幕的数据类型,通常通过eglGetDisplay方法来返回EGLDisplay作为OpenGl ES的渲染目标,eglGetDisplay()
if ( (mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)) == EGL14.EGL_NO_DISPLAY) {
throw new RuntimeException("unable to get EGL14 display");
}
2.初始化显示设备,第一参数代表Major版本,第二个代表Minor版本。如果不关心版本号,传0或者null就可以了。初始化与 EGLDisplay 之间的连接:eglInitialize()
if (!EGL14.eglInitialize(mEGLDisplay, 0, 0)) {
throw new RuntimeException("unable to initialize EGL14");
}
3.下面我们进行配置选项,使用eglChooseConfig()方法,Android平台的配置代码如下:
int[] attribList = {
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,
EGL_RECORDABLE_ANDROID, 1,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
numConfigs, 0);
3.接下来我们需要创建OpenGl的上下文环境 EGLContext 实例,这里值得留意的是,OpenGl的任何一条指令都是必须在自己的OpenGl上下文环境中运行,我们可以通过eglCreateContext()方法来构建上下文环境:
int[] attrib_list = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
};
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
attrib_list, 0);
eglCreateContext中的第三个参数可以传入一个EGLContext类型的变量,改变量的意义是可以与正在创建的上下文环境共享OpenGl资源,包括纹理ID,FrameBuffer以及其他Buffer资源。如果没有的话可以填写Null.
4.通过上面四步,获取OpenGl 上下文之后,说明EGL和OpenGl ES端的环境已经搭建完毕,也就是说OpengGl的输出我们可以获取到了。下面的步骤我们讲如何将EGl和设备屏幕连接起来。如果连接呢?当然,这时候我们就要使用EGLSurface了,我们通过EGL库提供eglCreateWindowSurface可以创建一个实际可以显示的surface.当然,如果需要离线的surface,我们可以通过eglCreatePbufferSurface创建。eglCreateWindowSurface()
private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
int[] surfaceAttribs = {
EGL14.EGL_NONE
};
mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface,
surfaceAttribs, 0);
5.通过上面的步骤,EGL的准备工作做好了,一方面我们为OpenGl ES渲染提供了目标及上下文环境,可以接收到OpenGl ES渲染出来的纹理,另一方面我们连接好了设备显示屏(这里指SurfaceView或者TextureView),接下来我们讲解如何在创建好的EGL环境下工作的。首先我们有一点必须要明确,OpenGl ES 的渲染必须新开一个线程,并为该线程绑定显示设备及上下文环境(Context)。因为前面有说过OpenGl指令必须要在其上下文环境中才能执行。所以我们首先要通过 eglMakeCurrent()方法来绑定该线程的显示设备及上下文。
EGL14.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
6.当我们绑定完成之后,我们就可以进行RenderLoop循环了。这里简单说一下,EGL的工作模式是双缓冲模式,其内部有两个FrameBuffer(帧缓冲区,可以理解为一个图像存储区域),当EGL将一个FrameBuffer显示到屏幕上的时候,另一个FrameBuffer就在后台等待OpenGl ES进行渲染输出。知道调用了eglSwapBuffers这条指令的时候,才会把前台的FrameBuffers和后台的FrameBuffer进行交换,这样界面呈现的就是OpenGl ES刚刚渲染的结构了。
mInputSurface.swapBuffers();
7.当然,在所有的操作都执行完之后,我们要销毁资源。特别注意,销毁资源必须在当前线程中进行,不然会报错滴。首先我们销毁显示设备(EGLSurface),然后销毁上下文(EGLContext),停止并释放线程,最后终止与EGLDisplay之间的链接,
EGL14.eglDestroySurface(mEGLDisplay, mEGLSurface);
EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
EGL14.eglReleaseThread();
EGL14.eglTerminate(mEGLDisplay);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。