天天看點

[EGL] EGL接口介紹(轉) 初始化EGL

EGL 是 OpenGL ES 和底層 Native 平台視窗系統之間的接口。本章主要講述 OpenGL ES 的 EGL API ,以及如何用它建立 Context 和繪制Surface 等,并對用于 OpenGL 的其他視窗 API 做了比較分析,比如 WGL 和 GLX 。本章中将涵蓋如下幾個方面:

         EGL 綜述

         EGL 主要構成( Display , Context , Configuration )

         在 Brew 和 Windows CE 上使用 EGL

         EGL 和其他 OpenGL 視窗系統的比較

EGL 介紹

EGL 是為 OpenGL ES 提供平台獨立性而設計。在本章中,你将詳細地學習每個 EGL API ,并了解使用 EGL 時候需要注意的平台特性和限制。 OpenGL ES 為附加功能和可能的平台特性開發提供了擴充機制,但仍然需要一個可以讓 OpenGL ES 和本地視窗系統互動且平台無關的層。 OpenGL ES 本質上是一個圖形渲染管線的狀态機,而 EGL 則是用于監控這些狀态以及維護 Frame buffer 和其他渲染 Surface 的外部層。圖 2-1 是一個典型的 EGL 系統布局圖。

[EGL] EGL接口介紹(轉) 初始化EGL

EGL 視窗設計是基于人們熟悉的用于 Microsoft Windows ( WGL )和 UNIX ( GLX )上的 OpenGL 的 Native 接口,與後者比較接近。OpenGL ES 圖形管線的狀态被存儲于 EGL 管理的一個 Context 中。 Frame Buffers 和其他繪制 Surfaces 通過 EGL API 建立、管理和銷毀。EGL 同時也控制和提供了對裝置顯示和可能的裝置渲染配置的通路。

EGL 資料類型

EGL 包含了自己的一組資料類型,同時也提供了對一組平台相關的本地資料類型的支援。這些 Native 資料類型定義在 EGL 系統的頭檔案中。一旦你了解這些資料類型之間的不同,使用它們将變得很簡單。多數情況下,為保證可移植性,開發人員将盡可能使用抽象資料類型而避免直接使用系統資料類型。通過使用定義在 EGL 中 Native 類型,可以讓你寫的 EGL 代碼運作在任意的 EGL 的實作上。 Native EGL 類型說明如下:

l         NativeDisplayType 平台顯示資料類型,辨別你所開發裝置的實體螢幕

l         NativeWindowType 平台視窗資料類型,辨別系統視窗

l         NativePixmapType 可以作為 Framebuffer 的系統圖像(記憶體)資料類型,該類型隻用于離屏渲染

下面的代碼是一個 NativeWindowType 定義的例子。這隻是一個例子,不同平台之間的實作千差萬别。使用 native 類型的關鍵作用在于為開發者抽象化這些細節。 QUALCOMM 使用 IDIB 結構定義 native 類型,如下:

struct IDIB {

     AEEVTBL(IBitmap) *pvt; // virtual table pointer

     IQueryInterface * pPaletteMap; // cache for computed palette mapping info

     byte * pBmp; // pointer to top row

     uint32 * pRGB; // palette

     NativeColor ncTransparent; // 32-bit native color value

     uint16 cx; // number of pixels in width

     uint16 cy; // number of pixels in height

     int16 nPitch; // offset from one row to the next

     uint16 cntRGB; // number of palette entries

     uint8 nDepth; // size of pixel in bits

     uint8 nColorScheme; // IDIB_COLORSCHEME_...(ie. 5-6-5)

     uint8 reserved[6];

};

接下來的小節中,我們将深入更多 EGL 資料類型細節。标準 EGL 資料類型如表 2.1 所示。

表 2.1 EGL 資料類型

資料類型
EGLBoolean EGL_TRUE =1, EGL_FALSE=0
EGLint int 資料類型
EGLDisplay 系統顯示 ID 或句柄
EGLConfig Surface 的 EGL 配置
EGLSurface 系統視窗或 frame buffer 句柄
EGLContext OpenGL ES 圖形上下文
NativeDisplayType Native 系統顯示類型
NativeWindowType Native 系統視窗緩存類型
NativePixmapType Native 系統 frame buffer

EGL Displays

