图形显示系统分析之OPENGL和EGL库的加载

4,078 阅读7分钟

「这是我参与2022首次更文挑战的第15天,活动详情查看:2022首次更文挑战」。

在此前分析开机动画启动流程的时候,有一段通过EGL和OPENGL绘制动画图片的流程,本篇将来分析一下OPENGL和EGL库的加载流程

// initialize opengl and egl
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, nullptr, nullptr);
EGLConfig config = getEglConfig(display);
EGLSurface surface = eglCreateWindowSurface(display, config, s.get(), nullptr);
EGLContext context = eglCreateContext(display, config, nullptr, nullptr);
EGLint w, h;
eglQuerySurface(display, surface, EGL_WIDTH, &w);
eglQuerySurface(display, surface, EGL_HEIGHT, &h);

if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
    return NO_INIT;

在开机动画启动流程过程中,通过如上的这些函数来初始化EGL和OPENGL的,此篇我们先分析下OPENGL和EGL库函数的加载

OPENGL和EGL库的加载

首先,先来看下eglGetDisplay函数的具体实现

eglApi.cpp
EGLDisplay eglGetDisplay(EGLNativeDisplayType display) {
    ATRACE_CALL();
    // 此处调用egl_init_drivers函数,来初始化驱动信息
    if (egl_init_drivers() == EGL_FALSE) {
        return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
    }

    // Call down the chain, which usually points directly to the impl
    // but may also be routed through layers
    clearError();
    // 获取显示设备的显示Display
    egl_connection_t* const cnx = &gEGLImpl;
    return cnx->platform.eglGetDisplay(display);
}

初始化EGL的驱动信息

egl.cpp
EGLBoolean egl_init_drivers() {
    EGLBoolean res;
    // 同步锁
    pthread_mutex_lock(&sInitDriverMutex);
    // 初始化EGL驱动
    res = egl_init_drivers_locked();
    pthread_mutex_unlock(&sInitDriverMutex);
    return res;
}

static EGLBoolean egl_init_drivers_locked() {
    // 1. early init state是否已经被设置
    // egl在初始化的时候有一个static对象,这个对象在运行时候就会被执行
    if (sEarlyInitState) {
        // initialized by static ctor. should be set here.
        return EGL_FALSE;
    }

    // get our driver loader
    // 2. Loader对象的初始化
    Loader& loader(Loader::getInstance());

    // dynamically load our EGL implementation
    // 3. 加载EGL库函数
    // 初始化一个cnx指针指向定义的全局gEGLImpl对象
    // 需要注意的是此处的gEGLImpl对象在初始化的过程中,会获取对应的platform_entries.in中的所有函数
    egl_connection_t* cnx = &gEGLImpl;
    // 初始化两个钩子对象指针指向两个gHooks的数据
    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];
    // 加载EGL库,并且返回EGL库的索引
    cnx->dso = loader.open(cnx);

    // Check to see if any layers are enabled and route functions through them
    if (cnx->dso) {
        // Layers can be enabled long after the drivers have been loaded.
        // They will only be initialized once.
        // 4. 初始化LayerLoader
        LayerLoader& layer_loader(LayerLoader::getInstance());
        layer_loader.InitLayers(cnx);
    }

    return cnx->dso ? EGL_TRUE : EGL_FALSE;
}

early init state是否已经被设置

sEarlyInitState参数是一个static对象参数,因此

static pthread_once_t once_control = PTHREAD_ONCE_INIT;
// pthread_once表明这个县城只会运行一次
static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);

static void early_egl_init(void)
{
    // 获取gHooksNoContext中包含函数的个数
    int numHooks = sizeof(gHooksNoContext) / sizeof(EGLFuncPointer);
    // 设置函数索引
    EGLFuncPointer *iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext);
    for (int hook = 0; hook < numHooks; ++hook) {
        *(iter++) = reinterpret_cast<EGLFuncPointer>(gl_no_context);
    }
    // 设置GL函数模板
    setGLHooksThreadSpecific(&gHooksNoContext);
}

从如上函数和解析这边主要是加载entries.in文件中的所有函数

Loader对象的初始化

Loader& loader(Loader::getInstance());

Loader& Loader::getInstance() {
    static Loader loader;
    return loader;
}

Loader::Loader() : getProcAddress(nullptr)
{}

初始化一个Loader对象

加载EGL库函数

// 新建一个指针参数指向一个gEGLImpl对象
// 需要注意的是此处的gEGLImpl对象在初始化的过程中,会获取对应的platform_entries.in中的所有函数
egl_connection_t* cnx = &gEGLImpl;
// 初始化两个egl_connection_t对象的钩子指针指向gHooks包含的对象
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函数
cnx->dso = loader.open(cnx);

