“This is the 15th day of my participation in the First Challenge 2022. For details: First Challenge 2022.”

In the previous analysis of the startup process of animation, there was a process of drawing animation pictures through EGL and OPENGL. This article will analyze the loading process of OPENGL and EGL library in the future

// 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;
Copy the code

In the boot animation startup process, through the above functions to initialize EGL and OPENGL, this chapter we first analyze the loading of OPENGL and EGL library functions

OPENGL and EGL library loading

First, let’s look at the implementation of eglGetDisplay

eglApi.cpp
EGLDisplay eglGetDisplay(EGLNativeDisplayType display) {
    ATRACE_CALL(a);// Call egl_init_drivers to initialize driver information
    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(a);// Get the Display Display of the Display device
    egl_connection_t* const cnx = &gEGLImpl;
    return cnx->platform.eglGetDisplay(display);
}
Copy the code

Initialize driver information for EGL

egl.cpp
EGLBoolean egl_init_drivers(a) {
    EGLBoolean res;
    / / synchronization locks
    pthread_mutex_lock(&sInitDriverMutex);
    // Initialize the EGL driver
    res = egl_init_drivers_locked(a);pthread_mutex_unlock(&sInitDriverMutex);
    return res;
}

static EGLBoolean egl_init_drivers_locked(a) {
    // 1. Whether the early init state has been set
    Egl is initialized with a static object, which is executed at run time
    if (sEarlyInitState) {
        // initialized by static ctor. should be set here.
        return EGL_FALSE;
    }

    // get our driver loader
    // 2. Initialize the Loader object
    Loader& loader(Loader::getInstance());

    // dynamically load our EGL implementation
    // 3. Load EGL library functions
    // Initialize a CNX pointer to the defined global gEGLImpl object
    // Note that the gEGLImpl object here gets all the functions in the corresponding platform_entries.in during initialization
    egl_connection_t* cnx = &gEGLImpl;
    // Initialize two hook object Pointers to two 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];
    // Load the EGL library and return the index of the EGL library
    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. Initialize LayerLoader
        LayerLoader& layer_loader(LayerLoader::getInstance());
        layer_loader.InitLayers(cnx);
    }

    return cnx->dso ? EGL_TRUE : EGL_FALSE;
}
Copy the code

Early Init state is set

The sEarlyInitState argument is a static object argument, so

static pthread_once_t once_control = PTHREAD_ONCE_INIT;
// pthread_once indicates that the county will run only once
static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);

static void early_egl_init(void)
{
    // Get the number of functions contained in gHooksNoContext
    int numHooks = sizeof(gHooksNoContext) / sizeof(EGLFuncPointer);
    // Set the functional index
    EGLFuncPointer *iter = reinterpret_cast<EGLFuncPointer*>(&gHooksNoContext);
    for (int hook = 0; hook < numHooks; ++hook) {
        *(iter++) = reinterpret_cast<EGLFuncPointer>(gl_no_context);
    }
    // Set the GL function template
    setGLHooksThreadSpecific(&gHooksNoContext);
}
Copy the code

From the above functions and parsing side the main thing is to load all the functions in the entries.in file

Initialization of the Loader object

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

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

Loader::Loader() : getProcAddress(nullptr)
{}
Copy the code

Initialize a Loader object

Load the EGL library function

// Create a pointer argument to point to a gEGLImpl object
// Note that the gEGLImpl object here gets all the functions in the corresponding platform_entries.in during initialization
egl_connection_t* cnx = &gEGLImpl;
// Initialize two eGL_connection_t object hook Pointers to gHooks contained objects
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];
// Finally call the Loader object's open function
cnx->dso = loader.open(cnx);