EGLDisplay 是一個關聯系統實體螢幕的通用資料類型。對于 PC 來說, Display 就是顯示器的句柄。不管是嵌入式系統或 PC ,都可能有多個實體顯示裝置。為了使用系統的顯示裝置, EGL 提供了 EGLDisplay 資料類型,以及一組操作裝置顯示的 API 。

       下面的函數原型用于擷取 Native Display :

EGLDisplay eglGetDisplay (NativeDisplayType display);

其中 display 參數是 native 系統的視窗顯示 ID 值。如果你隻是想得到一個系統預設的 Display ,你可以使用 EGL_DEFAULT_DISPLAY 參數。如果系統中沒有一個可用的 native display ID 與給定的 display 參數比對,函數将傳回 EGL_NO_DISPLAY ,而沒有任何 Error 狀态被設定。

由于設定無效的 display 值不會有任何錯誤狀态,在你繼續操作前請檢測傳回值。

下面是一個使用 EGL API 擷取系統 Display 的例子:

m_eglDisplay = eglGetDisplay( system.display);

if (m_eglDisplay == EGL_NO_DISPLAY || eglGetError() != EGL_SUCCESS))

throw error_egl_display;

Initialization 初始化

    和很多視窗 API 類似, EGL 在使用前需要初始化,是以每個 EGLDisplay 在使用前都需要初始化。初始化 EGLDisplay 的同時,你可以得到系統中 EGL 的實作版本号。了解目前的版本号在向後相容性方面是非常有價值的。嵌入式和移動裝置通常是持續的投放到市場上,是以你需要考慮到你的代碼将被運作在形形色色的實作上。通過動态查詢 EGL 版本号,你可以為新舊版本的 EGL 附加額外的特性或運作環境。基于平台配置,軟體開發可用清楚知道哪些 API 可用通路,這将會為你的代碼提供最大限度的可移植性。

       下面是初始化 EGL 的函數原型:

              EGLBoolean eglInitialize (EGLDisplay dpy, EGLint *major, EGLint *minor);

其中 dpy 應該是一個有效的 EGLDisplay 。函數傳回時, major 和 minor 将被賦予目前 EGL 版本号。比如 EGL1.0 , major 傳回 1 ,minor 則傳回 0 。給 major 和 minor 傳 NULL 是有效的,如果你不關心版本号。

       eglQueryString() 函數是另外一個擷取版本資訊和其他資訊的途徑。通過 eglQueryString() 擷取版本資訊需要解析版本字元串,是以通過傳遞一個指針給 eglInitializ() 函數比較容易獲得這個資訊。注意在調用 eglQueryString() 必須先使用 eglInitialize() 初始化 EGLDisplay ,否則将得到 EGL_NOT_INITIALIZED 錯誤資訊。

       下面是擷取 EGL 版本字元串資訊的函數原型:

const char * eglQueryString (EGLDisplay dpy, EGLint name);

參數 name 可以是 EGL_VENDOR, EGL_VERSION, 或者 EGL_EXTENSIONS 。這個函數最常用來查詢有哪些 EGL 擴充被實作。所有 EGL 擴充都是可選的,如果你想使用某個擴充特性,請檢查該擴充是否被實作了,而不要想當然假定已經實作了。如果沒有擴充被實作,将傳回一個Null 字元串,如果給定的 name 參數無效,則會得到 EGL_BAD_PARAMETER. 錯誤資訊。

初始化EGL

OpenGL ES是一個平台中立的圖形庫,在它能夠工作之前,需要與一個實際的視窗系統關聯起來,這與OpenGL是一樣的。但不一樣的是,這部份工作有标準,這個标 準就是EGL。而OpenGL時代在不同平台上有不同的機制以關聯視窗系統,在Windows上是wgl,在X-Window上是xgl,在Apple OS上是agl等。EGL的工作方式和部份術語都接近于xgl。

OpenGL ES的初始化過程如下圖所示意:

Display → Config → Surface



           
Context



           
Application → OpenGL Command



           

1. 擷取Display。

Display代表顯示器,在有些系統上可以有多個顯示器,也就會有多個Display。獲得Display要調用EGLboolean eglGetDisplay(NativeDisplay dpy),參數一般為 EGL_DEFAULT_DISPLAY 。該參數實際的意義是平台實作相關的,在X-Window下是XDisplay ID,在MS Windows下是Window DC。

2. 初始化egl。

調用 EGLboolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor),該函數會進行一些内部初始化工作,并傳回EGL版本号(major.minor)。

