二、OpenGL ES——创建窗口
1 使用EGL连接X11窗口
- 连接到X显示服务器
display = XOpenDisplay(display_name);
- 创建窗口
win = XCreateSimpleWindow(display, RootWindow(display, screen_num), win_x, win_y, width, height, win_border_width, BlackPixel(display, screen_num), WhitePixel(display, screen_num));
-
窗口映射到显示服务器
XMapWindow(display, win); /* 缺少映射到窗口函数,则不会显示 */
- 命令立即发送到X显示服务器
XFlush(display);
-
获取X显示服务器与EGL显示的连接
/** * @brief: 获取本地显示器native_display的EGL显示连接 * @param display_id: 指定要连接的显示器。EGL_DEFAULT_DISPLAY表示默认显示器 * @return: 成功返回 EGLDisplay 对象,失败返回 EGL_NO_DISPLAY */ egl_display = eglGetDisplay( (EGLNativeDisplayType) display );
从
EGLNativeDisplayType
定义,我们可以看出,EGL
支持不同平台或者不同窗口系统,使用EGL
可以连接到这些平台(Apple、Android、Unix)等,或者连接到不同的窗口(X11、Wayland)。Wayland需要使用EGL扩展。 - 初始化与 EGLDisplay 之间的连接
```c
/**
- @brief: 初始化使用eglGetDisplay获取的EGL显示连接。对已经初始化的EGL显示连接进行初始化除了返回版本号外没有任何效果
- @param dpy: 指定要初始化的EGL显示连接
- @param major: 返回EGL实现的主要版本号。可以为NULL
- @param minor: 返回EGL实现的次要版本号。可以为NULLs
- @note: 使用eglTerminate释放与EGL显示连接关联的资源
- @return: 如果eglInitialize失败,则返回EGL_FALSE,否则返回EGL_TRUE。当返回EGL_FALSE时,major和minor不会被修改。 */ if ( eglInitialize( egl_display, &egl_version_major, &egl_version_minor) == EGL_FALSE || eglGetError() != EGL_SUCCESS) { g_error (“Unable to initialize EGL\n”); return FALSE; } ```
- 获取 EGLConfig 对象
```c
/**
- 期望的EGL帧缓存配置列表,配置为一个key一个value的形式
- 指定EGL surface类型 */ EGLint attr[] = { // some attributes to set up our egl-interface EGL_BUFFER_SIZE, 16, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, EGL_NONE };
/**
- @brief: 返回一个和期望的EGL帧缓存配置列表configSpec匹配的EGL帧缓存配置列表,存储在eglConfig中
- @param attrib_list: 期望的配置属性
- @param configs(out): frame buffer配置列表
- @param config_size: 想要选中符合的frame buffer配置的最大个数
- @param num_config(out): 获取到多少个可用frame buffer配置列表 */ if ( eglChooseConfig( egl_display, attr, &egl_config, 1, &num_configs ) == EGL_FALSE ) { g_error(“Failed to choose config (eglError: %d)\n”, eglGetError()); return FALSE; } ```
- 创建 EGLContext 实例
/* 用于指定请求的 OpenGL 或 OpenGL ES 上下文的主版本和此版本号 */ EGLint ctxattr[] = { EGL_CONTEXT_MAJOR_VERSION, 3, EGL_CONTEXT_MINOR_VERSION, 2, EGL_NONE }; /* 通过Display和上面获取到的的EGL帧缓存配置列表创建一个EGLContext, EGL_NO_CONTEXT表示不需要多个设备共享上下文 */ egl_context = eglCreateContext ( egl_display, egl_config, EGL_NO_CONTEXT, ctxattr ); if ( egl_context == EGL_NO_CONTEXT ) { g_error("Unable to create EGL context (eglError: %d\n", eglGetError()); return FALSE; }
- 创建 EGLSurface 实例
/* 通过egl和NativeWindow以及EGL帧缓冲配置创建EGLSurface。最后一个参数为属性信息,0表示不需要属性) */ egl_surface = eglCreateWindowSurface ( egl_display, egl_config, win, NULL ); if ( egl_surface == EGL_NO_SURFACE ) { fprintf (stderr, "Unable to create EGL surface (eglError: %d\n", eglGetError()); return FALSE; }
- 连接 EGLContext 和 EGLSurface
/* 将EGLContext和当前线程以及draw和read的EGLSurface关联,关联之后,当前线程就成为了OpenGL es的渲染线程 */ eglMakeCurrent( egl_display, egl_surface, egl_surface, egl_context );
- 循环函数绘制图像 glDrawArrays 和交换前后 eglSwapBuffers
glDrawArrays (GL_TRIANGLES, 0, 6); eglSwapBuffers (egl_display, egl_surface);
-
释放资源
eglDestroyContext ( egl_display, egl_context ); eglDestroySurface ( egl_display, egl_surface ); eglTerminate ( egl_display ); XDestroyWindow ( display, win ); XCloseDisplay ( display );
具体可以参考程序/assets/OpenGLES/2024051002/01_创建窗口.cpp
2 EGL Frame Buffer配置组(EGLConfig)
通过NVIDIA X Server Settings
我们可以看见 EGL Frame Buffer Configurations
,如下图所示:
可以通过assets/OpenGLES/2024051002/02_打印配置属性.cpp程序设定属性,然后获取到配置信息,打印配置信息。
/**
* @brief: 获取系统可用的EGL配置信息
* @param dpy: 指定要获取配置的EGL显示连接
* @param configs: 输出参数,包含当前系统上所有有效的 EGLConfig 配置列表
* @param config_size: 要获取多少个有效 EGLConfig 配置列表
* @param num_config:实际返回的 GLConfig 个数
* @return: 成功返回 TRUE,失败返回 FALSE
*
*/
if ( eglGetConfigs (egl_display, NULL, 0, &num_configs) == EGL_FALSE || eglGetError() != EGL_SUCCESS) {
g_error ("Unable to get EGL configs\n");
return FALSE;
}
g_print ("egl num_configs = %d\n", num_configs);
我的电脑可以获取 egl num_configs = 70
,这就是NVIDIA帧缓存配置组,一共70个。
3 EGLContext
eglCreateContext (EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list);
这部分介绍创建 EGLContext
上下文时候所需的属性列表 @attrib_list。
-
EGL_CONTEXT_MAJOR_VERSION:必须跟随一个整数,用于指定请求的 OpenGL 或 OpenGL ES 上下文的主版本号。默认值为 1。此属性是旧的 EGL_CONTEXT_CLIENT_VERSION 的别名,这两个标记可以互换使用。
-
EGL_CONTEXT_MINOR_VERSION:必须跟随一个整数,用于指定请求的 OpenGL 或 OpenGL ES 上下文的次版本号。默认值为 0。
-
EGL_CONTEXT_OPENGL_PROFILE_MASK:必须跟随一个整数位掩码,用于指定 OpenGL 上下文的配置文件。可以设置的位包括 EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT(核心配置文件)和 EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT(兼容性配置文件)。默认值为 EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT。所有 OpenGL 3.2 及更高版本的实现都必须实现核心配置文件,但兼容性配置文件是可选的。
-
EGL_CONTEXT_OPENGL_DEBUG:必须跟随 EGL_TRUE,指定应创建一个 OpenGL 或 OpenGL ES 调试上下文;如果应创建非调试上下文,则为 EGL_FALSE。默认值为 EGL_FALSE。
-
EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE:必须跟随 EGL_TRUE,指定应创建一个向前兼容的 OpenGL 上下文;如果应创建非向前兼容的上下文,则为 EGL_FALSE。默认值为 EGL_FALSE。
-
EGL_CONTEXT_OPENGL_ROBUST_ACCESS:必须跟随 EGL_TRUE,指定应创建一个支持健壮缓冲区访问的 OpenGL 或 OpenGL ES 上下文;如果应创建非健壮上下文,则为 EGL_FALSE。默认值为 EGL_FALSE。
-
EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY:必须跟随 EGL_LOSE_CONTEXT_ON_RESET,指定应创建具有重置通知行为 GL_LOSE_CONTEXT_ON_RESET_ARB 的 OpenGL 或 OpenGL ES 上下文;或者跟随 EGL_NO_RESET_NOTIFICATION,指定应创建具有重置通知行为 GL_NO_RESET_NOTIFICATION_ARB 的 OpenGL 或 OpenGL ES 上下文,如 GL_ARB_robustness 扩展所描述的。