EGL使用记录

0.前言

EGL是Khronos渲染API(OpenGL、Vulkan、OpenVG等)和原生窗口系统之间的接口。使用EGL的流程较为固定,在搭建框架时又必不可少,简单记录一下。

1. EGL

1.1 EGLDisplay

EGLDisplay主要用于管理图形渲染与底层显示设备(如屏幕,窗口系统)之间的连接,是OpenGL,OpenGL ES或Vulkan等图形API与操作系统窗口系统(如X11,Android Surface,Windows GDI)之间交互的桥梁。
eglGetDisplay()的参数类型是EGLNativeDisplayType,在不同平台下具体实现的类型不同。

C++ 复制代码
EGLint majorVersion;
EGLint minorVersion;

// 获取display
display_ = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if(display_ == EGL_NO_DISPLAY) {
    return Error::NewError(ErrorCode::NO_DISPLAY, "Egl get display failed.");
}

// 初始化EGL,
if(!eglInitialize(display_, &majorVersion, &minorVersion)) {
    return Error::NewError(ErrorCode::INIT_EGL_FAIL, "Egl initialize failed.");
}

1.2 EGLConfig

一旦初始化完EGL,我就就可以获得当前平台下可以支持的渲染表面的类型和配置。EGL中用EGLConfig来表示这些配置。我们可以使用eglChooseConfig()方法指定一组我们需要的配置,让EGL来返回最佳匹配的配置。

C++ 复制代码
const EGLint MaxConfigs = 10;
EGLConfig configs[MaxConfigs] = {0};
EGLint numConfigs = 0;
EGLint selectedIdx = -1;

// 我们想要使用RGBA8888, 获取可用的configs,选择第一个
EGLint attribList[] = {
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_NONE
};
if(!eglChooseConfig(display_, attribList, configs, MaxConfigs, &numConfigs) || numConfigs <= 0) {
    return Error::NewError(ErrorCode::CHOOSE_EGL_CONFIG_FAIL, "Egl Choose config failed.");
}
if(numConfigs <= 0) {
    return Error::NewError(ErrorCode::NO_SUITABLE_CONFIG, "No suitable config");
}
selectedIdx = 0;

1.3 EGLContext

EGLContext表示的是图形渲染的"上下文环境",负责管理OpenGL/OpenGL ES/Vulkan等图形API的状态和资源(如纹理,着色器,帧缓冲区等),可将其视为图形渲染的"容器",所有的绘图操作都必须在这个上下文中进行。

EGLContext的作用:

  1. 状态管理
    保存当前图形API所有的状态配置,包括着色器程序状态,顶点缓冲区(VBO)绑定,混合模式,深度测试等等。
  2. 资源共享
    在使用eglCreateContext()创建EGLContext时可以传入一个已有的EGLContext,允许多个EGLContext共享资源,如纹理,缓冲区等,避免重复加载。
  3. 维护线程关联性
    一个EGLContext必须在一个线程内使用。
C++ 复制代码
// 创建eglContext
EGLint contextAttribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 3,  // 请求 ES 3.0
        EGL_NONE
};

// EGL_NO_CONTEXT可以换成一个已有的EGLContext,用于共享上下文
context_ = eglCreateContext(display_, configs[selectedIdx], EGL_NO_CONTEXT, contextAttribs);
if(context_ == EGL_NO_CONTEXT) {
    EGLint error = eglGetError();
    if(error == EGL_BAD_ATTRIBUTE) {
        LogE("Create Context, egl bad attribute.")
    }
    return Error::NewError(ErrorCode::NO_EGL_CONTEXT, "Egl Create Context failed.");
}

1.4 EGLSurface

EGLSurface是OpenGL/Vulkan等图形API绘制图形时的输出载体,表示图形渲染的目标表面,可以是屏幕,离屏缓冲区。

EGLSurface作用:

  1. 渲染目标管理
    图形API输出载体,决定渲染结果的最终显示位置。
  2. 连接窗口系统
    如Android的ANativeWindow,Linux的X11,与之绑定,实现渲染屏幕上更新。
  3. 离屏渲染
    提供离屏渲染的Buffer

以下是使用eglCreateWindowSurface创建EGLSurface,用于屏上渲染的例子:

C++ 复制代码
// 创建EGLSurface,在屏幕渲染
surface_ = eglCreateWindowSurface(display_, configs[selectedIdx], nativeWindow,
                                  nullptr);
// 离屏渲染,创建一个512x512渲染目标
//EGLint attribList[] = {
//    EGL_WIDTH, 512,
//    EGL_HEIGHT, 512,
//    EGL_LARGEST_PBUFFER, EGL_TRUE,
//    EGL_NONE
//};
//surface_ = eglCreatePbufferSurface(display_, configs[selectedIdx], nativeWindow,
//                                  attribList);
if(surface_ == EGL_NO_SURFACE) {
    return Error::NewError(ErrorCode::CREATE_SURFACE_FAILED, "Egl create surface failed.");
}

当然,我们也可以使用eglCreatePbufferSurface()来生成离屏渲染的目标区域。PBuffer即是Pixel buffer,像素缓冲区。

1.5 指定现在的渲染环境

最后,在使用OpenGL/Vulkan前,我们需要指定当前的渲染环境,也就是上面已经创建的一组EGLDisplay,EGLContext,EGLSurface

C++ 复制代码
// make current
if(!eglMakeCurrent(display_, surface_, surface_, context_)) {
    return Error::NewError(ErrorCode::MAKE_CURRENT_FAILED, "Egl make current failed.");
}

2. 其他注意事项

  1. Surface在后台时释放

Android在使用SurfaceView+egl+opengl进行渲染时,当Activity进入后台时,对应的Surface会进行销毁。

我们可以在SurfaceHolder.Callback()surfaceDestroyed()中添加触发逻辑,释放掉底层的EGLSurface

而在Activity重新进入前台时,会触发SurfaceHolder.Callback()onSurfaceChanged()方法,我们可以在此方法中重新创建EGLSurface

相关推荐
法欧特斯卡雷特2 小时前
Kotlin 2.2.20 现已发布!下个版本的特性抢先看!
android·前端·后端
小意恩2 小时前
el-table表头做过滤
前端·javascript·vue.js
小桥风满袖2 小时前
极简三分钟ES6 - 迭代器Iterator
前端·javascript
小菜全2 小时前
ElementUI 组件概览
前端·vue.js·elementui
JarvanMo3 小时前
提升生产力:每个 iOS 开发者都应该知道的 10 个 Xcode 技巧
前端
玲小珑3 小时前
LangChain.js 完全开发手册(七)RAG(检索增强生成)架构设计与实现
前端·langchain·ai编程
前端小巷子3 小时前
原生 JS 打造三级联动
前端·javascript·面试
江城开朗的豌豆3 小时前
useEffect vs componentDidUpdate:谁才是真正的更新之王?
前端·javascript·react.js
江城开朗的豌豆3 小时前
解密useEffect:让副作用无所遁形!
前端·javascript·react.js