3. 選擇Config。

所為Config實際指的是FrameBuffer的參數,在MS Windows下對應于PixelFormat,在X-Window下對應Visual。一般用EGLboolean eglChooseConfig(EGLDisplay dpy, const EGLint * attr_list, EGLConfig * config, EGLint config_size, EGLint *num_config),其中attr_list是以EGL_NONE結束的參數數組,通常以id,value依次存放,對于個别辨別性的屬性可以隻有 id,沒有value。另一個辦法是用EGLboolean eglGetConfigs(EGLDisplay dpy, EGLConfig * config, EGLint config_size, EGLint *num_config) 來獲得所有config。這兩個函數都會傳回不多于config_size個Config,結果儲存在config[]中,系統的總Config個數儲存 在num_config中。可以利用eglGetConfig()中間兩個參數為0來查詢系統支援的Config總個數。

Config有衆多的Attribute,這些Attribute決定FrameBuffer的格式和能力,通過eglGetConfigAttrib ()來讀取,但不能修改。

4. 構造Surface。

Surface實際上就是一個FrameBuffer,通過 EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig confg, NativeWindow win, EGLint *cfg_attr) 來建立一個可實際顯示的Surface。系統通常還支援另外兩種Surface:PixmapSurface和PBufferSurface,這兩種都不 是可顯示的Surface,PixmapSurface是儲存在系統記憶體中的位圖,PBuffer則是儲存在顯存中的幀。

Surface也有一些attribute,基本上都可以故名思意, EGL_HEIGHT EGL_WIDTH EGL_LARGEST_PBUFFER EGL_TEXTURE_FORMAT EGL_TEXTURE_TARGET EGL_MIPMAP_TEXTURE EGL_MIPMAP_LEVEL,通過eglSurfaceAttrib()設定、eglQuerySurface()讀取。

5. 建立Context。

OpenGL的pipeline從程式的角度看就是一個狀态機,有目前的顔色、紋理坐标、變換矩陣、絢染模式等一大堆狀态,這些狀态作用于程式送出的頂點 坐标等圖元進而形成幀緩沖内的像素。在OpenGL的程式設計接口中,Context就代表這個狀态機,程式的主要工作就是向Context提供圖元、設定狀 态,偶爾也從Context裡擷取一些資訊。

用EGLContext eglCreateContext(EGLDisplay dpy, EGLSurface write, EGLSurface read, EGLContext * share_list)來建立一個Context。

6. 繪制。

應用程式通過OpenGL API進行繪制,一幀完成之後,調用eglSwapBuffers(EGLDisplay dpy, EGLContext ctx)來顯示。

EGL Configurations

EGLConfigs 是一個用來描述 EGL surface 配置資訊的資料類型。要擷取正确的渲染結果, Surface 的格式是非常重要的。根據平台的不同,surface 配置可能會有限制,比如某個裝置隻支援 16 位色深顯示,或是不支援 stencil buffer ,還有其他的功能限制或精度的差異。

       下面是擷取系統可用的 EGL 配置資訊的函數原型:

EGLBoolean eglGetConfigs (EGLDisplay dpy, EGLConfig *configs,EGLint config_size, EGLint *num_config);

參數 configs 将包含在你的平台上有效的所有 EGL framebuffer 配置清單。支援的配置總數将通過 num_config 傳回。實際傳回的 configs 的配置個數依賴于程式傳入的 config_size 。如果 config_size < num_config ,則不是所有的配置資訊都将被傳回。如果想擷取系統支援的所有配置資訊,最好的辦法就是先給 eglGetConfig 傳一個 NULL 的 configs 參數, num_config 将得到系統所支援的配置總數,然後用它來給configs 配置設定合适的記憶體大小,再用得到的 configs 來調用 eglGetConfig 。

       下面是如果使用 eglGetConfig() 函數的例子:

EGLConfig *configs_list;

EGLint num_configs;

// Main Display

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 Configuration

基于 EGL 的屬性,你可以定義一個希望從系統獲得的配置,它将傳回一個最接近你的需求的配置。選擇一個你特有的配置是有點不合适的,因為隻是在你的平台上使用有效。 eglChooseConfig() 函數将适配一個你所期望的配置,并且盡可能接近一個有效的系統配置。

       下面是選擇一個 EGL 配置的函數原型:

EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list,

EGLConfig *configs, EGLint config_size, EGLint * num_config);

