「这是我参与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函数中调用对应的函数索引