void* Loader::open(egl_connection_t* cnx)
{
    ATRACE_CALL();
    const nsecs_t openTime = systemTime();
    // 若此前已经加载,此处需要将此前的加载流程解加载一下
    if (should_unload_system_driver(cnx)) {
        unload_system_driver(cnx);
    }

    // If a driver has been loaded, return the driver directly.
    // 确认此处是否已经加载过cnx-dso,若已经加载过,此处直接返回即可
    if (cnx->dso) {
        return cnx->dso;
    }

    // Firstly, try to load ANGLE driver.
    // 第一步加载ANGLE
    // 加载libEGL_angle.so,libGLESv1_CM_angle.so,libGLESv2_angle.so库
    // 设置hnd->dso[0]为libEGL_angle.so加载的API
    // 设置hnd->dso[1]为libGLESv1_CM_angle.so加载的API
    // 设置hnd->dso[2]为libGLESv2_angle.so加载的API
    driver_t* hnd = attempt_to_load_angle(cnx);
    if (!hnd) {
        // Secondly, try to load from driver apk.
        hnd = attempt_to_load_updated_driver(cnx);
    }

    bool failToLoadFromDriverSuffixProperty = false;
    // 若上述加载未完成(未找到对应的SO库),则进入这个if语句
    if (!hnd) {
        // If updated driver apk is set but fail to load, abort here.
        if (android::GraphicsEnv::getInstance().getDriverNamespace()) {
            LOG_ALWAYS_FATAL("couldn't find an OpenGL ES implementation from %s",
                             android::GraphicsEnv::getInstance().getDriverPath().c_str());
        }
        // Finally, try to load system driver, start by searching for the library name appended by
        // the system properties of the GLES userspace driver in both locations.
        // i.e.:
        //      libGLES_${prop}.so, or:
        //      libEGL_${prop}.so, libGLESv1_CM_${prop}.so, libGLESv2_${prop}.so
        // 可客制化对应的属性值,来获取对应的so库
        // 此处主要的属性值对应的是ro.hardware.egl和ro.board.platform
        for (auto key : HAL_SUBNAME_KEY_PROPERTIES) {
            auto prop = base::GetProperty(key, "");
            if (prop.empty()) {
                continue;
            }
            hnd = attempt_to_load_system_driver(cnx, prop.c_str(), true);
            if (hnd) {
                break;
            } else if (strcmp(key, DRIVER_SUFFIX_PROPERTY) == 0) {
                failToLoadFromDriverSuffixProperty = true;
            }
        }
    }
    // 若上述依旧为加载到对应的so库,则查找对应目录下的so库
    // 此处先查找对应的/vendor/lib64/egl/libGLES.so库,若查找到这个库,则直接加载这个库,若未找到,则加载/system/lib64/egl/libGLES.so库,若找到此库,则退出,若依旧未找到,则进行下一步
    // 查找/vendor/lib64/egl/libEGL.so库,若找到这个库,则加载/vendor/lib64/egl/libEGL.so库,若未找到此库,则查找/system/lib64/egl/libEGL.so库
    // 若上述步骤中加载libEGL库完成,则继续加载对应的/vendor/lib64/egl/libGLESv1_CM.so或者/system/lib64/egl/libGLESv1_CM.so库,
    // 以及/vendor/lib64/egl/libGLESv2_CM.so或者/system/lib64/egl/libGLESv2.so库
    if (!hnd) {
        // Can't find graphics driver by appending system properties, now search for the exact name
        // without any suffix of the GLES userspace driver in both locations.
        // i.e.:
        //      libGLES.so, or:
        //      libEGL.so, libGLESv1_CM.so, libGLESv2.so
        hnd = attempt_to_load_system_driver(cnx, nullptr, true);
    }   
    // 若上述依旧未找到对应的库,则加载对应目录下的libGLES_android.so库
    if (!hnd && !failToLoadFromDriverSuffixProperty) {
        hnd = attempt_to_load_system_driver(cnx, nullptr, false);
    }

    if (!hnd) {
        // 若上述加载失败,则使用GpuStatsInfo::Api::API_GL
        android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL,
                                                            false, systemTime() - openTime);
    } else {
        // init_angle_backend will check if loaded driver is ANGLE or not,
        // will set cnx->useAngle appropriately.
        // Do this here so that we use ANGLE path when driver is ANGLE (e.g. loaded as native),
        // not just loading ANGLE as option.
        // 判断是否加载ANGLE,若加载,则初始化,否则不作任何操作
        init_angle_backend(hnd->dso[0], cnx);
    }

    // ......

    // 设置cnx->libEgl对象值为加载的/system/lib64/libEGL.so
    if (!cnx->libEgl) {
        cnx->libEgl = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so");
    }
    // 设置cnx->libGles1对象值为加载的/system/lib64/libGLESv1_CM.so
    if (!cnx->libGles1) {
        cnx->libGles1 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so");
    }
    // 设置cnx->libGles2对象值为加载的/system/lib64/libGLESv2.so
    if (!cnx->libGles2) {
        cnx->libGles2 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv2.so");
    }
    //若上述加载不成功,则设置默认API
    if (!cnx->libEgl || !cnx->libGles2 || !cnx->libGles1) {
        android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL,
                                                            false, systemTime() - openTime);
    }

    // ......

    android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL, true,
                                                        systemTime() - openTime);
    // 返回加载的指向driver_t对象的指针,在返回后将其赋值给cnx->dso指针对象
    return (void*)hnd;
}

