【openGLES】安卓端EGL的使用

文章目录

  • [1. EGL是什么?](#1. EGL是什么?)
  • [2. EGL的使用流程](#2. EGL的使用流程)
    • [2.1 在线渲染](#2.1 在线渲染)
    • [2.2 离屏渲染](#2.2 离屏渲染)

1. EGL是什么?

EGL(Embedded-System Graphics Library)​​ 是 Khronos Group 制定的标准接口,用于 ​管理 OpenGL ES / OpenVG 与本地窗口系统之间的交互。它相当于一个"中间层",解决以下问题:

  • 跨平台兼容性:不同操作系统(Android、Linux、Windows 等)的窗口系统差异。
  • 资源管理:创建渲染表面(Surface)、上下文(Context)、缓冲区等。
  • 多API支持:支持 OpenGL ES、OpenGL、OpenVG 等多种图形API。
html 复制代码
EGLDisplay (连接显示系统)
    │
    ├── EGLSurface (渲染目标:窗口或离屏缓冲区)
    │
    └── EGLContext (渲染状态和环境)
          │
          └── OpenGL ES 绘制命令

2. EGL的使用流程

EGL 的初始化流程有严格的顺序要求,错误顺序会导致崩溃或上下文创建失败。

​标准流程​:

  1. 获取 Display → eglGetDisplay()
  2. 初始化 EGL → eglInitialize()
  3. 绑定 API → eglBindAPI()(明确使用 OpenGL ES 还是 OpenGL)
  4. 选择 Config → eglChooseConfig()。EGL Config 决定了渲染表面的属性(如颜色深度、深度缓冲区、模板缓冲区等)。
  5. 创建 Surface → eglCreateWindowSurface()(在线渲染)或者eglCreatePbufferSurface()(离屏渲染)
  6. 创建 Context → eglCreateContext()
  7. 绑定 Context 和 Surface → eglMakeCurrent()

EGL 资源必须手动释放,且顺序与创建相反:

  • 解绑 Context → eglMakeCurrent(..., EGL_NO_CONTEXT)
  • 销毁 Context → eglDestroyContext()
  • 销毁 Surface → eglDestroySurface()
  • 终止 Display → eglTerminate()

​注意事项​:

  • 在 eglInitialize() 之前调用 eglChooseConfig() 会直接失败。
  • 单线程绑定:eglMakeCurrent() 会将 Context 绑定到当前线程,同一线程只能绑定一个 Context。
  • 多线程渲染:需在每个线程单独创建和绑定 Context,并共享资源(通过 eglCreateContext() 的 share_context 参数)。
  • 解绑时机:在销毁 Context 前必须调用 eglMakeCurrent(..., EGL_NO_CONTEXT)。

若使用 ​PBuffer​ 或 ​FBO​ 离屏渲染:

  • PBuffer:通过 eglCreatePbufferSurface() 创建,需指定宽高。
  • FBO:更灵活,但需额外管理帧缓冲和纹理附件。
  • 性能优化:离屏渲染可能比窗口渲染更快(无垂直同步限制)。

2.1 在线渲染

cpp 复制代码
#include <EGL/egl.h>
#include <GLES3/gl3.h>

EGLDisplay eglDisplay;
EGLSurface eglSurface;
EGLContext eglContext;

bool initOnScreenEGL() {
    // 1. 获取默认显示连接
    eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (eglDisplay == EGL_NO_DISPLAY) return false;

    // 2. 初始化 EGL
    EGLint major, minor;
    if (!eglInitialize(eglDisplay, &major, &minor)) return false;

    // 3. 选择配置
    const EGLint configAttribs[] = {
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_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_NONE
    };
    EGLConfig eglConfig;
    EGLint numConfigs;
    if (!eglChooseConfig(eglDisplay, configAttribs, &eglConfig, 1, &numConfigs)) return false;

    // 4. 创建窗口表面(绑定到原生窗口)
    // 安卓端从 SurfaceHolder 获取 ANativeWindow
	ANativeWindow* nativeWindow = ANativeWindow_fromSurface(env, surface);
	eglCreateWindowSurface(display, config, window, nullptr);
    eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, nativeWindow, nullptr);  
    if (eglSurface == EGL_NO_SURFACE) return false;
    
    // 5. 创建上下文
    const EGLint contextAttribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 3,
        EGL_NONE
    };
    eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttribs);
    if (eglContext == EGL_NO_CONTEXT) return false;

    // 6. 绑定上下文和表面
    if (!eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) return false;

    return true;
}

void renderFrame() {
    glClearColor(0.2f, 0.3f, 0.4f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    // 绘制代码...
    eglSwapBuffers(eglDisplay, eglSurface); // 交换缓冲区,显示到屏幕
}

void cleanup() {
    eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroyContext(eglDisplay, eglContext);
    eglDestroySurface(eglDisplay, eglSurface);
    eglTerminate(eglDisplay);
}

2.2 离屏渲染

cpp 复制代码
#include <EGL/egl.h>
#include <GLES3/gl3.h>

EGLDisplay eglDisplay;
EGLSurface eglSurface; // PBuffer Surface
EGLContext eglContext;

bool initOffScreenEGL(int width, int height) {
    // 1. 获取默认显示连接
    eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (eglDisplay == EGL_NO_DISPLAY) return false;

    // 2. 初始化 EGL
    EGLint major, minor;
    if (!eglInitialize(eglDisplay, &major, &minor)) return false;

    // 3. 选择配置(不需要 EGL_WINDOW_BIT)
    const EGLint configAttribs[] = {
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, // 使用 PBuffer
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_DEPTH_SIZE, 24,
        EGL_NONE
    };
    EGLConfig eglConfig;
    EGLint numConfigs;
    if (!eglChooseConfig(eglDisplay, configAttribs, &eglConfig, 1, &numConfigs)) return false;

    // 4. 创建 PBuffer 表面(离屏渲染目标)
    const EGLint pbufferAttribs[] = {
        EGL_WIDTH, width,
        EGL_HEIGHT, height,
        EGL_NONE
    };
    eglSurface = eglCreatePbufferSurface(eglDisplay, eglConfig, pbufferAttribs);
    if (eglSurface == EGL_NO_SURFACE) return false;

    // 5. 创建上下文
    const EGLint contextAttribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 3,
        EGL_NONE
    };
    eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttribs);
    if (eglContext == EGL_NO_CONTEXT) return false;

    // 6. 绑定上下文和 PBuffer 表面
    if (!eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) return false;

    return true;
}