參數 attrib_list 指定了選擇配置時需要參照的屬性。參數 configs 将傳回一個按照 attrib_list 排序的平台有效的所有 EGL framebuffer 配置清單。參數 config_size 指定了可以傳回到 configs 的總配置個數。參數 num_config 傳回了實際比對的配置總數。

       下面是如果使用 eglChoosetConfig() 函數的例子:

       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();

}

如果找到多個合适的配置,有一個簡單的排序算法用來比對最接近你所查詢的配置。表 2-2 顯示了基于屬性值的用來選擇和排序的順序,也包括了 EGL 規範中所有 EGL 配置屬性及其預設值。

表 2.1 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

這裡不是講OpenGL的教程,事實上,OpenGL 是與硬體和平台無關的。這裡主要講在Android下,OpenGL 應用的基本架構、OpenGL 與Android 視窗系統通過EGL的綁定

根據官方網站( http://www.khronos.org/egl/)的描述,EGL是圖形資源管理層,工作在圖形渲染API(如OpenGL)與運作平台(Android)的視窗系統之間

從1.5(API 3)開始Android 支援 OpenGL ES 1.0,到 2.2(API 8)時支援 OpenGL ES 2.0。版本對應關系如下(待完善)

Android SDK API EGL OpenGL ES OpenGL
1.5 3 1.0 1.0 1.3
1.6 4
2.1 7
2.2 8 2.0

Android、EGL、OpenGL 三者關系如下:

Android Windowing    ( SurfaceView)          ^          | +--------+------------------------+ | EGL    |           +------------+ | |        |              | Display | | |        |           +------------+ | |  +-----v-------+   +------------+ | |    | Surface |     | Config | | |  +-----^-------+   +------------+ | |        |           +------------+ | |        |           | Context | | |        |           +------------+ | +--------+------------------------+          |          v        OpenGL      

EGL 官網有一個1.0版本的 Specification,詳細講述了Surface、Display、Context 概念。簡單地說

(1)Display 是圖形顯示裝置(顯示屏)的抽象表示。大部分EGL函數都要帶一個 Display 作為參數

(2)Context 是 OpenGL 狀态機。Context 與 Surface 可以是一對一、多對一、一對多的關系

(3)Surface 是繪圖緩沖,可以是 window、pbuffer、pixmap 三種類型之一

EGL 工作流程為:

(1)初始化

(2)配置

(3)建立Surface(綁定到平台Windowing系統)

(4)綁定Surface與Context

(5)Main Loop:渲染(OpenGL),交換離線緩沖(offline buffer)與顯示緩沖

(6)釋放資源

在Android SDK中,EGL類在javax.microedition.khronos.egl包中,OpenGL 類在 javax.microedition.khronos.opengles包中

下面是一個完整的 EGL/OpenGL 應用,當點選螢幕時,根據點選坐标更新螢幕背景顔色

package roxit.opengldemo; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.egl.EGLSurface; import javax.microedition.khronos.opengles.GL10; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.SurfaceHolder.Callback; import android.view.View.OnTouchListener; public class OpenGlDemo extends Activity implements Callback, Runnable, OnTouchListener { private SurfaceView view; private boolean rendering = false; private final Object renderLock = new Object(); private GL10 gl; private float red = 0.2f, green = 0.3f, blue = 0.8f; @Override public void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); super.onCreate(savedInstanceState); view = new SurfaceView(this); view.getHolder().addCallback(this); view.setOnTouchListener(this); setContentView(view); } public void surfaceCreated(SurfaceHolder holder) { synchronized (renderLock) { Log.d("OpenGlDemo >>>", "Start rendering..."); rendering = true; new Thread(this).start(); } } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {   } public void surfaceDestroyed(SurfaceHolder holder) { synchronized (renderLock) { rendering = false; } } public void run() { Init EGL10 egl = (EGL10) EGLContext.getEGL(); EGLDisplay disp = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); egl.eglInitialize(disp, new int[2]); Config EGLConfig[] configs = new EGLConfig[1]; egl.eglChooseConfig(disp, new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE }, configs, 1, new int[1]); EGLConfig config = configs[0]; Create surface and bind with native windowing EGLSurface surf = egl.eglCreateWindowSurface(disp, config, view .getHolder(), null);   Bind with OpenGL context EGLContext contx = egl.eglCreateContext(disp, config, EGL10.EGL_NO_CONTEXT, null); egl.eglMakeCurrent(disp, surf, surf, contx); gl = (GL10) contx.getGL(); while (true) { synchronized (renderLock) { if (!rendering) { break; } } render(gl); egl.eglSwapBuffers(disp, surf); } Log.d("OpenGlDemo >>>", "Stop rendering"); // Finalize egl.eglMakeCurrent(disp, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); egl.eglDestroyContext(disp, contx); egl.eglDestroySurface(disp, surf); gl = null; } private void render(GL10 gl) { gl.glClearColor(red, green, blue, 1.0f); gl.glClear(GL10.GL_COLOR_BUFFER_BIT); } public boolean onTouch(View view, MotionEvent e) { red = e.getX() / view.getWidth(); green = e.getY() / view.getHeight(); blue = 1.0f; return true; } }      

