OpenGL/OpenGL ES入门(二):EGL的介绍和使用(context上下文的创建和初始化)

一、简介

EGL 是渲染 API(如 OpenGL ES)和原生窗口系统之间的接口。

通常来说,OpenGL 是一个操作 GPU 的 API,它通过驱动向 GPU 发送相关指令,控制图形渲染管线状态机的运行状态,但是当涉及到与本地窗口系统进行交互时,就需要这么一个中间层,且它最好是与平台无关的。

因此 EGL 被设计出来,作为 OpenGL 和原生窗口系统之间的桥梁

二、功能

EGL API 是独立于 OpenGL ES 各版本标准的独立的一套 API。

2.1 主要作用:

  • 为 OpenGL 指令创建Context;
  • 绘制目标Surface;
  • 配置FrameBuffer属性;
  • Swap提交绘制结果等。

2.2 EGL提供如下机制:

  • 与设备原生窗口通信;
  • 查询绘制Surface的可用类型和配置;
  • 创建绘制surface;
  • 在OpenGL ES 3.0或其他渲染API之间同步渲染;
  • 管理纹理贴图等渲染资源

三、使用

3.1 检查错误的方法

EGL 中大部分函数成功时都是返回 EGL_TRUE,失败返回 EGL_FALSE

至于故障原因,需要调用如下函数获取:

EGLint eglGetError();

返回最近调用 EGL 函数的错误代码,如果返回 EGL_SUCCESS 说明没有错误。

3.2 创建。建立本地系统和OpenGL ES的连接

EGLDisplay eglGetDisplay(EGLNativeDisplayType displayId);

displayId 指定显示连接,一般使用默认的 EGL_DEFAULT_DISPLAY,即返回与默认原生窗口的连接。

相关错误码:

  • EGL_NO_DISPLAY :连接不可用

3.3 初始化

EGLBoolean eglInitialize(EGLDisplay display, // 创建步骤时返回的对象
                         EGLint *majorVersion, // 返回 EGL 主版本号
                         EGLint *minorVersion); // 返回 EGL 次版本号

相关错误码:

  • EGL_NO_DISPLAY :EGL 不能初始化
  • EGL_BAD_DISPLAY :没有指定有效的 display

实例:

EGLint majorVersion;
EGLint minorVersion;
EGLDisplay display;
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
    // Unable to open connection to local windowing system
}
if (!eglInitialize(display, &majorVersion, &minorVersion)) {
    // Unable to initialize EGL. Handle and recover
}

3.4 确定可用的surface的配置

一旦初始化了EGL,就可以确定可用渲染表面的类型和配置了。有两种方法:

  • 先使用 eglGetConfigs 查询每个配置,再使用 eglGetConfigAttrib 找出最好的选择
  • 指定一组需求,使用 eglChooseChofig 让 EGL 推荐最佳配置

通常使用第二种方式更简单。两种方法均得到 EGLConfig 对象。EGLConfig 包含了渲染表面的所有信息,包括可用颜色、缓冲区等其他特性。

方法一:查询每个配置,找出最好的选择

//查询每个配置
EGLBoolean eglGetConfigs(EGLDisplay display, // 指定显示的连接
                         EGLConfig *configs, // 指定 GLConfig 列表
                         EGLint maxReturnConfigs, // 最多返回的 GLConfig 数
                         EGLint *numConfigs); // 实际返回的 GLConfig 数

//找出最好的渲染
EGLBoolean eglGetConfigAttrib(EGLDisplay display, // 指定显示的连接
                              EGLConfig config, // 指定要查询的 GLConfig
                              EGLint attribute, // 返回特定属性
                              EGLint *value); // 返回值

方法二:EGL推荐最佳配置

EGLBoolean eglChooseChofig(EGLDispay display, // 指定显示的连接
                           const EGLint *attribList, // 指定 configs 匹配的属性列表,可以为 NULL
                           EGLConfig *config,   // 调用成功,返会符合条件的 EGLConfig 列表
                           EGLint maxReturnConfigs, // 最多返回的符合条件的 GLConfig 数
                           ELGint *numConfigs );  // 实际返回的符合条件的 EGLConfig 数

