OpenGL ES (二)EGL介绍和使用

OpenGL ES学习系列文章:
上一篇:OpenGL ES (一)EGL介绍和使用
下一篇:OpenGL ES (三)着色器和程序

一、EGL简介

与OpenGl ES一样,EGL同样是由Khronos Group所提供的一个与平台无关的API。

OpenGL作为一个图形化API,允许我们操作GPU以绘制图形,但是当涉及到本地的窗口时,就需要一个与平台无关的API来与之进行交互,EGL应运而生,承担起OpenGl和原生窗口系统之间桥梁的作用。

二、EGL的功能

EGL API作为一套与OpenGL ES各个版本相互独立的API,其作用主要是管理绘图表面。EGL提供以下机制:

  1. 与设备的原生窗口系统通信
  2. 查询绘图表面的可用类型和配置
  3. 创建绘图表面
  4. 在OpenGL ES3.0或其他渲染API之间同步渲染
  5. 管理纹理贴图等渲染资源

三、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) // 次版本号
  1. display默认为EGL_DEFAULT_DISPLAY,即返回与默认原生窗口的连接
  2. 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实现进行一次刷新,所以这一调用对于基于图块的架构代价很高。

猜你喜欢

转载自blog.csdn.net/mataojie/article/details/115357615