Android 系统图形栈(二):OpenGL ES 库和 EGL 库加载过程

1. OpenGL ES 和 OpenGL ES 库的区别

  • OpenGL ES : 它本身只是一个协议规范,定义了一套可以供上层应用程序进行调用的 API,它抽象了 GPU 的功能,使应用开发者不必关心底层的 GPU 类型和具体实现。
  • OpenGL ES 库:OpenGL ES 库就是上面 OpenGL ES 中定义的 API 的具体实现。由于每个显卡制造厂商的 GPU 硬件结构不同,从而导致各个厂商的OpenGL ES 库也各不相同,所以 Android 系统中的 OpenGL ES 库通常是由硬件厂商提供的,通常存放在 Android 系统中的 /system/lib64/egl 下面或者 /vendor/lib64/egl 目录下。
  • OpenGL ES Wrapper 库:OpenGL ES Wrapper 库是一个对 OpenGL ES API 进行封装的一个包裹库,它向上为应用程序提供了标准的 OpenGL ES API,向下可以和不同厂商实现的 OpenGL ES 库进行绑定,将 OpenGL ES API 和对应的实现函数一一绑定在一起。

并且,OpenGL ES 库的实现分为:

  • 软件模拟实现
  • 硬件加速实现

现在,因为我们 Android 手机中的 Soc 片上芯片中都集成了 GPU 模块,所以这里使用的就是硬件加速实现的 OpenGL ES 库。但是,像 Android Emulator 中的 Android 系统,如果不支持将 OpenGL ES API 指令重定向到主机系统的 GPU 加速执行的话,它所采用的 OpenGL ES 库就是软件模拟实现的。

补充: 同理,如前面一篇文章Android 系统图形栈: OpenGL ES 和 EGL 介绍中介绍的,EGL 也是一套 API,它的实现也需要系统厂商来提供。系统厂商通常会将这两套 API 的实现封装在一个共享链接库中,但是根据最新的标准,OpenGL ES API 实现的共享链接库和 EGL API 实现的共享链接库是独立分开的,例如 2.2 小节中列举的 Nexus 9 平板设备中 OpenGL ES 和 EGL API 实现库就是独立分开的。

2. Android 中 OpenGL ES 软件层次栈

按照分层理念的设计,Android 中的 OpenGL ES 实现也是层次设计的,形成一个软件层次栈。最上面的是 Java 层,接着下面是 JNI 层,再调用下面的 wrapper 层,wrapper 层下面则是 OpenGL ES API 的具体软件实或者硬件实现了。整个 OpenGL 软件层次栈的调用关系如下所示:

 

2.1 OpenGL ES/EGL Wrapper 库

前面我们已经介绍过 OpenGL ES/EGL Wrapper 库是一个将 OpenGL ES API 和 OpenGL ES API 具体实现绑定在一起的库,它对应的源码路径是:/frameworks/native/opengl/libs/,其中:

  • libGLESv1_CM.so:OpenGL ES 1.x API 的 Wrapper 库
  • libGLESv2.so:OpenGL ES 2.0 的 Wrapper 库
  • libGLESv3.so:OpenGL ES 3.0 的 Wrapper 库

其中因为 OpenGL ES 3.0 API 是兼容 OpenGL ES 2.0 API 的,所以 libGLESv2.so 库本质上和 libGLESv3.so 库是一样的。

2.2 OpenGL ES/EGL 实现库

如果Android系统平台支持 OpenGL ES 硬件加速渲染,那么 OpenGL ES/EGL 实现库由系统厂商以.so的共享链接库的形式提供,例如,Nexus 9 平板中的厂商提供的 OpenGL ES/EGL 实现库为:

flounder:/vendor/lib64/egl # ls
libEGL_tegra.so libGLESv1_CM_tegra.so libGLESv2_tegra.so

