1 使用EGL连接X11窗口

  1. 连接到X显示服务器
     display = XOpenDisplay(display_name);
    
  2. 创建窗口
     win = XCreateSimpleWindow(display, RootWindow(display, screen_num),
                               win_x, win_y, width, height, win_border_width,
                               BlackPixel(display, screen_num),
                               WhitePixel(display, screen_num));
    
  3. 窗口映射到显示服务器

     XMapWindow(display, win); /* 缺少映射到窗口函数,则不会显示 */
    
  4. 命令立即发送到X显示服务器
     XFlush(display);
    
  5. 获取X显示服务器与EGL显示的连接

     /**
      * @brief: 获取本地显示器native_display的EGL显示连接
     * @param display_id: 指定要连接的显示器。EGL_DEFAULT_DISPLAY表示默认显示器
     * @return: 成功返回 EGLDisplay 对象,失败返回 EGL_NO_DISPLAY
     */
     egl_display = eglGetDisplay( (EGLNativeDisplayType) display );
    

    alt text

    EGLNativeDisplayType 定义,我们可以看出,EGL 支持不同平台或者不同窗口系统,使用EGL 可以连接到这些平台(Apple、Android、Unix)等,或者连接到不同的窗口(X11、Wayland)。Wayland需要使用EGL扩展。

  6. 初始化与 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; } ```
  7. 获取 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; } ```
  8. 创建 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;
     }
    
  9. 创建 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;
     }
    
  10. 连接 EGLContext 和 EGLSurface
     /* 将EGLContext和当前线程以及draw和read的EGLSurface关联,关联之后,当前线程就成为了OpenGL es的渲染线程 */
     eglMakeCurrent( egl_display, egl_surface, egl_surface, egl_context );
    
  11. 循环函数绘制图像 glDrawArrays 和交换前后 eglSwapBuffers
     glDrawArrays (GL_TRIANGLES, 0, 6);
    
         eglSwapBuffers (egl_display, egl_surface);
    
  12. 释放资源

    eglDestroyContext ( egl_display, egl_context );
    eglDestroySurface ( egl_display, egl_surface );
    eglTerminate      ( egl_display );
    
    XDestroyWindow    ( display, win );
    XCloseDisplay     ( display );
    

具体可以参考程序/assets/OpenGLES/2024051002/01_创建窗口.cpp

alt text

2 EGL Frame Buffer配置组(EGLConfig)

通过NVIDIA X Server Settings我们可以看见 EGL Frame Buffer Configurations,如下图所示:

alt text

alt text

可以通过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 扩展所描述的。

参考

参考1:openGLES3.0编程指南笔记

参考2:OpenGL ES 着色器语言丨音视频基础

参考3:从渲染管线学 GLES2.0(二)—-着色器