OpenGL ES學習系列文章:
上一篇:OpenGL ES (一)EGL介紹和使用
下一篇:OpenGL ES (三)着色器和程式
EGL介紹和使用
- 一、EGL簡介
- 二、EGL的功能
- 三、EGL使用
-
- 1、建立OpenGL ES與原生視窗系統的連接配接
- 2、初始化EGL連接配接
- 3.選擇Surface配置
- 4. 建立渲染區域Surface
-
- 4.1 建立螢幕上的渲染區域:EGL視窗
- 4.2 其他操作surface方法
- 5. 建立渲染上下文
一、EGL簡介
與OpenGl ES一樣,EGL同樣是由Khronos Group所提供的一個與平台無關的API。
OpenGL作為一個圖形化API,允許我們操作GPU以繪制圖形,但是當涉及到本地的視窗時,就需要一個與平台無關的API來與之進行互動,EGL應運而生,承擔起OpenGl和原生視窗系統之間橋梁的作用。
二、EGL的功能
EGL API作為一套與OpenGL ES各個版本互相獨立的API,其作用主要是管理繪圖表面。EGL提供以下機制:
- 與裝置的原生視窗系統通信
- 查詢繪圖表面的可用類型和配置
- 建立繪圖表面
- 在OpenGL ES3.0或其他渲染API之間同步渲染
- 管理紋理貼圖等渲染資源
三、EGL使用
1、建立OpenGL ES與原生視窗系統的連接配接
eglGetDisplay為原生視窗系統displayId擷取一個EGL display連接配接,
在OpenGL ES與本地視窗系統之間架起了一座溝通的橋梁
2、初始化EGL連接配接
與視窗系統的連接配接建立以後,随後要進行EGL的初始化
EGLBoolean eglInitialize(EGLDisplay display, // 要進行初始化的EGL連接配接,即上一部中的傳回值
EGLint *majorVersion, // 主版本号
EGLint *minorVersion) // 次版本号
- display預設為EGL_DEFAULT_DISPLAY,即傳回與預設原生視窗的連接配接
- majorVersion 和minorVersion 會在調用後傳回版本号,例如EGL1.0, majorVersion傳回1, minorVersion傳回0
注:eglInitialize會初始化EGL内部資料,比如配置設定顯存等;如果初始化失敗,可以用eglGetError()
函數擷取具體的錯誤類型,其他EGL相關的函數,也都可以使用eglGetError()來擷取錯誤的類型。
EGL中大部分函數執行成功時傳回EGL_TRUE,失敗時傳回EGL_FALSE。eglGetError()函數可以訓示故障原因
常見錯誤傳回值(使用eglGetError擷取):
- EGL_BAD_DISPLAY :表示參數display不是一個EGL diaplay 連接配接
- EGL_NOT_INITIALIZED :表示傳入的display不可以被初始化
建立連接配接與初始化執行個體:
EGLint majorVersion;
EGLint minorVersion;
EGLDisplay display;
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
// 與本地視窗系統建立連接配接失敗
}
if (!eglInitialize(display, &majorVersion, &minorVersion)) {
// 初始化EGL連接配接失敗
}
3.選擇Surface配置
EGL初始化成功之後,就需要确定可用渲染表面的類型和配置。
一般來說,選擇配置的方法有兩種:
- 方法一:查詢所有可行的配置(eglGetConfigs),再讓EGL從中找出最優選擇(eglGetConfigAttrib)
- 方法二:指定一組需求,然後再讓EGL推薦(eglChooseChofig)最佳配置。
下面分寫介紹這兩種方法的使用。
方法一:從所有可行配置中找到最優配置
EGLBoolean eglGetConfigs(
EGLDisplay display, //指定EGL顯示連接配接
EGLConfig * configs, //指定一組config
EGLint config_size, //指定配置清單的大小
EGLint * num_config); //實際比對的配置總數
參數configs将包含在你平台上所有有效的EGLframebuffer配置清單。支援的總數通過num_config傳回,而實際傳回的 configs 的配置個數依賴于程式傳入的 config_size,如果 config_size < num_config,則不是所有可行的config都會被傳回。如果想要擷取系統支援的所有配置資訊,最好的方法就是先給eglGetConfigs傳一個null的configs參數,這樣執行後就會得到系統支援的配置總數num_config,然後使用num_config來給configs配置設定合适的記憶體大小,再用得到的configs來調用eglGetConfig。
示例:
EGLConfig *configs_list;
EGLint num_configs;
m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if( m_eglDisplay == EGL_NO_DISPLAY || eglGetError() != EGL_SUCCESS ){
return FALSE;
}
if( eglInitialize( m_eglDisplay, NULL, NULL ) == EGL_FALSE || eglGetError() != EGL_SUCCESS ){
return FALSE;
}
// find out how many configurations are supported
if(eglGetConfigs(m_eglDisplay, NULL, 0, &num_configs)== EGL_FALSE || eglGetError() != EGL_SUCCESS){
return FALSE;
}
configs_list = malloc(num_configs * sizeof(EGLConfig));
if (configs_list == (EGLConfig *)0){
return FALSE;
}
// Get Configurations
if( eglGetConfigs( m_eglDisplay, configs_list, num_configs, &num_configs)== EGL_FALSE || eglGetError() != EGL_SUCCESS ){
return FALSE;
}
由于目前平台的限制,通常隻有很少的配置可用。系統支援的配置通常是利用系統硬體提供最好的性能。當你移植遊戲到多個平台,它們的EGL 配置可能會有細微的差别,我們希望作為通用的移植問題來直接處理這些問題。
方法二:指定一組需求,讓EGL推薦最佳配置
EGLBoolean eglChooseConfig(
EGLDisplay display, // 指定EGL顯示連接配接
const EGLint *attribList, // 指定configs比對的屬性清單
EGLConfig *configs, // 指定配置清單
EGLint maxReturnConfigs, // 指定配置的大小
EGLint *numConfigs) // 指定傳回的配置大小
glChooseConfig()函數将适配一個你所期望的配置,并且盡可能接近一個有效的系統配置。參數 attribList 指定了選擇配置時需要參照的屬性。參數 configs 将傳回一個按照 attribList 排序的平台有效的所有 EGL framebuffer 配置清單。參數 config_size 指定了可以傳回到 configs 的總配置個數。參數 num_config 傳回了實際比對的配置總數。
示例:
EGLint attrs[3] = { EGL_DEPTH_SIZE, 16, EGL_NONE };
EGLint num_configs;
EGLConfigs *configs_list;
// Get the display device
if ((eglDisplay = eglGetDisplay(EGL_NO_DISPLAY)) == EGL_NO_DISPLAY) {
return eglGetError();
}
// Initialize the display
if (eglInitialize(eglDisplay, NULL, NULL) == EGL_FALSE) {
return eglGetError();
}
// Obtain the total number of configurations that match
if (eglChooseConfig(eglDisplay, attrs, NULL, 0, &num_configs) == EGL_FALSE) {
return eglGetError();
}
configs_list = malloc(num_configs * sizeof(EGLConfig));
if (configs_list == (EGLConfig *)0){
return eglGetError();
}
// Obtain the first configuration with a depth buffer of 16 bits
if (!eglChooseConfig(eglDisplay, attrs, &configs_list, num_configs, &num_configs)) {
return eglGetError();
}
如果找到了多個配置,則基于屬性的優先級進行排序,下表顯示了基于屬性值的用來選擇和排序的順序(不需過多關注),也包括了 EGL 規範中所有 EGL 配置屬性及其預設值。
屬性 | 資料類型 | 預設值 | 排序優先級 | 選擇排序 |
---|---|---|---|---|
EGL_BUFFER_SIZE | int | 3 | Smaller value | |
EGL_RED_SIZE | int | 2 | Larger value | |
EGL_GREEN_SIZE | int | 2 | Larger value | |
EGL_BLUE_SIZE | int | 2 | Larger value | |
EGL_ALPHA_SIZE | int | 2 | Larger value | |
EGL_CONFIG_CAVET | enum | EGL_DONT_CARE | 1(first) | Exact value |
EGL_CONFIG_ID | int | EGL_DONT_CARE | 9 | Exact value |
EGL_DEPTH_SIZE | int | 6 | Smaller value | |
EGL_LEVEL | int | - | Equal value | |
EGL_NATIVE_RENDERABLE | Boolean | EGL_DONT_CARE | - | Exact value |
EGL_NATIVE_VISUAL_TYPE | int | EGL_DONT_CARE | 8 | Exact value |
EGL_SAMPLE_BUFFERS | int | 4 | Smaller value | |
EGL_SAMPLES | int | 5 | Smaller value | |
EGL_STENCIL_SIZE | int | 7 | Smaller value | |
EGL_SURFACE_TYPE | bitmask | EGL_WINDOW_BIT | - | Mask value |
EGL_TRANSPARENT_TYPE | enum | EGL_NONE | - | Exact value |
EGL_TRANSPARENT_RED_VALUE | int | EGL_DONT_CARE | - | Exact value |
EGL_TRANSPARENT_GREEN_VALUE | int | EGL_DONT_CARE | - | Exact value |
EGL_TRANSPARENT_BLUE_VALUE | int | EGL_DONT_CARE | - | Exact value |
4. 建立渲染區域Surface
當有了符合條件的 EGLConfig 後,就可以通過 eglCreateWindowSurface 函數建立渲染區域。
4.1 建立螢幕上的渲染區域:EGL視窗
建立surface
EGLSurface eglCreateWindowSurface(
EGLDisplay display, //對應的EGL display連接配接
EGLConfig config, //EGL frame buffer配置,定義了可用于Surface的frame buffer資源
NativeWindowType native_window, //原生視窗
EGLint const * attrib_list); // attrib_list為Window Surface屬性清單,可以為NULL,成功時傳回新建立的EGLSurface,失敗時傳回EGL_NO_SURFACE.
// 可能出現的錯誤:
// EGL_BAD_DISPLAY: 連接配接不是一個EGL display連接配接
//EGL_NOT_INITIALIZED: EGL沒有初始化
//EGL_BAD_CONFIG: EGL frame buffer配置無效
//EGL_BAD_NATIVE_WINDOW: native window不是與display相同平台的有效Native Window
//EGL_BAD_ATTRIBUTE: attrib_list包含無效參數,或者參數未被識别,或者參數越界
//EGL_BAD_ALLOC:已經有一個與native window關聯的Surface,或者無法為新的EGL視窗配置設定資源,
//EGL_BAD_MATCH:本機視窗的像素格式與配置所需的顔色緩沖區的格式、類型和大小不一緻
4.2 其他操作surface方法
- 銷毀surface:
與eglCreateWindowSurface對應的,還有一個eglDestroySurface,用來銷毀surface,display為對應的Display,surface為将要銷毀的Surface, 如果任何其它線程都沒有使用這個Surface時,Surface将被立即銷毀,否則要等到這個Surface不被任何線程使用時才銷毀,另外,對于一個PBuffer Surface來說,其資源要等到綁定到紋理對象的顔色緩沖區釋放後才被銷毀。成功時傳回EGL_TRUE,失敗時傳回EGL_FALSE,可能的錯誤為EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_SURFACE。
- 擷取surface資訊
EGLAPI EGLBoolean EGLAPIENTRY eglQuerySurface(
EGLDisplay display,
EGLSurface surface,
EGLint attribute,
EGLint *value);
eglQuerySurface用于擷取Surface資訊,display為對應的Display,surface待查詢的Surface,attribute為待查詢的Surface屬性,value用于傳回Surface屬性值,成功時傳回EGL_TRUE,失敗時傳回EGL_FALSE,可能的錯誤為EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_SURFACE、EGL_BAD_ATTRIBUTE。attribute取值可以是:EGL_CONFIG_ID EGL_HEIGHT EGL_HORIZONTAL_RESOLUTION EGL_LARGEST_PBUFFER EGL_MIPMAP_LEVEL EGL_MIPMAP_TEXTURE EGL_MULTISAMPLE_RESOLVE EGL_PIXEL_ASPECT_RATIO EGL_RENDER_BUFFER EGL_SWAP_BEHAVIOR EGL_TEXTURE_FORMAT EGL_TEXTURE_TARGET EGL_VERTICAL_RESOLUTION EGL_WIDTH
- 設定surface屬性
EGLAPI EGLBoolean EGLAPIENTRY eglSurfaceAttrib(
EGLDisplay display,
EGLSurface surface,
EGLint attribute,
EGLint value);
eglSurfaceAttrib用于設定Surface屬性,display為對應的Display,surface為要設定的Surface,attribute為要設定的Surface屬性,value為要設定的Surface屬性值,成功時傳回EGL_TRUE,失敗時傳回EGL_FALSE,可能的錯誤為EGL_BAD_DISPLAY、EGL_BAD_MATCH、EGL_NOT_INITIALIZED、EGL_BAD_SURFACE、EGL_BAD_ATTRIBUTE。attribute取值可以是:EGL_MIPMAP_LEVEL:預設值為0。 EGL_MULTISAMPLE_RESOLVE:EGL_MULTISAMPLE_RESOLVE_DEFAULT或>EGL_MULTISAMPLE_RESOLVE_BOX,預設值為前者。 EGL_SWAP_BEHAVIOR:EGL_BUFFER_PRESERVED或EGL_BUFFER_DESTROYED,預設值由實作而定。
- 建立螢幕外的渲染區域
EGLSurface eglCreatePbufferSurface(
EGLDisplay display, // 指定EGL顯示連接配接
EGLConfig config, // 指定配置
const EGLint *attribList) // 指定像素緩沖區屬性
OpenGL ES 3.0不僅可以在螢幕上的視窗渲染,還可以渲染稱作pbuffer的不可見螢幕外表面。PBuffer Surface是不同于Window Surface的另一種EGLSurface,eglCreatePbufferSurface用于建立off-screen的pixel buffer Surface,display為對應的Display,config為frame buffer配置,attribList為PBuffer屬性清單,可以為NULL,成功時傳回新建立的EGLSurface,失敗時傳回EGL_NO_SURFACE,可能的錯誤為EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_CONFIG、EGL_BAD_ATTRIBUTE、EGL_BAD_ALLOC、EGL_BAD_MATCH。attribList的屬性可以是:EGL_HEIGHT:預設值為0。 EGL_LARGEST_PBUFFER:預設值為EGL_FALSE。 EGL_MIPMAP_TEXTURE:預設值為EGL_FALSE。 EGL_TEXTURE_FORMAT:預設值為EGL_NO_TEXTURE,還可以選擇EGL_TEXTURE_RGB或>EGL_TEXTURE_RGBA。 EGL_TEXTURE_TARGET:預設值為EGL_NO_TEXTURE,還可以選擇EGL_TEXTURE_2D。 EGL_VG_ALPHA_FORMAT:隻适用于OpenVG,預設值為EGL_VG_ALPHA_FORMAT_NONPRE,>還可以選擇EGL_VG_ALPHA_FORMAT_PRE。 EGL_VG_COLORSPACE:隻适用于OpenVG,預設值為EGL_VG_COLORSPACE_sRGB,還可以選>擇EGL_VG_COLORSPACE_LINEAR。 EGL_WIDTH:預設值為0。
PBuffer Surface與Window Surface
除了可以用OpenGL ES 3.0在螢幕上的視窗渲染之外,還可以渲染稱作PBuffer(像素緩沖區Pixel Buffer的縮寫)的不可見螢幕外表面,和視窗一樣,PBuffer可以利用OpenGL ES 3.0中的任何硬體加速,PBuffer最常用于生成紋理貼圖,如果想要做的是渲染到一個紋理,那麼建議使用幀緩沖區對象(FBO)代替PBuffer,因為幀緩沖區更高效,不過在某些FBO無法使用的情況下,PBuffer仍然有用,例如用OpenGL ES在螢幕外表面上渲染,然後将其作為其它API(如OpenVG)中的紋理。另外,EGLSurface還有個Pixmap Surface,簡單總結一下三者的特點。window是on-screen的,pbuffer和pixmap是off-screen的,window綁定到了NativeWindow,pixmap綁定到了NativePixmap,pbuffer沒有任何本地綁定,window是雙緩沖區的,pbuffer和pixmap是單緩沖區的,window預設在back buffer渲染,通過eglSwapBuffers交換到螢幕上顯示,pbuffer在顯存中配置設定,由EGL_HEIGHT和EGL_WIDTH指定大小,常用作紋理資料,pixmap綁定到本地的像素緩沖區,這個緩沖區可以被其它API使用。建立不同的EGLSurface時,需要在EGLConfig中配置EGL_SURFACE_TYPE,window、pbuffer、pixmap分别對應于EGL_WINDOW_BIT、EGL_PBUFFER_BIT、EGL_PIXMAP_BUFFER。
5. 建立渲染上下文
建立渲染上下文
EGLAPI EGLContext EGLAPIENTRY eglCreateContext(
EGLDisplay display, EGLConfig config,
EGLContext share_context,
const EGLint *attribList);
eglCreateContext用于建立EGL渲染Context,display為對應的Display,config為frame buffer配置,share_context為其它的共享Context,可以設定為EGL_NO_CONTEXT,attribList為Context屬性清單,成功時傳回新建立的EGLContext,失敗時傳回EGL_NO_CONTEXT,可能的錯誤為EGL_BAD_MATCH、EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_CONFIG、EGL_BAD_CONTEXT、EGL_BAD_ATTRIBUTE、EGL_BAD_ALLOC。attrib_list屬性目前隻有EGL_CONTEXT_CLIENT_VERSION,整數值,指定OpenGL ES版本,預設值為1,還可以是2、3等其它版本值,建立OpenGL ES Context時設定這個屬性,也就是說渲染API為EGL_OPENGL_ES_API時才設定這個屬性,為此還要設定或查詢渲染API,使用如下兩個函數。
銷毀渲染上下文
eglDestroyContext用于銷毀渲染Context,dpy為對應的Display,ctx為要銷毀的Context,如果有其它線程使用這個Context時就要等到不使用時再銷毀,否則立即銷毀,成功時傳回EGL_TRUE,失敗時傳回EGL_FALSE,可能的錯誤為EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_CONTEXT。
擷取Context資訊
EGLAPI EGLBoolean EGLAPIENTRY eglQueryContext(
EGLDisplay display,
EGLContext context,
EGLint attribute,
EGLint *value);
eglQueryContext用于擷取Context資訊,dpy為對應的Display,ctx為要查詢的Context,attribute為要查詢的Context屬性,value傳回查詢的Context屬性值,成功時傳回EGL_TRUE,失敗時傳回EGL_FALSE,可能的錯誤為EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_CONTEXT、EGL_BAD_ATTRIBUTE。attribute取值可以是:EGL_CONFIG_ID EGL_CONTEXT_CLIENT_TYPE EGL_CONTEXT_CLIENT_VERSION EGL_RENDER_BUFFER
eglMakeCurrent
EGLAPI EGLBoolean EGLAPIENTRY eglMakeCurrent(
EGLDisplay display, EGLSurface draw,
EGLSurface read, EGLContext context);
建立了Surface和Context之後,因為可能有多個Surface和Context,是以需要通過eglMakeCurrent綁定Context到Surface,display為對應的Display,draw用于繪制,read用于讀,ctx為要綁定的Context,成功是傳回EGL_TRUE,失敗時傳回EGL_FALSE,可能的錯誤為EGL_BAD_DISPLAY、EGL_NOT_INITIALIZED、EGL_BAD_SURFACE、EGL_BAD_CONTEXT、EGL_BAD_MATCH、EGL_BAD_ACCESS 、EGL_BAD_NATIVE_PIXMAP、EGL_BAD_NATIVE_WINDOW、EGL_BAD_CURRENT_SURFACE、EGL_BAD_ALLOC、EGL_CONTEXT_LOST。因為EGL規範要求eglMakeCurrent實作進行一次重新整理,是以這一調用對于基于圖塊的架構代價很高。