OpenGL——EGL

OpenGL——EGL

什么是EGL

EGL 是 OpenGL ES 渲染 API 和本地窗口系统(native platform window system)之间的一个中间接口层,它主要由系统制造商实现。EGL提供如下机制:

  • 与设备的原生窗口系统通信
  • 查询绘图表面的可用类型和配置
  • 创建绘图表面
  • 在OpenGL ES 和其他图形渲染API之间同步渲染
  • 管理纹理贴图等渲染资源

https://www.khronos.org/egl/

OpenGL——EGL_第1张图片

与窗口系统通信

因为每个窗口系统都有不同的语义,所以EGL提供基本的不透明类型——EGLDisplay 它封装了所有系统相关性,用于和系统窗口系统接口。任何使用EGL的应用程序必须执行的第一个操作是创建和初始化本地EGL显示的连接。

EGLDisplay eglGetDisplay(EGLNativeDisplayType displayId)

display: 指定显示连接,默认连接为EGL_DEFAULT_DISPLAY

如果显示连接不可用,则EGLGetDisplay将返回EGL_NO_DISPLAY,这个错误表示EGL不可用,因此无法使用OpenGL ES 3.0。

检查错误

错误代码不会直接从EGLBoolean返回值中看出,要想获取查询具体的EGL错误代码,需要调用如下函数完成:

EGLint eglGetError()

初始化EGL

成功的打开连接之后,需要初始化EGL,这通过调用如下函数完成

EGLBoolean eglInitialize(EGLDisplay display,EGLint *majorVersion, EGLint *minorVersion)

display: 指定EGL显示连接
majorVersion: 指定EGL实现返回的主版本号,可能NULL
minorVersion: 指定EGL实现返回的次版本号,可能NULL

如果无法初始化,将返回EGL_FALSE,并将EGL错误代码设置为:

  • EGL_BAD_DISPLAY——如果display没有指定有效的EGLDisplay
  • EGL_NOT_INITIALIZED——如果EGL不能初始化

确定可用表面配置

调用如下函数,查询底层窗口系统支持的所有EGL表面配置

EGLBoolean  eglGetConfigs(EGLDisplay display, EGLConfig *configs,EGLint maxReturnConfigs,EGLint *numConfigs)

display:: 指定EGL显示连接
configs: 指定configs列表
maxReturnConfigs: 指定configs的大小
numConfigs: 指定放回的configs大小

错误代码:

  • EGL_NOT_INITIALIZED ——如果display没有初始化
  • EGL_BAD_PARAMETER——如果numConfigs为空

两种调用方法

  1. 指定configs的值为NULL,系统返回EGL_TRUE,并将numConfigs设置为可用EGLConfigs的数量,没有关于系统中EGLConfigs的任何附加信息返回,但是知道可用配置的数量。
  2. 分配一个未初始化EGLConfigs值的数组,并将它们传递给eglGetConfigs作为configs参数,将maxReturnConfigs设置为分配的数组大小,这也制定了返回的最大配置数量。调用完成时,numConfigs 将用修改后的configs中的输入项数量更新。然后,你可以开始处理返回值的列表,查询各种配置的特性,确定你需要的最佳匹配。

查询EGLConfig属性

获得到EGLConfigs列表后,可以查询EGLConfig属性。它包含了关于可用颜色、与配置相关的其他缓冲区、表面类型和许多其它特性。

EGLBoolean eglGetConfigAttrib(EGLDisplay display, EGLConfig config, EGLint attribute, EGLint *value)

display : 指定EGL显示连接
config : 指定要查询的配置
attribute:· 指定返回的特定属性
value :·确定返回值

错误代码:

  • EGL_BAD_ATTRIBUTE——attribute不是有效的属性

让EGL选择配置

调用如下函数,让EGL选择匹配的EGLConfig

EGLBoolean eglChooseConfig(EGLDisplay display, const EGLint *attribList, EGLConfig *configs, EGLint maxReturnConfigs, EGLint *numConfigs)

dispplay :·指定EGL显示连接
attribList :·指定configs匹配的属性列表
configs : 指定配置列表
maxReturnConfigs: 指定配置的大小
numConfigs : 指定返回的配置大小

如果返回成功,我们就有足够的信息可以继续创建绘图表面。默认情况下,如果没有指定所需类型的渲染表面,通过制定EGL_SURFACE_TYPE属性,则EGL假定你所需的是屏幕上的窗口

创建屏幕上的渲染区域:EGL窗口

我们有了符合渲染需求的EGLConfig,就为创建窗口做好了准备,调用如下函数可以创建一个窗口

EGLSurface eglCreateWindowSurface(EGLDisplay display, EGLConfig config, EGLNativeWindowType window, const EGLint *attribList)

display:: 指定EGL显示连接
config: 指定配置
window: 指定原生窗口
attribList: 指定窗口属性列表;可能为NULL

