This page looks best with JavaScript enabled

理解 EGL

 ·  ☕ 7 min read

EGL

https://www.khronos.org/registry/EGL/

​EGL 是 Khronos 的渲染 API(如 OpenGL ES 或 OpenVG)与底层原生平台窗口系统之间的接口。 它处理图形上下文管理、surface/buffer 绑定和渲染同步,并使用其他 Khronos API 实现高性能、加速、混合模式 2D 和 3D 渲染。 EGL 还提供 Khronos 之间的互操作功能,以实现 API 之间的高效数据传输——例如在运行 OpenMAX AL 的视频子系统和运行 OpenGL ES 的 GPU 之间。

提供的能力

为什么需要 EGL

​如果要使用 OpenGL ES 进行渲染,使用者需要为 OpenGL 提供存储其状态的 Context 和窗口系统。​而 EGL 提供了创建 rendering surface 的机制,OpenGL ES 和 OpenVG 等客户端 API 可以在这些 surface 上绘制。

  • 与设备的原生窗口系统通信
  • 查询绘图 surface 的可用类型和配置
  • 创建绘图 surface
  • 在 OpenGL ES 3.0 和其他图形渲染 API 之间同步渲染
  • 管理纹理贴图等渲染资源

使用

检查错误

EGL 中的大部分函数,在成功时返回 EGL_TRUE,失败时返回 EGL_FALSE。但是错误代码需要主动调用 elgGetError 获取。 EGLError eglGetError() 返回特定线程中最近调用的 EGL 函数的错误代码,如果返回 EGL_SUCCESS,说明没有错误。

这样的错误检查机制,和 OpenGL 一样,不会在函数的调用结束时直接返回错误代码。其思想是让开发者只在开发和调试期间检查错误,当程序能够正常运作时,就可以减少错误检查。

与窗口系统通信

因为每个窗口系统都有不同的语义,所以 EGL 提供了不透明类型 EGLDisplay。该类型封装了所有系统相关的,用于和原生窗口系统通信的接口 —— EGLDisplay

任何 EGL 程序执行的第一个操作是创建和初始化与本地 EGLDisplay 的连接:

1
EGLDiaply mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
1
2
// C function EGLDisplay eglGetDisplay ( EGLNativeDisplayType display_id )
public static native EGLDisplay eglGetDisplay(int display_id);

eglGetDisplay 函数负责打开与 EGLDisplay 服务的连接,display_id 指定显示连接,默认为 EGL_DEFAULT_DISPLAY

定义 EGLNativeDisplayType 是为了匹配原生窗口系统的显示类型。例如,在 Windows 上,EGLNativeDisplayType 将被实现为一个 HDC(Windows 设备上下文的句柄)。但是为了方便地将代码转移到不同操作系统和平台,应该使用 EGL_DEFAULT_DISPLAY,返回与默认原生显示的连接。

初始化 EGL

成功的打开 EGLDisplay 连接之后,需要初始化 EGL:

1
2
3
4
5
6
7
8
// C function EGLBoolean eglInitialize ( EGLDisplay dpy, EGLint *major, EGLint *minor )
public static native boolean eglInitialize(
    EGLDisplay dpy,
    int[] major,
    int majorOffset,
    int[] minor,
    int minorOffset
)
  • dpy: 指定 EGLDisplay
  • major: 指定 EGL 实现返回的主版本号
  • minor: 指定 EGL 实现返回的次版本号

这个函数初始化 EGL 内部数据结构,返回 EGL 实现的主版本号和次版本号。如果 EGL 无法初始化,这个调用将返回 EGL_FALSE,并将 EGL 错误代码设置为:

  • EGL_BAD_DISPLAY —— 没有指定有效的 EGLDisplay
  • EGL_NOT_INITIALIZED —— EGL 不能初始化
1
2
3
4
5
int[] version = new int[2];
if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
    mEGLDisplay = null;
    throw new RuntimeException("unable to initialize EGL14");
}

EGLConfig

包含 EGL 启用的 surface 的所有信息的 EGL 内部数据结构

确定可用 surface 配置

一旦初始化了 EGL,就可以确定可用渲染 surface 的类型和配置了,有两种方法:

  1. 查询每个 surface 配置,找出最后选择
  2. 指定一组需求,让 EGL 推荐最佳匹配
1
2
3
4
5
6
7
8
9
// C function EGLBoolean eglGetConfigs ( EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config )
public static native boolean eglGetConfigs(
    EGLDisplay dpy,
    EGLConfig[] configs,
    int configsOffset,
    int config_size,
    int[] num_config,
    int num_configOffset
);