如果Android系统平台不支持 OpenGL ES 硬件加速渲染,那么它就会默认启用软件模拟渲染,这时 OpenGL ES/EGL 实现库就是由 AOSP 提供,链接库的存在的路径为: /system/lib64/egl/libGLES_android.so。而 libGLES_android.so 库在 Android 7.1 系统对应的实现源码路径为:/frameworks/native/opengl/libagl/ 。

3. Android 7.1 中加载 OpenGL ES 库的过程

Android 中图形渲染所采用的方式(硬件 or 软件)是在系统启动之后动态确定的,而确定渲染方式的这个源码文件就是 /frameworks/native/opengl/libs/EGL/Loader.cpp 。

3.1 Android 7.1 OpenGL ES 库和 EGL 库加载说明

How Android finds OpenGL libraries, and the death of egl.cfg 这篇文章中提到了非常关键的一点,就是从 Android Kitkat 4.4 之后,Android 中加载 OpenGL ES/EGL 库的方法发生了变化了(但是整个加载过程都是由 /frameworks/native/opengl/libs/EGL/Loader.cpp 程序所决定的,也就是说 Loader.cpp 文件发生了变化)。

在 Android 4.4 之前,加载 OpenGL ES 库是由 /system/lib/egl/egl.cfg 文件所决定的,通过读取这个配置文件来确定是加载 OpenGL ES 软件模拟实现的库,还是OpenGL ES 硬件加速实现的库。

但是,在Android 4.4 之后,Android 不再通过读取 egl.cfg 配置文件的方式来加载 OpenGL ES 库,新的加载 OpenGL ES 库的规则,如下所示:

  1. 从 /system/lib/egl 或者 /vendor/lib/egl/ 目录下加载 libGLES.so 库文件或者 libEGL_vendor.so,libGLESv1_CM_vendor.so,libGLESv2_vendor.so 库文件。
  2. 为了向下兼容旧的库的命名方式,同样也会加载 /system/lib/egl 或者 /vendor/lib/egl/ 目录下的 libGLES_*.so 或者 libEGL_*.so,libGLESv1CM*.so,libGLESv2_*.so 库文件。

3.2 硬件加速渲染 or 软件模拟渲染?

前面我们提到 OpenGL ES 库的实现方式有两种,一种是硬件加速实现,一种是软件模拟实现,那么系统是怎么确定加载那一种 OpenGL ES 库的呢?

Android 7.1 源码中负责加载 OpenGL ES/EGL 库部分的代码位于:/frameworks/native/opengl/libs/EGL/Loader.cpp 文件中,这个文件中代码的主要入口函数是 Loader::open() 函数,而决定加载硬件加速渲染库还是软件模拟渲染库主要涉及到下面两个函数:

  • setEmulatorGlesValue()
  • checkGlesEmulationStatus()