这个函数需要原生窗口系统创建的一个窗口(如Android事先创建一个Texture或者GLSurface)。

这个调用需要一个属性列表,由于EGL支持其他渲染API,所以在使用OpenGLES3.0时 EGLCreateWindowSurface接受的一些属性不适用。

属性列表可能为空,也可用EGL_NONE标志作为列表的第一个元素。

错误代码:

  • EGL_BAD_MATCH 原生窗口属性不匹配提供的EGLConfig,提供的EGLConfig不支持渲染到窗口
  • EGL_BAD_CONFIG 如果提供的EGLConfig没有得到系统的支持,标记错误
  • EGL_BAD_NATIVE_WINDOW 如果提供的原声窗口句柄无效,标记错误
  • EGL_BAD_ALLOC EGLCreateWindowSurface无法为新的EGL窗口分配资源,或者已经有和提供的原生窗口关联的EGLConfig,标记错误

创建屏幕外渲染区域: EGL Pbuffer

Pbuffer 像素缓冲区 Pixel buffer,不可见的屏幕外表面。

和窗口一样,Pbuffer可以 利用OpenGLES3.0中的任何硬件加速,Pbuffer最常用于生成纹理贴图。如果想做的是渲染到一个纹理,那么我们建议使用帧缓冲区对象代替Pbuffer,因为帧缓冲区更高效,不过,在某些帧缓冲区对象无法使用的 情况下,Pbuffer仍然有用。

EGLSurface eglCreatePbufferSurface(EGLDisplay display, EGLConfig config, const EGLint *attribList)

display: 指定EGL显示连接
config: 指定配置
attribList: 指定像素缓冲区属性列表;可能为NULL

创建Pbuffer和创建EGL窗口非常相似,不同的是,我们需要找到EGLConfig,并作一处修改:需要扩增EGL_SURFACE_TYPE的值,使其包含EGL_PBUFFER_BIT,拥有合适的EGLConfig之后,就可以调用上述函数创建pbuffer

错误代码:

  • EGL_BAD_ALLOC 缺乏资源无法分配
  • EGL_BAD_CONFIG 提供的EGLConfig不是系统支持的有效配置
  • EGL_BAD_PARAMETER 如果属性列表提供的EGL_WIDTH或者EGL_HEIGHT是负值
  • EGL_BAD_MATCH 提供的EGLC欧诺法IG不支持Pbuffer表面;Pbuffer被用作纹理贴图(EGL_TEXTURE_FORMAT而不是EGL_NO_TEXTURE),且指定的EGL_WIDTH和EGL_HEIGHT是无效的纹理尺寸;EGL_TEXTURE_FORMAT和EGL_TEXTURE_TARGET设置为EGL_NO_TEXTURE,而其他属性没有设置成EGL_NO_TEXTURE
  • EGL_BAD_ATTRIBUTE 如果指定EGL_TEXTURE_FORMAT、EGL_TEXTURE_TARGET或EGL_MIPMAP_TEXTURE,但提供的EGLConfig不支持OpenGL ES渲染(如只支持OpenVG渲染)

创建一个渲染上下文

EGLContext eglCreateContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, const EGLint *attribList)

display: 指定EGL显示连接
config: 指定配置
shareContext: 允许多个EGL上下文共享特定类型的数据,例如着色器程序和纹理贴图;使用EGL_NO_CONTEXT表示没有共享
attribList: 指定创建上下文使用的属性列表;只有一个可接收的属性——EGL_CONTEXT_CLIENT_VERSION

同样,你需要显示连接和最能代表应用程序需求的EGLConfig,shareContext参数允许多个EGLContext共享特定类型的数据,例如着色器程序和纹理贴图,我们如果传递EGL_NO_CONTEXT作为shareContext的值,表示我们不和其它上下文共享资源。

与其他EGL调用类似,指定特定于EGLCreateContext操作的属性列表,在这种情况系啊,只接受一个属性EGL_CONTEXT_CLIENT_VERSION。

EGLCreateContext成功的时候,他返回一个指向新创建上下文的句柄,如果不能创建上下文,则eglCreateContext返回EGL_NO_CONTEXT,并标记失败。

错误代码

  • EGL_BAD_CONFIG 提供的EGLConfig无效

同步渲染

我们可能需要协调多个图形API在单个窗口中的渲染,则需要让应用程序允许多个库渲染到共享窗口。

如果应用程序只使用OpenGL ES 3.0渲染,那么可以简单调用GLFinish来保证所有渲染已经发生。

但是如果用的还有其他APi,那么再切换窗口系统原生渲染API之前可能不知道使用的是哪个API,为此可以调用以下函数

EGLBoolean eglWaitClient()

延迟客户端的执行,知道通过某个Khronos API(如OpenGL ES 3.0 , OpenGL 或 OpenVGA)的所有渲染完成。

