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。

相关推荐
水瓶丫头站住8 小时前
安卓APP如何适配不同的手机分辨率
android·智能手机
xvch8 小时前
Kotlin 2.1.0 入门教程(五)
android·kotlin
xvch12 小时前
Kotlin 2.1.0 入门教程(七)
android·kotlin
望风的懒蜗牛12 小时前
编译Android平台使用的FFmpeg库
android
浩宇软件开发13 小时前
Android开发,待办事项提醒App的设计与实现(个人中心页)
android·android studio·android开发
ac-er888813 小时前
Yii框架中的多语言支持:如何实现国际化
android·开发语言·php
苏金标14 小时前
The maximum compatible Gradle JVM version is 17.
android
zhangphil14 小时前
Android BitmapShader简洁实现马赛克,Kotlin(一)
android·kotlin
iofomo19 小时前
Android平台从上到下,无需ROOT/解锁/刷机,应用级拦截框架的最后一环,SVC系统调用拦截。
android
我叫特踏实19 小时前
SensorManager开发参考
android·sensormanager