EGL 介绍和使用

为君一笑 提交于 2019-12-13 16:34:48

1、形象介绍

原文地址:https://www.jianshu.com/p/95dfe935b169

部分内容摘自其他博客,请见最下[参考资料]

首先来看看Android官方对EGL的解释:

OpenGL ES 定义了一个渲染图形的 API,但没有定义窗口系统。为了让 GLES 能够适合各种平台,GLES 将与知道如何通过操作系统创建和访问窗口的库结合使用。用于 Android 的库称为 EGL。如果要绘制纹理多边形,应使用 GLES 调用;如果要在屏幕上进行渲染,应使用 EGL 调用。

OpenGL ES 是Android绘图API,但OpenGL ES是平台通用的,在特定设备上使用需要一个中间层做适配,这个中间层就是EGL。

 

EGL架构

  • Display(EGLDisplay) 是对实际显示设备的抽象。
  • Surface(EGLSurface)是对用来存储图像的内存区域 FrameBuffer 的抽象,包括 Color Buffer, Stencil Buffer ,Depth Buffer。
  • Context (EGLContext) 存储 OpenGL ES绘图的一些状态信息。

Android中的OpenGL 与EGL
Android 2.0版本之后图形系统的底层渲染均由OpenGL负责,OpenGL除了负责处理3D API调用,还需负责管理显示内存及处理Android SurfaceFlinger或上层应用对其发出的2D API调用请求。

  • 本地代码:
    frameworks/native/opengl/libs/EGL
    Android EGL框架,负责加载OpenGL函数库和EGL本地实现。
    frameworks/native/opengl/libagl
    Android提供的OpenGL软件库

  • JNI代码:
    frameworks/base/core/jni/com_google_android_gles_jni_EGLImpl.cpp
    EGL本地代码的JNI调用接口
    frameworks/base/core/jni/com_google_android_gles_jni_GLImpl.cpp
    frameworks/base/core/jni/android_opengl_GLESXXX.cpp
    OpenGL功能函数的JNI调用接口

  • Java代码:
    frameworks/base/opengl/java/javax/microedition/khronos/egl
    frameworks/base/opengl/java/javax/microedition/khronos/opengles
    frameworks/base/opengl/java/com/google/android/gles_jni/
    frameworks/base/opengl/java/android/opengl
    EGL和OpenGL的Java层接口,提供给应用开发者,通过JNI方式调用底层函数。

首先从Native代码入手: frameworks/native/opengl/libs/EGL,该目录下文件如图所示:

 

frameworks/native/opengl/libs/EGL

 

依次解析该目录下各个文件的作用,由于文件缺少注释,只能从代码解读含义:
eglApi.cpp:提供暴露给上层的API。包含EGL对象的创建、配置、销毁等操作。

初始化EGL
OpenGL ES的初始化过程(EGL初始化)如下图所示意:
Display → Config → Surface

Context

Application → OpenGL Command

  1. 获取Display。
    获得Display要调用EGLboolean eglGetDisplay(NativeDisplay dpy),参数一般为 EGL_DEFAULT_DISPLAY 。

  2. 初始化egl。
    调用 EGLboolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor),该函数会进行一些内部初始化工作,并传回EGL版本号(major.minor)。
    开机时打出的信息:
    如下信息可以由const char * eglQueryString (EGLDisplay dpy, EGLint name);给出,name可以是EGL_VENDOR, EGL_VERSION, 或者EGL_EXTENSIONS 。该函数常用来查询当前版本EGL实现了哪些扩展,方便向下兼容。

 

