OpenGL ES:EGL接口解析与理解

一. EGL 介绍

  OpenGL是一个操作GPU的API,它通过驱动向GPU发送相关指令,控制图形渲染管线状态机的运行状态。但OpenGL需要本地视窗系统进行交互,这就需要一个中间控制层,最好与平台无关。
  EGL——因此被独立的设计出来,它作为OpenGL ES和本地窗口的桥梁。
  EGL 是 OpenGL ES和底层 Native 平台视窗系统之间的接口。EGL API 是独立于OpenGL ES各版本标准的独立API ,其主要作用是为OpenGL指令创建 Context 、绘制目标Surface 、配置Framebuffer属性、Swap提交绘制结果等。此外,EGL为GPU厂商和OS窗口系统之间提供了一个标准配置接口。

  一般来说,OpenGL ES 图形管线的状态被存储于 EGL 管理的一个Context中。而Frame Buffers 和其他绘制 Surfaces 通过 EGL API进行创建、管理和销毁。 EGL 同时也控制和提供了对设备显示和可能的设备渲染配置的访问。

二.EGL 数据类型与初始化

  EGL 包含了自己的一组数据类型,同时也提供了对一组平台相关的本地数据类型的支持。

  标准 EGL 数据类型如下所示:

  EGLBoolean ——EGL_TRUE =1, EGL_FALSE=0
  EGLint —————int 数据类型
  EGLDisplay ———系统显示 ID 或句柄,可以理解为一个前端的显示窗口
  EGLConfig ——Surface的EGL配置,可以理解为绘制目标framebuffer的配置属性
  EGLSurface ——系统窗口或 frame buffer 句柄 ,可以理解为一个后端的渲染目标窗口。
  EGLContext ——OpenGL ES 图形上下文,它代表了OpenGL状态机;如果没有它,OpenGL指令就没有执行的环境。

  EGL Displays

  EGLDisplay 是一个关联系统物理屏幕的通用数据类型,表示显示设备句柄,也可以认为是一个前端显示窗。为了使用系统的显示设备, EGL 提供了 EGLDisplay 数据类型,以及一组操作设备显示的 API 。
  下面的函数原型用于获取 Native Display :

EGLDisplay eglGetDisplay (NativeDisplayType display);

  其 中 display 参数是 native 系统的窗口显示 ID 值。如果你只是想得到一个系统默认的 Display ,你可以使用 EGL_DEFAULT_DISPLAY 参数。如果系统中没有一个可用的 native display ID 与给定的 display 参数匹配,函数将返回 EGL_NO_DISPLAY ,而没有任何 Error 状态被设置。
  由于设置无效的 display 值不会有任何错误状态,在你继续操作前请检测返回值。下面是一个使用 EGL API 获取系统 Display 的例子:

m_eglDisplay = eglGetDisplay( system.display);
if (m_eglDisplay == EGL_NO_DISPLAY || eglGetError() != EGL_SUCCESS))
    throw error_egl_display;
  Initialization 初始化

  每个 EGLDisplay 在使用前都需要初始化。初始化 EGLDisplay 的同时,你可以得到系统中 EGL 的实现版本号。了解当前的版本号在向后兼容性方面是非常有价值的。在移动设备上,通过动态查询 EGL 版本号,你可以为新旧版本的 EGL 附加额外的特性或运行环境。基于平台配置,软件开发可用清楚知道哪些 API 可用访问,这将会为你的代码提供最大限度的可移植性。
  下面是初始化 EGL 的函数原型:

EGLBoolean eglInitialize (EGLDisplay dpy, EGLint *major, EGLint *minor);

其中 dpy 应该是一个有效的 EGLDisplay 。函数返回时, major 和 minor 将被赋予当前 EGL 版本号。比如 EGL1.0 , major 返回 1 , minor 则返回 0 。给 major 和 minor 传 NULL 是有效的,如果你不关心版本号。

初始化EGL

  EGL初始化如下:

  1. 获取Display。

  获得Display要调用

EGLboolean eglGetDisplay(NativeDisplay dpy)

  参数一般为 EGL_DEFAULT_DISPLAY 。

  2. 初始化egl。

  调用

EGLboolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)

  该函数会进行一些内部初始化工作,并传回EGL版本号(major.minor)。

  3. 选择Config。

  Config实际指的是FrameBuffer的参数,
  一般用

EGLboolean eglChooseConfig(EGLDisplay dpy, const EGLint * attr_list, EGLConfig * config, EGLint config_size, EGLint *num_config)

  其中attr_list是以EGL_NONE结束的参数数组,通常以id,value依次存放,对于个别标识性的属性可以只有 id,没有value。
  这个函数会返回不多于config_size个Config,结果保存在config[]中,系统的总Config个数保存 在num_config中。

  4、构造Surface。

  Surface实际上就是一个FrameBuffer,也就是渲染目的地,
通过

EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig confg, NativeWindow win, EGLint *cfg_attr)

  来创建一个可实际显示的Surface。

5、创建Context。

  OpenGL ES的pipeline从程序的角度看就是一个状态机,有当前的颜色、纹理坐标、变换矩阵、绚染模式等一大堆状态,这些状态作用于OpenGL API程序提交的顶点坐标等图元从而形成帧缓冲内的像素。在OpenGL的编程接口中,Context就代表这个状态机,OpenGL API程序的主要工作就是向Context提供图元、设置状态,偶尔也从Context里获取一些信息。
  可以用

