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 获取匹配列表,再从中挑选最合适的一项。如果属性要求过严,可能返回空列表,导致后续创建表面失败。因此,实际项目中常采用“先宽松匹配,再按优先级筛选”的策略。

EGL 桥接层:连接 Wayland 显示与 OpenGL ES 渲染 Wayland 合成器 wl_display wl_surface 帧回调 / 缓冲区提交 EGL 桥接层 EGLDisplay EGLSurface EGLConfig OpenGL ES 渲染指令 着色器 / 纹理 GPU 管线 EGLContext OpenGL ES 状态机 着色器 / 纹理 / FBO eglMakeCurrent 典型调用顺序 wl_display_connect → eglGetPlatformDisplay → eglInitialize → eglChooseConfig → eglCreateWindowSurface → eglCreateContext → eglMakeCurrent → eglSwapBuffers

六、案例与场景:Wayland 合成器中的 EGL 初始化

在一个基于 Wayland 的合成器或客户端中,典型的 EGL 初始化流程如下:

  1. 连接 Wayland 显示:wl_display_connect。
  2. 获取 EGLDisplay:eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, wl_display, NULL)。
  3. 初始化 EGL:eglInitialize,并检查版本。
  4. 选择配置:eglChooseConfig,匹配 RGBA、深度、OpenGL ES 3 等属性。
  5. 创建窗口表面:wl_egl_window_create + eglCreateWindowSurface。
  6. 创建上下文:eglCreateContext,指定客户端版本。
  7. 绑定上下文:eglMakeCurrent。
  8. 渲染循环: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 缓冲区共享的基础。