如果 eglChooseChofig 成功返回,则返回一组匹配你的标准的 EGLConfig

实例:

EGLint attribList[] = {
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
    EGL_RED_SIZE, 5,
    EGL_GREEN_SIZE, 6,
    EGL_BLUE_SIZE, 5,
    EGL_DEPTH_SIZE, 1,
    EGL_NONE
};

const EGLint MaxConfigs = 10;
EGLConfig configs[MaxConfigs]; // We'll only accept 10 configs
EGLint numConfigs;
if (!eglChooseConfig(dpy, attribList, configs, MaxConfigs, &numConfigs)) {
    // Something didn't work … handle error situation
} else {
    // Everything's okay. Continue to create a rendering surface
}

3.5 创建渲染区域surface

当有了符合条件的 EGLConfig 后,就可以通过 eglCreateWindowSurface 函数创建渲染区域。

EGLSurface eglCreateWindowSurface(EGLDisplay display, // 指定显示的连接
                                  EGLConfig config, // 符合条件的 EGLConfig
                                  EGLNatvieWindowType window, // 指定原生窗口
                                  const EGLint *attribList); // 指定窗口属性列表,可为 NUL

eglCreateWindowSurface 方法在很多情况下可能失败,失败返回 EGL_NO_SURFACE。我们可以通过 eglGetError 获取相关错误。

相关错误码:

  • EGL_BAD_MATCH :提供了与窗口属性不匹配的 EGLConfig,或该 EGLConfig 不支持渲染到窗口
  • EGL_BAD_CONFIG :提供的 EGLConfig 没有得到系统支持
  • EGL_BAD_NATIVE_WINDOW :提供的原生窗口句柄无效
  • EGL_BAD_ALLOC :无法为新的窗口分配资源,或已经有和提供的原生窗口关联的 EGLConfig
EGLint attribList[] = {
  EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
  EGL_NONE
);

// 这里先省略了创建原生窗口的过程
EGLRenderSurface window = eglCreateWindowSurface(display, config, nativeWindow,attribList);

if (window == EGL_NO_SURFACE) {
    switch (eglGetError()) {
    case EGL_BAD_MATCH:
        // Check window and EGLConfig attributes to determine
        // compatibility, or verify that the EGLConfig
        // supports rendering to a window,
        break;
    case EGL_BAD_CONFIG:
        // Verify that provided EGLConfig is valid
        break;
    case EGL_BAD_NATIVE_WINDOW:
        // Verify that provided EGLNativeWindow is valid
        break;
    case EGL_BAD_ALLOC:
        // Not enough resources available. Handle and recover
        break;
    }
}

3.6 创建上下文

上下文包含了操作所需的所有状态信息,OpenGL ES 3.0 必须有一个可用的上下文才能进行绘图。

EGLContext eglCreateContext(EGLDisplay display, // 指定显示的连接
                            EGLConfig config, // 前面选好的 EGLConfig
                            EGLContext shareContext, // 允许其它 EGLContext 共享数据,使用 EGL_NO_CONTEXT 表示不共享
                            const EGLint* attribList); // 指定操作的属性列表,只能接受一个属性 EGL_CONTEXT_CLIENT_VERSION

实例:

const ELGint attribList[] = {
    EGL_CONTEXT_CLIENT_VERSION, 3,
    EGL_NONE
};

EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, attribList);

if (context == EGL_NO_CONTEXT) {
    EGLError error = eglGetError();
    if (error == EGL_BAD_CONFIG) {
        // Handle error and recover
    }
}

3.7 关联上下文

指定某个 EGLContext 为当前上下文,关联特定的 EGLContext 和 EGLSurface

EGLBoolean eglMakeCurrent(EGLDisplay display, // 指定显示的连接
                          EGLSurface draw, // EGL 绘图表面
                          EGLSurface read, // EGL 读取表面
                          EGLContext context); // 指定连接到该表面的上下文

这里存在两个 EGLSurface,具有更好的灵活性,在后续一些高级的 EGL 用法中将利用它。目前我们先把它们设置为同一个值。

