从上面的例子中,我们发现通过egl_函数调用后,直接就能调用gl_函数去画图了,为何?难道在egl*函数调用过程中,已经将opengl相关实现的库加载了吗?
涉及的库
首先,由于涉及的库较多,先列出来(高通平台,原生的只有前4个),
//算是android中的egl库,用来加载具体的实现
system\lib\libEGL.so
//opengl具体实现的wrapper
system\lib\libGLESv1_CM.so
system\lib\libGLESv2.so
//opengl软件实现,即agl
system\lib\egl\libGLES_android.so
//egl的实现
system\vendor\lib\egl\libEGL_adreno.so
//opengl硬件实现
system\vendor\lib\egl\libGLESv1_CM_adreno.so
system\vendor\lib\egl\libGLESv2_adreno.so
库的加载
继续以bootanimation为例,在bootanimation的makefile中,使用了libEGL 库,
// frameworks\base\cmds\bootanimation\Android.mk
LOCAL_SHARED_LIBRARIES := \
libcutils \
liblog \
libandroidfw \
libutils \
libbinder \
libui \
libskia \
libEGL \
libGLESv1_CM \
libgui
LOCAL_MODULE:= bootanimation
在frameworks\native\opengl\libs\Android.mk
中,定义了libEGL.so,makefile中并未指定特殊的存放path,所以最终生成的库保存在system\lib\libEGL.so
,这个库是用来加载具体的egl和opengl实现的,起到桥梁作用,需要和EGL实现的库区分开。
//frameworks\native\opengl\libs\Android.mk
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
EGL/egl_tls.cpp \
EGL/egl_cache.cpp \
EGL/egl_display.cpp \
EGL/egl_object.cpp \
EGL/egl.cpp \
EGL/eglApi.cpp \
EGL/trace.cpp \
EGL/getProcAddress.cpp.arm \
EGL/Loader.cpp \
LOCAL_MODULE:= libEGL
include $(BUILD_SHARED_LIBRARY)
接下来看eglGetDisplay(),这个函数就是在system\lib\libEGL.so
中实现的。
EGLDisplay eglGetDisplay(EGLNativeDisplayType display)
{
clearError();
uint32_t index = uint32_t(display);
if (index >= NUM_DISPLAYS) {
return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
}
//从函数名上看是加载相关实现
if (egl_init_drivers() == EGL_FALSE) {
return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
}
EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display);
return dpy;
}
EGLBoolean egl_init_drivers() {
EGLBoolean res;
pthread_mutex_lock(&sInitDriverMutex);
res = egl_init_drivers_locked();
pthread_mutex_unlock(&sInitDriverMutex);
return res;
}
static EGLBoolean egl_init_drivers_locked() {
if (sEarlyInitState) {
// initialized by static ctor. should be set here.
return EGL_FALSE;
}
// get our driver loader
Loader& loader(Loader::getInstance());
// dynamically load our EGL implementation
egl_connection_t* cnx = &gEGLImpl;
if (cnx->dso == 0) {
//gHooks保存的是gl开头的函数实现
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);
}
return cnx->dso ? EGL_TRUE : EGL_FALSE;
egl_connection_t gEGLImpl;
gl_hooks_t gHooks[2];
struct egl_connection_t {
enum {
GLESv1_INDEX = 0,
GLESv2_INDEX = 1
};
inline egl_connection_t() : dso(0) { }
void * dso;
gl_hooks_t * hooks[2];
EGLint major;
EGLint minor;
//保存egl 实现
egl_t egl;
void* libGles1;
void* libGles2;
};
// 还能这么用,以前真是没用过
// 将EGL/egl_entries.in文件include进来,都是些egl entry
struct egl_t {
#include “EGL/egl_entries.in”
};
//将entries.in文件include进来
struct gl_hooks_t {
struct gl_t {
#include “entries.in”
} gl;
struct gl_ext_t {
__eglMustCastToProperFunctionPointerType extensions[MAX_NUMBER_OF_GL_EXTENSIONS];
} ext;
};
EGL/http://egl_entries.in文件中都是如下的一条一条entry,以egl开头,都是函数声明,
EGL_ENTRY(EGLDisplay, eglGetDisplay, NativeDisplayType)
http://entries.in文件中,都是以gl开头的entry,都是函数声明,
GL_ENTRY(void, glActiveShaderProgramEXT, GLuint pipeline, GLuint program)
从下面的宏可以看出,转换完成后,上面的entry都是返回值,函数名,函数参数的型式。
#undef GL_ENTRY
#undef EGL_ENTRY
#define GL_ENTRY(_r, _api, …) _r (*_api)(VA_ARGS);
#define EGL_ENTRY(_r, _api, …) _r (*_api)(VA_ARGS);
所以,
struct egl_t {
EGLDisplay eglGetDisplay(NativeDisplayType );
…
};
struct gl_t {
void glActiveShaderProgramEXT(GLuint pipeline, GLuint program);
} gl;
下面看cnx->dso = loader.open(cnx),
void* Loader::open(egl_connection_t* cnx)
{
void* dso;
driver_t* hnd = 0;
//首先,kind为GLES,mask为EGL,GLESv1_CM ,GLESv2
//在/vendor/lib/egl和/system/lib/egl下寻找libGLES.so或者libGLES_*.so
//这里只能找到system\lib\egl\libGLES_android.so,opengl的软件实现
//但是代码中直接continue了,不使用软件实现
//所以dso返回空
dso = load_driver(“GLES”, cnx, EGL | GLESv1_CM | GLESv2);
if (dso) {
hnd = new driver_t(dso);
} else {
// Always load EGL first
// kind为EGL,mask为EGL
// 可以找到system\vendor\lib\egl\libEGL_adreno.so
// 然后填充egl相关函数
dso = load_driver(“EGL”, cnx, EGL);
if (dso) {
hnd = new driver_t(dso);
//找到system\vendor\lib\egl\libGLESv1_CM_adreno.so库,
//填充gl相关函数
hnd->set( load_driver(“GLESv1_CM”, cnx, GLESv1_CM), GLESv1_CM );
//找到system\vendor\lib\egl\libGLESv2_adreno.so库,
//填充gl相关函数
hnd->set( load_driver(“GLESv2”, cnx, GLESv2), GLESv2 );
}
}
LOG_ALWAYS_FATAL_IF(!hnd, “couldn’t find an OpenGL ES implementation”);
//上面是opengle的实现,下面的cnx->libGles2和libGles1是gles实现的wrapper
// 只是打开/system/lib/libGLESv2.so和/system/lib/libGLESv1_CM.so这两个库,将地址返回给cnx
cnx->libGles2 = load_wrapper(“/system/lib/libGLESv2.so”);
cnx->libGles1 = load_wrapper(“/system/lib/libGLESv1_CM.so”);
LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1,
“couldn’t load system OpenGL ES wrapper libraries”);
return (void*)hnd;
}
void Loader::load_driver(const char kind,
egl_connection_t* cnx, uint32_t mask)
{
class MatchFile {
public:
static String8 find(const char* kind) {
String8 result;
String8 pattern;
pattern.appendFormat(“lib%s”, kind);
//在下面路径中寻找
const char* const searchPaths[] = {
“/vendor/lib/egl”,
“/system/lib/egl”
};
// first, we search for the exact name of the GLES userspace
// driver in both locations.
// i.e.:
// libGLES.so, or:
// libEGL.so, libGLESv1_CM.so, libGLESv2.so
for (size_t i=0 ; i<NELEM(searchPaths) ; i++) {
if (find(result, pattern, searchPaths[i], true)) {
return result;
}
}
// for compatibility with the old “egl.cfg” naming convention
// we look for files that match:
// libGLES_*.so, or:
// libEGL_.so, libGLESv1_CM_.so, libGLESv2_*.so
pattern.append(“_”);
for (size_t i=0 ; i<NELEM(searchPaths) ; i++) {
if (find(result, pattern, searchPaths[i], false)) {
return result;
}
}
// we didn’t find the driver. gah.
result.clear();
return result;
}
private:
static bool find(String8& result,
const String8& pattern, const char* const search, bool exact) {
// in the emulator case, we just return the hardcoded name
// of the software renderer.
if (checkGlesEmulationStatus() == 0) {
ALOGD("Emulator without GPU support detected. "
“Fallback to software renderer.”);
result.setTo(“/system/lib/egl/libGLES_android.so”);
return true;
}
if (exact) {
String8 absolutePath;
absolutePath.appendFormat(“%s/%s.so”, search, pattern.string());
if (!access(absolutePath.string(), R_OK)) {
result = absolutePath;
return true;
}
return false;
}
DIR* d = opendir(search);
if (d != NULL) {
struct dirent cur;
struct dirent* e;
while (readdir_r(d, &cur, &e) == 0 && e) {
if (e->d_type == DT_DIR) {
continue;
}
//system\lib\egl\libGLES_android.so直接跳过软件实现
//太慢,但是在某个项目中没有gpu,也没有overlay,
//只能用agl去合成layer。
if (!strcmp(e->d_name, “libGLES_android.so”)) {
// always skip the software renderer
continue;
}
if (strstr(e->d_name, pattern.string()) == e->d_name) {
if (!strcmp(e->d_name + strlen(e->d_name) - 3, “.so”)) {
result.clear();
result.appendFormat(“%s/%s”, search, e->d_name);
closedir(d);
return true;
}
}
}
closedir(d);
}
return false;
}
};
String8 absolutePath = MatchFile::find(kind);
if (absolutePath.isEmpty()) {
// this happens often, we don’t want to log an error
return 0;
}
const char* const driver_absolute_path = absolutePath.string();
//打开找到的库
void* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);
if (dso == 0) {
const char* err = dlerror();
ALOGE(“load_driver(%s): %s”, driver_absolute_path, err?err:“unknown”);
return 0;
}
ALOGD(“loaded %s”, driver_absolute_path);
//mask为EGL
if (mask & EGL) {
//首先在库中找到eglGetProcAddress这个函数
getProcAddress = (getProcAddressType)dlsym(dso, “eglGetProcAddress”);
ALOGE_IF(!getProcAddress,
“can’t find eglGetProcAddress() in %s”, driver_absolute_path);
egl_t* egl = &cnx->egl;
__eglMustCastToProperFunctionPointerType* curr =
(__eglMustCastToProperFunctionPointerType*)egl;
//egl_names就是egl的entry
char const * const * api = egl_names;
while (*api) {
char const * name = *api;
//寻找entry中的函数
__eglMustCastToProperFunctionPointerType f =
(__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
//如果未找到,用eglGetProcAddress再去找一下
if (f == NULL) {
// couldn’t find the entry-point, use eglGetProcAddress()
f = getProcAddress(name);
//依然未找到,则置为0
if (f == NULL) {
f = (__eglMustCastToProperFunctionPointerType)0;
}
}
*curr++ = f;
api++;
}
}
if (mask & GLESv1_CM) {
init_api(dso, gl_names,
(__eglMustCastToProperFunctionPointerType*)
&cnx->hooks[egl_connection_t::GLESv1_INDEX]->gl,
getProcAddress);
}
if (mask & GLESv2) {
init_api(dso, gl_names,
(__eglMustCastToProperFunctionPointerType*)
&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl,
getProcAddress);
}
return dso;
}
学习分享
在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了
很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘
如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。
2021最新上万页的大厂面试真题
七大模块学习资料:如NDK模块开发、Android框架体系架构…
只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。
这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。
由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
mask & GLESv2) {
init_api(dso, gl_names,
(__eglMustCastToProperFunctionPointerType*)
&cnx->hooks[egl_connection_t::GLESv2_INDEX]->gl,
getProcAddress);
}
return dso;
}
学习分享
在当下这个信息共享的时代,很多资源都可以在网络上找到,只取决于你愿不愿意找或是找的方法对不对了
很多朋友不是没有资料,大多都是有几十上百个G,但是杂乱无章,不知道怎么看从哪看起,甚至是看后就忘
如果大家觉得自己在网上找的资料非常杂乱、不成体系的话,我也分享一套给大家,比较系统,我平常自己也会经常研读。
2021最新上万页的大厂面试真题
[外链图片转存中…(img-rKrDRPu3-1715863022816)]
七大模块学习资料:如NDK模块开发、Android框架体系架构…
[外链图片转存中…(img-g8i0J3A3-1715863022818)]
只有系统,有方向的学习,才能在段时间内迅速提高自己的技术。
这份体系学习笔记,适应人群:
**第一,**学习知识比较碎片化,没有合理的学习路线与进阶方向。
**第二,**开发几年,不知道如何进阶更进一步,比较迷茫。
**第三,**到了合适的年纪,后续不知道该如何发展,转型管理,还是加强技术研究。
由于文章内容比较多,篇幅不允许,部分未展示内容以截图方式展示 。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!