Emscripten 中的 EGL 支持

注意

本文正在建设中。

Khronos Group 发布了一个名为 EGL 的规范,它是一个 API,负责(除其他任务外)图形上下文创建、渲染表面管理以及不同 Khronos Group 图形 API(OpenGL、OpenGL ES、OpenVG)之间的互操作。有关详细信息,请参阅 Khronos EGL 网页.

目前,EGL 在操作系统/图形驱动程序供应商中并不广泛使用。最显著的采用是在 Android 架构中,EGL 是在使用 Android NDK 时为 OpenGL ES 1&2 创建渲染上下文的首要方法。此外,Mesa 在其 图形驱动程序中实现了 EGL 规范。

Emscripten 还提供 EGL v1.4 规范的实现。这允许 C/C++ 客户端代码使用(几乎)统一的代码库来在 Web、Linux(使用 Mesa)和 Android NDK 之间创建 GLES2(WebGL)渲染上下文。Emscripten 中的 EGL 规范实现并不完美,请参阅本页末尾了解状态图表。

EGL 是什么?

有点令人失望的是,EGL 并不是一个能够独立完成初始化 GLES2 图形渲染(在任何平台上,不仅仅是 Emscripten)并监督各种相关任务的完整解决方案。该规范的范围有限,缺少一些功能。特别是,EGL 无法帮助完成以下任务

  • 创建渲染窗口。EGL 规范没有指定要渲染到的目标窗口是如何创建的。必须使用特定于平台的本机窗口系统函数(X11、Win32 API、ANativeWindow)首先创建一个渲染窗口。

  • 以任意像素增量指定渲染窗口大小。EGL 没有任何功能可以请求主渲染窗口的所需大小,或调整其大小。

  • 指定全屏视频模式/屏幕分辨率。EGL 不能用来控制渲染是在窗口模式还是全屏模式下,或者在运行时在这些模式之间切换。

因此,对于每个平台(包括 Emscripten),都存在特定于平台的方法来执行这些任务。

如何使用 EGL 创建 WebGL 上下文?

在 Web 环境中,WebGL 是用于 3D 加速渲染的技术。WebGL 几乎与 GLES2 相同,并且由于 EGL 根本不适用于 WebGL,因此在本页中,WebGL 和 GLES2 术语可互换使用。因此,要创建 WebGL 上下文,可以使用 EGL,并根据其措辞,创建一个 GLES2 上下文。

初始化

执行以下步骤以使用 EGL 创建 GLES2 上下文

  1. 通过调用 eglGetDisplay 获取 EGLDisplay 对象的句柄。

  2. 通过调用 eglInitialize 初始化该显示器上的 EGL。

  3. 调用 eglGetConfigs 和/或 eglChooseConfig 一次或多次以查找表示所需主渲染目标参数的 EGLConfig。要检查 EGLConfig 的属性,请调用 eglGetConfigAttrib

  4. 此时,可以使用可用的任何特定于平台的函数(X11Win32 APIANativeWindow)来设置要渲染到的本机窗口。对于 Emscripten,此步骤不适用,可以跳过。

  5. 通过调用 eglCreateWindowSurface,使用有效的显示和配置参数创建一个主渲染目标表面 (EGLSurface)。将窗口和属性列表参数设置为 null。

  6. 通过调用 eglCreateContext 创建一个 GLES2 渲染上下文 (EGLContext),然后调用 eglMakeCurrent 激活渲染上下文。在创建上下文时,指定上下文属性 EGL_CONTEXT_CLIENT_VERSION == 2

完成这些步骤后,将获得一组 EGL 对象 EGLDisplayEGLConfigEGLSurfaceEGLContext,它们代表主 GLES2 渲染上下文。

清理

取消初始化时清理的顺序如下

  1. 通过调用 eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) 释放当前活动的渲染上下文。

  2. 通过对它调用 eglDestroyContext 取消初始化 EGLContext 对象。

  3. 通过对它们调用 eglDestroySurface 销毁所有初始化的 EGLSurface 对象。

  4. 通过调用 eglTerminate(display) 取消初始化 EGL。

  5. 删除本机渲染窗口。此步骤不适用于 Emscripten。