上面的應用,就是在Android 下的OpenGL 應用的最基本結構,涉及了不少EGL細節的操作

如果你google 一下“Android OpenGL”,得到結果十之八九是使用了android.opengl.GLSurfaceView。使用GLSurfaceView實作上面的簡單應用,代碼要簡單得多

是這樣的:GLSurfaceView隐藏了EGL操作及渲染線程的細節,并提供了生命周期回調方法

但,基本上,使用 GLSurfaceView 沒法控制渲染循環,例如:你沒法控制幀速(fps)

EGL是由Khronos Group提供的一組平台無關的API。它的功能:

1> 和本地視窗系統(native windowing system)通訊;

2> 查詢可用的配置;

3> 建立OpenGL ES可用的“繪圖表面”(drawing surface);

4> 同步不同類别的API之間的渲染,比如在OpenGL ES和OpenVG之間同步,或者在OpenGL和本地視窗的繪圖指令之間;

5> 管理“渲染資源”,比如紋理映射(rendering map)。

● EGLDisplay

EGL可運作于GNU/Linux的X Window System,Microsoft Windows和MacOS X的Quartz。

EGL把這些平台的顯示系統抽象為一個獨立的類型:EGLDisplay。

使用EGL的第一步就是初始化一個可用的EGLDisplay:

  1. EGLint majorVersion;  
  2. EGLint minorVersion;  
  3. EGLBoolean success = EGL_FALSE;  
  4. EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);  
  5. if (display != EGL_NO_DISPLAY)  
  6. {  
  7.     success = eglInitialize(display, &majorVersion, &minorVersion);  
  8. }  
  9. if (success != EGL_TRUE)  
  10. {  
  11.     EGLint errno = eglGetError();  
  12.     if (errno != EGL_SUCCESS)  
  13.     {  
  14.         _TCHAR errmsg[32];  
  15.         _stprintf(errmsg, _T("[EGL] Initialization failed. Error code: 0x%04x"), errno);  
  16.         // EGL_BAD_DISPLAY      EGLDisplay參數錯誤  
  17.         // EGL_NOT_INITIALIZED  EGL不能初始化  
  18.     }  
  19. }  

這裡用到了三個EGL函數:

  1. EGLDisplay eglGetDisplay(EGLNativeDisplayType id);  
  2. EGLBoolean eglInitialize(EGLDisplay display, EGLint* majorVersion, EGLint* minorVersion);  
  3. EGLint eglGetError();  

● EGLConfig

初始化過後,要選擇一個合适的“繪圖表面”。

  1. EGLBoolean eglGetConfigs(EGLDisplay display,    // 已初始化好  
  2.                          EGLConfig* configs,    // 如果為NULL,則傳回EGL_TRUE和numConfigs,即圖形系統所有可用的配置  
  3.                          EGLint maxConfigs,     // 上面那個configs數組的容量  
  4.                          EGLint* numConfigs);   // 圖形系統傳回的實際的可用的配置個數,存儲在configs數組裡  

用例:

  1. static const EGLint CONFIG_ATTRIBS[] =  
  2. {  
  3.     EGL_RED_SIZE,       5,  
  4.     EGL_GREEN_SIZE,     6,  
  5.     EGL_BLUE_SIZE,      5,  
  6.     EGL_DEPTH_SIZE,     16,  
  7.     EGL_ALPHA_SIZE,     EGL_DONT_CARE,  
  8.     EGL_STENCIL_SIZE,   EGL_DONT_CARE,  
  9.     EGL_SURFACE_TYPE,   EGL_WINDOW_BIT,  
  10.     EGL_NONE            // 屬性表以該常量為結束符  
  11. };  
  12. GLint numConfigs;  
  13. EGLConfig config;  
  14. if (success != EGL_FALSE)  
  15.     success = eglGetConfigs(display, NULL, 0, &numConfigs);  
  16. if (success != EGL_FALSE && numConfigs > 0)  
  17.     success = eglChooseConfig(display, CONFIG_ATTRIBS, &config, 1, &numConfigs);  