eglGetConfigs 函数可以查询底层窗口系统支持的所有 EGL Surface 的配置。

调用 eglGetConfigs 有两种方式:

  1. 获取系统可用的 EGLConfig 的数量
1
2
int[] numConfig = new int[1];
eglGetConfigs(mEGLDiaplay, null, 0, numConfig, 0);

系统可用的 EGLConfig 的数量将写入到 numConfig 的第一个元素中

  1. 获取指定数量的 EGLConfig
1
2
3
EGLConfig[] configs = new EGLConfig[2];
int[] numConfigs = new int[1];
eglGetConfigs(mEGLDiaplay, configs, configs.length, numConfig, 0);

configs 数据中将会写入 2 个 EGLConfig 数据

查询 EGLConfig 属性

EGLConfig 包含了关于 EGL 启用的 Surface 的所有信息。包括关于可用颜色、与配置相关的其他缓冲区、Surface 类型和许多其他特性。

1
2
3
4
5
6
7
8
// C function EGLBoolean eglGetConfigAttrib ( EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value )
public static native boolean eglGetConfigAttrib(
    EGLDisplay dpy,
    EGLConfig config,
    int attribute,
    int[] value,
    int offset
);

eglGetConfigAttrib 函数用于查询 EGLConfig 中特定的属性值。

EGL 属性表 https://www.khronos.org/files/egl-1-4-quick-reference-card.pdf