下面就来简要的分析一下 Android 系统是如何选择加载硬件加速渲染库还是软件模拟渲染库

  1. 首先,Loader::open() 入口函数会调用 setEmulatorGlesValue() 从 property 属性系统中获取一些属性值来判断当前 Android 系统是否在 Emulator 环境中运行,并根据读取出来的信息来重新设置新的属性键值对,setEmulatorGlesValue() 函数的代码如下所示:

     static void setEmulatorGlesValue(void) {
         char prop[PROPERTY_VALUE_MAX];
         property_get("ro.kernel.qemu", prop, "0"); //读取 ro.kernel.qemu 属性值,判断Android系统是否运行在 qemu 中
         if (atoi(prop) != 1) return;
        
         property_get("ro.kernel.qemu.gles", prop, "0"); //读取 ro.kernel.qemu.gles 属性值,判断 qemu 中 OpenGL ES 库的实现方式
         if (atoi(prop) == 1) {
             ALOGD("Emulator has host GPU support, qemu.gles is set to 1.");
             property_set("qemu.gles", "1");
             return;
         }
        
         // for now, checking the following
         // directory is good enough for emulator system images
         const char* vendor_lib_path =
     #if defined(__LP64__)
             "/vendor/lib64/egl";
     #else
             "/vendor/lib/egl";
     #endif
        
         const bool has_vendor_lib = (access(vendor_lib_path, R_OK) == 0);
         //如果存在 vendor_lib_path 这个路径,那么就说明厂商提供了 OpenGL ES库自己的软件模拟渲染库,而不是 Android 系统自己编译得到的软件模拟渲染库
         if (has_vendor_lib) {
             ALOGD("Emulator has vendor provided software renderer, qemu.gles is set to 2.");
             property_set("qemu.gles", "2");
         } else {
             ALOGD("Emulator without GPU support detected. "
                   "Fallback to legacy software renderer, qemu.gles is set to 0.");
             property_set("qemu.gles", "0"); //最后,默认采取的是方案就是调用传统的Android系统自己编译得到软件模拟渲染库
         }
     }
    
  2. 在 load_system_driver() 函数中,内部类 MatchFile 类中会调用 checkGlesEmulationStatus() 函数来检查 Android 系统是否运行在模拟器中,以及在模拟器中是否启用了主机硬件加速的功能,然后根据 checkGlesEmulationStatus() 函数的返回状态值来确定要加载共享链接库的文件绝对路径。load_system_driver() 和 checkGlesEmulationStatus() 函数代码如下所示:

     static void* load_system_driver(const char* kind) {
         ATRACE_CALL();
         class MatchFile {
         public:
             //这个函数作用是返回需要加载打开的 OpenGL ES 和 EGL API 实现库文件的绝对路径
             static String8 find(const char* kind) {
                 String8 result;
                 int emulationStatus = checkGlesEmulationStatus(); //检查 Android 系统是否运行在模拟器中,以及在模拟器中是否启用了主机硬件加速的功能
                 switch (emulationStatus) {
                 case 0: //Android 运行在模拟器中,使用系统软件模拟实现的 OpenGL ES API 库 libGLES_android.so
     #if defined(__LP64__)
                     result.setTo("/system/lib64/egl/libGLES_android.so");
     #else
                     result.setTo("/system/lib/egl/libGLES_android.so");
     #endif
                     return result;
                 case 1: // Android 运行在模拟器中,通过主机系统中实现 OpenGL ES 加速渲染,通过 libGLES_emulation.so 库将  OpenGL ES API 指令重定向到 host 中执行
                     // Use host-side OpenGL through the "emulation" library
     #if defined(__LP64__)
                     result.appendFormat("/system/lib64/egl/lib%s_emulation.so", kind);
     #else
                     result.appendFormat("/system/lib/egl/lib%s_emulation.so", kind);
     #endif
                     return result;
                 default:
                     // Not in emulator, or use other guest-side implementation
                     break;
                 }
        
                 // 如果不是上面两种情况,就根据库的命名规则去找到厂商实现库文件的绝对路径
                 String8 pattern;
                 pattern.appendFormat("lib%s", kind);
                 const char* const searchPaths[] = {
     #if defined(__LP64__)
                     "/vendor/lib64/egl",
                     "/system/lib64/egl"
     #else
                     "/vendor/lib/egl",
                     "/system/lib/egl"
     #endif
                 };
                    
                 ......
         }
            
     }
    
     static int
     checkGlesEmulationStatus(void)
     {
         /* We're going to check for the following kernel parameters:
          *
          *    qemu=1                      -> tells us that we run inside the emulator
          *    android.qemu.gles=<number>  -> tells us the GLES GPU emulation status
          *
          * Note that we will return <number> if we find it. This let us support
          * more additionnal emulation modes in the future.
          */
         char  prop[PROPERTY_VALUE_MAX];
         int   result = -1;
        
         /* Check if hardware acceleration disabled explicitly */
         property_get("debug.egl.hw", prop, "1"); //读取 debu.egl.hw 属性值,判断3D硬件加速功能是否被关闭了
         if (!atoi(prop)) {
             ALOGD("3D hardware acceleration is disabled");
             return 0;
         }
        
         /* First, check for qemu=1 */
         property_get("ro.kernel.qemu", prop, "0"); //读取ro.kernel.qemu,判断是否运行在 qemu 中
         if (atoi(prop) != 1)
             return -1;
        
         /* We are in the emulator, get GPU status value */
         property_get("qemu.gles", prop, "0"); // 如果 Android 系统运行在 qemu 中,就返回 qemu.gles 的值,根据这个值就可以确定加载的是那种 OpenGL ES 库了
         return atoi(prop);
     }
    
  3. 总结一下上面代码的功能就是,首先判断 Android 是否在 qemu 虚拟机中运行,如果不是,那么就直接去加载厂商存放库的路径中去加载 OpenGL ES 实现库(不管是硬件加速实现的,还是软件模拟实现的);如果是在 qemu 中运行,那么就要根据返回的 emulationStatus 值 来确定是加软件模拟实现的 OpenGL ES API 库 libGLES_android.so,还是加载 libGLES_emulation.so库将 OpenGL ES 指令重定向到 Host 系统中去执行。

3.3 OpenGL ES/EGL 库加载和解析过程

正如Android 系统图形栈: OpenGL ES 和 EGL 介绍这篇文章中分析的那样,在进行 OpenGL 编程时,最先开始需要获取 Display,这将调用 eglgGetDisplay() 函数被调用。在 eglGetDisplay() 里则会调用 egl_init_drivers() 初始化驱动:装载各个库进行解析,将 OpenGL ES/EGL API 函数接口和具体的实现绑定在一起,并将结果保存在 egl_connection_t 类型的全局变量 gEGLImpl 的结构体的成员变量中。

下面以 SurfaceFlinger 进程为例进行分析,整个 OpenGL ES/EGL 库的加载和解析流程如下所示:

st=>start: Start
op1=>operation: SurfaceFlinger::init()
sub=>subroutine: Your Subroutine
op2=>operation: eglGetDisplay(EGL_DEFAULT_DISPLAY)
op3=>operation: egl_init_drivers()
op4=>operation: egl_init_drivers_locked();
op5=>operation: loader.open()
op6=>operation: load_driver()
op7=>operation: load_system_driver()
op8=>operation: Loader::init_api()
e=>end

st->op1->op2->op3->op4->op5->op6->op7->op8->e

3.3.1 gEGLImpl 全局变量

struct egl_connection_t 类型的 gEGLImpl 全局变量是一个非常重要变量,它里面的成员指向了打开的 OpenGL ES/EGL Wrapper 库和 OpenGL ES/EGL 实现库: /frameworks/native/opengl/libs/EGL/egl.cpp

egl_connection_t gEGLImpl;
gl_hooks_t gHooks[2];
gl_hooks_t gHooksNoContext;

其中 egl_connection_t 的定义: /frameworks/native/opengl/libs/EGL/egldefs.h

struct egl_connection_t {
    enum {
        GLESv1_INDEX = 0,
        GLESv2_INDEX = 1
    };

    inline egl_connection_t() : dso(0) { }
    void *              dso; //指向打开的共享链接库的句柄
    gl_hooks_t *        hooks[2]; //指向打开的 OpenGL ES API 对象
    EGLint              major; // 主版本好
    EGLint              minor;
    egl_t               egl; //dui x

    void*               libEgl;
    void*               libGles1;
    void*               libGles2;
};

下面就对其中的主要成员进行一个说明:

  • hooks:这是一个 gl_hook_t* 类型的指针数组,它最终将 OpenGL ES API 和实现库钩在一起。
  • egl:这是一个 egl_t 类型的成员变量,它最终将 EGL API 和 EGL 实现库了钩在一起。

那么 gl_hook_t 和 egl_t 是什么呢?

gl_hook_t 和 egl_t 的定义如下所示: /frameworks/native/opengl/libs/hooks.h

#define GL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);
#define EGL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__);

struct egl_t {
    #include "EGL/egl_entries.in"
};

struct gl_hooks_t {
    struct gl_t {
        #include "entries.in"
    } gl;
    struct gl_ext_t {
        // __eglMustCastToProperFunctionPointerType 是一个 typedef 的函数指针类型,它的返回值是void,参数也是void
        __eglMustCastToProperFunctionPointerType extensions[MAX_NUMBER_OF_GL_EXTENSIONS];
    } ext;
};

从上面的定义中我们发现,egl_t 和 gl_hooks_t 这两个结构体中主要就是一个 include 语句,那么它们包含的是什么呢?

#include “EGL/egl_entries.in 包含的文件路径如下所示:/frameworks/native/opengl/libs/EGL/egl_entries.in

EGL_ENTRY(EGLDisplay, eglGetDisplay, NativeDisplayType)
EGL_ENTRY(EGLBoolean, eglInitialize, EGLDisplay, EGLint*, EGLint*)
EGL_ENTRY(EGLBoolean, eglTerminate, EGLDisplay)
EGL_ENTRY(EGLBoolean, eglGetConfigs, EGLDisplay, EGLConfig*, EGLint, EGLint*)
EGL_ENTRY(EGLBoolean, eglChooseConfig, EGLDisplay, const EGLint *, EGLConfig *, EGLint, EGLint *)
...

在这个文件中,我们可以看到所有的内容都是 EGL_ENTRY 宏定义的,根据 EGL_ENTRY 宏定义:/frameworks/native/opengl/libs/EGL/hooks.h

//下面的两个宏定义分别是 OpenGL 和 EGL API 函数的函数指针变量
//这些函数变量最后会和具体的 OpenGL 和 EGL API 的实现绑定在一起
#define GL_ENTRY(_r,_api,...) _r (*_api)(__VA_ARGS__);
#define EGL_ENTRY(_r,_api,...) _r(*_api)(__VA_ARGS__);

我们可以将下面的这个宏定义展开成如下的形式:

EGL_ENTRY(EGLDisplay, eglGetDisplay, NativeDisplayType)
//展开后的形式如下所示,它实际上就是 EGL API 函数声明
EGLDisplay eglGetDisplay(NativeDisplayType)

#include “entries.in” 包含的文件路径为: /frameworks/native/opengl/libs/entries.in

...
GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels)
...