SurfaceFlinger: EGL information:
SurfaceFlinger: vendor    : Android
SurfaceFlinger: version   : 1.4 Android META-EGL
SurfaceFlinger: extensions: EGL_KHR_get_all_proc_addresses EGL_ANDROID_presentation_time EGL_KHR_swap_buffers_with_damage EGL_ANDROID_get_native_client_buffer EGL_ANDROID_front_buffer_auto_refresh EGL_ANDROID_get_frame_timestamps EGL_KHR_image EGL_KHR_image_base EGL_KHR_gl_colorspace EGL_KHR_gl_texture_2D_image EGL_KHR_gl_texture_cubemap_image EGL_KHR_gl_renderbuffer_image EGL_KHR_fence_sync EGL_KHR_create_context EGL_KHR_config_attribs EGL_KHR_surfaceless_context EGL_EXT_create_context_robustness EGL_ANDROID_image_native_buffer EGL_KHR_wait_sync EGL_ANDROID_recordable EGL_KHR_partial_update EGL_KHR_mutable_render_buffer EGL_IMG_context_priority 
SurfaceFlinger: Client API: OpenGL_ES
SurfaceFlinger: EGLSurface: 8-8-8-8, config=0x785f32d008
SurfaceFlinger: OpenGL ES informations:
SurfaceFlinger: vendor    : ARM
SurfaceFlinger: renderer  : Mali-T860
SurfaceFlinger: version   : OpenGL ES 3.2 v1.r18p0-00cet0.e348142bb0bcdf18abb600a2670c56a1
SurfaceFlinger: extensions: GL_EXT_debug_marker GL_ARM_rgba8 GL_ARM_mali_shader_binary GL_OES_depth24 GL_OES_depth_texture GL_OES_depth_texture_cube_map GL_OES_packed_depth_stencil GL_OES_rgb8_rgba8 GL_EXT_read_format_bgra GL_OES_compressed_paletted_texture GL_OES_compressed_ETC1_RGB8_texture GL_OES_standard_derivatives GL_OES_EGL_image GL_OES_EGL_image_external GL_OES_EGL_image_external_essl3 GL_OES_EGL_sync GL_OES_texture_npot GL_OES_vertex_half_float GL_OES_required_internalformat GL_OES_vertex_array_object GL_OES_mapbuffer GL_EXT_texture_format_BGRA8888 GL_EXT_texture_rg GL_EXT_texture_type_2_10_10_10_REV GL_OES_fbo_render_mipmap GL_OES_element_index_uint GL_EXT_shadow_samplers GL_OES_texture_compression_astc GL_KHR_texture_compression_astc_ldr GL_KHR_texture_compression_astc_hdr GL_KHR_texture_compression_astc_sliced_3d GL_KHR_debug GL_EXT_occlusion_query_boolean GL_EXT_disjoint_timer_query GL_EXT_blend_minmax GL_EXT_discard_framebuffer GL_OES_get_prog...
SurfaceFlinger: GL_MAX_TEXTURE_SIZE = 8192
SurfaceFlinger: GL_MAX_VIEWPORT_DIMS = 8192
  1. 选择Config。
    Config实际指的是FrameBuffer的参数,
    一般用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 ()来读取,但不能修改。

  2. 构造Surface
    Surface实际上就是一个FrameBuffer,也就是渲染目的地,通过EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig confg, NativeWindow win, EGLint *cfg_attr)来创建一个可实际显示的Surface。
    系统通常还支持另外两种Surface:PixmapSurface和PBufferSurface,这两种都不是可显示的Surface,PixmapSurface是保存在系统内存中的位图,PBuffer则是保存在显存中的帧。
    对于这两种surface,Android系统中,支持PBufferSurface。Surface也有一些attribute,基本上都可以顾名思义,

 

EGL_HEIGHT EGL_WIDTH EGL_LARGEST_PBUFFER
EGL_TEXTURE_FORMAT EGL_TEXTURE_TARGET
EGL_MIPMAP_TEXTURE EGL_MIPMAP_LEVEL