错误代码

  • EGL_BAD_CURRENT_SURFACE

上述函数的操作与GLFinish操作类似,但是不管当前操作的是哪个Khronos API 均有效

同样,你需要保证原生窗口系统的渲染完成,调用如下函数

EGLBoolean eglWatiNative(EGLint engine)

engine: 指定渲染程序等待渲染完成

参数值EGL_CORE_NATIVE_ENGINE总是可接受的,代表所支持的最常见引擎;其他引擎特定于实现,通过EGL扩展指定。

错误代码

  • EGL_BAD_PARAMETER

指定某个EGLContext为当前上下文

一个应用程序可能创建多个EGLContext用于不同的用途,所以需要关联特定的EGLContext和渲染表面

EGLBoolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context)

display: 指定EGL显示连接
draw: 指定EGL绘图表面
read: 指定EGL读取表面
context: 指定连接到该表面的渲染上下文

Android中使用EGL绘图的一般步骤:

  • 1、获取 EGL Display 对象:eglGetDisplay()
  • 2、初始化与 EGLDisplay 之间的连接:eglInitialize()
  • 3、获取 EGLConfig 对象:eglChooseConfig()
  • 4、创建 EGLContext 实例:eglCreateContext()
  • 5、创建 EGLSurface 实例:eglCreateWindowSurface()
  • 6、连接 EGLContext 和 EGLSurface:eglMakeCurrent()
  • 7、使用 OpenGL ES API 绘制图形:gl_*()
  • 8、切换 front buffer 和 back buffer 送显:eglSwapBuffer()
    • 双缓冲(前后台缓冲 SurfaceView中交换前后台,提高渲染效率,避免刷新率过高出现闪烁现象)
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kkiwkSuO-1603105574497)(https://www.pianshen.com/images/235/92bd6b7857c8ccd62015e0c8e0c32ca3.png)]
  • 9、断开并释放与 EGLSurface 关联的 EGLContext 对象:eglRelease()
  • 10、删除 EGLSurface 对象
  • 11、删除 EGLContext 对象
  • 12、终止与 EGLDisplay 之间的连接

OpenGL——EGL_第2张图片

Java层代码示例


	/**
     * 初始化EGL环境
     *
     * @param clientVersion
     */
    private void initEGLContext(int clientVersion) {
        //获取默认显示设备
        mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
        if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
            throw new RuntimeException("eglGetDisplay error: " + EGL14.eglGetError());
        }
        //存放EGL版本号
        int[] version = new int[2];
        version[0] = 3;
        if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
            throw new RuntimeException("eglInitialize error: " + EGL14.eglGetError());
        }
        //配置列表
        int[] attributes = {
                EGL14.EGL_BUFFER_SIZE, 32,
                EGL14.EGL_RED_SIZE, 8,
                EGL14.EGL_GREEN_SIZE, 8,
                EGL14.EGL_BLUE_SIZE, 8,
                EGL14.EGL_ALPHA_SIZE, 8,
                EGL14.EGL_RENDERABLE_TYPE, 4,
                EGL14.EGL_SURFACE_TYPE, EGL14.EGL_WINDOW_BIT,
                EGL14.EGL_NONE
        };
        int[] numConfigs = new int[1];
        //EGL选择配置
        if (!EGL14.eglChooseConfig(mEGLDisplay, attributes, 0, configs, 0, configs.length, numConfigs, 0)) {
            throw new RuntimeException("eglChooseConfig error: " + EGL14.eglGetError());
        }
        //获取TextureView内置的SurfaceTexture作为EGL的绘图表面,也就是跟系统屏幕打交道
        SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
        if (surfaceTexture == null) {
            throw new RuntimeException("surfaceTexture is null");
        }
        //创建EGL显示窗口
        final int[] surfaceAttributes = {EGL14.EGL_NONE};
        mEglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, configs[0], surfaceTexture, surfaceAttributes, 0);
        //创建上下文环境
        int[] contextAttributes = {
                EGL14.EGL_CONTEXT_CLIENT_VERSION, clientVersion,
                EGL14.EGL_NONE
        };
        mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT, contextAttributes, 0);

        if (mEGLDisplay == EGL14.EGL_NO_DISPLAY || mEGLContext == EGL14.EGL_NO_CONTEXT) {
            throw new RuntimeException("eglCreateContext fail error: " + EGL14.eglGetError());
        }
        if (!EGL14.eglMakeCurrent(mEGLDisplay, mEglSurface, mEglSurface, mEGLContext)) {
            throw new RuntimeException("eglMakeCurrent error: " + EGL14.eglGetError());
        }

        //加载渲染器
        try {
            mTextureRenderer = (ITextureRenderer)(mRendererClass.getConstructor(Integer.class).newInstance(mOESTextureId));
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

你可能感兴趣的:(OpenGL,Android,android,opengles)