和上面的 EGL_ENTRY 一样,GL_ENTRY 都是通过宏定义的形式来将 OpenGL ES 的 API 函数接口进行声明,例如,上面的宏定义声明可以展开成下面的形式:

GL_ENTRY(void, glReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels)
// 展开后的形式如下所示,它实际上就是 OpenGL ES API 函数声明
void glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void * pixels)

最后,通过 entries.in 和 egl_entries.in 这两个文件,我们就可以得到 OpenGL ES 和 EGL 中的所有 API 函数接口的说明。

3.3.2 SurfaceFlinger 初始化成员变量 mEGLDisplay

在 SurfaceFlinger 类中有一个 EGLDisplay 类型的成员变量 mEGLDisplay,它是 EGL 中用来构建 OpenGL ES 渲染环境所需的参数。

SurfaceFlinger 中调用 eglGetDisplay() 初始化 mEGLDisplay 的代码如下:/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

void SurfaceFlinger::init() {
    ALOGI(  "SurfaceFlinger's main thread ready to run. "
            "Initializing graphics H/W...");

    status_t err;
    Mutex::Autolock _l(mStateLock);

    // initialize EGL for the default display
    // 调用 eglGetDisplay 函数获取默认的显示设备
    mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); 
    eglInitialize(mEGLDisplay, NULL, NULL);
    ...