通过eglSurfaceAttrib()设置、eglQuerySurface()读取。

  1. 创建Context
    OpenGL ES的pipeline从程序的角度看就是一个状态机,有当前的颜色、纹理坐标、变换矩阵、绚染模式等一大堆状态,这些状态作用于OpenGL API程序提交的顶点坐标等图元从而形成帧缓冲内的像素。在OpenGL的编程接口中,Context就代表这个状态机,OpenGL API程序的主要工作就是向Context提供图元、设置状态,偶尔也从Context里获取一些信息。
    可以用EGLContext eglCreateContext(EGLDisplay dpy, EGLSurface write, EGLSurface read, EGLContext * share_list)来创建一个Context。

  2. EGL变量之间的绑定
    boolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context)
    该接口将申请到的display,draw(surface)和 context进行了绑定。也就是说,在context下的OpenGLAPI指令将draw(surface)作为其渲染最终目的地。而display作为draw(surface)的前端显示。调用后,当前线程使用的EGLContex为context。

  3. 绘制。
    应用程序通过OpenGL API进行绘制,一帧完成之后,调用eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)来显示。

If surface is a window surface, eglSwapBuffers posts its color buffer to the associated native window.
The contents of ancillary buffers are always undefined after calling eglSwapBuffers. The contents of the color buffer are left unchanged if the value of the EGL_SWAP_BEHAVIOR attribute of surface is EGL_BUFFER_PRESERVED, and are undefined if the value is EGL_BUFFER_DESTROYED. The value of EGL_SWAP_BEHAVIOR can be set for some surfaces using eglSurfaceAttrib.
eglSwapBuffers performs an implicit flush operation on the context (glFlush for an OpenGL ES or OpenGL context, vgFlush for an OpenVG context) bound to surface before swapping. Subsequent client API commands may be issued on that context immediately after calling eglSwapBuffers, but are not executed until the buffer exchange is completed.
If surface is a pixel buffer or a pixmap, eglSwapBuffers has no effect, and no error is generated.

看看frameworks/native/opengl/libagl/egl.cpp对eglSwapBuffers的实现:

 

EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
{
    egl_surface_t* d = static_cast<egl_surface_t*>(draw);

    ...

    // post the surface
    d->swapBuffers();

    // if it's bound to a context, update the buffer
    if (d->ctx != EGL_NO_CONTEXT) {
        d->bindDrawSurface((ogles_context_t*)d->ctx);
        // if this surface is also the read surface of the context
        // it is bound to, make sure to update the read buffer as well.
        // The EGL spec is a little unclear about this.
        egl_context_t* c = egl_context_t::context(d->ctx);
        if (c->read == draw) {
            d->bindReadSurface((ogles_context_t*)d->ctx);
        }
    }

    return EGL_TRUE;
}
  1. eglSwapBuffers之后
    应该是生成了layer供HWC合成,这部分仍在学习当中

 

 

2、具体方法使用

原文地址:https://blog.csdn.net/afei__/article/details/84867104

一、简介


EGL 是渲染 API(如 OpenGL ES)和原生窗口系统之间的接口。

通常来说,OpenGL 是一个操作 GPU 的 API,它通过驱动向 GPU 发送相关指令,控制图形渲染管线状态机的运行状态,但是当涉及到与本地窗口系统进行交互时,就需要这么一个中间层,且它最好是与平台无关的。

因此 EGL 被设计出来,作为 OpenGL 和原生窗口系统之间的桥梁。

二、功能


EGL API 是独立于 OpenGL ES 各版本标准的独立的一套 API,其主要作用是为 OpenGL 指令 创建 Context 、绘制目标 Surface 、配置 FrameBuffer 属性、Swap 提交绘制结果 等。

EGL 提供如下机制:

与设备原生窗口通信
查询绘制 surface 的可用类型和配置
创建绘制 surface
在 OpenGL ES 3.0 或其他渲染 API 之间同步渲染
管理纹理贴图等渲染资源
三、使用
1. 首先介绍一下检查错误的方法:
EGL 中大部分函数成功时都是返回 EGL_TRUE,失败返回 EGL_FALSE。

至于故障原因,需要调用如下函数获取:

EGLint eglGetError();
1
返回最近调用 EGL 函数的错误代码,如果返回 EGL_SUCCESS 说明没有错误。

2. 创建,建立本地系统和 OpenGL ES 的连接
EGLDisplay eglDisplay(EGLNativeDisplayType displayId);
1
displayId 指定显示连接,一般使用默认的 EGL_DEFAULT_DISPLAY,即返回与默认原生窗口的连接。

