天天看點

Android OpenGL ES與EGL

1 名詞解釋

OpenGL ES (OpenGL for Embedded Systems,以下簡稱OpenGL)

OpenGL 三維圖形 API 的子集,針對手機、PDA和遊戲主機等嵌入式裝置而設計。該API由Khronos集團定義推廣,Khronos是一個圖形軟硬體行業協會,該協會主要關注圖形和多媒體方面的開放标準。

EGL

EGL™ 是介于諸如OpenGL 或OpenVG的Khronos渲染API與底層本地平台視窗系統的接口。它被用于處理圖形管理、表面/緩沖捆綁、渲染同步及支援使用其他Khronos API進行的高效、加速、混合模式2D和3D渲染。

http://www.khronos.cn/index.shtml

2 Android中的OpenGL 與EGL

Android 2.0版本之後圖形系統的底層渲染均由OpenGL負責,OpenGL除了負責處理3D API調用,還需負責管理顯示記憶體及處理Android SurfaceFlinger或上層應用對其發出的2D API調用請求。

本地代碼:

framework/base/opengl/libs/egl

Android EGL架構,負責加載OpenGL函數庫和EGL本地實作。

framework/base/opengl/libagl  

Android提供的OpenGL軟體庫

JNI代碼:

framework/base/core/jni/com_google_android_gles_jni_EGLImpl.cpp

EGL本地代碼的JNI調用接口

framework/base/core/jni/com_google_android_gles_jni_GLImpl.cpp

framework/base/core/jni/android_opengl_GLESXXX.cpp

OpenGL功能函數的JNI調用接口

Java代碼:

framework/base/opengl/java/javax/microedition/khronos/egl

framework/base/opengl/java/javax/microedition/khronos/opengles

framework/base/opengl/java/com/google/android/gles_jni/

framework/base/opengl/android/opengl

EGL和OpenGL的Java層接口,提供給應用開發者,通過JNI方式調用底層函數。

3 Android EGL實作

3.1 EGL的主要功能

EGL是用來管理繪圖表面(Drawing surfaces),并且提供了如下的機制

1) 與本地視窗系統進行通信

2) 查找繪圖表面可用的類型和配置資訊

3) 建立繪圖表面

4) 同步OpenGL ES 2.0和其他的渲染API(Open VG、本地視窗系統的繪圖指令等)

5) 管理渲染資源,比如材質

3.2 EGL資料結構

egl_object_t結構用來描述沒一個elg對象。

egl_display_t結構用來存儲get_display函數擷取的實體顯示裝置。

egl_surface_t結構用來存儲surface對象,系統可以同時擁有多個surface對象。

egl_context_t結構用用來存儲OpenGL狀态機資訊。

egl_p_w_picpath_t結構用來存儲EGLImage對象。

以下結構體在libagl中實作

egl_window_surface_v2_t繼承egl_surface_t,提供egl_surface_t功能的具體實作,屬于可實際顯示的Surface。

egl_pixmap_surface_t存儲儲存在系統記憶體中的位圖。

egl_pbuffer_surface_t存儲儲存在顯存中的幀,以上兩種位圖屬于不可顯示的Surface。

Android OpenGL ES與EGL

3.3 EGL主要功能函數(省略了函數參數)

EGLDisplay eglGetDisplay()

eglGetDisplay調用egl_display_t::get_display(dpy)擷取顯示裝置的句柄。

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

初始化EGL,擷取EGL版本号。

EGLBoolean eglTerminate()

結束一個EGLDisplay,并不結束EGL本身。

EGLBoolean eglGetConfigs()

擷取EGL配置參數。

EGLBoolean eglChooseConfig()

選擇EGL配置參數,配置一個期望并盡可能接近一個有效的系統配置。

EGLBoolean eglGetConfigAttrib()

擷取EGL配置時需要參照的屬性。

EGLSurface eglCreateWindowSurface()

建立一個EGL surface,surface是可以實際顯示在螢幕上類型。

EGLSurface eglCreatePixmapSurface()

EGLSurface eglCreatePbufferSurface()

建立EGL PixmapSurface和PbufferSurface類型,這兩種類型不可直接顯示與螢幕上。

EGLBoolean eglDestroySurface()

銷毀一個EGL surface對象

EGLBoolean eglQuerySurface()

查詢surface的參數。

EGLContext eglCreateContext()

建立一個OpenGL狀态機。

EGLBoolean eglDestroyContext()

銷毀一個OpenGL狀态機

EGLBoolean eglMakeCurrent()

将OpenGL context與surface綁定。

EGLBoolean eglQueryContext()

查詢context的參數。

EGLBoolean eglSwapBuffers()

繪制完圖形後用于顯示的函數。

const char* eglQueryString()

查詢EGL參數字串。

以上僅為EGL 1.0提供的基礎功能,後續EGL版本陸續添加了更多的API。

3.4 EGL本地調用關系

framework/base/opengl/libs/egl編譯生成libEGL.so。

   libEGL.so是Android系統的EGL架構,預設通過以下調用關系加載OpenGL庫libGLES_android.so:

eglGetDisplay()->egl_init_drivers()->egl_init_drivers_locked()-> loader.open()->load_driver()。

4 Android OpenGL的實作

4.1 Android 提供的OpenGL庫

framework/base/opengl/libagl 編譯生成libGLES_android.so。