可以查詢某個配置的某個屬性:

  1. EGLBoolean eglGetConfigAttrib(EGLDisplay display,    // 已初始化  
  2.                               EGLConfig config,      // 某個配置  
  3.                               EGLint attribute,      // 某個屬性  
  4.                               EGLint * value);  

讓EGL為你選擇一個配置:

  1. EGLBoolean eglChooseConfig(EGLDisplay display,  
  2.                            const EGLint* attribs,    // 你想要的屬性事先定義到這個數組裡  
  3.                            EGLConfig* configs,       // 圖形系統将傳回若幹滿足條件的配置到該數組  
  4.                            EGLint maxConfigs,        // 上面數組的容量  
  5.                            EGLint* numConfigs);      // 圖形系統傳回的可用的配置個數  

EGL如果選擇了多個配置給你,則按一定規則放到數組裡:

1> EGL_CONFIG_CAVEAT

2> EGL_COLOR_BUFFER_TYPE

3> 按color buffer所占位寬

4> EGL_BUFFER_SIZE

5> EGL_SAMPLE_BUFFERS

6> EGL_SAMPLES

7> EGL_DEPTH_SIZE

8> EGL_STENCIL_SIZE

9> EGL_ALPHA_MASK_SIZE

10> EGL_NATIVE_VISUAL_TYPE

11> EGL_CONFIG_ID

● EGLSurface

  1. EGLSurface eglCreateWindowSurface(EGLDisplay display,  
  2.                                   EGLConfig config,  
  3.                                   EGLNativeWindowType window, // 在Windows上就是HWND類型  
  4.                                   const EGLint* attribs);     // 此屬性表非彼屬性表  

這裡的屬性表并非用于OpenGL ES 2.0,而是其它的API,比如OpenVG。我們隻需要記住一個:EGL_RENDER_BUFFER [EGL_BACK_BUFFER, EGL_FRONT_BUFFER]。

OpenGL ES 2.0是必須工作于雙緩沖視窗系統的。

該屬性表當然也可以為NULL,也可以隻有一個EGL_NONE。那表示所有屬性使用預設值。

如果函數傳回EGL_NO_SURFACE,則失敗。錯誤碼:

EGL_BAD_MATCH:          屬性設定錯誤。比如EGL_SURFACE_TYPE沒有設定EGL_WINDOW_BIT

EGL_BAD_CONFIG:         因為配置錯誤,圖形系統不支援

EGL_BAD_NATIVE_WINDOW:  視窗句柄錯誤

EGL_BAD_ALLOC:          無法建立繪圖表面。比如先前已經建立一個了。

● pixel buffer

OpenGL ES 2.0可以向pixel buffer渲染,同樣使用硬體加速。pbuffer經常用來生成紋理映射。如果想渲染到紋理,常用更高效的framebuffer對象。

在EGL_SURFACE_TYPE裡使用使用EGL_PBUFFER_BIT可建立pbuffer:

  1. EGLSurface eglCreatePbufferSurface(EGLDisplay display,  
  2.                                    EGLConfig config,  
  3.                                    const EGLint* attribs);  

使用到的屬性:

EGL_WIDTH, EGL_HEIGHT

EGL_LARGEST_PBUFFER:        如果參數不合适,可使用最大的pbuffer

EGL_TEXTURE_FORMAT:         [EGL_NO_TEXTURE] 如果pbuffer要綁定到紋理映射,要指定紋理的格式

EGL_TEXTURE_TARGET:            [EGL_NO_TEXTURE, EGL_TEXTURE_2D]

EGL_MIPMAP_TEXTRUE:         [EGL_TRUE, EGL_FALSE]

建立失敗時傳回EGL_NO_SURFACE,錯誤碼:

EGL_BAD_ALLOC:      缺少資源

EGL_BAD_CONFIG:     配置錯誤

