EGL学习笔记

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的绘图的一般步骤:

  1. 获取 EGLDisplay 对象
  2. 初始化与 EGLDisplay 之间的连接
  3. 获取 EGLConfig 对象
  4. 创建 EGLContext 实例
  5. 创建 EGLSurface 实例
  6. 连接 EGLContext 和 EGLSurface
  7. 使用 GL 指令绘制图形
  8. 断开并释放与 EGLSurface 关联的 EGLContext 对象
  9. 删除 EGLSurface 对象
  10. 删除 EGLContext 对象
  11. 终止与 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 数据类型如下表所示。

数据类型
EGLBooleanEGL_TRUE =1, EGL_FALSE=0
EGLintint 数据类型
EGLDisplay系统显示 ID 或句柄
EGLConfigSurface 的 EGL 配置
EGLSurface系统窗口或 frame buffer 句柄
EGLContextOpenGL ES 图形上下文
NativeDisplayTypeNative 系统显示类型
NativeWindowTypeNative 系统窗口缓存类型
NativePixmapTypeNative 系统 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
  1. 获取Display。
    Display代表显示器,在有些系统上可以有多个显示器,也就会有多个Display。获得Display要调用

    EGLboolean eglGetDisplay(NativeDisplay dpy)
    

    参数一般为 EGL_DEFAULT_DISPLAY 。该参数实际的意义是平台实现相关的,在X-Window下是XDisplay ID,在MS Windows下是Window DC。

  2. 初始化egl。
    调用

    EGLboolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
    

    该函数会进行一些内部初始化工作,并传回EGL版本号(major.minor)。

  3. 选择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 ()来读取,但不能修改。

  4. 构造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()读取。

  5. 创建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。

  6. 绘制。
    应用程序通过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_SIZEint03Smaller value帧缓冲区颜色分量的总位数(R + G + B + A)
EGL_RED_SIZEint02Larger value帧缓冲区中用于红色分量的位数
EGL_GREEN_SIZEint02Larger value帧缓冲区中用于绿色分量的位数
EGL_BLUE_SIZEint02Larger value帧缓冲区中用于蓝色分量的位数
EGL_ALPHA_SIZEint02Larger value帧缓冲区中用于alpha通道的位数
EGL_CONFIG_CAVEATenumEGL_DONT_CARE1(first)Exact value客户端应用在配置上期望的任何限制
EGL_CONFIG_IDintEGL_DONT_CARE9Exact value匹配的帧缓冲配置的ID号
EGL_DEPTH_SIZEint06Smaller value深度缓冲区中用于深度值的位数
EGL_LEVELint0-Equal value帧缓冲区的Mipmap等级
EGL_NATIVE_RENDERABLEBooleanEGL_DONT_CARE-Exact value是否需要匹配可渲染的本地表面类型
EGL_NATIVE_VISUAL_TYPEintEGL_DONT_CARE8Exact value匹配的本地可视化表面类型(用于窗口系统)
EGL_SAMPLE_BUFFERSint04Smaller value指示帧缓冲区是否包含多重采样缓冲区
EGL_SAMPLESint05Smaller value指示多重采样缓冲区的样本数
EGL_STENCIL_SIZEint07Smaller value模板缓冲区中用于模板值的位数
EGL_SURFACE_TYPEbitmaskEGL_WINDOW_BIT-Mask value匹配的表面类型,可以是任何组合的支持的表面类型。
EGL_TRANSPARENT_TYPEenumEGL_NONE-Exact value帧缓冲区透明类型(例如: EGL_TRANSPARENT_RGB)
EGL_TRANSPARENT_RED_VALUEintEGL_DONT_CARE-Exact valueRGB透明色的红色分量

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函数:

  1. EGLDisplay eglGetDisplay(EGLNativeDisplayType id);
  2. EGLBoolean eglInitialize(EGLDisplay display, EGLint* majorVersion, EGLint* minorVersion);
  3. 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类型说明
1EGL_PLATFORM_X11_EXTX11
2EGL_PLATFORM_XCB_EXTXCB
3EGL_PLATFORM_GBM_MESAGBM
4EGL_PLATFORM_WAYLAND_EXTWAYLAND
5EGL_PLATFORM_SURFACELESS_MESASURFACE
6EGL_PLATFORM_ANDROID_KHRANDROID
7EGL_PLATFORM_DEVICE_EXTDEVICE

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创建的调用关系

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值