屏幕上可見的幀緩沖區(qū)由一個(gè)像素?cái)?shù)據(jù)的二維數(shù)組表示。直接在可顯示緩沖區(qū)上更新像素由一個(gè)嚴(yán)重的問題——用戶在部分更新幀緩沖區(qū)時(shí)看到偽像或者閃爍的現(xiàn)象
為了解決這個(gè)問題竹揍,引入了雙緩沖區(qū)州刽。
OpenGLES應(yīng)用程序中迎献,這種活動(dòng)通過EGL函數(shù)eglSwapBuffers控制:
eglSwapBuffers(exContext->eglDisplay, esContext->eglSurface);
EGL提供以下機(jī)制:
與設(shè)備的原生窗口系統(tǒng)通信
查詢繪圖表面的可用類型和配置
創(chuàng)建繪圖表面
在OpenGLES3.0和其它圖形渲染API(如桌面OpenGL和OpenVG——硬件加速矢量圖形的跨平臺(tái)API绢慢,活著窗口系統(tǒng)的原生繪圖命令)之間同步渲染
管理紋理貼圖等渲染資源
與窗口系統(tǒng)通信
Apple提供自己的EGL API的iOS 實(shí)現(xiàn)只嚣,稱為EAGL
因?yàn)槊總€(gè)窗口系統(tǒng)都有不同語義沮稚,所以EGL提供基本的不透明類型——EGLDisplay,該類封裝了所有系統(tǒng)相關(guān)性册舞,用于和原生窗口系統(tǒng)接口交互蕴掏。
任何使用EGL的應(yīng)用程序必須執(zhí)行的第一個(gè)操作是創(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)志藐石,此時(shí)無法創(chuàng)建窗口
if (esContext->eglDisplay == EGL_NO_DISPLAY) {
return GL_FALSE;
}
// 初始化egl并回傳版本號(hào)
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的主版本號(hào)
minor —— 返回EGL的副版本號(hào)
二于微、確定可用表面配置
一旦初始化了EGL逗嫡,就可以確定可用渲染表面的類型和配置,有兩種方法:
查詢每個(gè)表面配置株依,找出最好的選擇
指定一組需求祸穷,讓EGL推薦最佳配置
通常使用第二種方法更簡(jiǎn)單,而且最有可能得到匹配勺三。
任何一種情況下雷滚,EGL將返回一個(gè)EGLConfig,這是包含有關(guān)特定表面及其特性(每個(gè)顏色變量分量的位數(shù)吗坚、與EGLConfig相關(guān)的深度緩沖區(qū)(如果存在))的EGL內(nèi)部數(shù)據(jù)結(jié)構(gòu)的標(biāo)識(shí)符祈远。
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啟用的表面的所有信息商源〕捣荩可用顏色、與配置相關(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)建一個(gè)窗口,以我們得到的原生顯示管理器的連接和前一步得到的EGLConfig為參數(shù)庄吼。它需要原生窗口系統(tǒng)事先創(chuàng)建一個(gè)窗口缎除。
創(chuàng)建窗口示例:
// 創(chuàng)建一個(gè)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)建一個(gè)繪圖場(chǎng)景,但是我們還必須完成兩個(gè)步驟总寻,才能成功地用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)信息郁副。它包含了對(duì)頂點(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;
}
六豌习、指定某個(gè)EGLContext為當(dāng)前上下文
// 指定某個(gè)EGLContext為當(dāng)前上下文
if (!eglMakeCurrent(esContext->eglDisplay, esContext->eglSurface,
esContext->eglSurface, esContext->eglContext)) {
return GL_FALSE;
}
同步渲染
多個(gè)圖形API在單個(gè)窗口中的渲染存谎,此時(shí)需要使用同步渲染,需要應(yīng)用程序允許多個(gè)庫渲染到共享窗口肥隆。
如果不止使用OpenGLES3.0渲染既荚,則不能簡(jiǎn)單地調(diào)用glFinish來抱著個(gè)所有渲染已經(jīng)發(fā)生,你可以調(diào)用:
EGLBoolean eglWaitClient()
延遲客戶端的執(zhí)行栋艳,直到某個(gè)API的所有渲染完成恰聘,成功返回EGL_TRUE,失敗時(shí)返回EGL_FALSE并發(fā)送EGL_BAD_CURRENT_SURFACE錯(cuò)誤
同樣吸占,如果你需要保證原生窗口系統(tǒng)的渲染完成晴叨,則調(diào)用如下函數(shù):
EGLBoolean eglWaitNative(EGLint engine)
engine,指定渲染程序等待渲染完成