EGL接口介紹

EGL 是 OpenGL ES 和底層 Native 平臺視窗系統之間的接口。本章主要講述 OpenGL ES 的 EGL API ,以及如何用它建立 Context 和繪製Surface 等,並對用於 OpenGL 的其餘視窗 API 作了比較分析,好比 WGL 和 GLX 。本章中將涵蓋以下幾個方面:java

EGL 綜述android

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 視窗設計是基於人們熟悉的用於 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 類型說明以下:app

l         NativeDisplayType 平臺顯示數據類型,標識你所開發設備的物理屏幕ide

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

0

3

Smaller value

EGL_RED_SIZE

int

0

2

Larger value

EGL_GREEN_SIZE

int

0

2

Larger value

EGL_BLUE_SIZE

int

0

2

Larger value

EGL_ALPHA_SIZE

int

0

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

0

6

Smaller value

EGL_LEVEL

int

0

-

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

0

4

Smaller value

EGL_SAMPLES

int

0

5

Smaller value

EGL_STENCIL_SIZE

int

0

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的綁定
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 能夠是一對1、多對1、一對多的關係
(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 應用,當點擊屏幕時,根據點擊座標更新屏幕背景顏色

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:

    EGLint majorVersion;  
    EGLint minorVersion;  
    EGLBoolean success = EGL_FALSE;  
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);  
    if (display != EGL_NO_DISPLAY)  
    {  
        success = eglInitialize(display, &majorVersion, &minorVersion);  
    }  
    if (success != EGL_TRUE)  
    {  
        EGLint errno = eglGetError();  
        if (errno != EGL_SUCCESS)  
        {  
            _TCHAR errmsg[32];  
            _stprintf(errmsg, _T("[EGL] Initialization failed. Error code: 0x%04x"), errno);  
            // EGL_BAD_DISPLAY      EGLDisplay參數錯誤  
            // EGL_NOT_INITIALIZED  EGL不能初始化  
        }  
    } 

這裏用到了三個EGL函數:

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

● EGLConfig

初始化事後,要選擇一個合適的「繪圖表面」。

    EGLBoolean eglGetConfigs(EGLDisplay display,    // 已初始化好

   EGLConfig* configs,    // 若是爲NULL,則返回EGL_TRUE和numConfigs,即圖形系統全部可用的配置  
   EGLint maxConfigs,     // 上面那個configs數組的容量  
   EGLint* numConfigs);   // 圖形系統返回的實際的可用的配置個數,存儲在configs數組裏 

用例:

    static const EGLint CONFIG_ATTRIBS[] =  
    {  
        EGL_RED_SIZE,       5,  
        EGL_GREEN_SIZE,     6,  
        EGL_BLUE_SIZE,      5,  
        EGL_DEPTH_SIZE,     16,  
        EGL_ALPHA_SIZE,     EGL_DONT_CARE,  
        EGL_STENCIL_SIZE,   EGL_DONT_CARE,  
        EGL_SURFACE_TYPE,   EGL_WINDOW_BIT,  
        EGL_NONE            // 屬性表以該常量爲結束符  
    };  
    GLint numConfigs;  
    EGLConfig config;  
    if (success != EGL_FALSE)  
        success = eglGetConfigs(display, NULL, 0, &numConfigs);  
    if (success != EGL_FALSE && numConfigs > 0)  
        success = eglChooseConfig(display, CONFIG_ATTRIBS, &config, 1, &numConfigs); 

能夠查詢某個配置的某個屬性:

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

讓EGL爲你選擇一個配置:

    EGLBoolean eglChooseConfig(EGLDisplay display,  
                               const EGLint* attribs,    // 你想要的屬性事先定義到這個數組裏  
                               EGLConfig* configs,       // 圖形系統將返回若干知足條件的配置到該數組  
                               EGLint maxConfigs,        // 上面數組的容量  
                               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

    EGLSurface eglCreateWindowSurface(EGLDisplay display,  
                                      EGLConfig config,  
                                      EGLNativeWindowType window, // 在Windows上就是HWND類型  
                                      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:

    EGLSurface eglCreatePbufferSurface(EGLDisplay display,  
                                       EGLConfig config,  
                                       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的例子:

    EGLint cfgAttribs[] =  
    {  
        EGL_SURFACE_TYPE,    EGL_PBUFFER_BIT,  
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,  
        EGL_RED_SIZE,        5,  
        EGL_GREEN_SIZE,      6,  
        EGL_BLUE_SIZE,       5,  
        EGL_DEPTH_SIZE,      1,  
        EGL_NONE  
    };  
    const EGLint MAX_CONFIG = 10;  // 咱們要從10個配置裏挑選一個  
    EGLConfig configs[MAX_CONFIG];  
    EGLint numConfigs;  
    if (!eglChooseConfig(display, cfgAttribs, configs, MAX_CONFIG, &numConfigs))  
    {  
        // 報錯  
    }  
    else  
    {  
        // 挑選一個配置  
    }  
    EGLint PBufAttribs[] =  
    {  
        EGL_WIDTH,  512,  
        EGL_HEIGHT, 512,  
        EGL_LARGEST_PBUFFER, EGL_TRUE,  
        EGL_NONE  
    };  
    EGLRenderSurface pbuffer = eglCreatePbufferSurface(display, config, PBufAttribs);  
    if (pbuffer == EGL_NO_SURFACE)  
    {  
        // 建立失敗,報各類錯  
    }  
    EGLint width, height;  
    if (!eglQuerySurface(display, pbuffer, EGL_HEIGHT, &height)  
        || !eglQuerySurface(display, pbuffer, EGL_WIDTH, &width)  
    {  
        // 查詢不到信息,報錯  
    } 

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

● EGLContext

    EGLContext eglCreateContext(EGLDisplay display,  
                                EGLConfig config,  
                                EGLContext context,    // EGL_NO_CONTEXT表示不向其它的context共享資源  
                                const EGLint * attribs)// 咱們暫時只用EGL_CONTEXT_CLIENT_VERSION  
    const EGLint attribs[] =  
    {  
        EGL_CONTEXT_CLIENT_VERSION, 2,  
        EGL_NONE  
    };  
    EGLContext context = eglCreateContext(display, cfg, EGL_NO_CONTEXT, attribs);  
    if (context == EGL_NO_CONTEXT)  
    {  
        if (EGL_BAD_CONFIG == eglGetError())  
        {  
            ...  
        }  
    }  
    if (!eglMakeCurrent(display, window, window, context)) // 兩個window表示讀寫都在一個窗口  
    {  
        // 報錯  
    } 

● 渲染同步 只使用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。