4. EGL基础
在 Wayland 与 OpenGL ES 集成的技术栈中,EGL 是连接本地窗口系统与图形 API 的“桥梁层”。OpenGL ES 本身只负责渲染指令和 GPU 管线,它并不关心窗口从哪里来、缓冲区如何显示、垂直同步如何等待。EGL 的职责正是把这些平台相关的问题抽象出来,让 OpenGL ES 可以在 Wayland、X11、GDM 甚至无显示环境下以统一方式工作。理解 EGL 的五个核心概念——作用、显示连接、表面、上下文、配置选择,是后续在 Wayland 合成器中创建渲染环境的前提。
一、EGL 的核心作用
EGL 位于 OpenGL ES 与原生窗口系统之间,主要完成四件事:
- 管理显示连接:与 Wayland 的
wl_display建立 EGLDisplay,使 EGL 知道渲染结果要送到哪个显示服务器。 - 管理渲染表面:创建 EGLSurface,把 OpenGL ES 的绘制目标与 Wayland 的
wl_surface或wl_egl_window关联起来。 - 管理渲染上下文:创建 EGLContext,保存 OpenGL ES 的状态机、着色器、纹理、缓冲区等资源。
- 协商配置:通过 EGLConfig 选择颜色格式、深度、模板、多重采样等帧缓冲属性,确保渲染结果与合成器要求匹配。
没有 EGL,OpenGL ES 只能渲染到离屏缓冲区,无法直接呈现在 Wayland 桌面上。因此,EGL 是“渲染”与“显示”之间的粘合剂。
二、显示连接:EGLDisplay 与 Wayland
EGLDisplay 代表一个显示连接。在 Wayland 环境下,通常通过 eglGetPlatformDisplay 并传入 EGL_PLATFORM_WAYLAND_KHR 来创建,参数是 Wayland 的 wl_display 指针。这样 EGL 就知道后续的窗口、缓冲区、同步事件都要通过 Wayland 协议与合成器交互。
wl_display *display = wl_display_connect(NULL);
EGLDisplay egl_display = eglGetPlatformDisplay(
EGL_PLATFORM_WAYLAND_KHR,
display,
NULL);
eglInitialize(egl_display, &major, &minor);
初始化后,EGL 会与 Wayland 合成器协商可用的像素格式和缓冲区类型。如果显示连接失败,后续所有表面和上下文都无法创建,因此这是第一步,也是排障时最先检查的环节。
三、表面:EGLSurface 与窗口系统
EGLSurface 是 OpenGL ES 的绘制目标,分为窗口表面和离屏表面。在 Wayland 集成中,窗口表面通常由 wl_egl_window 创建,再通过 eglCreateWindowSurface 包装成 EGLSurface。它负责把 GPU 渲染出的像素缓冲区提交给 Wayland 合成器显示。
wl_egl_window *egl_window = wl_egl_window_create(
wl_surface, width, height);
EGLSurface egl_surface = eglCreateWindowSurface(
egl_display, config, egl_window, NULL);
表面还承担交换缓冲区的职责。调用 eglSwapBuffers 时,EGL 会把当前渲染完成的缓冲区提交给 Wayland,并等待合成器的帧回调,从而实现垂直同步和流畅显示。如果表面尺寸与窗口尺寸不一致,会出现拉伸或裁剪,因此窗口 resize 后必须同步更新 wl_egl_window 和 EGLSurface。
四、上下文:EGLContext 与 OpenGL ES 状态
EGLContext 保存 OpenGL ES 的所有渲染状态,包括当前绑定的着色器程序、纹理、帧缓冲、混合模式等。创建上下文时需要指定客户端 API 版本,例如 OpenGL ES 3.2 或 2.0。上下文必须与表面绑定后才能执行绘制命令。
EGLint context_attribs[] = {
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
};
EGLContext context = eglCreateContext(
egl_display, config, EGL_NO_CONTEXT, context_attribs);
eglMakeCurrent(egl_display, egl_surface, egl_surface, context);
在 Wayland 合成器中,通常每个输出或每个渲染线程拥有独立的上下文。多线程渲染时,可以通过共享上下文来共享纹理和缓冲区,但同一时刻一个上下文只能在一个线程中处于 current 状态。
五、配置选择:EGLConfig 与属性匹配
EGLConfig 描述帧缓冲的格式和能力。选择配置时,需要根据渲染需求指定颜色分量位数、深度缓冲、模板缓冲、多重采样、表面类型等属性。Wayland 合成器通常要求支持 EGL_WINDOW_BIT 和 EGL_PBUFFER_BIT,并匹配合成器支持的像素格式。
| 属性 | 常见取值 | 作用 |
|---|---|---|
| EGL_SURFACE_TYPE | EGL_WINDOW_BIT | 支持窗口表面 |
| EGL_RED_SIZE | 8 | 红色分量位数 |
| EGL_GREEN_SIZE | 8 | 绿色分量位数 |
| EGL_BLUE_SIZE | 8 | 蓝色分量位数 |
| EGL_ALPHA_SIZE | 8 | 透明度分量位数 |
| EGL_DEPTH_SIZE | 24 | 深度缓冲位数 |
| EGL_RENDERABLE_TYPE | EGL_OPENGL_ES3_BIT | 支持 OpenGL ES 3.x |
选择配置时,通常先用 eglChooseConfig 获取匹配列表,再从中挑选最合适的一项。如果属性要求过严,可能返回空列表,导致后续创建表面失败。因此,实际项目中常采用“先宽松匹配,再按优先级筛选”的策略。
六、案例与场景:Wayland 合成器中的 EGL 初始化
在一个基于 Wayland 的合成器或客户端中,典型的 EGL 初始化流程如下:
- 连接 Wayland 显示:
wl_display_connect。 - 获取 EGLDisplay:
eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wl_display, NULL)。 - 初始化 EGL:
eglInitialize,并检查版本。 - 选择配置:
eglChooseConfig,匹配 RGBA、深度、OpenGL ES 3 等属性。 - 创建窗口表面:
wl_egl_window_create+eglCreateWindowSurface。 - 创建上下文:
eglCreateContext,指定客户端版本。 - 绑定上下文:
eglMakeCurrent。 - 渲染循环:OpenGL ES 绘制 →
eglSwapBuffers→ 等待帧回调。
提示:在 Wayland 下,eglSwapBuffers 会触发缓冲区提交和帧回调。如果合成器没有及时返回帧回调,渲染线程可能阻塞,因此需要合理设计渲染循环,避免在帧回调中执行耗时操作。
七、常见误区与复盘
- 误区一:先创建上下文再选配置。实际上 EGLConfig 是创建表面和上下文的前提,顺序不能颠倒。
- 误区二:忽略 EGL_SURFACE_TYPE。如果配置不支持
EGL_WINDOW_BIT,eglCreateWindowSurface会失败。 - 误区三:窗口 resize 后不更新表面。Wayland 窗口尺寸变化后,必须调用
wl_egl_window_resize并重新绑定,否则渲染区域错位。 - 误区四:多线程共用同一个上下文。同一上下文不能同时在多个线程中 current,需要加锁或使用共享上下文。
- 误区五:忘记检查 EGL 错误。每次 EGL 调用后应检查
eglGetError,否则问题会延迟到渲染阶段才暴露。
核心结论:EGL 在 Wayland 与 OpenGL ES 之间承担显示连接、表面管理、上下文管理和配置协商四项职责。掌握 EGLDisplay、EGLSurface、EGLContext、EGLConfig 的创建顺序与匹配关系,是后续实现 Wayland 合成器渲染、客户端绘制和 GPU 缓冲区共享的基础。