示例代码

使用 EGL 初始化 WebGL 上下文的示例代码可以在 emscripten/test/third_party/glbook 目录中的示例应用程序中找到,更具体地说是在 esUtil.c 文件中。

实现状态和注意事项

本节列出了所有 EGL v1.4 函数,并描述了它们在 Emscripten 中的当前实现状态。

完全实现

  • eglInitializeeglGetConfigseglQueryContexteglQueryStringeglQuerySurfaceeglGetCurrentContextglGetCurrentSurfaceeglGetCurrentDisplayeglReleaseThreadeglDestroySurfaceeglDestroyContext:已实现,应根据 EGL v1.4 规范工作。

  • eglSwapBuffers:已实现,但此函数在 WebGL 下无法真正控制交换行为。在 Emscripten 下,调用此函数是可选的。在 WebGL 中,只有在代码将执行权返回到浏览器后,才会将显示屏的内容始终呈现到屏幕上,也就是说,当您从传递给 emscripten_set_main_loop() 的滴答回调处理程序返回时。但是,eglSwapBuffers 函数仍然可以用来检测何时发生 GL 上下文丢失事件。

  • eglGetDisplay:已根据规范实现。Emscripten 不使用多个 EGLNativeDisplayType 对象,因此在此处传入 EGL_DEFAULT_DISPLAY。实际上,Emscripten 目前忽略了为 Linux 模拟目的在此处传入的任何值,但您不应在将来依赖此值。

  • eglGetError:已根据规范实现。

    重要

    根据规范,eglGetError 报告单个最新的错误,而不是所有先前错误的列表。不要像调用 glGetError 一样在循环中调用此函数。

部分实现

  • eglChooseConfig:已实现为存根,但此函数不执行搜索/过滤,目前与 eglGetConfigs 相同 (问题 #643).

  • eglGetConfigAttrib: 已实现。查询属性 EGL_BUFFER_SIZE, EGL_ALPHA_SIZE, EGL_BLUE_SIZE, EGL_GREEN_SIZE, EGL_RED_SIZE, EGL_DEPTH_SIZEEGL_STENCIL_SIZE 当前返回硬编码的默认值 (issue #644)。 属性 EGL_MIN_SWAP_INTERVALEGL_MAX_SWAP_INTERVAL 目前没有任何功能。 相反,调用 emscripten_set_main_loop() 来指定主循环更新速率。

  • eglCreateWindowSurface: 已实现,但无法多次调用此函数来创建多个渲染窗口。

  • eglCreateContext: 作为存根实现。 无法多次调用此函数来创建多个上下文。

  • eglBindAPI, eglQueryAPI: 已实现,尽管这些函数在 Emscripten 中几乎没有用处,因为只支持 GLES2 客户端 API。

  • eglWaitClient, eglWaitNative: 作为无操作函数实现。 这些在 Emscripten 中没有意义。

  • eglSwapInterval: 作为无操作存根实现。 目前此函数无法设置 vsync 间隔,也无法启用/禁用它。

  • eglMakeCurrent: 作为无操作存根实现。

  • eglTerminate: 作为无操作函数存根实现。 JavaScript 应用程序通常不会手动关闭,但在关闭浏览器或切换网页时,浏览器会自动管理所有拆卸。 因此,此函数在 Emscripten 中没有关键作用。

  • eglGetProcAddress: 已实现,实验性。

缺少的功能

以下函数目前未实现

  • eglCreatePbufferSurface, eglCreatePixmapSurface, eglCreatePbufferFromClientBuffer, eglSurfaceAttrib, eglBindTexImage, eglReleaseTexImage, eglWaitGL, eglCopyBuffers.

重要

不要调用 Emscripten 代码中的这些函数,否则应用程序将在尝试执行未定义函数时停止。

EGL 扩展

目前,Emscripten 在 EGL Extension Registry 中没有实现任何扩展。