Android SurfaceFlinger——OpenGL ES初始化(十三)

上一篇文章我们对 OpenGL ES 相关知识有了一定的了解,并知道在使用 OpenGL ES 是需要先通过 eglGetDisplay() 方法获取 EGLDisplay 默认主屏幕句柄。这里就分析一下 eglGetDisplay() 中的 egl_init_drivers() 初始化 OpenGL ES 的对应流程。

一、OpenGL ES初始化

1、egl.cpp

源码位置:/frameworks/native/opengl/libs/EGL/egl.cpp

egl_init_drivers

cpp 复制代码
EGLBoolean egl_init_drivers() {
    EGLBoolean res;
    pthread_mutex_lock(&sInitDriverMutex);
    res = egl_init_drivers_locked();
    pthread_mutex_unlock(&sInitDriverMutex);
    return res;
}

这里调用 egl_init_drivers_locked() 函数来实现驱动程序的初始化工作。

egl_init_drivers_locked

cpp 复制代码
static EGLBoolean egl_init_drivers_locked() {
    if (sEarlyInitState) {
        // 已经初始化过
        return EGL_FALSE;
    }

    // 获取驱动加载程序
    Loader& loader(Loader::getInstance());

    // 动态加载EGL实现
    egl_connection_t* cnx = &gEGLImpl;
    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);

    // 初始化Layer层
    if (cnx->dso) {
        // 层可以在加载驱动程序很久之后启用。它们只初始化一次。
        LayerLoader& layer_loader(LayerLoader::getInstance());
        layer_loader.InitLayers(cnx);
    }

    return cnx->dso ? EGL_TRUE : EGL_FALSE;
}

函数实现了 EGL 驱动的动态加载和层的初始化,是 EGL 启动过程中非常关键的一部分。其中 egl_connection_t 是一个结构体,用于存储 EGL 实现的相关信息,一开始没有初始化,因此此时里面所有的数据都是 0。这里将会调用一个静态变量 Loader 的 open 方法,使得 EGL 能够根据运行时环境加载合适的实现。

3、Loader.cpp

源码位置:/frameworks/native/opengl/libs/EGL/Loader.cpp

cpp 复制代码
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 (cnx->dso) {
        return cnx->dso;
    }

    // 加载ANGLE驱动(一个高性能图形转换层,允许OpenGL ES在多种后端上运行)
    driver_t* hnd = attempt_to_load_angle(cnx);
    if (!hnd) {
        // 如果ANGLE加载失败,尝试从特定的驱动APK中加载更新的驱动
        hnd = attempt_to_load_updated_driver(cnx);
    }

    // 尝试加载系统驱动(带后缀)
    bool failToLoadFromDriverSuffixProperty = false;
    if (!hnd) {
        ......
        // 遍历系统属性中定义的后缀列表,尝试加载带有这些后缀的系统驱动库。
        // 这包括尝试加载EGL和OpenGL ES的各种版本。
        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 (!hnd) {
        hnd = attempt_to_load_system_driver(cnx, nullptr, true);
    }

    if (!hnd && !failToLoadFromDriverSuffixProperty) {
        hnd = attempt_to_load_system_driver(cnx, nullptr, false);
    }

    if (!hnd) {
        android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL, false, systemTime() - openTime);
    } else {
        // 初始化ANGLE后端
        init_angle_backend(hnd->dso[2], cnx);
    }

    // 致命错误检查:如果最终仍未找到图形驱动,通过LOG_ALWAYS_FATAL抛出致命错误
    LOG_ALWAYS_FATAL_IF(!hnd, "couldn't find an OpenGL ES implementation, make sure you set %s or %s",
                        HAL_SUBNAME_KEY_PROPERTIES[0], HAL_SUBNAME_KEY_PROPERTIES[1]);

    // 加载EGL和OpenGL ES的Wrapper库
    if (!cnx->libEgl) {
        cnx->libEgl = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so");
    }
    if (!cnx->libGles1) {
        cnx->libGles1 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so");
    }
    if (!cnx->libGles2) {
        cnx->libGles2 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv2.so");
    }

    if (!cnx->libEgl || !cnx->libGles2 || !cnx->libGles1) {
        android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL, false, systemTime() - openTime);
    }

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

    // 报告驱动加载状态和耗时
    android::GraphicsEnv::getInstance().setDriverLoaded(android::GpuStatsInfo::Api::API_GL, true, systemTime() - openTime);

    return (void*)hnd;
}

该函数展示了复杂的驱动加载逻辑,旨在确保系统能够从多个可能的来源加载到合适的图形驱动程序,以支持 OpenGL ES 的运行。如果所有尝试都失败,将记录严重的错误日志,因为没有图形驱动意味着图形渲染将无法进行。

二、驱动加载

1、库加载

attempt_to_load_system_driver

cpp 复制代码
Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix,
                                                        const bool exact) {
    ATRACE_CALL();
    android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL);
    driver_t* hnd = nullptr;
    // 尝试加载GLES驱动
    void* dso = load_system_driver("GLES", suffix, exact);
    if (dso) {
        // 初始化EGL、GLESv1_CM和GLESv2的API
        initialize_api(dso, cnx, EGL | GLESv1_CM | GLESv2);
        hnd = new driver_t(dso);
        return hnd;
    }
    // GLES直接加载失败,单独加载EGL、GLESv1_CM和GLESv2的库
    // 注意每次加载成功后都会调用initialize_api来配置相应的API
    dso = load_system_driver("EGL", suffix, exact);
    if (dso) {
        initialize_api(dso, cnx, EGL);
        hnd = new driver_t(dso);

        dso = load_system_driver("GLESv1_CM", suffix, exact);
        initialize_api(dso, cnx, GLESv1_CM);
        hnd->set(dso, GLESv1_CM);

        dso = load_system_driver("GLESv2", suffix, exact);
        initialize_api(dso, cnx, GLESv2);
        hnd->set(dso, GLESv2);
    }
    return hnd;
}

