EGL资料链接
EGL官方网站:https://www.khronos.org/api/egl
EGL API 官方文档:https://registry.khronos.org/EGL/sdk/docs/man/
EGL代码:https://github.com/KhronosGroup/EGL-Registry
EGL 接口介绍:http://www.cnitblog.com/zouzheng/archive/2011/05/30/74326.html
OpenGL 之 EGL 使用实践:https://glumes.com/post/opengl/opengl-egl-usage/
OpenGL ES 2.0 知识串讲(2)――EGL详解:http://geekfaner.com/shineengine/blog3_OpenGLESv2_2.html
EGL 介绍和使用:https://blog.51cto.com/u_15449929/4763699
OpenGL 之 EGL 使用实践:https://blog.51cto.com/u_12127193/5739325?articleABtest=0
OPenGL ES 基础:https://www.codersrc.com/opengl-es-%e5%9f%ba%e7%a1%80
OpenGL ES:EGL接口解析与理解: https://www.jianshu.com/p/1ecd13d465ae
EGL概述
EGL (Embedded-System Graphics Library) 是一个图形库,主要用于在嵌入式系统中管理渲染表面(surface)和 GPU 之间的通信。EGL 定义了一组 API,用于连接 OpenGL ES、OpenVG或其他原生渲染 API 与底层图形设备驱动程序之间的通信。EGL™ 是 Khronos 渲染 API(例如 OpenGL ES 或 OpenVG)与底层原生平台窗口系统之间的接口。它处理图形上下文管理、表面/缓冲区绑定和渲染同步,并使用其他 Khronos API 实现高性能、加速、混合模式的 2D 和 3D 渲染。
EGL 的主要功能包括:
- 和本地窗口系统(native windowing system)通讯;
- 查询可用的配置;
- 创建OpenGL ES可用的“绘图表面”(drawing surface);
- 同步不同类别的API之间的渲染,比如在OpenGL ES和OpenVG之间同步,或者在OpenGL和本地窗口的绘图命令之间;
- 管理“渲染资源”,比如纹理映射(rendering map)。
EGL 可以在多个操作系统和图形硬件上运行,并且它的实现可以被嵌入到嵌入式系统中。它的主要用途是支持 OpenGL ES 和其他原生渲染 API 的跨平台开发。
EGL定义了控制 displays,contexts 以及 surfaces 的统一的平台接口。
- Display(EGLDisplay) 是对实际显示设备的抽象。
- Surface(EGLSurface)是对用来存储图像的内存区域 FrameBuffer 的抽象,包括 Color Buffer, Stencil Buffer ,Depth Buffer。
- Context (EGLContext) 存储 OpenGL ES绘图的一些状态信息。
使用EGL的绘图的一般步骤:
- 获取 EGLDisplay 对象
- 初始化与 EGLDisplay 之间的连接
- 获取 EGLConfig 对象
- 创建 EGLContext 实例
- 创建 EGLSurface 实例
- 连接 EGLContext 和 EGLSurface
- 使用 GL 指令绘制图形
- 断开并释放与 EGLSurface 关联的 EGLContext 对象
- 删除 EGLSurface 对象
- 删除 EGLContext 对象
- 终止与 EGLDisplay 之间的连接
EGL 是为 OpenGL ES 提供平台独立性而设计。 OpenGL ES 为附加功能和可能的平台特性开发提供了扩展机制,但仍然需要一个可以让 OpenGL ES 和本地视窗系统交互且平台无关的层。 OpenGL ES 本质上是一个图形渲染管线的状态机,而 EGL 则是用于监控这些状态以及维护 Frame buffer 和其他渲染 Surface 的外部层。下图是一个典型的 EGL 系统布局图。
EGL 视窗设计是基于人们熟悉的用于 Microsoft Windows ( WGL )和 UNIX ( GLX )上的 OpenGL 的 Native 接口,与后者比较接近。OpenGL ES 图形管线的状态被存储于 EGL 管理的一个 Context 中。 Frame Buffers 和其他绘制 Surfaces 通过 EGL API 创建、管理和销毁。EGL 同时也控制和提供了对设备显示和可能的设备渲染配置的访问。
EGL数据类型
标准 EGL 数据类型如下表所示。
数据类型 | 值 |
---|---|
EGLBoolean | EGL_TRUE =1, EGL_FALSE=0 |
EGLint | int 数据类型 |
EGLDisplay | 系统显示 ID 或句柄 |
EGLConfig | Surface 的 EGL 配置 |
EGLSurface | 系统窗口或 frame buffer 句柄 |
EGLContext | OpenGL ES 图形上下文 |
NativeDisplayType | Native 系统显示类型 |
NativeWindowType | Native 系统窗口缓存类型 |
NativePixmapType | Native 系统 frame buffer |
1.EGL Display
EGLDisplay 是一个关联系统物理屏幕的通用数据类型。对于 PC 来说, Display 就是显示器的句柄。不管是嵌入式系统或 PC ,都可能有多个物理显示设备。为了使用系统的显示设备, EGL 提供了 EGLDisplay 数据类型,以及一组操作设备显示的 API 。
下面的函数原型用于获取 Native Display :
EGLDisplay eglGetDisplay (NativeDisplayType display);
其中 display 参数是 native 系统的窗口显示 ID 值。如果你只是想得到一个系统默认的 Display ,你可以使用 EGL_DEFAULT_DISPLAY 参数。如果系统中没有一个可用的 native display ID 与给定的 display 参数匹配,函数将返回 EGL_NO_DISPLAY ,而没有任何 Error 状态被设置。
由于设置无效的 display 值不会有任何错误状态,在你继续操作前请检测返回值。
下面是一个使用 EGL API 获取系统 Display 的例子:
m_eglDisplay = eglGetDisplay( system.display);
if (m_eglDisplay == EGL_NO_DISPLAY || eglGetError() != EGL_SUCCESS))
throw error_egl_display;
2.EGL 初始化
和很多视窗 API 类似, EGL 在使用前需要初始化,因此每个 EGLDisplay 在使用前都需要初始化。初始化 EGLDisplay 的同时,你可以得到系统中 EGL 的实现版本号。
下面是初始化 EGL 的函数原型:
EGLBoolean eglInitialize (EGLDisplay dpy, EGLint *major, EGLint *minor);
其中 dpy 应该是一个有效的 EGLDisplay 。函数返回时, major 和 minor 将被赋予当前 EGL 版本号。比如 EGL1.0 , major 返回 1 ,minor 则返回 0 。给 major 和 minor 传 NULL 是有效的,如果你不关心版本号。
OpenGL ES的初始化过程如下图所示意:
Display → Config → Surface
↑
Context
↑
Application → OpenGL Command
-
获取Display。
Display代表显示器,在有些系统上可以有多个显示器,也就会有多个Display。获得Display要调用EGLboolean eglGetDisplay(NativeDisplay dpy)
参数一般为 EGL_DEFAULT_DISPLAY 。该参数实际的意义是平台实现相关的,在X-Window下是XDisplay ID,在MS Windows下是Window DC。
-
初始化egl。
调用EGLboolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
该函数会进行一些内部初始化工作,并传回EGL版本号(major.minor)。
-
选择Config。
所谓Config实际指的是FrameBuffer的参数,在MS Windows下对应于PixelFormat,在X-Window下对应Visual。一般用EGLboolean eglChooseConfig(EGLDisplay dpy, const EGLint * attr_list, EGLConfig * config, EGLint config_size, EGLint *num_config)
其中attr_list是以EGL_NONE结束的参数数组,通常以id,value依次存放,对于个别标识性的属性可以只有 id,没有value。另一个办法是用
EGLboolean eglGetConfigs(EGLDisplay dpy, EGLConfig * config, EGLint config_size, EGLint *num_config)
来获得所有config。这两个函数都会返回不多于config_size个Config,结果保存在config[]中,系统的总Config个数保存 在num_config中。可以利用eglGetConfig()中间两个参数为0来查询系统支持的Config总个数。
Config有众多的Attribute,这些Attribute决定FrameBuffer的格式和能力,通过eglGetConfigAttrib ()来读取,但不能修改。 -
构造Surface。
Surface实际上就是一个FrameBuffer,也就是渲染目的地,通过EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig confg, NativeWindow win, EGLint *cfg_attr)
来创建一个可实际显示的Surface。
系统通常还支持另外两种Surface:PixmapSurface和PBufferSurface,这两种都不 是可显示的Surface,PixmapSurface是保存在系统内存中的位图,PBuffer则是保存在显存中的帧。
Surface也有一些attribute,基本上都可以故名思意, EGL_HEIGHT EGL_WIDTH EGL_LARGEST_PBUFFER EGL_TEXTURE_FORMAT EGL_TEXTURE_TARGET EGL_MIPMAP_TEXTURE EGL_MIPMAP_LEVEL,通过eglSurfaceAttrib()设置、eglQuerySurface()读取。 -
创建Context。
OpenGL的pipeline从程序的角度看就是一个状态机,有当前的颜色、纹理坐标、变换矩阵、绚染模式等一大堆状态,这些状态作用于程序提交的顶点 坐标等图元从而形成帧缓冲内的像素。在OpenGL的编程接口中,Context就代表这个状态机,程序的主要工作就是向Context提供图元、设置状 态,偶尔也从Context里获取一些信息。
可以用EGLContext eglCreateContext(EGLDisplay dpy, EGLSurface write, EGLSurface read, EGLContext * share_list)
来创建一个Context。
6、EGL变量之间的绑定
boolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context)
该接口将申请到的display,draw(surface)和 context进行了绑定。也就是说,在context下的OpenGLAPI指令将draw(surface)作为其渲染最终目的地。而display作为draw(surface)的前端显示。调用后,当前线程使用的EGLContex为context。
-
绘制。
应用程序通过OpenGL API进行绘制,一帧完成之后,调用eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)
来显示。
3.EGL Configurations
EGLConfigs 是一个用来描述 EGL surface 配置信息的数据类型。要获取正确的渲染结果, Surface 的格式是非常重要的。根据平台的不同,surface 配置可能会有限制,比如某个设备只支持 16 位色深显示,或是不支持 stencil buffer ,还有其他的功能限制或精度的差异。
下面是获取系统可用的 EGL 配置信息的函数原型:
EGLBoolean eglGetConfigs (EGLDisplay dpy, EGLConfig *configs,EGLint config_size, EGLint *num_config);
参数 configs 将包含在你的平台上有效的所有 EGL framebuffer 配置列表。支持的配置总数将通过 num_config 返回。实际返回的 configs 的配置个数依赖于程序传入的 config_size 。如果 config_size < num_config ,则不是所有的配置信息都将被返回。如果想获取系统支持的所有配置信息,最好的办法就是先给 eglGetConfig 传一个 NULL 的 configs 参数, num_config 将得到系统所支持的配置总数,然后用它来给configs 分配合适的内存大小,再用得到的 configs 来调用 eglGetConfig 。
下面是如果使用 eglGetConfig() 函数的例子:
EGLConfig *configs_list;
EGLint num_configs;
// Main Display
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 Configuration
基于 EGL 的属性,你可以定义一个希望从系统获得的配置,它将返回一个最接近你的需求的配置。选择一个你特有的配置是有点不合适的,因为只是在你的平台上使用有效。 eglChooseConfig() 函数将适配一个你所期望的配置,并且尽可能接近一个有效的系统配置。
下面是选择一个 EGL 配置的函数原型:
EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint * num_config);
参数 attrib_list 指定了选择配置时需要参照的属性。参数 configs 将返回一个按照 attrib_list 排序的平台有效的所有 EGL framebuffer 配置列表。参数 config_size 指定了可以返回到 configs 的总配置个数。参数 num_config 返回了实际匹配的配置总数。
下面是如果使用 eglChoosetConfig() 函数的例子:
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 配置属性默认值和匹配法则
属性 | 数据类型 | 默认值 | 排序优先级 | 选择顺序 | 含义 |
---|---|---|---|---|---|
EGL_BUFFER_SIZE | int | 0 | 3 | Smaller value | 帧缓冲区颜色分量的总位数(R + G + B + A) |
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 | 帧缓冲区中用于alpha通道的位数 |
EGL_CONFIG_CAVEAT | enum | EGL_DONT_CARE | 1(first) | Exact value | 客户端应用在配置上期望的任何限制 |
EGL_CONFIG_ID | int | EGL_DONT_CARE | 9 | Exact value | 匹配的帧缓冲配置的ID号 |
EGL_DEPTH_SIZE | int | 0 | 6 | Smaller value | 深度缓冲区中用于深度值的位数 |
EGL_LEVEL | int | 0 | - | Equal value | 帧缓冲区的Mipmap等级 |
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_RGB) |
EGL_TRANSPARENT_RED_VALUE | int | EGL_DONT_CARE | - | Exact value | RGB透明色的红色分量 |
EGL 官网有一个1.0版本的 Specification,详细讲述了Surface、Display、Context 概念。简单地说
(1)Display 是图形显示设备(显示屏)的抽象表示。大部分EGL函数都要带一个 Display 作为参数
(2)Context 是 OpenGL 状态机。Context 与 Surface 可以是一对一、多对一、一对多的关系
(3)Surface 是绘图缓冲,可以是 window、pbuffer、pixmap 三种类型之一
EGL 工作流程为:
(1)初始化
(2)配置
(3)创建Surface(绑定到平台Windowing系统)
(4)绑定Surface与Context
(5)Main Loop:渲染(OpenGL),交换离线缓冲(offline buffer)与显示缓冲
(6)释放资源
● EGLDisplay
EGL可运行于GNU/Linux的X Window System,Microsoft Windows和MacOS X的Quartz。EGL把这些平台的显示系统抽象为一个独立的类型:EGLDisplay。
使用EGL的第一步就是初始化一个可用的EGLDisplay:
EGLint majorVersion;
EGLint minorVersion;
EGLBoolean success = EGL_FALSE;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display != EGL_NO_DISPLAY)
success = eglInitialize(display, &majorVersion, &minorVersion);
if (success != EGL_TRUE)
EGLint errno = eglGetError();
if (errno != EGL_SUCCESS)
{
_TCHAR errmsg[32];
_stprintf(errmsg, _T("[EGL] Initialization failed. Error code: 0x%04x"), errno);
// EGL_BAD_DISPLAY EGLDisplay参数错误
// EGL_NOT_INITIALIZED EGL不能初始化
}
}
这里用到了三个EGL函数:
- EGLDisplay eglGetDisplay(EGLNativeDisplayType id);
- EGLBoolean eglInitialize(EGLDisplay display, EGLint* majorVersion, EGLint* minorVersion);
- EGLint eglGetError();
● EGLConfig
初始化过后,要选择一个合适的“绘图表面”。
EGLBoolean eglGetConfigs(EGLDisplay display, // 已初始化好
EGLConfig* configs, // 如果为NULL,则返回EGL_TRUE和numConfigs,即图形系统所有可用的配置
EGLint maxConfigs, // 上面那个configs数组的容量
EGLint* numConfigs); // 图形系统返回的实际的可用的配置个数,存储在configs数组里
用例:
static const EGLint CONFIG_ATTRIBS[] =
{
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_DEPTH_SIZE, 16,
EGL_ALPHA_SIZE, EGL_DONT_CARE,
EGL_STENCIL_SIZE, EGL_DONT_CARE,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE // 属性表以该常量为结束符
};
GLint numConfigs;
EGLConfig config;
if (success != EGL_FALSE)
success = eglGetConfigs(display, NULL, 0, &numConfigs);
if (success != EGL_FALSE && numConfigs > 0)
success = eglChooseConfig(display, CONFIG_ATTRIBS, &config, 1, &numConfigs);
可以查询某个配置的某个属性:
EGLBoolean eglGetConfigAttrib(EGLDisplay display, // 已初始化
EGLConfig config, // 某个配置
EGLint attribute, // 某个属性
EGLint * value);
让EGL为你选择一个配置:
EGLBoolean eglChooseConfig(EGLDisplay display,
const EGLint* attribs, // 你想要的属性事先定义到这个数组里
EGLConfig* configs, // 图形系统将返回若干满足条件的配置到该数组
EGLint maxConfigs, // 上面数组的容量
EGLint* numConfigs); // 图形系统返回的可用的配置个数
EGL如果选择了多个配置给你,则按一定规则放到数组里:
1> EGL_CONFIG_CAVEAT
2> EGL_COLOR_BUFFER_TYPE
3> 按color buffer所占位宽
4> EGL_BUFFER_SIZE
5> EGL_SAMPLE_BUFFERS
6> EGL_SAMPLES
7> EGL_DEPTH_SIZE
8> EGL_STENCIL_SIZE
9> EGL_ALPHA_MASK_SIZE
10> EGL_NATIVE_VISUAL_TYPE
11> EGL_CONFIG_ID
● EGLSurface
EGLSurface eglCreateWindowSurface(EGLDisplay display,
EGLConfig config,
EGLNativeWindowType window, // 在Windows上就是HWND类型
const EGLint* attribs); // 此属性表非彼属性表
这里的属性表并非用于OpenGL ES 2.0,而是其它的API,比如OpenVG。我们只需要记住一个:EGL_RENDER_BUFFER [EGL_BACK_BUFFER, EGL_FRONT_BUFFER]。
OpenGL ES 2.0是必须工作于双缓冲窗口系统的。
该属性表当然也可以为NULL,也可以只有一个EGL_NONE。那表示所有属性使用默认值。
如果函数返回EGL_NO_SURFACE,则失败。错误码:
EGL_BAD_MATCH: 属性设置错误。比如EGL_SURFACE_TYPE没有设置EGL_WINDOW_BIT
EGL_BAD_CONFIG: 因为配置错误,图形系统不支持
EGL_BAD_NATIVE_WINDOW: 窗口句柄错误
EGL_BAD_ALLOC: 无法创建绘图表面。比如先前已经创建一个了。
● pixel buffer
OpenGL ES 2.0可以向pixel buffer渲染,同样使用硬件加速。pbuffer经常用来生成纹理映射。如果想渲染到纹理,常用更高效的framebuffer对象。
在EGL_SURFACE_TYPE里使用使用EGL_PBUFFER_BIT可创建pbuffer:
EGLSurface eglCreatePbufferSurface(EGLDisplay display,
EGLConfig config,
const EGLint* attribs);
使用到的属性:
EGL_WIDTH, EGL_HEIGHT
EGL_LARGEST_PBUFFER: 如果参数不合适,可使用最大的pbuffer
EGL_TEXTURE_FORMAT: [EGL_NO_TEXTURE] 如果pbuffer要绑定到纹理映射,要指定纹理的格式
EGL_TEXTURE_TARGET: [EGL_NO_TEXTURE, EGL_TEXTURE_2D]
EGL_MIPMAP_TEXTRUE: [EGL_TRUE, EGL_FALSE]
创建失败时返回EGL_NO_SURFACE,错误码:
EGL_BAD_ALLOC: 缺少资源
EGL_BAD_CONFIG: 配置错误
EGL_BAD_PARAMETER: EGL_WIDTH和EGL_HEIGHT为负数
EGL_BAD_MATCH: 配置错误;如果用于纹理映射,则高宽参数错误;EGL_TEXTURE_FORMAT和EGL_TEXTURE_TARGET只有一个不是EGL_NO_TEXTURE
EGL_BAD_ATTRIBUTE: 指定了EGL_TEXTURE_FORMAT、EGL_TEXTURE_TARGET或者EGL_MIPMAP_TEXTRUE,却不指定使用OpenGLES在配置里
使用pbuffer的例子:
EGLint cfgAttribs[] =
{
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_DEPTH_SIZE, 1,
EGL_NONE
};
const EGLint MAX_CONFIG = 10; // 我们要从10个配置里挑选一个
EGLConfig configs[MAX_CONFIG];
EGLint numConfigs;
if (!eglChooseConfig(display, cfgAttribs, configs, MAX_CONFIG, &numConfigs))
{ // 报错 }
else
{ // 挑选一个配置 }
EGLint PBufAttribs[] =
{
EGL_WIDTH, 512,
EGL_HEIGHT, 512,
EGL_LARGEST_PBUFFER, EGL_TRUE,
EGL_NONE
};
EGLRenderSurface pbuffer = eglCreatePbufferSurface(display, config, PBufAttribs);
if (pbuffer == EGL_NO_SURFACE)
{ // 创建失败,报各种错 }
EGLint width, height;
if (!eglQuerySurface(display, pbuffer, EGL_HEIGHT, &height) || !eglQuerySurface(display, pbuffer, EGL_WIDTH, &width)
{ // 查询不到信息,报错 }
pbuffer和普通的窗口渲染最大的不同是不能swap,要么拷贝其值,要么修改其绑定成为纹理。
● EGLContext
EGLContext eglCreateContext(EGLDisplay display,
EGLConfig config,
EGLContext context, // EGL_NO_CONTEXT表示不向其它的context共享资源
const EGLint * attribs)// 我们暂时只用EGL_CONTEXT_CLIENT_VERSION
const EGLint attribs[] =
{
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLContext context = eglCreateContext(display, cfg, EGL_NO_CONTEXT, attribs);
if (context == EGL_NO_CONTEXT)
{
if (EGL_BAD_CONFIG == eglGetError())
{ ... }
}
if (!eglMakeCurrent(display, window, window, context)) // 两个window表示读写都在一个窗口
{ // 报错 }
● 渲染同步
只使用OpenGL ES 2.0,那么,glFinish即可保证所有的渲染工作进行下去。
但使用OpenVG或本地图形API渲染字体,要比使用OpenGL ES 2.0要容易。所以,你可能要在同一个窗口使用多个库来渲染。
可以用EGL的同步函数:EGLBoolean eglWaitClient() 延迟客户端的执行,等待服务器端完成OpenGL ES 2.0或者OpenVG的渲染。
如果失败,返回错误码:EGL_BAD_CURRENT_SURFACE。
如果要等待本地图形API的渲染完成,使用:EGLBoolean eglWaitNative(EGLint engine)。
engine参数必须是EGL_CORE_NATIVE_ENGINE。其它值都是通过EGL扩展来指定。
如果失败,返回错误码:EGL_BAD_PARAMETER。
EGL 使用实践
EGL 的使用要遵循一些固定的步骤,按照这些步骤去配置、创建、渲染、释放。
1.创建与本地窗口系统的连接
调用 eglGetDisplay 方法得到 EGLDisplay
2.初始化 EGL 方法
调用 eglInitialize 方法初始化
3.确定渲染表面的配置信息
调用 eglChooseConfig 方法得到 EGLConfig
4.创建渲染上下文
通过 EGLDisplay 和 EGLConfig ,调用 eglCreateContext 方法创建渲染上下文,得到 EGLContext
5.创建渲染表面
通过 EGLDisplay 和 EGLConfig ,调用 eglCreateWindowSurface 方法创建渲染表面,得到 EGLSurface
6.绑定上下文
通过 eglMakeCurrent 方法将 EGLSurface、EGLContext、EGLDisplay 三者绑定,接下来就可以使用 OpenGL 进行绘制了。
7.交换缓冲
当用 OpenGL 绘制结束后,使用 eglSwapBuffers 方法交换前后缓冲,将绘制内容显示到屏幕上
8.释放 EGL 环境
绘制结束,不再需要使用 EGL 时,取消 eglMakeCurrent 的绑定,销毁 EGLDisplay、EGLSurface、EGLContext。
下面是EGL初始化的代码:
virtual bool Init(void *win)
{
Close();
//初始化EGL
m_mutex.lock();
//1 获取EGLDisplay对象 显示设备
m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (m_eglDisplay == EGL_NO_DISPLAY)
{
m_mutex.unlock();
XLOGE("eglGetDisplay failed!");
return false;
}
XLOGE("eglGetDisplay success!");
//2 初始化Display
if (EGL_TRUE != eglInitialize(m_eglDisplay, 0, 0))
{
m_mutex.unlock();
XLOGE("eglInitialize failed!");
return false;
}
XLOGE("eglInitialize success!");
//3 获取配置并创建surface
EGLint configs [] = {
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT,
EGL_NONE
};
EGLConfig eglConfig = 0;
EGLint numConfigs = 0;
if (EGL_TRUE != eglChooseConfig(m_eglDisplay, configs, &eglConfig, 1, &numConfigs))
{
m_mutex.unlock();
XLOGE("eglChooseConfig failed!");
return false;
}
XLOGE("eglChooseConfig success!");
ANativeWindow *nativeWindow = (ANativeWindow *)win;
m_eglSurface = eglCreateWindowSurface(m_eglDisplay, eglConfig, nativeWindow, NULL);
//4 创建并打开EGL上下文
const EGLint contextAttr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
m_eglContext = eglCreateContext(m_eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttr);
if (m_eglContext == EGL_NO_CONTEXT)
{
m_mutex.unlock();
XLOGE("eglCreateContext failed!");
return false;
}
XLOGE("eglCreateContext success!");
if (EGL_TRUE != eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext))
{
m_mutex.unlock();
XLOGE("eglMakeCurrent failed!");
return false;
}
XLOGE("eglMakeCurrent success!");
m_mutex.unlock();
return true;
}
每次Opengles完成后,调用EGL显示
virtual void Draw()
{
m_mutex.lock();
if (m_eglDisplay == EGL_NO_DISPLAY || m_eglSurface == EGL_NO_SURFACE)
{
m_mutex.unlock();
return;
}
eglSwapBuffers(m_eglDisplay, m_eglSurface);
m_mutex.unlock();
}
释放EGL的资源如下:
virtual void Close()
{
m_mutex.lock();
if (m_eglDisplay == EGL_NO_DISPLAY)
{
m_mutex.unlock();
return;
}
eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (m_eglSurface != EGL_NO_SURFACE)
eglDestroySurface(m_eglDisplay, m_eglSurface);
if (m_eglContext != EGL_NO_CONTEXT)
eglDestroyContext(m_eglDisplay, m_eglContext);
eglTerminate(m_eglDisplay);
m_eglDisplay = EGL_NO_DISPLAY;
m_eglSurface = EGL_NO_SURFACE;
m_eglContext = EGL_NO_CONTEXT;
m_mutex.unlock();
}
完整Ubutun上运行示例如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
int main(int argc, char** argv)
{
Display *x_display = XOpenDisplay(NULL);
if (!x_display)
{
fprintf(stderr, "Unable to open X display\n");
return -1;
}
Window root = DefaultRootWindow(x_display);
XSetWindowAttributes swa;
swa.event_mask = ExposureMask | ButtonPressMask;
Window win = XCreateWindow(
x_display,
root,
0, 0, 640, 480, 0,
CopyFromParent, InputOutput,
CopyFromParent, CWEventMask,
&swa
);
XMapWindow(x_display, win);
XStoreName(x_display, win, "EGL Window");
// 获取EGL display
EGLDisplay egl_display = eglGetDisplay((EGLNativeDisplayType)x_display);
// 初始化EGL
EGLint major, minor;
if (!eglInitialize(egl_display, &major, &minor))
{
fprintf(stderr, "Unable to initialize EGL\n");
return -1;
}
// 配置属性
EGLConfig config;
EGLint num_configs;
EGLint config_attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL_DEPTH_SIZE, 0,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_NONE
};
eglChooseConfig(egl_display, config_attribs, &config, 1, &num_configs);
// 创建EGL surface
EGLSurface egl_surface = eglCreateWindowSurface(
egl_display, config, (EGLNativeWindowType)win, NULL
);
// 创建EGL context
EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
EGLContext egl_context = eglCreateContext(
egl_display, config, EGL_NO_CONTEXT, context_attribs
);
// 将EGL context设置为当前context
if (!eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context))
{
fprintf(stderr, "Unable to make EGL context current\n");
return -1;
}
// 开始渲染
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
eglSwapBuffers(egl_display, egl_surface);
getchar();
// 清理
eglDestroySurface(egl_display, egl_surface);
eglDestroyContext(egl_display, egl_context);
eglTerminate(egl_display);
XDestroyWindow(x_display, win);
XCloseDisplay(x_display);
return 0;
}
编译:
gcc egl_example1.c -o egl_example1 -lEGL -lGLESv2 -lX11
EGL深入分析
1.eglGetDisplay
PUBLIC EGLDisplay EGLAPIENTRY
eglGetDisplay(EGLNativeDisplayType nativeDisplay)
{
_EGLPlatformType plat;
_EGLDisplay *disp;
void *native_display_ptr;
util_perfetto_init();
_EGL_FUNC_START(NULL, EGL_OBJECT_THREAD_KHR, NULL);
STATIC_ASSERT(sizeof(void*) == sizeof(nativeDisplay));
native_display_ptr = (void*) nativeDisplay;
plat = _eglGetNativePlatform(native_display_ptr);
//通过本地平台相关的函数获取一个平台类型 _EGLPlatformType,用于表示本地平台的类型。
disp = _eglFindDisplay(plat, native_display_ptr, NULL);
//查找与给定本地平台类型和本地平台句柄相匹配的 EGLDispla
return _eglGetDisplayHandle(disp);
//获取表示 EGLDisplay 的指针,并将其返回
}
/*函数 _eglFindDisplay 的功能是在 _eglGlobal.DisplayList 中查找一个与传入的 plat 和 plat_dpy 对应的 EGLDisplay
,或者创建一个新的 EGLDisplay 并添加到 _eglGlobal.DisplayList 中。*/
_EGLDisplay *
_eglFindDisplay(_EGLPlatformType plat, void *plat_dpy,
const EGLAttrib *attrib_list)
{
_EGLDisplay *disp;
size_t num_attribs;
if (plat == _EGL_INVALID_PLATFORM)
return NULL;
simple_mtx_lock(_eglGlobal.Mutex);
/* search the display list first */
for (disp = _eglGlobal.DisplayList; disp; disp = disp->Next) {
if (disp->Platform == plat && disp->PlatformDisplay == plat_dpy &&
_eglSameAttribs(disp->Options.Attribs, attrib_list))
goto out;
}
/*先使用一个 for 循环遍历 _eglGlobal.DisplayList,查找符合条件的 EGLDisplay。
在 for 循环中,每次比较当前 EGLDisplay 的 Platform 和 PlatformDisplay 字段是否与传入的 plat 和 plat_dpy 相同,
并检查 EGLDisplay 的 Options.Attribs 字段是否与传入的 attrib_list 相同,
如果都相同,则表示找到了目标 EGLDisplay,跳转到 out 标签处直接返回找到的 EGLDisplay。
如果遍历了整个 _eglGlobal.DisplayList 都没有找到符合条件的 EGLDisplay,则进入下面的代码块创建一个新的 EGLDisplay。*/
/* create a new display */
assert(!disp);
disp = calloc(1, sizeof(_EGLDisplay));
if (!disp)
goto out;
//首先用 calloc 函数分配了一个 _EGLDisplay 结构体,并调用 simple_mtx_init 和 u_rwlock_init 函数初始化互斥锁。
simple_mtx_init(&disp->Mutex, mtx_plain);
u_rwlock_init(&disp->TerminateLock);
disp->Platform = plat;
disp->PlatformDisplay = plat_dpy;
num_attribs = _eglNumAttribs(attrib_list);
/*然后设置 EGLDisplay 的 Platform 和 PlatformDisplay 字段为传入的 plat 和 plat_dpy,
接着计算出 attrib_list 中元素个数,并为 EGLDisplay 的 Options.Attribs 字段分配足够的内存。
*/
if (num_attribs) {
disp->Options.Attribs = calloc(num_attribs, sizeof(EGLAttrib));
if (!disp->Options.Attribs) {
free(disp);
disp = NULL;
goto out;
}
memcpy(disp->Options.Attribs, attrib_list,
num_attribs * sizeof(EGLAttrib));
}
/*如果分配失败,则释放之前分配的内存并将 EGLDisplay 指针设为 NULL,并跳转到 out 标签处释放互斥锁和返回 NULL。
否则,使用 memcpy 函数将 attrib_list 中的元素复制到 EGLDisplay 的 Options.Attribs 字段中。*/
/* add to the display list */
disp->Next = _eglGlobal.DisplayList;
_eglGlobal.DisplayList = disp;
//最后,将新创建的 EGLDisplay 添加到 _eglGlobal.DisplayList 中。
out:
simple_mtx_unlock(_eglGlobal.Mutex);
//最后,释放互斥锁并返回找到的 EGLDisplay 或新创建的 EGLDisplay。
return disp;
}
Display的Platform的类型
序号 | Platform类型 | 说明 |
---|---|---|
1 | EGL_PLATFORM_X11_EXT | X11 |
2 | EGL_PLATFORM_XCB_EXT | XCB |
3 | EGL_PLATFORM_GBM_MESA | GBM |
4 | EGL_PLATFORM_WAYLAND_EXT | WAYLAND |
5 | EGL_PLATFORM_SURFACELESS_MESA | SURFACE |
6 | EGL_PLATFORM_ANDROID_KHR | ANDROID |
7 | EGL_PLATFORM_DEVICE_EXT | DEVICE |
2.eglInitialize
Display初始化过程分析
//初始化指定的EGLDisplay并获取EGL版本信息。
PUBLIC EGLBoolean EGLAPIENTRY
eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
{
_EGLDisplay *disp = _eglLockDisplay(dpy);
//首先调用了_eglLockDisplay函数获取指定的EGLDisplay对象,并判断该对象是否已经被初始化。
_EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL);
if (!disp)
RETURN_EGL_ERROR(NULL, EGL_BAD_DISPLAY, EGL_FALSE);
if (!disp->Initialized) {
/* set options */
disp->Options.ForceSoftware =
debug_get_bool_option("LIBGL_ALWAYS_SOFTWARE", false);
if (disp->Options.ForceSoftware)
_eglLog(_EGL_DEBUG, "Found 'LIBGL_ALWAYS_SOFTWARE' set, will use a CPU renderer");
const char *env = getenv("MESA_LOADER_DRIVER_OVERRIDE");
disp->Options.Zink = env && !strcmp(env, "zink");
disp->Options.ForceSoftware |= disp->Options.Zink;
//如果该对象尚未被初始化,则设置一些选项,如是否使用软件渲染、是否使用zink渲染等,并调用驱动程序提供的_eglDriver.Initialize函数进行初始化。
if (!_eglDriver.Initialize(disp)) {
if (disp->Options.ForceSoftware)
RETURN_EGL_ERROR(disp, EGL_NOT_INITIALIZED, EGL_FALSE);
else {
disp->Options.ForceSoftware = EGL_TRUE;
if (!_eglDriver.Initialize(disp))
RETURN_EGL_ERROR(disp, EGL_NOT_INITIALIZED, EGL_FALSE);
}
}
//如果初始化失败,且之前没有强制使用软件渲染,则尝试以软件渲染方式进行初始化。
disp->Initialized = EGL_TRUE;
disp->Driver = &_eglDriver;
/*如果初始化成功,函数会将EGLDisplay对象的Initialized成员设置为EGL_TRUE,表示对象已经被成功初始化,并设置Driver成员指向驱动程序提供的_EGLDriver结构体。
此外,函数还会计算并记录EGL版本信息,并创建EGL支持的API字符串和扩展字符串。*/
/* limit to APIs supported by core */
disp->ClientAPIs &= _EGL_API_ALL_BITS;
disp->Extensions.KHR_get_all_proc_addresses = EGL_TRUE;
disp->Extensions.KHR_config_attribs = EGL_TRUE;
_eglComputeVersion(disp);
_eglCreateExtensionsString(disp);
_eglCreateAPIsString(disp);
snprintf(disp->VersionString, sizeof(disp->VersionString),
"%d.%d", disp->Version / 10, disp->Version % 10);
}
//如果传入了非空的major和minor指针,函数会将EGL版本信息分别存入这两个指针指向的内存位置。
/* Update applications version of major and minor if not NULL */
if ((major != NULL) && (minor != NULL)) {
*major = disp->Version / 10;
*minor = disp->Version % 10;
}
//最后,函数返回EGL_TRUE表示初始化成功。如果发生错误,函数会返回EGL_FALSE并设置相应的错误码。
RETURN_EGL_SUCCESS(disp, EGL_TRUE);
}
static EGLBoolean
dri2_initialize(_EGLDisplay *disp)
{
EGLBoolean ret = EGL_FALSE;
struct dri2_egl_display *dri2_dpy = dri2_egl_display(disp);
//首先通过传递的 _EGLDisplay 指针获得 dri2_egl_display 结构体指针。
if (dri2_dpy) {
p_atomic_inc(&dri2_dpy->ref_count);
return EGL_TRUE;
}
//如果已经存在一个 dri2_egl_display 结构体,则会增加其引用计数,并返回 EGL_TRUE 表示初始化成功。
loader_set_logger(_eglLog);
//如果 dri2_egl_display 结构体不存在,则 loader_set_logger 函数被调用以设置日志记录器。然后根据 _EGLDisplay 的 Platform 成员选择对应的函数进行初始化。
/*根据 Platform 不同,选择对应的函数, 这些函数是 Mesa 中具体实现各种平台 EGL 初始化的函数。 */
switch (disp->Platform) {
case _EGL_PLATFORM_SURFACELESS:
ret = dri2_initialize_surfaceless(disp);
break;
case _EGL_PLATFORM_DEVICE:
ret = dri2_initialize_device(disp);
break;
case _EGL_PLATFORM_X11:
case _EGL_PLATFORM_XCB:
ret = dri2_initialize_x11(disp);
break;
case _EGL_PLATFORM_DRM:
ret = dri2_initialize_drm(disp);
break;
case _EGL_PLATFORM_WAYLAND:
ret = dri2_initialize_wayland(disp);
break;
case _EGL_PLATFORM_ANDROID:
ret = dri2_initialize_android(disp);
break;
default:
unreachable("Callers ensure we cannot get here.");
return EGL_FALSE;
}
/*如果初始化函数调用成功,则再次获取 dri2_egl_display 结构体指针,增加其引用计数,并初始化锁,最后返回 EGL_TRUE 表示初始化成功。
如果初始化函数调用失败,则返回 EGL_FALSE。*/
if (!ret)
return EGL_FALSE;
dri2_dpy = dri2_egl_display(disp);
p_atomic_inc(&dri2_dpy->ref_count);
mtx_init(&dri2_dpy->lock, mtx_plain);
return EGL_TRUE;
}
3.EGLContext
EGLContext 上下文包含了操作所需的所有状态信息,OpenGL ES 必须有一个可用的上下文 EGLContext 才能进行绘图。如果没有 EGLContext ,OpenGL 指令就没有执行的环境。
PUBLIC EGLContext EGLAPIENTRY
eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_list,
const EGLint *attrib_list)
{
_EGLDisplay *disp = _eglLockDisplay(dpy);
_EGLConfig *conf = _eglLookupConfig(config, disp);
_EGLContext *share = _eglLookupContext(share_list, disp);
_EGLContext *context;
EGLContext ret;
//首先,该函数使用传递的 EGLDisplay、EGLConfig 和 EGLContext 参数从 EGL 的数据结构中查找对应的对象,并进行必要的错误检查。如果检查失败,函数会返回相应的错误码。
_EGL_FUNC_START(disp, EGL_OBJECT_DISPLAY_KHR, NULL);
_EGL_CHECK_DISPLAY(disp, EGL_NO_CONTEXT);
if (config != EGL_NO_CONFIG_KHR)
_EGL_CHECK_CONFIG(disp, conf, EGL_NO_CONTEXT);
else if (!disp->Extensions.KHR_no_config_context)
RETURN_EGL_ERROR(disp, EGL_BAD_CONFIG, EGL_NO_CONTEXT);
if (!share && share_list != EGL_NO_CONTEXT)
RETURN_EGL_ERROR(disp, EGL_BAD_CONTEXT, EGL_NO_CONTEXT);
else if (share && share->Resource.Display != disp) {
RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_NO_CONTEXT);
}
/*接下来,该函数调用驱动程序的 CreateContext 函数来创建 EGLContext 对象,并通过 _eglLinkContext 函数将其与 EGL 的数据结构连接起来。
如果创建成功,函数将返回 EGLContext 对象;否则返回 EGL_NO_CONTEXT。*/
context = disp->Driver->CreateContext(disp, conf, share, attrib_list);
ret = (context) ? _eglLinkContext(context) : EGL_NO_CONTEXT;
RETURN_EGL_EVAL(disp, ret);
}
4.eglMakeCurrent
eglMakeCurrent将上下文绑定到当前渲染线程以及绘制和读取Surface。
//该函数的作用是将指定的EGLContext与指定的EGLSurface关联起来,以便在这个EGLSurface上进行渲染操作时,使用指定的EGLContext进行渲染。
PUBLIC EGLBoolean EGLAPIENTRY
eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read,
EGLContext ctx)
{
/*先通过_EGLLockDisplay()函数获取EGLDisplay对象的指针,然后通过_eglLookupContext()、_eglLookupSurface()函数获取EGLContext和EGLSurface对象的指针,
然后进行一系列的参数合法性检查,包括检查句柄是否有效、EGLDisplay对象是否初始化、EGLContext和EGLSurface是否已经关联等等。*/
_EGLDisplay *disp = _eglLockDisplay(dpy);
_EGLContext *context = _eglLookupContext(ctx, disp);
_EGLSurface *draw_surf = _eglLookupSurface(draw, disp);
_EGLSurface *read_surf = _eglLookupSurface(read, disp);
EGLBoolean ret = EGL_FALSE;
_EGL_FUNC_START(disp, EGL_OBJECT_CONTEXT_KHR, context);
if (!disp)
RETURN_EGL_ERROR(disp, EGL_BAD_DISPLAY, EGL_FALSE);
/* display is allowed to be uninitialized under certain condition */
if (!disp->Initialized) {
if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE ||
ctx != EGL_NO_CONTEXT)
RETURN_EGL_ERROR(disp, EGL_BAD_DISPLAY, EGL_FALSE);
}
if (!disp->Driver)
RETURN_EGL_SUCCESS(disp, EGL_TRUE);
if (!context && ctx != EGL_NO_CONTEXT)
RETURN_EGL_ERROR(disp, EGL_BAD_CONTEXT, EGL_FALSE);
if (!draw_surf || !read_surf) {
if (!disp->Extensions.KHR_surfaceless_context && ctx != EGL_NO_CONTEXT)
RETURN_EGL_ERROR(disp, EGL_BAD_SURFACE, EGL_FALSE);
if ((!draw_surf && draw != EGL_NO_SURFACE) ||
(!read_surf && read != EGL_NO_SURFACE))
RETURN_EGL_ERROR(disp, EGL_BAD_SURFACE, EGL_FALSE);
if (draw_surf || read_surf)
RETURN_EGL_ERROR(disp, EGL_BAD_MATCH, EGL_FALSE);
}
if (draw_surf && draw_surf->Lost)
RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_WINDOW, EGL_FALSE);
if (read_surf && read_surf->Lost)
RETURN_EGL_ERROR(disp, EGL_BAD_NATIVE_WINDOW, EGL_FALSE);
if (read_surf && read_surf->ProtectedContent &&
draw_surf && !draw_surf->ProtectedContent)
RETURN_EGL_ERROR(disp, EGL_BAD_ACCESS, EGL_FALSE);
//最后,通过调用EGLDisplay对象的Driver->MakeCurrent()函数,将EGLContext和EGLSurface关联起来
egl_relax (disp, &draw_surf->Resource, &read_surf->Resource, &context->Resource) {
ret = disp->Driver->MakeCurrent(disp, draw_surf, read_surf, context);
}
RETURN_EGL_EVAL(disp, ret);
}
5. egl的Surface
Surface类别
egl提供三种Surface,分别为:
1) Window Surface: 专门用于窗口显示的Surface
2) Pixmap Surface: 专门用于离屏的Surface
3) Pbuffer Surface: 专门用于像素缓冲的Surface,创建时,包含了 EGL_GL_COLORSPACE 、EGL_HEIGHT、EGL_LARGEST_PBUFFER、EGL_MIPMAP_TEXTURE、EGL_TEXTURE_FORMAT、EGL_TEXTURE_TARGET、EGL_VG_ALPHA_FORMAT、EGL_VG_COLORSPACE、EGL_WIDTH相关属性
PUBLIC EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType window, const EGLint *attrib_list)
PUBLIC EGLSurface EGLAPIENTRY eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list)
PUBLIC EGLSurface EGLAPIENTRY eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list)
Surface创建的调用关系