對 Android 來說, 我們呼叫 eglXXX 的 API 時是呼叫到 libEGL 內部的實作
舉個 BootAnimation 的例子, 在創建 EGLSurface 會呼叫 eglCreateWindowSurface
frameworks/base/cmds/bootanimation/BootAnimation.cpp
status_t BootAnimation::readyToRun() {
... EGLSurface surface; surface = eglCreateWindowSurface(display, config, s.get(), NULL);
... }
那我們可以看到 libEGL 最後又呼叫了 cnx->egl.eglCreateWindowSurface
frameworks/native/opengl/libs/EGL/eglApi.cpp
EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list) { ... EGLSurface surface = cnx->egl.eglCreateWindowSurface }
那這個 cnx->egl 又是哪裡來的?
我們往前面看到開頭的地方呼叫的 validate_display_connection
EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, NativeWindowType window, const EGLint *attrib_list){ egl_connection_t* cnx = NULL; const egl_display_ptr dp = validate_display_connection(dpy, cnx); ... }
跳轉進入 egl.cpp
frameworks/native/opengl/libs/EGL/egl.cpp
egl_display_ptr validate_display_connection(EGLDisplay dpy, egl_connection_t*& cnx) { cnx = NULL; egl_display_ptr dp = validate_display(dpy); if (!dp) return dp; cnx = &gEGLImpl; if (cnx->dso == 0) { return setError(EGL_BAD_CONFIG, egl_display_ptr(NULL)); } return dp; }
這邊把 cnx 指向了 gEGLImpl 的地址, 一般這種形式可以預期他指向一個 function pointer
搜尋一下, 可以發現到在 init 的時候確實做了這件事:
static EGLBoolean egl_init_drivers_locked() { if (sEarlyInitState) { // initialized by static ctor. should be set here. return EGL_FALSE; } // get our driver loader Loader& loader(Loader::getInstance()); // dynamically load our EGL implementation egl_connection_t* cnx = &gEGLImpl; if (cnx->dso == 0) { cnx->hooks[egl_connection_t::GLESv1_INDEX] = &gHooks[egl_connection_t::GLESv1_INDEX]; cnx->hooks[egl_connection_t::GLESv2_INDEX] = &gHooks[egl_connection_t::GLESv2_INDEX]; cnx->dso = loader.open(cnx); } return cnx->dso ? EGL_TRUE : EGL_FALSE; }
我們再看看 Loader::open 做了什麼
void* Loader::open(egl_connection_t* cnx) { void* dso; driver_t* hnd = 0; dso = load_driver("GLES", cnx, EGL | GLESv1_CM | GLESv2); if (dso) { hnd = new driver_t(dso); } else { // Always load EGL first dso = load_driver("EGL", cnx, EGL); if (dso) { hnd = new driver_t(dso); hnd->set( load_driver("GLESv1_CM", cnx, GLESv1_CM), GLESv1_CM ); hnd->set( load_driver("GLESv2", cnx, GLESv2), GLESv2 ); } }
...
}
展開 load_driver...完畢!
void *Loader::load_driver(const char* kind, egl_connection_t* cnx, uint32_t mask) { /* 定義了 MatchFile 的 class, 設定搜尋路徑是 /vendor/lib/egl 與 /system/lib/egl */ ... // 假設已經找到了對應的 so void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL); if (mask & EGL) { /* 比較麻煩的是這個 getProcAddress 也是從 dlsym 拿出來的...所以如果你都是 prebuilt library 就找不到實作 不過我們可以隨便找一個實作來看, 都是大同小異的. 簡單來說就是對照 name, 取出 API 的型態 */ getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress"); // 這裡取出了 cnx->egl 的地址, 並轉型為 __eglMustCastToProperFunctionPointerType (其實就是void) egl_t* egl = &cnx->egl; __eglMustCastToProperFunctionPointerType* curr = (__eglMustCastToProperFunctionPointerType*)egl; /* 這個 elg_names 的定義是從 egl_entries.in 來的, 放在 frameworks/native/opengl/libs/EGL/egl_entries.in char const * const egl_names[] = { #include "egl_entries.in" NULL }; */ char const * const * api = egl_names; while (*api) { char const * name = *api; __eglMustCastToProperFunctionPointerType f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); // 如果 dlsym 找不到才會嘗試去使用 getProcAddress 來做對照, 應該是為了擴充性的考量. if (f == NULL) { // couldn't find the entry-point, use eglGetProcAddress() f = getProcAddress(name); if (f == NULL) { f = (__eglMustCastToProperFunctionPointerType)0; } } // 把 function pointer 一個一個 mapping... 完畢 *curr++ = f; api++; } } ... }
另外, 由於你的 library 可能都是 vendor 封裝好的, 所以要看 EGL 的行為的話,
可以參考 Google 為了 Emulator 所寫的 Software OpenGL ES library
檔案放在這裡: frameworks/native/opengl/libagl/egl.cpp
沒有留言:
張貼留言