EGL 是 OpenGL ES 渲染 API 和本地窗口系统(native platform window system)之间的一个中间接口层,它主要由系统制造商实现。EGL提供如下机制:
https://www.khronos.org/egl/
因为每个窗口系统都有不同的语义,所以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,这通过调用如下函数完成
EGLBoolean eglInitialize(EGLDisplay display,EGLint *majorVersion, EGLint *minorVersion)
display: 指定EGL显示连接
majorVersion: 指定EGL实现返回的主版本号,可能NULL
minorVersion: 指定EGL实现返回的次版本号,可能NULL
如果无法初始化,将返回EGL_FALSE,并将EGL错误代码设置为:
调用如下函数,查询底层窗口系统支持的所有EGL表面配置
EGLBoolean eglGetConfigs(EGLDisplay display, EGLConfig *configs,EGLint maxReturnConfigs,EGLint *numConfigs)
display:: 指定EGL显示连接
configs: 指定configs列表
maxReturnConfigs: 指定configs的大小
numConfigs: 指定放回的configs大小
错误代码:
两种调用方法
获得到EGLConfigs列表后,可以查询EGLConfig属性。它包含了关于可用颜色、与配置相关的其他缓冲区、表面类型和许多其它特性。
EGLBoolean eglGetConfigAttrib(EGLDisplay display, EGLConfig config, EGLint attribute, EGLint *value)
display : 指定EGL显示连接
config : 指定要查询的配置
attribute:· 指定返回的特定属性
value :·确定返回值
错误代码:
调用如下函数,让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假定你所需的是屏幕上的窗口
我们有了符合渲染需求的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标志作为列表的第一个元素。
错误代码:
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
错误代码:
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,并标记失败。
错误代码
我们可能需要协调多个图形API在单个窗口中的渲染,则需要让应用程序允许多个库渲染到共享窗口。
如果应用程序只使用OpenGL ES 3.0渲染,那么可以简单调用GLFinish来保证所有渲染已经发生。
但是如果用的还有其他APi,那么再切换窗口系统原生渲染API之前可能不知道使用的是哪个API,为此可以调用以下函数
EGLBoolean eglWaitClient()
延迟客户端的执行,知道通过某个Khronos API(如OpenGL ES 3.0 , OpenGL 或 OpenVGA)的所有渲染完成。
错误代码
上述函数的操作与GLFinish操作类似,但是不管当前操作的是哪个Khronos API 均有效
同样,你需要保证原生窗口系统的渲染完成,调用如下函数
EGLBoolean eglWatiNative(EGLint engine)
engine: 指定渲染程序等待渲染完成
参数值EGL_CORE_NATIVE_ENGINE总是可接受的,代表所支持的最常见引擎;其他引擎特定于实现,通过EGL扩展指定。
错误代码
一个应用程序可能创建多个EGLContext用于不同的用途,所以需要关联特定的EGLContext和渲染表面
EGLBoolean eglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context)
display: 指定EGL显示连接
draw: 指定EGL绘图表面
read: 指定EGL读取表面
context: 指定连接到该表面的渲染上下文
/**
* 初始化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();
}
}