4. EGL核心概念
在 Android 图形栈中,EGL 是连接 OpenGL ES 与本地窗口系统的桥梁。它并不负责绘制像素,而是负责“把渲染结果送到哪里、用什么格式、由谁管理”。理解 EGL 的核心对象及其生命周期,是排查黑屏、花屏、内存泄漏和上下文丢失问题的前提。本节围绕 Display、Config、Context、Surface、NativeWindow 五个对象展开,说明它们之间的依赖关系与创建、使用、销毁的完整路径。
一、核心对象与职责
EGL 的对象模型可以理解为一条“资源链”:Display 是入口,Config 是约束条件,Context 是渲染状态容器,Surface 是渲染目标,NativeWindow 是平台侧的像素缓冲载体。它们的关系如下:
- EGLDisplay:代表与本地显示系统的连接。多数场景使用默认显示
EGL_DEFAULT_DISPLAY,通过eglGetDisplay获取,再用eglInitialize初始化。 - EGLConfig:描述帧缓冲的格式与能力,如 RGBA 位宽、深度位宽、模板位宽、多重采样、表面类型等。它由
eglChooseConfig从一组属性中筛选得到。 - EGLContext:OpenGL ES 的渲染状态容器,保存着色器程序、纹理、缓冲区、混合状态等。所有 GL 调用都作用于“当前绑定”的 Context。
- EGLSurface:渲染目标,分为窗口表面(Window Surface)和离屏表面(Pbuffer Surface)。它由 Config 和 NativeWindow 共同创建。
- NativeWindow:Android 平台侧的本地窗口对象,通常来自
Surface、SurfaceTexture或ANativeWindow,是 EGLSurface 的实际像素承载者。
二、对象关系与生命周期全景
下图展示了五个核心对象的依赖关系与生命周期阶段。注意:Display 和 Config 通常贯穿应用全程,而 Context 与 Surface 可以按需创建和销毁;NativeWindow 由平台层管理,EGL 只是借用其缓冲队列。
三、创建与绑定流程
典型的 EGL 初始化流程如下。每一步都有明确的失败返回,工程中必须逐项检查,否则后续 GL 调用会静默失败。
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, &major, &minor);
const EGLint configAttrs[] = {
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, configAttrs, &config, 1, &numConfigs);
const EGLint contextAttrs[] = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE
};
EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttrs);
EGLSurface surface = eglCreateWindowSurface(display, config, nativeWindow, NULL);
eglMakeCurrent(display, surface, surface, context);
其中 eglMakeCurrent 是“绑定”动作:它把 Context 与 Surface 关联到当前线程。OpenGL ES 是单线程状态机,同一时刻一个线程只能有一个当前 Context。渲染完成后,通过 eglSwapBuffers 提交帧缓冲,再由平台合成显示。
四、生命周期与销毁顺序
EGL 对象的销毁必须遵循“后创建先销毁”的原则,否则会出现资源泄漏或上下文悬空。常见顺序为:先 eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) 解绑,再 eglDestroySurface,然后 eglDestroyContext,最后 eglTerminate。Display 通常由进程级共享,不建议频繁 terminate。
| 对象 | 创建接口 | 销毁接口 | 生命周期建议 |
|---|---|---|---|
| EGLDisplay | eglGetDisplay / eglInitialize | eglTerminate | 进程级,通常只初始化一次 |
| EGLConfig | eglChooseConfig | 无需单独销毁 | 随 Display 存在,可缓存复用 |
| EGLContext | eglCreateContext | eglDestroyContext | 按渲染线程创建,线程退出时销毁 |
| EGLSurface | eglCreateWindowSurface | eglDestroySurface | 随窗口/画面创建,窗口销毁时释放 |
| NativeWindow | 平台层创建 | 平台层释放 | 由 SurfaceFlinger 或应用框架管理 |
五、典型场景与常见误区
在视频播放器或相机预览中,常见做法是:用 SurfaceTexture 创建 NativeWindow,再创建 EGLSurface,在独立渲染线程中完成纹理绘制。此时若窗口尺寸变化,必须销毁旧 EGLSurface 并重新创建,否则会出现拉伸或黑边。
误区一:在未 eglMakeCurrent 的情况下调用 GL 接口,导致 GL_INVALID_OPERATION 或直接崩溃。
误区二:多个线程共用一个 Context 而未做同步,造成状态竞争和花屏。
误区三:只销毁 Surface 而忘记销毁 Context,长时间运行后显存持续增长。
排查 EGL 问题时,优先检查 eglGetError() 的返回值,并确认 Display、Config、Context、Surface 四者的创建顺序与绑定状态。多数“黑屏”并非 GL 绘制错误,而是 EGLSurface 未正确绑定或 NativeWindow 已失效。
掌握这些对象的职责边界与生命周期,就能在集成显示系统时快速定位问题:是配置不匹配、上下文丢失,还是窗口缓冲已释放。下一阶段将结合具体代码,完成从 EGL 初始化到首帧上屏的完整实操。