3.8 使用OpenGL相关的API进行绘制

这个就是 OpenGL 里的东西了,这里不做介绍。

四、总结

EGL 是 OpenGL 和原生窗口系统之间的桥梁接口。

使用流程:

1.创建连接

eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);

2.初始化连接

eglInitialize(eglDisplay, &nMajor, &nMinor)

3.获取配置

eglChooseConfig(eglDisplay, configAttribsOffScreen, &eglConfig, 1, &nNumConfigs)

4.创建渲染区域

 eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, (EGLNativeWindowType)hWindow, NULL);

5.创建渲染上下文

eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attribs);

6.关联上下文和渲染区域

eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)

 

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的示例代码,可以通过使用 OpenGL ES 和 MediaCodec 将 RGB 图像转换为 MP4 视频: ```c++ #include <stdlib.h> #include <stdio.h> #include <EGL/egl.h> #include <GLES2/gl2.h> #include <media/NdkMediaCodec.h> #include <media/NdkMediaMuxer.h> int main() { // 初始化 EGL EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, 0, 0); // 创建 EGL 配置 EGLConfig eglConfig; EGLint numConfigs; EGLint configAttribs[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_NONE }; eglChooseConfig(display, configAttribs, &eglConfig, 1, &numConfigs); // 创建 EGL 上下文 EGLSurface surface = eglCreatePbufferSurface(display, eglConfig, NULL); EGLint contextAttribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; EGLContext context = eglCreateContext(display, eglConfig, EGL_NO_CONTEXT, contextAttribs); eglMakeCurrent(display, surface, surface, context); // 创建 OpenGL ES 纹理 GLuint texId; glGenTextures(1, &texId); glBindTexture(GL_TEXTURE_2D, texId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 640, 480, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); // 创建 MediaCodec 编码器 AMediaCodec *codec = AMediaCodec_createEncoderByType("video/avc"); AMediaFormat *format = AMediaFormat_new(); AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, "video/avc"); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, 2000000); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, 30); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, 19); AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1); AMediaCodec_configure(codec, format, NULL, NULL, AMEDIACODEC_CONFIGURE_FLAG_ENCODE); AMediaCodec_start(codec); // 创建 MediaMuxer 封装器 AMediaMuxer *muxer = AMediaMuxer_new("/sdcard/test.mp4", AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4); // 循环编码每一帧图像 for (int i = 0; i < 300; i++) { // 渲染一帧图像到纹理 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glViewport(0, 0, 640, 480); // 此处应该将 RGB 图像数据更新到纹理中 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // 从纹理中获取图像数据 uint8_t *buf = (uint8_t*)malloc(640 * 480 * 3); glReadPixels(0, 0, 640, 480, GL_RGB, GL_UNSIGNED_BYTE, buf); // 将图像数据编码为 H.264 帧 AMediaCodecBufferInfo info; ssize_t bufIdx = AMediaCodec_dequeueInputBuffer(codec, -1); uint8_t *inputBuf = AMediaCodec_getInputBuffer(codec, bufIdx, NULL); memcpy(inputBuf, buf, 640 * 480 * 3); AMediaCodec_queueInputBuffer(codec, bufIdx, 0, 640 * 480 * 3, i * 1000000 / 30, 0); // 编码器输出 H.264 帧 ssize_t outIdx = AMediaCodec_dequeueOutputBuffer(codec, &info, 0); if (outIdx >= 0) { AMediaCodecBufferInfo info; uint8_t *outputBuf = AMediaCodec_getOutputBuffer(codec, outIdx, &info); AMediaMuxer_writeSampleData(muxer, 0, outputBuf, &info); AMediaCodec_releaseOutputBuffer(codec, outIdx, false); } free(buf); } // 停止并释放资源 AMediaCodec_stop(codec); AMediaCodec_delete(codec); AMediaFormat_delete(format); AMediaMuxer_stop(muxer); AMediaMuxer_delete(muxer); eglDestroyContext(display, context); eglDestroySurface(display, surface); eglTerminate(display); return 0; } ``` 以上代码仅供参考,实际实现过程可能需要根据具体情况进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值