该函数主要目的是尝试从系统中加载 OpenGL ES 和 EGL 的驱动程序,最后把句柄赋值给 driver_t。

load_system_driver

cpp 复制代码
static void* load_system_driver(const char* kind, const char* suffix, const bool exact) {
    ATRACE_CALL();
    // 辅助类,在指定的搜索路径中查找匹配的库文件
    class MatchFile {
    public:
        // 核心方法,递归查找匹配的.so文件
        static std::string find(const char* libraryName, const bool exact) {
            const char* const searchPaths[] = {
// 区分32位和64位
#if defined(__LP64__)
                    "/vendor/lib64/egl",
                    "/system/lib64/egl"
#else
                    "/vendor/lib/egl",
                    "/system/lib/egl"
#endif
            };

            for (auto dir : searchPaths) {
                std::string absolutePath;
                // 调用private中的对应方法
                if (find(absolutePath, libraryName, dir, exact)) {
                    return absolutePath;
                }
            }

            // 没有找到驱动程序
            return std::string();
        }
    private:
        static bool find(std::string& result, const std::string& pattern, const char* const search, bool exact) {
            ......
        }
    };

    // 构建库名
    std::string libraryName = std::string("lib") + kind;
    if (suffix) {
        libraryName += std::string("_") + suffix;
    } else if (!exact) {
        // 已弃用
        libraryName += std::string("_");
    }
    // 查找库文件
    std::string absolutePath = MatchFile::find(libraryName.c_str(), exact);
    ......
    const char* const driver_absolute_path = absolutePath.c_str();

    // 加载库
    void* dso = do_android_load_sphal_library(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);
    ......
    return dso;
}

这里主要是从系统目录中查找并加载指定的图形驱动库(如 EGL 或 OpenGL ES)。接下来看一下驱动的初始化函数 initialize_api()。

2、驱动初始化

cpp 复制代码
void Loader::initialize_api(void* dso, egl_connection_t* cnx, uint32_t mask) {
    if (mask & EGL) {
        getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress");

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

        egl_t* egl = &cnx->egl;
        __eglMustCastToProperFunctionPointerType* curr = (__eglMustCastToProperFunctionPointerType*)egl;
        char const * const * api = egl_names;
        while (*api) {
            char const * name = *api;
            __eglMustCastToProperFunctionPointerType f =
                (__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
            if (f == nullptr) {
                // 找不到入口点,使用eglGetProcAddress()
                f = getProcAddress(name);
                if (f == nullptr) {
                    f = (__eglMustCastToProperFunctionPointerType)nullptr;
                }
            }
            *curr++ = f;
            api++;
        }
    }

    if (mask & GLESv1_CM) {
        init_api(dso, gl_names_1, gl_names,
            (__eglMustCastToProperFunctionPointerType*)
                &cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl,
            getProcAddress);
    }

    if (mask & GLESv2) {
        init_api(dso, gl_names, nullptr,
            (__eglMustCastToProperFunctionPointerType*)
                &cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl,
            getProcAddress);
    }
}

这里就要给 egl_t 初始化。此时有一个 egl_names 变量,这个变量其实是 egl_entries.in 字符串。它实际上指向的是如下地址:

/frameworks/native/opengl/libs/EGL/egl_entries.in

里面包含着所有当前 OpenGL es 暴露出来的接口,接着以此为参数调用 eglGetProcAddress获取所有函数的指针,并且赋值给 curr 指针数组中。接着 init_api 则是处理 OpenGL es 版本 1 和 2 之间差异方法。

此时 egl_connection_t 中的 EGL 持有了所有的方法指针,以后只需要使用 EGL 这个结构体就能调用 so 库的逻辑了。注意这里面只包含操作 OpenGL ES 中操作像素的 API,关于着色器 API 编译链接并不在这里实现。这种软件机制我们也叫它 pixelflinger。

相关推荐
带电的小王2 小时前
WhisperKit: Android 端测试 Whisper -- Android手机(Qualcomm GPU)部署音频大模型
android·智能手机·whisper·qualcomm
梦想平凡2 小时前
PHP 微信棋牌开发全解析:高级教程
android·数据库·oracle
元争栈道2 小时前
webview和H5来实现的android短视频(短剧)音视频播放依赖控件
android·音视频
阿甘知识库3 小时前
宝塔面板跨服务器数据同步教程:双机备份零停机
android·运维·服务器·备份·同步·宝塔面板·建站
元争栈道4 小时前
webview+H5来实现的android短视频(短剧)音视频播放依赖控件资源
android·音视频
MuYe4 小时前
Android Hook - 动态加载so库
android
居居飒5 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
Henry_He7 小时前
桌面列表小部件不能点击的问题分析
android
工程师老罗8 小时前
Android笔试面试题AI答之Android基础(1)
android
qq_397562319 小时前
android studio更改应用图片,和应用名字。
android·ide·android studio