紧接着在 eglGetDisplay() 中调用 egl_init_drivers() /frameworks/native/opengl/libs/EGL/eglApi.cpp

EGLDisplay eglGetDisplay(EGLNativeDisplayType display)
{
    ...
    // 调用egl_init_drivers() 加载 OpenGL ES 库和 EGL 库
    if (egl_init_drivers() == EGL_FALSE) {
        return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
    }

    EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display);
    return dpy;
}

最后,egl_init_drivers() 函数中使用了 pthread_mutex_lock 上锁保护,继续调用 egl_init_drivers_locked() 函数 /frameworks/native/opengl/libs/EGL/egl.cpp

EGLBoolean egl_init_drivers() {
    EGLBoolean res;
    pthread_mutex_lock(&sInitDriverMutex);
    // 使用了 pthread_mutex_lock 上锁保护,继续调用 egl_init_drivers_locked
    res = egl_init_drivers_locked();
    pthread_mutex_unlock(&sInitDriverMutex);
    return res;
}

下面就是对 egl_init_drivers_locked() 函数进行一个分析:

egl_init_drivers_locked() 函数代码如下所示: /frameworks/native/opengl/libs/EGL/egl.cpp

//在该文件起始位置定义的全局变量
egl_connection_t gEGLImpl; // 描述EGL实现内容的结构体对象
gl_hooks_t gHooks[2]; // gl_hooks_t 是包含 OpenGL ES API 函数声明对应的函数指针结构体
gl_hooks_t gHooksNoContext;
pthread_key_t gGLWrapperKey = -1;

