天天看點

OpenGL ES (二)EGL介紹和使用一、EGL簡介二、EGL的功能三、EGL使用

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提供以下機制:

  1. 與裝置的原生視窗系統通信
  2. 查詢繪圖表面的可用類型和配置
  3. 建立繪圖表面
  4. 在OpenGL ES3.0或其他渲染API之間同步渲染
  5. 管理紋理貼圖等渲染資源

三、EGL使用

1、建立OpenGL ES與原生視窗系統的連接配接

eglGetDisplay為原生視窗系統displayId擷取一個EGL display連接配接,
在OpenGL ES與本地視窗系統之間架起了一座溝通的橋梁
           

2、初始化EGL連接配接

與視窗系統的連接配接建立以後,随後要進行EGL的初始化

EGLBoolean eglInitialize(EGLDisplay display,   // 要進行初始化的EGL連接配接,即上一部中的傳回值
    					 EGLint *majorVersion, // 主版本号
    					 EGLint *minorVersion) // 次版本号
           
  1. display預設為EGL_DEFAULT_DISPLAY,即傳回與預設原生視窗的連接配接
  2. 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實作進行一次重新整理,是以這一調用對于基于圖塊的架構代價很高。

繼續閱讀