背景
這段時(shí)間經(jīng)常跟 OpenGL ES 和 EGL 打交道江咳。在這整理記錄 EGL 接口的使用方法,方便后續(xù)理解
問題
- EGL 是一層接口哥放,上層跟 OpenGL 對(duì)接歼指,下層跟本地窗口系統(tǒng)對(duì)接,負(fù)責(zé)隔離 OpenGL 與本地窗口的依賴
- OpenGL 本身只專注于渲染流程甥雕,核心就是 Pipeline 的處理
-
如果沒有 EGL 提供的渲染上下文踩身,則 OpenGL 無法執(zhí)行
使用方法
由于 OpenGL ES 依賴于 EGL,故需要先建立 EGL社露,再使用 OpenGL ES挟阻。下面以 Android SDK 的源碼為例說明經(jīng)典用法。
- 以下源碼出自 android.hardware.camera2.legacy.SurfaceTextureRenderer 類
- 初始化 EGLContext 對(duì)象
private void configureEGLContext() {
// 查詢默認(rèn)顯示屏對(duì)象 EGLDisplay峭弟。Android 支持多個(gè)顯示屏附鸽。
mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
throw new IllegalStateException("No EGL14 display");
}
int[] version = new int[2];
// 初始化 EGL
if (!EGL14.eglInitialize(mEGLDisplay, version, /*offset*/ 0, version, /*offset*/ 1)) {
throw new IllegalStateException("Cannot initialize EGL14");
}
int[] attribList = {
EGL14.EGL_RED_SIZE, EGL_COLOR_BITLENGTH,
EGL14.EGL_GREEN_SIZE, EGL_COLOR_BITLENGTH,
EGL14.EGL_BLUE_SIZE, EGL_COLOR_BITLENGTH,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL_RECORDABLE_ANDROID, 1,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT | EGL14.EGL_WINDOW_BIT,
EGL14.EGL_NONE
};
EGLConfig[] configs = new EGLConfig[1];
int[] numConfigs = new int[1];
// 選擇最佳的 Surface 配置
EGL14.eglChooseConfig(mEGLDisplay, attribList, /*offset*/ 0, configs, /*offset*/ 0,
configs.length, numConfigs, /*offset*/ 0);
checkEglError("eglCreateContext RGB888+recordable ES2");
mConfigs = configs[0];
int[] attrib_list = {
EGL14.EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,
EGL14.EGL_NONE
};
// 創(chuàng)建記錄 OpenGL ES 狀態(tài)機(jī)信息的對(duì)象 EGLContext
mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
attrib_list, /*offset*/ 0);
checkEglError("eglCreateContext");
if(mEGLContext == EGL14.EGL_NO_CONTEXT) {
throw new IllegalStateException("No EGLContext could be made");
}
}
- 指定當(dāng)前線程操作的 EGLContext 對(duì)象÷魅常可以創(chuàng)建多個(gè)不同的 EGLContext 對(duì)象
private void makeCurrent(EGLSurface surface) {
EGL14.eglMakeCurrent(mEGLDisplay, surface, surface, mEGLContext);
checkEglError("makeCurrent");
}
- 交換渲染好的 Buffer 去顯示出來
private boolean swapBuffers(EGLSurface surface)
throws LegacyExceptionUtils.BufferQueueAbandonedException {
boolean result = EGL14.eglSwapBuffers(mEGLDisplay, surface);
int error = EGL14.eglGetError();
...
}
- 銷毀上下文資源
private void releaseEGLContext() {
if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
// 讓當(dāng)前線程操作的 EGLContext 指向默認(rèn)的 EGL_NO_CONTEXT 對(duì)象坷备。解綁
EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
EGL14.EGL_NO_CONTEXT);
dumpGlTiming();
if (mSurfaces != null) {
for (EGLSurfaceHolder holder : mSurfaces) {
if (holder.eglSurface != null) {
// 銷毀 Surface 對(duì)象
EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface);
}
}
}
if (mConversionSurfaces != null) {
for (EGLSurfaceHolder holder : mConversionSurfaces) {
if (holder.eglSurface != null) {
EGL14.eglDestroySurface(mEGLDisplay, holder.eglSurface);
}
}
}
// 銷毀 EGLContext 對(duì)象
EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
// 釋放線程?
EGL14.eglReleaseThread();
// 終止 Display 對(duì)象
EGL14.eglTerminate(mEGLDisplay);
}
mConfigs = null;
mEGLDisplay = EGL14.EGL_NO_DISPLAY;
mEGLContext = EGL14.EGL_NO_CONTEXT;
clearState();
}
- 離屏渲染
android.opengl.EGL14#eglCreatePbufferSurface
采用該方法創(chuàng)建的 EGLSurface 對(duì)象不需要 swapBuffer 進(jìn)行一幀數(shù)據(jù)的顯示情臭。