static EGLBoolean egl_init_drivers_locked() {
    if (sEarlyInitState) {
        // initialized by static ctor. should be set here.
        return EGL_FALSE;
    }

    // 得到 Loader 对象单例
    // get our driver loader
    Loader& loader(Loader::getInstance());

    //  gEGLImple 是一个全局变量,数据类型为 egl_connection_t 结构体类型
    // dynamically load our EGL implementation
    egl_connection_t* cnx = &gEGLImpl;

    // cnx->dso 本质上是一个 (void *)类型的指针,它指向的对象是 EGL 共享库打开之后的句柄
    if (cnx->dso == 0) { 
        // >= 将cnx中的 hooks 数组中指向OpenGL ES API 函数指针结构体指的数组成员,用 gHooks 中的成员的地址去初始化
        //也就是说 gEGLImpl 中 hook 数组指向 gHooks 数组,最终指向同一个 OpenGL ES API 函数指针的实现
        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];

        // >= 最后通过loader对象的open函数开始加载 OpenGL ES 和 EGL wrapper 库
        cnx->dso = loader.open(cnx);
    }

    return cnx->dso ? EGL_TRUE : EGL_FALSE;
}

在这个函数中,有一个非常关键的 egl_connection_t 指针指向一个全局变量 gEGLImpl,当第一次初始化加载 OpenGL ES 实现库和 EGL 实现库时,还需要将 gEGLImpl 中的 hooks 数组中的两个指针指向一个全局的 gl_hooks_t 数组 gHooks(这就是两个指针钩子,最终初始化完成后将分别勾住 OpenGL ES 1.0 和 OpenGL ES 2.0 的实现库),接着调用 Loader 类的实例的 open() 函数完成从 OpenGL ES 实现库中完成符号解析工作。

3.3.3 通过 Loader 类加载和解析 OpenGL ES 库和 EGL 库

Loader::open() 函数的代码如下所示: /frameworks/native/opengl/libs/EGL/Loader.cpp

// >= Loader 类对象构造完成后,就在 /EGL/egl.cpp 文件中的 egl_init_drivers_locked() 中被调用
void* Loader::open(egl_connection_t* cnx)
{
    ATRACE_CALL();

    void* dso;
    driver_t* hnd = 0;

    setEmulatorGlesValue();

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

    LOG_ALWAYS_FATAL_IF(!hnd, "couldn't find an OpenGL ES implementation");

    cnx->libEgl   = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so");
    cnx->libGles2 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv2.so");
    cnx->libGles1 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so");

    LOG_ALWAYS_FATAL_IF(!cnx->libEgl,
            "couldn't load system EGL wrapper libraries");

    LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1,
            "couldn't load system OpenGL ES wrapper libraries");

    return (void*)hnd;
}

