2017年10月19日 星期四

Android系統筆記 - EGL library 的接入口

對 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

沒有留言:

張貼留言

不定參數印 log

From the UNIXProcess_md.c #ifdef DEBUG_PROCESS   /* Debugging process code is difficult; where to write debug output? */ static void deb...