相关错误码:
EGL_NO_DISPLAY :连接不可用
2. 初始化
EGLBoolean eglInitialize(EGLDisplay display, // 创建步骤时返回的对象
                         EGLint *majorVersion, // 返回 EGL 主版本号
                         EGLint *minorVersion); // 返回 EGL 次版本号
1
2
3
相关错误码:
EGL_NO_DISPLAY :EGL 不能初始化
EGL_BAD_DISPLAY :没有指定有效的 display
示例:
EGLint majorVersion;
EGLint minorVersion;
EGLDisplay display;
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
    // Unable to open connection to local windowing system
}
if (!eglInitialize(display, &majorVersion, &minorVersion)) {
    // Unable to initialize EGL. Handle and recover
}
1
2
3
4
5
6
7
8
9
10
3. 确定可用的 Surface 的配置
一旦初始化了EGL,就可以确定可用渲染表面的类型和配置了。有两种方法:

先使用 eglGetConfigs 查询每个配置,再使用 eglGetConfigAttrib 找出最好的选择
指定一组需求,使用 eglChooseChofig 让 EGL 推荐最佳配置
通常使用第二种方式更简单。两种方法均得到 EGLConfig 对象。EGLConfig 包含了渲染表面的所有信息,包括可用颜色、缓冲区等其他特性。

获取所有配置的函数
EGLBoolean eglGetConfigs(EGLDisplay display, // 指定显示的连接
                         EGLConfig *configs, // 指定 GLConfig 列表
                         EGLint maxReturnConfigs, // 最多返回的 GLConfig 数
                         EGLint *numConfigs); // 实际返回的 GLConfig 数
1
2
3
4
查询 EGLConfig 配置
EGLBoolean eglGetConfigAttrib(EGLDisplay display, // 指定显示的连接
                              EGLConfig config, // 指定要查询的 GLConfig
                              EGLint attribute, // 返回特定属性
                              EGLint *value); // 返回值
1
2
3
4
让 EGL 选择配置
EGLBoolean eglChooseChofig(EGLDispay display, // 指定显示的连接
                           const EGLint *attribList, // 指定 configs 匹配的属性列表,可以为 NULL
                           EGLConfig *config,   // 调用成功,返会符合条件的 EGLConfig 列表
                           EGLint maxReturnConfigs, // 最多返回的符合条件的 GLConfig 数
                           ELGint *numConfigs );  // 实际返回的符合条件的 EGLConfig 数
1
2
3
4
5
如果 eglChooseChofig 成功返回,则返回一组匹配你的标准的 EGLConfig。

示例:
EGLint attribList[] = {
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
    EGL_RED_SIZE, 5,
    EGL_GREEN_SIZE, 6,
    EGL_BLUE_SIZE, 5,
    EGL_DEPTH_SIZE, 1,
    EGL_NONE
};

