屏幕上可見的幀緩沖區(qū)由一個像素數(shù)據(jù)的二維數(shù)組表示。直接在可顯示緩沖區(qū)上更新像素由一個嚴(yán)重的問題——用戶在部分更新幀緩沖區(qū)時看到偽像或者閃爍的現(xiàn)象
為了解決這個問題,引入了雙緩沖區(qū)。
OpenGLES應(yīng)用程序中,這種活動通過EGL函數(shù)eglSwapBuffers控制:
eglSwapBuffers(exContext->eglDisplay, esContext->eglSurface);
EGL提供以下機(jī)制:
與設(shè)備的原生窗口系統(tǒng)通信
查詢繪圖表面的可用類型和配置
創(chuàng)建繪圖表面
在OpenGLES3.0和其它圖形渲染API(如桌面OpenGL和OpenVG——硬件加速矢量圖形的跨平臺API,活著窗口系統(tǒng)的原生繪圖命令)之間同步渲染
管理紋理貼圖等渲染資源
與窗口系統(tǒng)通信
Apple提供自己的EGL API的iOS 實現(xiàn),稱為EAGL
因為每個窗口系統(tǒng)都有不同語義,所以EGL提供基本的不透明類型——EGLDisplay,該類封裝了所有系統(tǒng)相關(guān)性,用于和原生窗口系統(tǒng)接口交互。
任何使用EGL的應(yīng)用程序必須執(zhí)行的第一個操作是創(chuàng)建和初始化與本地EGL顯示的連接。
一、初始化EGL
EGLConfig config;
EGLint majorVersion;
EGLint minorVersion;
EGLint contextAttribs[] = {
EGL_CONTEXT_CLIENT_VERSION,? ? ? ? 3,? ? ? ? EGL_NONE
};
// 獲取寬和高
esContext->width = ANativeWindow_getWidth(esContext->eglNativeWindow);esContext->height = ANativeWindow_getHeight(esContext->eglNativeWindow);
// 打開與EGL顯示服務(wù)器的連接,默認(rèn)為EGL_DEFAULT_DISPLAY
esContext->eglDisplay = eglGetDisplay(esContext->eglNativeDisplay);
// 如果顯示連接不可用,則返回EGL_NO_DISPLAY標(biāo)志,此時無法創(chuàng)建窗口
if (esContext->eglDisplay == EGL_NO_DISPLAY) {
return GL_FALSE;
}
// 初始化egl并回傳版本號
if (!eglInitialize(esContext->eglDisplay, &majorVersion, &minorVersion)) {
return GL_FALSE;
}
成功打開連接后,需要初始化EGL,這通過調(diào)用如下函數(shù)完成:
EGLAPI EGLBoolean EGLAPIENTRY eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);
dpy —— 指定EGL顯示連接
major —— 返回EGL的主版本號
minor —— 返回EGL的副版本號
二、確定可用表面配置
一旦初始化了EGL,就可以確定可用渲染表面的類型和配置,有兩種方法:
查詢每個表面配置,找出最好的選擇
指定一組需求,讓EGL推薦最佳配置
通常使用第二種方法更簡單,而且最有可能得到匹配。
任何一種情況下,EGL將返回一個EGLConfig,這是包含有關(guān)特定表面及其特性(每個顏色變量分量的位數(shù)、與EGLConfig相關(guān)的深度緩沖區(qū)(如果存在))的EGL內(nèi)部數(shù)據(jù)結(jié)構(gòu)的標(biāo)識符。
EGLint numConfigs = 0;
EGLint attribList[] = {
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_ALPHA_SIZE, (flags & ES_WINDOW_ALPHA) ? 8 : EGL_DONT_CARE,
EGL_DEPTH_SIZE, (flags & ES_WINDOW_DEPTH) ? 8 : EGL_DONT_CARE,
EGL_STENCIL_SIZE, (flags & ES_WINDOW_STENCIL) ? 8 : EGL_DONT_CARE,
EGL_SAMPLE_BUFFERS, (flags & ES_WINDOW_MULTISAMPLE) ? 1 : 0,
EGL_RENDERABLE_TYPE, getContextRenderableType(esContext->eglDisplay),
EGL_NONE
};
// 選擇Config
if (!eglChooseConfig(esContext->eglDisplay, attribList, &config, 1, &numConfigs)) {
return GL_FALSE;
}
if (numConfigs < 1) {
return GL_FALSE;
}
三、查詢EGLConfig屬性
EGLConfig包含關(guān)于EGL啟用的表面的所有信息??捎妙伾⑴c配置相關(guān)的其它緩沖區(qū)(深度和模板緩沖區(qū)等)、表面類型和其它特性
// Android需要獲取EGL_NATIVE_VISUAL_ID的值并將其放如ANativeWindow_setBuffersGeometry函數(shù)中
EGLint format = 0;
eglGetConfigAttrib(esContext->eglDisplay, config, EGL_NATIVE_VISUAL_ID, &format);
ANativeWindow_setBuffersGeometry(esContext->eglNativeWindow, 0, 0, format);
四、創(chuàng)建屏幕上的渲染區(qū)域:EGL窗口
EGLAPI EGLSurface EGLAPIENTRY eglCreateWindowSurface(EGLDisplay dpy,
EGLConfig config,
EGLNativeWindowType win,
const EGLint *attrib_list);
該函數(shù)用于創(chuàng)建一個窗口,以我們得到的原生顯示管理器的連接和前一步得到的EGLConfig為參數(shù)。它需要原生窗口系統(tǒng)事先創(chuàng)建一個窗口。
創(chuàng)建窗口示例:
// 創(chuàng)建一個EGL窗口表面示例
EGLint list[] = {
EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE
};
EGLSurface window = eglCreateWindowSurface(esContext->eglDisplay, config,
esContext->eglNativeWindow, list);
if (window == EGL_NO_SURFACE) {
switch (eglGetError()) {
case EGL_BAD_MATCH:
break;
case EGL_BAD_CONFIG:
break;
case EGL_BAD_NATIVE_WINDOW:
break;
case EGL_BAD_ALLOC:
break;
}
}
上面的代碼是創(chuàng)建一個繪圖場景,但是我們還必須完成兩個步驟,才能成功地用OpenGLES繪圖。然而窗口不是唯一可用的渲染表面,也可以使用屏幕外渲染區(qū)域,也就是離屏渲染。
屏幕外渲染區(qū)域(離屏渲染):EGL Pbuffer
OpenGLES3.0除了可以進(jìn)行屏幕渲染,也可以在不可見的屏幕外渲染,也就是在像素緩沖區(qū)上進(jìn)行渲染,支持硬件加速。Pbuffer最常用于紋理貼圖。
創(chuàng)建Pbuffer 和 創(chuàng)建EGL窗口非常相似,只有少數(shù)微笑的不同。
創(chuàng)建Pbuffer需要和窗口一樣找到EGLConfig,并作一些修改:需要擴(kuò)增EGL_SURFACE_TYPE的值,使其包含EGL_PBUFFER_BIT。
創(chuàng)建EGL像素緩沖區(qū)示例如下:
EGLint bufferList[] = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT_KHR,
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_DEPTH_SIZE, 1,
EGL_NONE
};
const EGLint MaxConfigs = 10;
EGLConfig configs[MaxConfigs];
EGLint numConfigs;
if (!eglChooseConfig(esContext->eglDisplay, bufferList, configs, MaxConfigs, &numConfigs)) {
return GL_FALSE;
}
EGLSurface pbuffer;
EGLint attribufferList[] = {
EGL_WIDTH, 512,
EGL_HEIGHT, 512,
EGL_LARGEST_PBUFFER, EGL_TRUE,
EGL_NONE
};
pbuffer = eglCreatePbufferSurface(esContext->eglDisplay, config, attribufferList);
if (pbuffer == EGL_NO_SURFACE) {
switch (eglGetError()) {
case EGL_BAD_ALLOC:
break;
case EGL_BAD_CONFIG:
break;
case EGL_BAD_PARAMETER:
break;
case EGL_BAD_MATCH:
break;
}
}
// 檢查pbuffer 分配的內(nèi)存大小
EGLint width;
EGLint height;
if (!eglQuerySurface(esContext->eglDisplay, pbuffer, EGL_WIDTH, &width)
|| !eglQuerySurface(esContext->eglDisplay, pbuffer, EGL_HEIGHT, &height)) {
// 不能查詢 surface的信息
return GL_FALSE;
}
五、創(chuàng)建渲染上下文
渲染上下文是OpenGLES3.0的內(nèi)部數(shù)據(jù)結(jié)構(gòu),包含操作所需要的所有狀態(tài)信息。它包含了對頂點(diǎn)著色器和片元著色器數(shù)據(jù)的引用。
// 5.創(chuàng)建上下文
esContext->eglContext = eglCreateContext(esContext->eglDisplay, config,
EGL_NO_CONTEXT, contextAttribs);
// 判斷上下文是否創(chuàng)建成功
if (esContext->eglContext == EGL_NO_CONTEXT) {
return GL_FALSE;
}
// 根據(jù)之前的配置構(gòu)建上下文
if (!eglMakeCurrent(esContext->eglDisplay, esContext->eglSurface, esContext->eglSurface, esContext->eglContext)) {
return GL_FALSE;
}
六、指定某個EGLContext為當(dāng)前上下文
// 指定某個EGLContext為當(dāng)前上下文
if (!eglMakeCurrent(esContext->eglDisplay, esContext->eglSurface,
esContext->eglSurface, esContext->eglContext)) {
return GL_FALSE;
}
同步渲染
多個圖形API在單個窗口中的渲染,此時需要使用同步渲染,需要應(yīng)用程序允許多個庫渲染到共享窗口。
如果不止使用OpenGLES3.0渲染,則不能簡單地調(diào)用glFinish來抱著個所有渲染已經(jīng)發(fā)生,你可以調(diào)用:
EGLBoolean eglWaitClient()
延遲客戶端的執(zhí)行,直到某個API的所有渲染完成,成功返回EGL_TRUE,失敗時返回EGL_FALSE并發(fā)送EGL_BAD_CURRENT_SURFACE錯誤
同樣,如果你需要保證原生窗口系統(tǒng)的渲染完成,則調(diào)用如下函數(shù):
EGLBoolean eglWaitNative(EGLint engine)
engine,指定渲染程序等待渲染完成