3. EGL 核心概念

EGL 是 OpenGL ES 与底层原生窗口系统之间的“胶水层”。它不负责绘制三角形,而是负责回答三个关键问题:画到哪里、用什么规则画、画完如何呈现。要理解 EGL,必须先厘清它的一组核心术语:Display、Config、Context、Surface,以及 Surface 的几种具体形态——Window Surface、Pbuffer、Pixmap。它们之间不是并列关系,而是层层依赖、协同工作的对象模型。

一句话概括依赖链:Display → Config → Context / Surface → 绑定后渲染 → Swap 呈现。Context 是“画笔与状态”,Surface 是“画布”,Config 是“画布规格与画笔能力”,Display 是“画布所在的桌面/显示服务器连接”。

一、核心对象逐一定义

Display(显示连接):代表 EGL 与底层窗口系统之间的一个连接。在 Android 上通常对应默认显示设备,在 X11 上对应一个 X Display 连接,在 Wayland 上对应 wl_display。它是一切 EGL 资源的根句柄,创建 Config、Context、Surface 之前必须先获得可用的 Display。多屏或离屏渲染场景下,可能存在多个 Display。

Config(配置):描述帧缓冲的“规格清单”,包括颜色缓冲位数(R/G/B/A)、深度缓冲位数、模板缓冲位数、多重采样样本数、Surface 类型(WINDOW / PBUFFER / PIXMAP)、渲染 API(OpenGL ES 版本)等。Config 不是画布本身,而是创建 Context 和 Surface 时必须匹配的“能力契约”。选择 Config 的本质是:在硬件支持范围内,找到满足渲染需求且性能合适的一组参数。

Context(上下文):保存 OpenGL ES 的全部渲染状态,例如当前绑定的着色器程序、纹理、混合模式、视口、矩阵栈等。可以把 Context 理解为“画笔和调色板的状态集合”。Context 本身不存储像素,它必须绑定到一个 Surface 才能进行绘制。一个 Context 在同一时刻只能绑定到一个绘制 Surface,但可以切换绑定到不同 Surface。

Surface(表面):真正承载像素的缓冲区,是渲染输出的目标。EGL 中 Surface 是一个抽象概念,具体分为 Window Surface、Pbuffer、Pixmap 三类。Context 通过 eglMakeCurrent 与 Surface 绑定后,OpenGL ES 的绘制命令才会写入该 Surface 对应的缓冲区。

二、三种 Surface 形态对比

Window Surface 是“可上屏”的表面,通常由原生窗口创建,支持双缓冲与 Swap 呈现,适合实时显示。Pbuffer 是“离屏像素缓冲”,不直接显示,常用于离屏渲染、生成纹理、GPU 计算中间结果。Pixmap 是“绑定到原生像素图的表面”,在部分平台上用于将渲染结果直接写入系统位图,但现代移动平台支持有限,Android 上基本不使用。

术语 本质 是否可显示 典型用途 关键 API
Display 与窗口系统的连接 — 获取 EGL 资源入口 eglGetDisplay / eglInitialize
Config 帧缓冲规格模板 — 匹配 Context/Surface 能力 eglChooseConfig
Context 渲染状态集合 否 保存 GL 状态、执行绘制 eglCreateContext
Window Surface 可上屏双缓冲表面 是 实时画面显示 eglCreateWindowSurface
Pbuffer 离屏像素缓冲 否 离屏渲染、纹理生成 eglCreatePbufferSurface
Pixmap 原生像素图表面 否 写入系统位图(平台受限) eglCreatePixmapSurface

三、对象关系与生命周期

下图展示了 EGL 核心对象之间的依赖与绑定关系。Display 是根,Config 由 Display 查询得到;Context 和 Surface 都依赖 Config 创建;Context 通过 eglMakeCurrent 绑定到 Surface 后,OpenGL ES 才能向该 Surface 写入像素;Window Surface 通过 eglSwapBuffers 将后台缓冲提交到屏幕。

EGL 核心对象依赖与绑定关系 Display 窗口系统连接 Config 帧缓冲规格模板 Context 渲染状态集合 Surface 像素承载抽象 Window Surface 可上屏 / Swap Pbuffer 离屏像素缓冲 Pixmap 原生位图表面 eglChooseConfig eglCreateContext eglCreateXxxSurface eglMakeCurrent 绑定 OpenGL ES 绘制命令 写入当前绑定的 Surface

四、典型初始化流程与代码骨架

下面是一段典型的 EGL 初始化骨架,展示了各术语在代码中的出现顺序。注意每一步的失败处理都不可省略,尤其是 Config 匹配失败和 Context 创建失败,在低端设备或多版本 GL 共存时非常常见。

EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, &major, &minor);

const EGLint configAttribs[] = {
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_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, 16,
    EGL_NONE
};
EGLConfig config;
EGLint numConfigs;
eglChooseConfig(display, configAttribs, &config, 1, &numConfigs);

const EGLint contextAttribs[] = {
    EGL_CONTEXT_CLIENT_VERSION, 2,
    EGL_NONE
};
EGLContext context = eglCreateContext(display, config,
                                      EGL_NO_CONTEXT, contextAttribs);

EGLSurface surface = eglCreateWindowSurface(display, config,
                                            nativeWindow, NULL);

eglMakeCurrent(display, surface, surface, context);
// ... OpenGL ES 绘制 ...
eglSwapBuffers(display, surface);

五、常见误区与复盘要点

  • 误区一:把 Config 当成 Surface。Config 只是规格描述,不持有像素;真正持有像素的是 Surface。选择 Config 时若 EGL_SURFACE_TYPE 未包含 EGL_WINDOW_BIT,后续创建 Window Surface 会直接失败。
  • 误区二:Context 可以脱离 Surface 绘制。没有 eglMakeCurrent 绑定 Surface 时,任何 GL 绘制命令都不会产生有效输出,甚至触发错误。
  • 误区三:Pbuffer 与 FBO 混为一谈。Pbuffer 是 EGL 层面的离屏表面,FBO 是 GL 层面的帧缓冲对象。两者都能离屏渲染,但层级不同,现代应用中 FBO 更常用,Pbuffer 多用于跨上下文共享或特定平台需求。
  • 误区四:忘记 eglSwapBuffers。Window Surface 默认双缓冲,绘制完成后必须 Swap,否则屏幕看不到新内容。
  • 复盘建议:排查 EGL 问题时,按 Display → Config → Context → Surface → MakeCurrent → Swap 的顺序逐段检查返回值,并打印 eglGetError(),能快速定位是配置不匹配、上下文版本冲突还是表面创建失败。
记忆口诀:Display 找入口,Config 定规格,Context 存状态,Surface 装像素,MakeCurrent 做绑定,SwapBuffers 上屏幕。把这六个动作串成一条线,EGL 的对象模型就不会乱。