const EGLint MaxConfigs = 10;
EGLConfig configs[MaxConfigs]; // We'll only accept 10 configs
EGLint numConfigs;
if (!eglChooseConfig(dpy, attribList, configs, MaxConfigs, &numConfigs)) {
    // Something didn't work … handle error situation
} else {
    // Everything's okay. Continue to create a rendering surface
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
4. 创建渲染区域 Surface
当有了符合条件的 EGLConfig 后,就可以通过 eglCreateWindowSurface 函数创建渲染区域。

EGLSurface eglCreateWindowSurface(EGLDisplay display, // 指定显示的连接
                                  EGLConfig config, // 符合条件的 EGLConfig
                                  EGLNatvieWindowType window, // 指定原生窗口
                                  const EGLint *attribList); // 指定窗口属性列表,可为 NULL
1
2
3
4
eglCreateWindowSurface 方法在很多情况下可能失败,失败返回 EGL_NO_SURFACE。我们可以通过 eglGetError 获取相关错误。

相关错误码:
EGL_BAD_MATCH :提供了与窗口属性不匹配的 EGLConfig,或该 EGLConfig 不支持渲染到窗口
EGL_BAD_CONFIG :提供的 EGLConfig 没有得到系统支持
EGL_BAD_NATIVE_WINDOW :提供的原生窗口句柄无效
EGL_BAD_ALLOC :无法为新的窗口分配资源,或已经有和提供的原生窗口关联的 EGLConfig
示例:
EGLint attribList[] = {
  EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
  EGL_NONE
);

// 这里先省略了创建原生窗口的过程
EGLRenderSurface window = eglCreateWindowSurface(display, config, nativeWindow, attribList);

if (window == EGL_NO_SURFACE) {
    switch (eglGetError()) {
    case EGL_BAD_MATCH:
        // Check window and EGLConfig attributes to determine
        // compatibility, or verify that the EGLConfig
        // supports rendering to a window,
        break;
    case EGL_BAD_CONFIG:
        // Verify that provided EGLConfig is valid
        break;
    case EGL_BAD_NATIVE_WINDOW:
        // Verify that provided EGLNativeWindow is valid
        break;
    case EGL_BAD_ALLOC:
        // Not enough resources available. Handle and recover
        break;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
5. 创建上下文
上下文包含了操作所需的所有状态信息,OpenGL ES 3.0 必须有一个可用的上下文才能进行绘图。

EGLContext eglCreateContext(EGLDisplay display, // 指定显示的连接
                            EGLConfig config, // 前面选好的 EGLConfig
                            EGLContext shareContext, // 允许其它 EGLContext 共享数据,使用 EGL_NO_CONTEXT 表示不共享
                            const EGLint* attribList); // 指定操作的属性列表,只能接受一个属性 EGL_CONTEXT_CLIENT_VERSION
1
2
3
4
示例:
const ELGint attribList[] = {
    EGL_CONTEXT_CLIENT_VERSION, 3,
    EGL_NONE
};

EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, attribList);

if (context == EGL_NO_CONTEXT) {
    EGLError error = eglGetError();
    if (error == EGL_BAD_CONFIG) {
        // Handle error and recover
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
6. 关联上下文
指定某个 EGLContext 为当前上下文,关联特定的 EGLContext 和 EGLSurface。

EGLBoolean eglMakeCurrent(EGLDisplay display, // 指定显示的连接
                          EGLSurface draw, // EGL 绘图表面
                          EGLSurface read, // EGL 读取表面
                          EGLContext context); // 指定连接到该表面的上下文
1
2
3
4
这里存在两个 EGLSurface,具有更好的灵活性,在后续一些高级的 EGL 用法中将利用它。目前我们先把它们设置为同一个值。

7. 使用 OpenGL 相关 API 进行绘制
这个就是 OpenGL 里的东西了,这里不做介绍。

四、总结
概念
EGL 是 OpenGL 和原生窗口系统之间的桥梁接口。

使用流程
1. 创建连接
EGLDisplay eglDisplay(EGLNativeDisplayType displayId);
1
2. 初始化连接
EGLBoolean eglInitialize(EGLDisplay display, EGLint *majorVersion, EGLint *minorVersion);
1
3. 获取配置
EGLBoolean eglChooseChofig(EGLDispay display, 
                           const EGLint *attribList,
                           EGLConfig *config,
                           EGLint maxReturnConfigs,
                           ELGint *numConfigs );
1
2
3
4
5
4. 创建渲染区域
EGLSurface eglCreateWindowSurface(EGLDisplay display,
                                  EGLConfig config,
                                  EGLNatvieWindowType window,
                                  const EGLint *attribList);
1
2
3
4
5. 创建渲染上下文
EGLContext eglCreateContext(EGLDisplay display,
                            EGLConfig config,
                            EGLContext shareContext,
                            const EGLint* attribList);
1
2
3
4
6. 关联上下文和渲染区域
EGLBoolean eglMakeCurrent(EGLDisplay display,
                          EGLSurface draw,
                          EGLSurface read,
                          EGLContext context);
1
2
3
4

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!