libGLES_android.so, 這是一個由系統提供的純軟體3D加速庫,可以相容各種環境。libGLES_android.so中包含了一個EGL架構的實作和OpenGL各種API 的實作。OpenGL的API底層是通過libpixelflinger.so庫實作的。

針對不同的硬體設計,GPU廠商都會提供相應的硬體3D加速庫,需要重寫libGLES_android.so并實作相對應的libpixelflinger.so,工程量較大,一般由GPU廠商的軟體開發團隊來完成。

4.2 EGL加載OpenGL API的方法

libGLES_android.so提供了兩種API,一種是egl實作API,另一種是OpenGL标準API。

4.2.1 加載EGL API

在函數load_driver中,通過dlsym從動态連結庫中擷取egl函數指針。其中egl_names包含egl_entries.in 檔案,egl_entries.in 檔案則是egl API的聲明。

void *Loader::load_driver(const char* driver_absolute_path,

        egl_connection_t* cnx, uint32_t mask)

{

... ...

        char const * const * api = egl_names;

        while (*api) {

            char const * name = *api;

            __eglMustCastToProperFunctionPointerType f =

                (__eglMustCastToProperFunctionPointerType)dlsym(dso, name);

            if (f == NULL) {

                // couldn't find the entry-point, use eglGetProcAddress()

                f = getProcAddress(name);

                if (f == NULL) {

                    f = (__eglMustCastToProperFunctionPointerType)0;

                }

            }

            *curr++ = f;

            api++;

        }

}

每次讀取的函數位址指派給了cnx->egl指向的egl_t結構體。

4.2.2 加載OpenGL API(這一段摘自師弟寫的文章,要修)

Android 2.0系統之前,加載OpenGL API和EGL API方式相同,均是通過dlsym加載。新版本的Android系統采用TLS技術進行動态加載。

TLS 讓多線程程式設計更容易。TLS 是一個機制,通過它,程式可以擁有全局變量,但處于“每一線程各不相同”的狀态。也就是說,程序中的所有線程都可以擁有全局變量,但這些變量隻對某個線程 才有意義。一段代碼在任何情況下都是相同的,而從TLS中取出的對每個線程卻各不相同。

在EGL的early_egl_init()中,對TLS 機制進行初始化。将TLS裡放入一個結構體指針,這個指針指向gHooksNoContext,這個結構體裡的每個函數指針被初始化為 gl_no_context。也就是現在如果通過TLS調用的OpenGL ES API都會調到gl_no_context這個函數中。

在 eglMakeCurrent中,會将渲染上下文綁定到渲染面。在EGL中首先會處理完一系列和本地視窗系統的變量後,調用 libGLES_android.so中的eglMakeCurrent,調用成功的話會設定TLS。将TLS指針指向前面已經初始化化好的 gl_hooks_t結構體指針,這個結構體裡的成員都已經指向了libGLES_android.so中的OpenGL API函數。

4.3動态加載庫的優化

Android 2.0以上系統為libagl的Android.mk定義新的編譯器參數-fvisibility=hidden。根據GCC編譯器定義,使用該參數生成 的動态庫将不會導出函數符号表,亦是動态庫函數的Vis參數為HIDDEN。這樣可以防止導出多餘函數,提高動态庫的加載速度,對于需要導出的函數,可用 參數__attribute__((visibility("default")))手動指定其為“default”屬性。

針對libGLES_android.so庫,以egl和gl的API均由手動指定其屬性為“default”。在opengl/include/中,定義了API的額外參數GL_API。

#define GL_API      KHRONOS_APICALL

#define KHRONOS_APICALL __attribute__((visibility("default")))

5 Android OpenGL 2D部分

libagl中包含egl的實作和OpenGL API,雖然OpenGL API屬于軟體實作,但是已為2D硬體加速預留了接口,主要位于Texture相關函數,調用Android的Copybit和Gralloc子產品。

5.1宏定義

libagl的Android.mk中有如下定義,如果定義了LIBAGL_USE_GRALLOC_COPYBITS宏,編譯時将加入libagl的copybit.cpp檔案,并連結libui庫。

# Set to 1 to use gralloc and copybits

LIBAGL_USE_GRALLOC_COPYBITS := 1

ifeq ($(LIBAGL_USE_GRALLOC_COPYBITS),1)

    LOCAL_CFLAGS += -DLIBAGL_USE_GRALLOC_COPYBITS

    LOCAL_SRC_FILES += copybit.cpp

    LOCAL_SHARED_LIBRARIES += libui

endif

5.2 2D圖形API

 void glDrawTexsvOES()

 void glDrawTexivOES()

 void glDrawTexsOES()

 void glDrawTexiOES()

以上四個API調用函數

static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_context_t* c)

 void glDrawTexfvOES()

 void glDrawTexxvOES()

 void glDrawTexfOES()

 void glDrawTexxOES()

static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h,

ogles_context_t* c)

以上8個API的主要不同在于傳入參數的類型。

drawTexiOES 函數用于處理×××資料,drawTexxOES函數用于處理浮點轉定點後的資料。以上兩個函數如果定義了 LIBAGL_USE_GRALLOC_COPYBITS,則直接調用位于copybit.cpp中的 drawTexiOESWithCopybit(x, y, z, w, h, c)進行硬體加速,否則調用drawTexxOESImp()純軟體實作。drawTexiOESWithCopybit調用copybit函數處理 surface以适應copybit子產品。