Attribute Name Type Sorting Selection Description and default value
EGL_BUFFER_SIZE integer (4) (-) AtLeast The total color component bits in the color buffer. Default is 0.
EGL_RED_SIZE integer (3) (+) AtLeast EGL_DONT_CARE or the number of bits of Red in the color buffer. 0 means no Red is in the color buffer.
EGL_GREEN_SIZE integer (3) (+) AtLeast EGL_DONT_CARE or the number of bits of Green in the color buffer. 0 means no Green is in the color buffer.
EGL_BLUE_SIZE integer (3) (+) AtLeast EGL_DONT_CARE or the number of bits of Blue in the color buffer. 0 means no Blue is in the color buffer.
EGL_LUMINANCE_SIZE integer (3) (+) AtLeast The number of bits of Luminance in the color buffer. 0 means no Luminance in the color buffer.
EGL_ALPHA_SIZE integer (3) (+) AtLeast The number of bits of Alpha in the color buffer. 0 means no Alpha in the color buffer.
EGL_ALPHA_MASK_SIZE integer (9) (-) AtLeast The number of bits of Alpha Mask in the color buffer. 0 means no Alpha Mask in the color buffer.
EGL_BIND_TO_TEXTURE_RGB boolean (°) Exact EGL_DONT_CARE, EGL_TRUE, or EGL_FALSE. EGL_TRUE if bindable to RGB textures.
EGL_BIND_TO_TEXTURE_RGBA boolean (°) Exact EGL_DONT_CARE, EGL_TRUE, or EGL_FALSE. EGL_TRUE if bindable to RGBA textures.
EGL_COLOR_BUFFER_TYPE enum (2) (°) Exact EGL_DONT_CARE, EGL_RGB_BUFFER, or EGL_LUMINANCE_BUFFER to represent the color buffer type.
EGL_CONFIG_CAVEAT enum (1) (+) Exact EGL_DONT_CARE or one of the following values indicating caveats for the configuration: EGL_NONE; EGL_SLOW_CONFIG - rendering to a surface may run at reduced performance; EGL_NON_CONFORMANT_CONFIG - rendering to a surface will not pass the required OpenGL ES conformance tests
EGL_CONFIG_ID integer (11) (-) Exact EGL_DONT_CARE indicating unique EGLConfig identifier.
EGL_CONFORMANT bitmask (°) Mask Indicates whether contexts created are conformant. May be 0 or one or more of the following values: EGL_OPENGL_BIT (OpenGL 1.x or 2.x), EGL_OPENGL_ES_BIT (OpenGL ES 1.x), EGL_OPENGL_ES2_BIT (OpenGL ES 2.x), EGL_OPENVG_BIT (OpenVG 1.x)
EGL_DEPTH_SIZE integer (7) (-) AtLeast A positive integer indicating the number of bits of Z in the depth buffer. Default is 0.
EGL_LEVEL integer (°) Exact The frame buffer level. Default is 0.
EGL_MATCH_NATIVE_PIXMAP integer (°) Special EGL_NONE or handle of a valid native pixmap
EGL_MAX_PBUFFER_WIDTH integer (°) The maximum width of pbuffer. Default is 0.
EGL_MAX_PBUFFER_HEIGHT integer (°) The maximum height of pbuffer. Default is 0.
EGL_MAX_PBUFFER_PIXELS integer (°) The maximum size of pbuffer. Default is 0.
EGL_MAX_SWAP_INTERVAL integer (°) Exact EGL_DONT_CARE or the maximum value that can be passed to eglSwapInterval; indicating the maximum number of swap intervals that will elapse before a buffer swap takes place after calling eglSwapBuffers.
EGL_MIN_SWAP_INTERVAL integer (°) Exact EGL_DONT_CARE or the minimum value that can be passed to eglSwapInterval; indicating the minimum number of swap intervals that will elapse before a buffer swap takes place after calling eglSwapBuffers.
EGL_NATIVE_RENDERABLE boolean (°) Exact EGL_DONT_CARE, EGL_TRUE, or EGL_FALSE. EGL_TRUE if native rendering APIs can render to surface EGL_NATIVE_VISUAL_ID integer (°) Default is 0.
EGL_NATIVE_VISUAL_TYPE integer (10) (+) Exact EGL_DONT_CARE, EGL_NONE, or the native visual type of the associated visual.
EGL_RENDERABLE_TYPE bitmask (°) Mask Indicates which client APIs are supported. May be one or more of the following values: EGL_OPENGL_BIT (OpenGL 1.x or 2.x) EGL_OPENGL_ES_BIT (OpenGL ES 1.x), EGL_OPENGL_ES2_BIT (OpenGL ES 2.x), EGL_OPENVG_BIT (OpenVG 1.x)
EGL_SAMPLE_BUFFERS integer (5) (-) AtLeast The number of multisample buffers. Default is 0.
EGL_SAMPLES integer (6) (-) AtLeast The number of samples per pixel. Default is 0.
EGL_STENCIL_SIZE integer (8) (-) AtLeast The number of bits of Stencil in the stencil buffer. 0 means no Stencil in the stencil buffer.
EGL_SURFACE_TYPE bitmask (°) Mask The types of EGL surfaces are supported. May be one or more of EGL_WINDOW_BIT, EGL_PIXMAP_BIT, EGL_PBUFFER_BIT, MULTISAMPLE_RESOLVE_BOX_BIT, EGL_VG_ALPHA_FORMAT_PRE_BIT, EGL_SWAP_BEHAVIOR_PRESERVED_BIT, EGL_VG_COLORSPACE_LINEAR_BIT.
EGL_TRANSPARENT_TYPE enum (°) Exact EGL_NONE means windows created with the EGLConfig will not have any transparent pixels; EGL_TRANSPARENT_RGB means the EGLConfig supports transparency.
EGL_TRANSPARENT_RED_VALUE integer (°) Exact EGL_DONT_CARE or an integer in the range 0..2^EGL_RED_SIZE - 1 to indicate the transparent red value.
EGL_TRANSPARENT_GREEN_VALUE integer (°) Exact EGL_DONT_CARE or an integer in the range 0..2^EGL_GREEN_SIZE - 1 to indicate the transparent green value.
EGL_TRANSPARENT_BLUE_VALUE integer (°) Exact EGL_DONT_CARE or an integer in the range 0..2^EGL_BLUE_SIZE - 1 to indicate the transparent blue value.

让 EGL 选择配置

可以看到 EGL 有很多属性,如果每个都由我们自己去配置,不免有些麻烦。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// C function EGLBoolean eglChooseConfig ( EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config )
public static native boolean eglChooseConfig(
    EGLDisplay dpy,
    int[] attrib_list,
    int attrib_listOffset,
    EGLConfig[] configs,
    int configsOffset,
    int config_size,
    int[] num_config,
    int num_configOffset
);

EGL 提供了 eglChooseConfig 函数让 EGL 根据你传入的部分属性,为你选择匹配的 EGLCofig,这样你只需要提供几个自己关心的属性和其对应的属性值就可以获取一个完整的 EGLConfig。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 示例
int[] attribList = {
        EGL14.EGL_RED_SIZE, 8,
        EGL14.EGL_GREEN_SIZE, 8,
        EGL14.EGL_BLUE_SIZE, 8,
        EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
        EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT | EGLExt.EGL_OPENGL_ES3_BIT_KHR,
        EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
        numConfigs, 0)) {
    throw new RuntimeException("unable to find RGB888+recordable ES2 EGL config");
}

创建屏幕上的渲染区域