void renderToTexture() {
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 512, 512, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

    // 绑定 FBO 并渲染到纹理
    GLuint fbo;
    glGenFramebuffers(1, &fbo);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    // 绘制代码...

    glBindFramebuffer(GL_FRAMEBUFFER, 0); // 解绑 FBO
}

void cleanup() {
    eglMakeCurrent(eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroyContext(eglDisplay, eglContext);
    eglDestroySurface(eglDisplay, eglSurface);
    eglTerminate(eglDisplay);
}
相关推荐
齊家治國平天下37 分钟前
AAOS 电源管理深度解析:休眠/唤醒/功耗优化
android·车载系统·aaos·aosp·电源管理·休眠唤醒·carpowermanager
Xzaveir1 小时前
企业号码展示不可观测怎么办:状态机、拨测事件与异常回放
android·人工智能
2501_916007471 小时前
iOS和macOS应用程序性能分析和优化工具使用综合指南
android·macos·ios·小程序·uni-app·iphone·webview
黄林晴2 小时前
Nearby Connections 重大变更:不再自动开启 Wi-Fi / 蓝牙,适配指南
android
恋猫de小郭2 小时前
Flutter 全新真 3D 实现,用 flutter_scene 能开发一个「我的世界」
android·前端·flutter
HLC++15 小时前
Linux的进程间通信
android·linux·服务器
爱笑鱼18 小时前
Binder(二):AIDL 生成的 Proxy、Stub 和 Parcel 到底在做什么?
android
爱笑鱼19 小时前
Binder(一):一次方法调用,究竟怎样跨进程执行?
android
壮哥_icon20 小时前
【Android 系统开发】使用 BAT 脚本高效自动化管理 /system/priv-app/ 系统应用(安装与卸载)
android·运维·自动化
用户693717500138421 小时前
Claude Code终端日志Token占用实测:一个过滤器砍掉60%-90%
android·前端·后端