参见上述的代码可知,在这个函数中主要初始化了cnx参数指向的对象的一些参数,由于在前面设置cnx指向的是gEGLImpl对象,因此此处实际会设置gEGLImpl对象的各项参数

初始化LayerLoader对象

egl.cpp
// 1. 初始化一个LayerLader对象
LayerLoader& layer_loader(LayerLoader::getInstance());
layer_loader.InitLayers(cnx);

void LayerLoader::InitLayers(egl_connection_t* cnx) {
    if (!layers_loaded_) return;

    if (initialized_) return;

    if (layer_setup_.empty()) {
        initialized_ = true;
        return;
    }

    // Include the driver in layer_functions
    layer_functions.resize(layer_setup_.size() + 1);

    // Walk through the initial lists and create layer_functions[0]
    int func_idx = 0;
    char const* const* entries;
    EGLFuncPointer* curr;

    // 如下的代码中会加载对应的函数API到对应的cnx指针的参数中
    // 如cnx->platform, cnx->egl, cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl
    entries = platform_names;
    curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
    SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
    ALOGV("InitLayers: func_idx after platform_names: %i", func_idx);

    entries = egl_names;
    curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl);
    SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
    ALOGV("InitLayers: func_idx after egl_names: %i", func_idx);

    entries = gl_names;
    curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl);
    SetupFuncMaps(layer_functions[0], entries, curr, func_idx);
    ALOGV("InitLayers: func_idx after gl_names: %i", func_idx);

    // Walk through each layer's entry points per API, starting just above the driver
    for (current_layer_ = 0; current_layer_ < layer_setup_.size(); current_layer_++) {
        // Init the layer with a key that points to layer just below it
        layer_init_[current_layer_](reinterpret_cast<void*>(&layer_functions[current_layer_]),
                                    reinterpret_cast<PFNEGLGETNEXTLAYERPROCADDRESSPROC>(
                                            getNextLayerProcAddress));

        // Check functions implemented by the platform
        func_idx = 0;
        entries = platform_names;
        curr = reinterpret_cast<EGLFuncPointer*>(&cnx->platform);
        // 加载对应的cnx->platform函数索引值
        LayerPlatformEntries(layer_setup_[current_layer_], curr, entries);

        // Populate next function table after layers have been applied
        SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);

        // EGL
        // 加载对应的cnx->egl函数索引值
        entries = egl_names;
        curr = reinterpret_cast<EGLFuncPointer*>(&cnx->egl);
        LayerDriverEntries(layer_setup_[current_layer_], curr, entries);

        // Populate next function table after layers have been applied
        SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);

        // GLES 2+
        // NOTE: We route calls to GLESv2 hooks, not GLESv1, so layering does not support GLES 1.x
        // If it were added in the future, a different layer initialization model would be needed,
        // that defers loading GLES entrypoints until after eglMakeCurrent, so two phase
        // initialization.
        // 加载对应的cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl函数索引值
        entries = gl_names;
        curr = reinterpret_cast<EGLFuncPointer*>(&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl);
        LayerDriverEntries(layer_setup_[current_layer_], curr, entries);

        // Populate next function table after layers have been applied
        SetupFuncMaps(layer_functions[current_layer_ + 1], entries, curr, func_idx);
    }

    // We only want to apply layers once
    initialized_ = true;
}

分析上述的函数的代码可以知道,此处初始化LayerLoader对象指针,为后续的函数处理做初始化准备

调用对应的函数eglGetDisplay

 // 获取显示设备的显示Display
egl_connection_t* const cnx = &gEGLImpl;
return cnx->platform.eglGetDisplay(display);

根据上述的代码分析,此处的cnx->platform在LayerLader.InitLayers处加载 事实上,在Android源码中,若无特定的客制化的库函数,此处会调用到libEGL.so库中的egl_platform_entries.cpp文件中的eglGetDisplay

总结

在eglGetDisplay函数中,会初始化egl和opengl的so库,加载其函数索引,并在后续的EGL和OPENGL函数中调用对应的函数索引