EGL_BAD_PARAMETER:  EGL_WIDTH和EGL_HEIGHT為負數

EGL_BAD_MATCH:      配置錯誤;如果用于紋理映射,則高寬參數錯誤;EGL_TEXTURE_FORMAT和EGL_TEXTURE_TARGET隻有一個不是EGL_NO_TEXTURE

EGL_BAD_ATTRIBUTE:  指定了EGL_TEXTURE_FORMAT、EGL_TEXTURE_TARGET或者EGL_MIPMAP_TEXTRUE,卻不指定使用OpenGLES在配置裡

使用pbuffer的例子:

  1. EGLint cfgAttribs[] =  
  2. {  
  3.     EGL_SURFACE_TYPE,    EGL_PBUFFER_BIT,  
  4.     EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,  
  5.     EGL_RED_SIZE,        5,  
  6.     EGL_GREEN_SIZE,      6,  
  7.     EGL_BLUE_SIZE,       5,  
  8.     EGL_DEPTH_SIZE,      1,  
  9.     EGL_NONE  
  10. };  
  11. const EGLint MAX_CONFIG = 10;  // 我們要從10個配置裡挑選一個  
  12. EGLConfig configs[MAX_CONFIG];  
  13. EGLint numConfigs;  
  14. if (!eglChooseConfig(display, cfgAttribs, configs, MAX_CONFIG, &numConfigs))  
  15. {  
  16.     // 報錯  
  17. }  
  18. else  
  19. {  
  20.     // 挑選一個配置  
  21. }  
  22. EGLint PBufAttribs[] =  
  23. {  
  24.     EGL_WIDTH,  512,  
  25.     EGL_HEIGHT, 512,  
  26.     EGL_LARGEST_PBUFFER, EGL_TRUE,  
  27.     EGL_NONE  
  28. };  
  29. EGLRenderSurface pbuffer = eglCreatePbufferSurface(display, config, PBufAttribs);  
  30. if (pbuffer == EGL_NO_SURFACE)  
  31. {  
  32.     // 建立失敗,報各種錯  
  33. }  
  34. EGLint width, height;  
  35. if (!eglQuerySurface(display, pbuffer, EGL_HEIGHT, &height)  
  36.     || !eglQuerySurface(display, pbuffer, EGL_WIDTH, &width)  
  37. {  
  38.     // 查詢不到資訊,報錯  
  39. }  

pbuffer和普通的視窗渲染最大的不同是不能swap,要麼拷貝其值,要麼修改其綁定成為紋理。

● EGLContext

  1. EGLContext eglCreateContext(EGLDisplay display,  
  2.                             EGLConfig config,  
  3.                             EGLContext context,    // EGL_NO_CONTEXT表示不向其它的context共享資源  
  4.                             const EGLint * attribs)// 我們暫時隻用EGL_CONTEXT_CLIENT_VERSION  
  5. const EGLint attribs[] =  
  6. {  
  7.     EGL_CONTEXT_CLIENT_VERSION, 2,  
  8.     EGL_NONE  
  9. };  
  10. EGLContext context = eglCreateContext(display, cfg, EGL_NO_CONTEXT, attribs);  
  11. if (context == EGL_NO_CONTEXT)  
  12. {  
  13.     if (EGL_BAD_CONFIG == eglGetError())  
  14.     {  
  15.         ...  
  16.     }  
  17. }  
  18. if (!eglMakeCurrent(display, window, window, context)) // 兩個window表示讀寫都在一個視窗  
  19. {  
  20.     // 報錯  
  21. }  

● 渲染同步

隻使用OpenGL ES 2.0,那麼,glFinish即可保證所有的渲染工作進行下去。

但使用OpenVG或本地圖形API渲染字型,要比使用OpenGL ES 2.0要容易。是以,你可能要在同一個視窗使用多個庫來渲染。

可以用EGL的同步函數:EGLBoolean eglWaitClient() 延遲用戶端的執行,等待伺服器端完成OpenGL ES 2.0或者OpenVG的渲染。

如果失敗,傳回錯誤碼:EGL_BAD_CURRENT_SURFACE。

如果要等待本地圖形API的渲染完成,使用:EGLBoolean eglWaitNative(EGLint engine)。

engine參數必須是EGL_CORE_NATIVE_ENGINE。其它值都是通過EGL擴充來指定。

如果失敗,傳回錯誤碼:EGL_BAD_PARAMETER。