void* Loader::open(egl_connection_t* cnx)
{
    ATRACE_CALL(a);const nsecs_t openTime = systemTime(a);// If it has been loaded before, unload the previous loading process
    if (should_unload_system_driver(cnx)) {
        unload_system_driver(cnx);
    }

    // If a driver has been loaded, return the driver directly.
    // Check whether cnX-dSO has been loaded. If so, return to cnx-dSO
    if (cnx->dso) {
        return cnx->dso;
    }

    // Firstly, try to load ANGLE driver.
    // Step 1 load ANGLE
    // Load the libegl_angle. so, libglesv1_cm_angle. so, libglesv2_angle. so libraries
    // set HND ->dso[0] to libegl_angle. so
    // set HND ->dso[1] to the API loaded by libglesv1_cm_angle. so
    // set HND ->dso[2] to the API loaded by libglesv2_angle. so
    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;
    // If the load is not complete (the corresponding SO library is not found), enter the if statement
    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
        // We can customize the corresponding attribute values to get the corresponding so library
        // The main attribute values here correspond to ro.hardware.egl and 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; }}}// If the above is still loaded into the corresponding so library, find the corresponding directory so library
    / / here first to find the corresponding/vendor/lib64 / egl/libGLES so library, if the library to find, is to load the library directly, if not found, the loading/system/lib64 / egl/libGLES. So libraries, if found this library, exit, if still not found, then on to the next step
    / / find/vendor/lib64 / egl/libEGL. So libraries, if find the library, the loading/vendor/lib64 / egl/libEGL so libraries, if not found this library, then find/system/lib64 / egl/libEGL. So the library
    / / if the above steps in loading libEGL library is complete, continue to load the corresponding/vendor/lib64 / egl/libGLESv1_CM so or/system/lib64 / egl/libGLESv1_CM. So libraries,
    / / and/vendor/lib64 / egl/libGLESv2_CM. So or/system/lib64 / egl/libGLESv2. So libraries
    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);
    }   
    // If the libgles_android. so library is not found, load the libgles_android. so library
    if(! hnd && ! failToLoadFromDriverSuffixProperty) { hnd =attempt_to_load_system_driver(cnx, nullptr.false);
    }

    if(! hnd) {// If the above load fails, use 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.
        // Check whether ANGLE is loaded, if so, initialize, otherwise do nothing
        init_angle_backend(hnd->dso[0], cnx);
    }

    / /...

    // set the CNX ->libEgl object value to the loaded /system/lib64/ libegl.so
    if(! cnx->libEgl) { cnx->libEgl =load_wrapper(EGL_WRAPPER_DIR "/libEGL.so");
    }
    // set CNX ->libGles1 to the loaded /system/lib64/ libglesv1_cm.so
    if(! cnx->libGles1) { cnx->libGles1 =load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so");
    }
    / / Settings CNX - > libGles2 object values for loading/system/lib64 / libGLESv2. So
    if(! cnx->libGles2) { cnx->libGles2 =load_wrapper(EGL_WRAPPER_DIR "/libGLESv2.so");
    }
    // If the above loading fails, set the default 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);
    // Return the loaded pointer to driver_t and assign it to CNX -> dSO
    return (void*)hnd;
}
Copy the code

As you can see from the above code, this function mainly initializes some parameters of the object to which the CNX parameter points. Since CNX was set to point to the gEGLImpl object earlier, the parameters of the gEGLImpl object are actually set here

Initialize the LayerLoader object

egl.cpp
Initialize a LayerLader object
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;

    // The following code loads the corresponding function API into the parameter of the corresponding CNX pointer
    // see 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(a); 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);
        // load the corresponding CNX ->platform index
        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
        // load the corresponding CNX ->egl index value
        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.
        // load the corresponding CNX ->hooks[egl_connection_t::GLESv2_INDEX]->gl function index values
        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;
}
Copy the code

Analysis of the above function code shows that the LayerLoader object pointer is initialized in preparation for subsequent function processing

Call the corresponding function eglGetDisplay

 // Get the Display Display of the Display device
egl_connection_t* const cnx = &gEGLImpl;
return cnx->platform.eglGetDisplay(display);
Copy the code

According to the above code analysis, the CNX ->platform is loaded at layerlader.initlayers.In fact, in Android source code, if there is no specific custom library function, Here the eglGetDisplay in the egl_platform_entries.cpp file in the libegl.so library is called

conclusion

In eglGetDisplay, the SO libraries of EGL and OpengL are initialized, their functional indexes are loaded, and the corresponding functional indexes are called in subsequent egL and OpengL functions