EGLContext eglCreateContext(EGLDisplay dpy, EGLSurface write, EGLSurface read, EGLContext * share_list)

  来创建一个Context。

  6、EGL变量之间的绑定
boolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context)

  该接口将申请到的display,draw(surface)和 context进行了绑定。也就是说,在context下的OpenGLAPI指令将draw(surface)作为其渲染最终目的地。而display作为draw(surface)的前端显示。调用后,当前线程使用的EGLContex为context。

  7. 绘制。

  应用程序通过OpenGL API进行绘制,一帧完成之后,调用eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)来显示。

EGL Configurations 属性配置

  EGLConfigs 是一个用来描述 EGL surface 配置信息的数据类型。要获取正确的渲染结果, Surface 的格式是非常重要的。根据平台的不同, surface 配置可能会有限制,比如某个平台或者设备支持 16 位色深显示,或是不支持 stencil buffer ,还有其他的功能限制或精度的差异。
  下面是获取系统可用的 EGL 配置信息的函数原型:
EGLBoolean eglGetConfigs (EGLDisplay dpy, EGLConfig *configs,EGLint config_size, EGLint *num_config);
  参数 configs 将包含在你的平台上有效的所有 EGL framebuffer 配置列表。支持的配置总数将通过 num_config 返回。实际返回的 configs 的配置个数依赖于程序传入的 config_size 。如果 config_size < num_config ,则不是所有的配置信息都将被返回。如果想获取系统支持的所有配置信息,最好的办法就是先给 eglGetConfig 传一个 NULL 的 configs 参数, num_config 将得到系统所支持的配置总数,然后用它来给 configs 分配合适的内存大小,再用得到的 configs 来调用 eglGetConfig 。

下面是EGL初始化的代码:

virtual bool Init(void *win)
    {
        Close();
        //初始化EGL
        m_mutex.lock();

        //1 获取EGLDisplay对象 显示设备
        m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        if (m_eglDisplay == EGL_NO_DISPLAY)
        {
            m_mutex.unlock();
            XLOGE("eglGetDisplay failed!");
            return false;
        }
        XLOGE("eglGetDisplay success!");

        //2 初始化Display
        if (EGL_TRUE != eglInitialize(m_eglDisplay, 0, 0))
        {
            m_mutex.unlock();
            XLOGE("eglInitialize failed!");
            return false;
        }
        XLOGE("eglInitialize success!");

        //3 获取配置并创建surface
        EGLint configs [] = {
                EGL_RED_SIZE, 8,
                EGL_GREEN_SIZE, 8,
                EGL_BLUE_SIZE, 8,
                EGL_SURFACE_TYPE,
                EGL_WINDOW_BIT,
                EGL_NONE
        };
        EGLConfig eglConfig = 0;
        EGLint numConfigs = 0;
        if (EGL_TRUE != eglChooseConfig(m_eglDisplay, configs, &eglConfig, 1, &numConfigs))
        {
            m_mutex.unlock();
            XLOGE("eglChooseConfig failed!");
            return false;
        }
        XLOGE("eglChooseConfig success!");

        ANativeWindow *nativeWindow = (ANativeWindow *)win;
        m_eglSurface = eglCreateWindowSurface(m_eglDisplay, eglConfig, nativeWindow, NULL);


        //4 创建并打开EGL上下文
        const EGLint contextAttr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
        m_eglContext = eglCreateContext(m_eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttr);
        if (m_eglContext == EGL_NO_CONTEXT)
        {
            m_mutex.unlock();
            XLOGE("eglCreateContext failed!");
            return false;
        }
        XLOGE("eglCreateContext success!");

        if (EGL_TRUE != eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext))
        {
            m_mutex.unlock();
            XLOGE("eglMakeCurrent failed!");
            return false;
        }
        XLOGE("eglMakeCurrent success!");
        m_mutex.unlock();
        return true;
    }

每次Opengles完成后,调用EGL显示

    virtual void Draw()
    {
        m_mutex.lock();
        if (m_eglDisplay == EGL_NO_DISPLAY || m_eglSurface == EGL_NO_SURFACE)
        {
            m_mutex.unlock();
            return;
        }
        eglSwapBuffers(m_eglDisplay, m_eglSurface);
        m_mutex.unlock();
    }

释放EGL的资源如下:

virtual void Close()
    {
        m_mutex.lock();
        if (m_eglDisplay == EGL_NO_DISPLAY)
        {
            m_mutex.unlock();
           return;
        }

        eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);

        if (m_eglSurface != EGL_NO_SURFACE)
            eglDestroySurface(m_eglDisplay, m_eglSurface);
        if (m_eglContext != EGL_NO_CONTEXT)
            eglDestroyContext(m_eglDisplay, m_eglContext);

        eglTerminate(m_eglDisplay);

        m_eglDisplay = EGL_NO_DISPLAY;
        m_eglSurface = EGL_NO_SURFACE;
        m_eglContext = EGL_NO_CONTEXT;
        m_mutex.unlock();
    }

参考:
https://blog.csdn.net/baidu_37503452/article/details/80800632

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容