open() 函数主要负责 OpenGL ES 库加载前的准备工作,具体的加载细节,则是通过调用 load_driver() 去完成的。

Loader::load_driver() 函数代码如下所示: /frameworks/native/opengl/libs/EGL/Loader.cpp

void *Loader::load_driver(const char* kind,
                          egl_connection_t* cnx, uint32_t mask)
{
    ATRACE_CALL();

    void* dso = nullptr;
    if (mGetDriverNamespace) {
        android_namespace_t* ns = mGetDriverNamespace();
        if (ns) {
            dso = load_updated_driver(kind, ns); //加载 OpenGL ES 实现库,放回打开的共享链接库的句柄
        }
    }
    if (!dso) {
        dso = load_system_driver(kind);
        if (!dso)
            return NULL;
    }

    // 解析 EGL 库,并将wrapper 库 libEGL.so 中的函数 API 指针和具体的实现绑定在一起
    if (mask & EGL) {
        getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress");

        ALOGE_IF(!getProcAddress,
                 "can't find eglGetProcAddress() in EGL driver library");

        egl_t* egl = &cnx->egl; //将 egl 指针指向描述当前系统支持 OpenGL ES和 EGL 全局变量的 gEGLImpl
        __eglMustCastToProperFunctionPointerType* curr =
            (__eglMustCastToProperFunctionPointerType*)egl;
        char const * const * api = egl_names; //egl_names 是定义在 egl.cpp 文件中的一个数组,数组中的元素是 EGL API 函数指针
        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; //这一步就是最关键的将共享链接库中的 EGL API 的实现和上层调用的 API 函数指针绑定在一起
            api++; //指向下一个需要绑定的 api 函数
        }
    }

    // 解析 OpenGL ES 库中的 OpenGL ES 1.x API 符号
    if (mask & GLESv1_CM) {
        // 调用 init_api 实现 OpenGL API 和对应实现函数的绑定
        init_api(dso, gl_names, // gl_names 是定义在 egl.cpp 文件中的一个数组,数组中的元素是 OpenGL ES API 函数指针
                 (__eglMustCastToProperFunctionPointerType*)
                 &cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl, //gl成员变量是一个结构体变量,结构体中的是 OpenGL ES API 函数指针
                 getProcAddress);
    }

    // 解析 OpenGL ES 库中的 OpenGL ES 2.0 API 符号
    if (mask & GLESv2) {
        init_api(dso, gl_names,
                 (__eglMustCastToProperFunctionPointerType*)
                 &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl,
                 getProcAddress);
    }

    return dso;
}

Loader::load_driver() 它主要实现了两个功能:

  • 通过 load_system_driver() 函数查找 OpenGL ES/EGL 实现库,并在指定的存放路径中找到共享链接库文件并打开它。
  • 调用 init_api()解析打开的 OpenGL ES/EGL 共享链接库,将 OpenGL ES/EGL API 函数指针和共享链接库中实现的对应的函数符号绑定在一起,这样调用 OpenGL ES/EGL API 就会调用到具体实现的OpenGL ES/EGL 共享链接库中对应函数。

具体 load_system_driver() 函数和 init_api() 函数的实现就不一一展开了,大家可以到 /frameworks/native/opengl/libs/EGL/Loader.cpp 文件中查看,应该写得非常直接清楚了。

3.4 小结

至此,有关 OpenGL ES/EGL 库的加载和解析过程就分析完了,整个加载和解析的目的就是将 OpenGL ES/EGL API 和具体实现函数绑定在一起,这样当程序调用 OpenGL ES/EGL API 时,就会调用到实际的实现函数了。然后,在硬件平台上的 Android 系统加载的是硬件厂商提供的 OpenGL ES/EGL 实现库,而在 qemu 模拟器中运行的 Android 系统中加载的则是软件模拟实现的 OpenGL ES/EGL 库或者是将 OpenGL ES 重定向到主机系统中进行硬件加速的库。

3.5 参考文章

  1. How Android finds OpenGL libraries, and the death of egl.cfg
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值