1
2
3
4
5
6
7
8
// C function EGLSurface eglCreateWindowSurface ( EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list )
private static native EGLSurface eglCreateWindowSurface(
    EGLDisplay dpy,
    EGLConfig config,
    Object win,
    int[] attrib_list,
    int offset
);

eglCreateWindowSurface 函数用于创建 EGL 窗口 Surface。
eglCreateWindowSurface() 将「窗口对象」(win)作为参数,在 Android 上,该对象是 Surface。
Surface 是 BufferQueue 的生产方端。消费方(SurfaceView、SurfaceTexture、TextureView 或 ImageReader)创建 Surface。当调用 eglCreateWindowSurface() 时,EGL 将创建一个新的 EGLSurface 对象,并将其连接到窗口对象的 BufferQueue 的生产方接口。此后,渲染到该 EGLSurface 会导致一个缓冲区离开队列、进行渲染,然后排队等待消费方使用。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 示例
int[] surfaceAttribs = {
        EGL14.EGL_NONE
};

mEGLSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], mSurface, surfaceAttribs, 0);
checkEglError("eglCreateWindowSurface");
if (mEGLSurface == null) {
    throw new RuntimeException("surface was null");
}

创建屏幕外的渲染区域

除了可以用 OpenGL ES 3.0 在屏幕上的窗口渲染之外,还可以渲染离屏缓冲区(pbuffer)。pbuffer 可以利用 OpenGL ES 中的任何硬件加速,pbuffer 常用于生成纹理贴图。

1
2
3
4
5
6
7
// C function EGLSurface eglCreatePbufferSurface ( EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list )
public static native EGLSurface eglCreatePbufferSurface(
    EGLDisplay dpy,
    EGLConfig config,
    int[] attrib_list,
    int offset
);

eglCreatePbufferSurface 用于创建 Pbuffer。和 Window Surface 一样,Pbuffer 支持所有 OpenGL ES 渲染机制,主要区别在于:无法在屏幕上显示 Pbuffer,渲染完成后,一般是从 Pbuffer 将数据复制到程序或者将 Pbuffer 的绑定更改为纹理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// 示例
int[] surfaceAttribs = {
        EGL14.EGL_WIDTH, width,
        EGL14.EGL_HEIGHT, height,
        EGL14.EGL_NONE
};
mEGLSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, configs[0], surfaceAttribs, 0);
checkEglError("eglCreatePbufferSurface");
if (mEGLSurface == null) {
    throw new RuntimeException("surface was null");
}

创建渲染上下文

渲染上下文是 OpenGL ES 的内部数据结构,包含 OpenGL 操作需要的所有状态信息。

1
2
3
4
5
6
7
8
// C function EGLContext eglCreateContext ( EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list )
public static native EGLContext eglCreateContext(
    EGLDisplay dpy,
    EGLConfig config,
    EGLContext share_context,
    int[] attrib_list,
    int offset
);

eglCreateContext 函数负责创建上下文 Context。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 示例
int[] attrib_list = {
        EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
        EGL14.EGL_NONE
};
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], shareContext, attrib_list, 0);
checkEglError("eglCreateContext");
if (mEGLContext == null) {
    throw new RuntimeException("null context");
}

当一个程序中存在多个 EGLContext 时,在执行渲染前,需要将特定的 EGLContext 和渲染 Surface 进行关联。

1
2
3
4
5
6
7
// C function EGLBoolean eglMakeCurrent ( EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx )
public static native boolean eglMakeCurrent(
    EGLDisplay dpy,
    EGLSurface draw,
    EGLSurface read,
    EGLContext ctx
);

eglMakeCurrent 函数就是执行这个过程的,通常叫做指定当前上下文。

Android

EGLSurface 和 OpenGL ES

为了创建 OpenGL ES 上下文并为 OpenGL ES 渲染提供窗口系统,Android 使用 EGL 库。OpenGL ES 调用用于渲染纹理多边形,而 EGL 调用用于将渲染放到屏幕上。在使用 OpenGL ES 进行绘制之前,您需要创建 OpenGL 上下文。在 EGL 中,这意味着要创建一个 EGLContext 和一个 EGLSurface。 OpenGL ES 操作适用于当前上下文,该上下文通过线程局部存储(ThreadLocal)访问,而不是作为参数进行传递。渲染代码应该在当前 OpenGL ES 线程(而不是界面线程)上执行。

iOS

EAGL

Apple 提供了自己的 EGL API 的实现 —— EAGL

Support the author with
alipay QR Code
wechat QR Code

Yang
WRITTEN BY
Yang
Developer