OpenGL ES学习系列文章:
上一篇:OpenGL ES (一)EGL介绍和使用
下一篇:OpenGL ES (三)着色器和程序
EGL介绍和使用
一、EGL简介
与OpenGl ES一样,EGL同样是由Khronos Group所提供的一个与平台无关的API。
OpenGL作为一个图形化API,允许我们操作GPU以绘制图形,但是当涉及到本地的窗口时,就需要一个与平台无关的API来与之进行交互,EGL应运而生,承担起OpenGl和原生窗口系统之间桥梁的作用。
二、EGL的功能
EGL API作为一套与OpenGL ES各个版本相互独立的API,其作用主要是管理绘图表面。EGL提供以下机制:
- 与设备的原生窗口系统通信
- 查询绘图表面的可用类型和配置
- 创建绘图表面
- 在OpenGL ES3.0或其他渲染API之间同步渲染
- 管理纹理贴图等渲染资源
三、EGL使用
1、创建OpenGL ES与原生窗口系统的连接
EGLDisplay eglGetDisplay(EGLNativeDisplayType displayId);
eglGetDisplay为原生窗口系统displayId获取一个EGL display连接,
在OpenGL ES与本地窗口系统之间架起了一座沟通的桥梁
2、初始化EGL连接
与窗口系统的连接建立以后,随后要进行EGL的初始化
EGLBoolean eglInitialize(EGLDisplay display, // 要进行初始化的EGL连接,即上一部中的返回值
EGLint *majorVersion, // 主版本号
EGLint *minorVersion) // 次版本号
- display默认为EGL_DEFAULT_DISPLAY,即返回与默认原生窗口的连接
- majorVersion 和minorVersion 会在调用后返回版本号,例如EGL1.0, majorVersion返回1, minorVersion返回0
注:eglInitialize会初始化EGL内部数据,比如分配显存等;如果初始化失败,可以用eglGetError()
函数获取具体的错误类型,其他EGL相关的函数,也都可以使用eglGetError()来获取错误的类型。
EGL中大部分函数执行成功时返回EGL_TRUE,失败时返回EGL_FALSE。eglGetError()函数可以指示故障原因
常见错误返回值(使用eglGetError获取):
- EGL_BAD_DISPLAY :表示参数display不是一个EGL diaplay 连接
- EGL_NOT_INITIALIZED :表示传入的display不可以被初始化
创建连接与初始化实例:
EGLint majorVersion;
EGLint minorVersion;
EGLDisplay display;
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
// 与本地窗口系统创建连接失败
}
if (!eglInitialize(display, &majorVersion, &minorVersion)) {
// 初始化EGL连接失败
}
3.选择Surface配置
EGL初始化成功之后,就需要确定可用渲染表面的类型和配置。
一般来说,选择配置的方法有两种:
-
方法一:查询所有可行的配置(eglGetConfigs),再让EGL从中找出最优选择(eglGetConfigAttrib)
-
方法二:指定一组需求,然后再让EGL推荐(eglChooseChofig)最佳配置。
下面分写介绍这两种方法的使用。
方法一:从所有可行配置中找到最优配置
EGLBoolean eglGetConfigs(
EGLDisplay display, //指定EGL显示连接
EGLConfig * configs, //指定一组config
EGLint config_size, //指定配置列表的大小
EGLint * num_config); //实际匹配的配置总数
参数configs将包含在你平台上所有有效的EGLframebuffer配置列表。支持的总数通过num_config返回,而实际返回的 configs 的配置个数依赖于程序传入的 config_size,如果 config_size < num_config,则不是所有可行的config都会被返回。如果想要获取系统支持的所有配置信息,最好的方法就是先给eglGetConfigs传一个null的configs参数,这样执行后就会得到系统支持的配置总数num_config,然后使用num_config来给configs分配合适的内存大小,再用得到的configs来调用eglGetConfig。
示例:
EGLConfig *configs_list;
EGLint num_configs;
m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if( m_eglDisplay == EGL_NO_DISPLAY || eglGetError() != EGL_SUCCESS ){
return FALSE;
}
if( eglInitialize( m_eglDisplay, NULL, NULL ) == EGL_FALSE || eglGetError() != EGL_SUCCESS ){
return FALSE;
}
// find out how many configurations are supported
if(eglGetConfigs(m_eglDisplay, NULL, 0, &num_configs)== EGL_FALSE || eglGetError() != EGL_SUCCESS){
return FALSE;
}
configs_list = malloc(num_configs * sizeof(EGLConfig));
if (configs_list == (EGLConfig *)0){
return FALSE;
}
// Get Configurations
if( eglGetConfigs( m_eglDisplay, configs_list, num_configs, &num_configs)== EGL_FALSE || eglGetError() != EGL_SUCCESS ){
return FALSE;
}
由于当前平台的限制,通常只有很少的配置可用。系统支持的配置通常是利用系统硬件提供最好的性能。当你移植游戏到多个平台,它们的EGL 配置可能会有细微的差别,我们希望作为通用的移植问题来直接处理这些问题。
方法二:指定一组需求,让EGL推荐最佳配置
EGLBoolean eglChooseConfig(
EGLDisplay display, // 指定EGL显示连接
const EGLint *attribList, // 指定configs匹配的属性列表
EGLConfig *configs, // 指定配置列表
EGLint maxReturnConfigs, // 指定配置的大小
EGLint *numConfigs) // 指定返回的配置大小
glChooseConfig()函数将适配一个你所期望的配置,并且尽可能接近一个有效的系统配置。参数 attribList 指定了选择配置时需要参照的属性。参数 configs 将返回一个按照 attribList 排序的平台有效的所有 EGL framebuffer 配置列表。参数 config_size 指定了可以返回到 configs 的总配置个数。参数 num_config 返回了实际匹配的配置总数。
示例:
EGLint attrs[3] = {
EGL_DEPTH_SIZE, 16, EGL_NONE };
EGLint num_configs;
EGLConfigs *configs_list;
// Get the display device
if ((eglDisplay = eglGetDisplay(EGL_NO_DISPLAY)) == EGL_NO_DISPLAY) {
return eglGetError();
}
// Initialize the display
if (eglInitialize(eglDisplay, NULL, NULL) == EGL_FALSE) {
return eglGetError();
}
// Obtain the total number of configurations that match
if (eglChooseConfig(eglDisplay, attrs, NULL, 0, &num_configs) == EGL_FALSE) {
return eglGetError();
}
configs_list = malloc(num_configs * sizeof(EGLConfig));
if (configs_list == (EGLConfig *)0){
return eglGetError();
}
// Obtain the first configuration with a depth buffer of 16 bits
if (!eglChooseConfig(eglDisplay, attrs, &configs_list, num_configs, &num_configs)) {
return eglGetError();
}
如果找到了多个配置,则基于属性的优先级进行排序,下表显示了基于属性值的用来选择和排序的顺序(不需过多关注),也包括了 EGL 规范中所有 EGL 配置属性及其默认值。
属性 | 数据类型 | 默认值 | 排序优先级 | 选择排序 |
---|---|---|---|---|
EGL_BUFFER_SIZE | int | 0 | 3 | Smaller value |
EGL_RED_SIZE | int | 0 | 2 | Larger value |
EGL_GREEN_SIZE | int | 0 | 2 | Larger value |
EGL_BLUE_SIZE | int | 0 | 2 | Larger value |
EGL_ALPHA_SIZE | int | 0 | 2 | Larger value |
EGL_CONFIG_CAVET | enum | EGL_DONT_CARE | 1(first) | Exact value |
EGL_CONFIG_ID | int | EGL_DONT_CARE | 9 | Exact value |
EGL_DEPTH_SIZE | int | 0 | 6 | Smaller value |
EGL_LEVEL | int | 0 | - | Equal value |
EGL_NATIVE_RENDERABLE | Boolean | EGL_DONT_CARE | - | Exact value |
EGL_NATIVE_VISUAL_TYPE | int | EGL_DONT_CARE | 8 | Exact value |
EGL_SAMPLE_BUFFERS | int | 0 | 4 | Smaller value |
EGL_SAMPLES | int | 0 | 5 | Smaller value |
EGL_STENCIL_SIZE | int | 0 | 7 | Smaller value |
EGL_SURFACE_TYPE | bitmask | EGL_WINDOW_BIT | - | Mask value |
EGL_TRANSPARENT_TYPE | enum | EGL_NONE | - | Exact value |
EGL_TRANSPARENT_RED_VALUE | int | EGL_DONT_CARE | - | Exact value |
EGL_TRANSPARENT_GREEN_VALUE | int | EGL_DONT_CARE | - | Exact value |
EGL_TRANSPARENT_BLUE_VALUE | int | EGL_DONT_CARE | - | Exact value |
4. 创建渲染区域Surface
当有了符合条件的 EGLConfig 后,就可以通过 eglCreateWindowSurface 函数创建渲染区域。
4.1 创建屏幕上的渲染区域:EGL窗口
创建surface
EGLSurface eglCreateWindowSurface(
EGLDisplay display, //对应的EGL display连接
EGLConfig config, //EGL frame buffer配置,定义了可用于Surface的frame buffer资源
NativeWindowType native_window, //原生窗口
EGLint const * attrib_list); // attrib_list为Window Surface属性列表,可以为NULL,成功时返回新创建的EGLSurface,失败时返回EGL_NO_SURFACE.
// 可能出现的错误:
// EGL_BAD_DISPLAY: 连接不是一个EGL display连接
//EGL_NOT_INITIALIZED: EGL没有初始化
//EGL_BAD_CONFIG: EGL frame buffer配置无效
//EGL_BAD_NATIVE_WINDOW: native window不是与display相同平台的有效Native Window
//EGL_BAD_ATTRIBUTE: attrib_list包含无效参数,或者参数未被识别,或者参数越界
//EGL_BAD_ALLOC:已经有一个与native window关联的Surface,或者无法为新的EGL窗口分配资源,
//EGL_BAD_MATCH:本机窗口的像素格式与配置所需的颜色缓冲区的格式、类型和大小不一致
4.2 其他操作surface方法
- 销毁surface:
EGLAPI EGLBoolean EGLAPIENTRY eglDestroySurface(EGLDisplay display, EGLSurface surface);
与eglCreateWindowSurface对应的,还有一个eglDestroySurface,用来销毁surface,display为对应的Display,surface为将要销毁的Surface, 如果任何其它线程都没有使用这个Surface时,Surface将被立即销毁,否则要等到这个Surface不被任何线程使用时才销毁,另外,对于一个PBuffer Surface来说,其资源要等到绑定到纹理对象的颜色缓冲区释放后才被销毁。成功时返回EGL_TRUE,失败时返回EGL_FALSE,可能的错误为EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_SURFACE。
- 获取surface信息
EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurface(
EGLDisplay display,
EGLSurface surface,
EGLint attribute,
EGLint *value);
eglQuerySurface用于获取Surface信息,display为对应的Display,surface待查询的Surface,attribute为待查询的Surface属性,value用于返回Surface属性值,成功时返回EGL_TRUE,失败时返回EGL_FALSE,可能的错误为EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_SURFACE、EGL_BAD_ATTRIBUTE。attribute取值可以是:
EGL_CONFIG_ID EGL_HEIGHT EGL_HORIZONTAL_RESOLUTION EGL_LARGEST_PBUFFER EGL_MIPMAP_LEVEL EGL_MIPMAP_TEXTURE EGL_MULTISAMPLE_RESOLVE EGL_PIXEL_ASPECT_RATIO EGL_RENDER_BUFFER EGL_SWAP_BEHAVIOR EGL_TEXTURE_FORMAT EGL_TEXTURE_TARGET EGL_VERTICAL_RESOLUTION EGL_WIDTH
- 设置surface属性
EGLAPI EGLBoolean EGLAPIENTRY eglSurfaceAttrib(
EGLDisplay display,
EGLSurface surface,
EGLint attribute,
EGLint value);
eglSurfaceAttrib用于设置Surface属性,display为对应的Display,surface为要设置的Surface,attribute为要设置的Surface属性,value为要设置的Surface属性值,成功时返回EGL_TRUE,失败时返回EGL_FALSE,可能的错误为EGL_BAD_DISPLAY、EGL_BAD_MATCH、EGL_NOT_INITIALIZED、EGL_BAD_SURFACE、EGL_BAD_ATTRIBUTE。attribute取值可以是:
EGL_MIPMAP_LEVEL:默认值为0。 EGL_MULTISAMPLE_RESOLVE:EGL_MULTISAMPLE_RESOLVE_DEFAULT或>EGL_MULTISAMPLE_RESOLVE_BOX,默认值为前者。 EGL_SWAP_BEHAVIOR:EGL_BUFFER_PRESERVED或EGL_BUFFER_DESTROYED,默认值由实现而定。
- 创建屏幕外的渲染区域
EGLSurface eglCreatePbufferSurface(
EGLDisplay display, // 指定EGL显示连接
EGLConfig config, // 指定配置
const EGLint *attribList) // 指定像素缓冲区属性
OpenGL ES 3.0不仅可以在屏幕上的窗口渲染,还可以渲染称作pbuffer的不可见屏幕外表面。PBuffer Surface是不同于Window Surface的另一种EGLSurface,eglCreatePbufferSurface用于创建off-screen的pixel buffer Surface,display为对应的Display,config为frame buffer配置,attribList为PBuffer属性列表,可以为NULL,成功时返回新创建的EGLSurface,失败时返回EGL_NO_SURFACE,可能的错误为EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_CONFIG、EGL_BAD_ATTRIBUTE、EGL_BAD_ALLOC、EGL_BAD_MATCH。attribList的属性可以是:
EGL_HEIGHT:默认值为0。 EGL_LARGEST_PBUFFER:默认值为EGL_FALSE。 EGL_MIPMAP_TEXTURE:默认值为EGL_FALSE。 EGL_TEXTURE_FORMAT:默认值为EGL_NO_TEXTURE,还可以选择EGL_TEXTURE_RGB或>EGL_TEXTURE_RGBA。 EGL_TEXTURE_TARGET:默认值为EGL_NO_TEXTURE,还可以选择EGL_TEXTURE_2D。 EGL_VG_ALPHA_FORMAT:只适用于OpenVG,默认值为EGL_VG_ALPHA_FORMAT_NONPRE,>还可以选择EGL_VG_ALPHA_FORMAT_PRE。 EGL_VG_COLORSPACE:只适用于OpenVG,默认值为EGL_VG_COLORSPACE_sRGB,还可以选>择EGL_VG_COLORSPACE_LINEAR。 EGL_WIDTH:默认值为0。
PBuffer Surface与Window Surface
除了可以用OpenGL ES 3.0在屏幕上的窗口渲染之外,还可以渲染称作PBuffer(像素缓冲区Pixel Buffer的缩写)的不可见屏幕外表面,和窗口一样,PBuffer可以利用OpenGL ES 3.0中的任何硬件加速,PBuffer最常用于生成纹理贴图,如果想要做的是渲染到一个纹理,那么建议使用帧缓冲区对象(FBO)代替PBuffer,因为帧缓冲区更高效,不过在某些FBO无法使用的情况下,PBuffer仍然有用,例如用OpenGL ES在屏幕外表面上渲染,然后将其作为其它API(如OpenVG)中的纹理。另外,EGLSurface还有个Pixmap Surface,简单总结一下三者的特点。window是on-screen的,pbuffer和pixmap是off-screen的,window绑定到了NativeWindow,pixmap绑定到了NativePixmap,pbuffer没有任何本地绑定,window是双缓冲区的,pbuffer和pixmap是单缓冲区的,window默认在back buffer渲染,通过eglSwapBuffers交换到屏幕上显示,pbuffer在显存中分配,由EGL_HEIGHT和EGL_WIDTH指定大小,常用作纹理数据,pixmap绑定到本地的像素缓冲区,这个缓冲区可以被其它API使用。创建不同的EGLSurface时,需要在EGLConfig中配置EGL_SURFACE_TYPE,window、pbuffer、pixmap分别对应于EGL_WINDOW_BIT、EGL_PBUFFER_BIT、EGL_PIXMAP_BUFFER。
5. 创建渲染上下文
创建渲染上下文
EGLAPI EGLContext EGLAPIENTRY eglCreateContext(
EGLDisplay display, EGLConfig config,
EGLContext share_context,
const EGLint *attribList);
eglCreateContext用于创建EGL渲染Context,display为对应的Display,config为frame buffer配置,share_context为其它的共享Context,可以设置为EGL_NO_CONTEXT,attribList为Context属性列表,成功时返回新创建的EGLContext,失败时返回EGL_NO_CONTEXT,可能的错误为EGL_BAD_MATCH、EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_CONFIG、EGL_BAD_CONTEXT、EGL_BAD_ATTRIBUTE、EGL_BAD_ALLOC。attrib_list属性目前只有EGL_CONTEXT_CLIENT_VERSION,整数值,指定OpenGL ES版本,默认值为1,还可以是2、3等其它版本值,创建OpenGL ES Context时设置这个属性,也就是说渲染API为EGL_OPENGL_ES_API时才设置这个属性,为此还要设置或查询渲染API,使用如下两个函数。
销毁渲染上下文
EGLAPI EGLBoolean EGLAPIENTRY eglDestroyContext(EGLDisplay display, EGLContext context);
eglDestroyContext用于销毁渲染Context,dpy为对应的Display,ctx为要销毁的Context,如果有其它线程使用这个Context时就要等到不使用时再销毁,否则立即销毁,成功时返回EGL_TRUE,失败时返回EGL_FALSE,可能的错误为EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_CONTEXT。
获取Context信息
EGLAPI EGLBoolean EGLAPIENTRY eglQueryContext(
EGLDisplay display,
EGLContext context,
EGLint attribute,
EGLint *value);
eglQueryContext用于获取Context信息,dpy为对应的Display,ctx为要查询的Context,attribute为要查询的Context属性,value返回查询的Context属性值,成功时返回EGL_TRUE,失败时返回EGL_FALSE,可能的错误为EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_CONTEXT、EGL_BAD_ATTRIBUTE。attribute取值可以是:
EGL_CONFIG_ID EGL_CONTEXT_CLIENT_TYPE EGL_CONTEXT_CLIENT_VERSION EGL_RENDER_BUFFER
eglMakeCurrent
EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent(
EGLDisplay display, EGLSurface draw,
EGLSurface read, EGLContext context);
创建了Surface和Context之后,因为可能有多个Surface和Context,所以需要通过eglMakeCurrent绑定Context到Surface,display为对应的Display,draw用于绘制,read用于读,ctx为要绑定的Context,成功是返回EGL_TRUE,失败时返回EGL_FALSE,可能的错误为EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_SURFACE、EGL_BAD_CONTEXT、EGL_BAD_MATCH、EGL_BAD_ACCESS 、EGL_BAD_NATIVE_PIXMAP、EGL_BAD_NATIVE_WINDOW、EGL_BAD_CURRENT_SURFACE、EGL_BAD_ALLOC、EGL_CONTEXT_LOST。因为EGL规范要求eglMakeCurrent实现进行一次刷新,所以这一调用对于基于图块的架构代价很高。