上一篇文章我们对 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。