AndroidOpenGLES分析与实践文档格式.docx
《AndroidOpenGLES分析与实践文档格式.docx》由会员分享,可在线阅读,更多相关《AndroidOpenGLES分析与实践文档格式.docx(19页珍藏版)》请在冰点文库上搜索。
![AndroidOpenGLES分析与实践文档格式.docx](https://file1.bingdoc.com/fileroot1/2023-5/3/83715d28-741d-4285-9194-47cba57c52cd/83715d28-741d-4285-9194-47cba57c52cd1.gif)
out/target/product/generic/system/lib/libGLESv1_CM.so
out/target/product/generic/system/lib/libEGL.so
3.3使用OpenGLES画图必经的步骤
1、获取Display,Display代表显示器。
函数原型:
EGLDisplayeglGetDisplay(NativeDisplayTypedisplay);
display参数是native系统的窗口显示ID值,一般为EGL_DEFAULT_DISPLAY。
该参数实际的意义是平台实现相关的,在X-Window下是XDisplayID,在MSWindows下是WindowDC。
2、初始化egl库。
EGLBooleaneglInitialize(EGLDisplaydpy,EGLint*major,EGLint*minor);
其中dpy应该是一个有效的EGLDisplay。
函数返回时,major和minor将被赋予当前EGL版本号。
3、选择一个合适的EGLConfigurationFrameBuffer,实际指的是FrameBuffer的参数
EGLBooleaneglChooseConfig(EGLDisplaydpy,constEGLint*attrib_list,EGLConfig*configs,EGLintconfig_size,
EGLint*num_config);
参数attrib_list:
指定了选择配置时需要参照的属性。
参数configs:
将返回一个按照attrib_list排序的平台有效的所有EGLframebuffer配置列表。
参数config_size:
指定了可以返回到configs的总配置个数。
参数num_config:
返回了实际匹配的配置总数。
4、创建一个可实际显示的EGLSurface,实际上就是一个FrameBuffer
EGLSurfaceeglCreateWindowSurface(EGLDisplaydpy,EGLConfigconfig,
NativeWindowTypewin,
constEGLint*attrib_list);
5、创建Context
EGLContexteglCreateContext(EGLDisplaydpy,EGLConfigconfig,
EGLContextshare_context,
6、绑定Display、Surface、Context
EGLBooleaneglMakeCurrent(EGLDisplaydpy,EGLSurfacedraw,
EGLSurfaceread,EGLContextctx);
3.4OpenGLES执行过程
运行android操作系统之后,输入logcat命令,然后执行gltest中的test-opengl-tritex,屏幕上打印了以下信息
D/libEGL(1962):
egl.cfgnotfound,usingdefaultconfig
loaded/system/lib/egl/libGLES_android.so
可以看出,在执行OpenGL调用的过程中,会自动加载libGLES_android.so动态链接库。
后面将会通过分析和修改源码的方式,了解OpenGLES系统的调用过程。
通过3.3中的说明,我们在tritex测试程序中插入一些调试信息,查看OpenGLES的调用过程。
在调用eglGetDisplay之前会执行early_egl_init函数,这是一个静态的函数。
在eglGetDisplay中会去初始化驱动,最终调用到egl_init_drivers_locked函数中。
这个函数的主要内容如下
EGLBooleanegl_init_drivers_locked()
{
if(sEarlyInitState){
//initializedbystaticctor.shouldbesethere.
returnEGL_FALSE;
}
//getourdriverloader
Loader&
loader(Loader:
:
getInstance());
//dynamicallyloadallourEGLimplementationsforalldisplays
//andretrievethecorrespondingEGLDisplay
//ifthatfails,don'
tusethisdriver.
//TODO:
currentlyweonlydealwithEGL_DEFAULT_DISPLAY
egl_connection_t*cnx;
egl_display_t*d=&
gDisplay[0];
cnx=&
gEGLImpl[IMPL_SOFTWARE];
if(cnx->
dso==0){
cnx->
hooks[GLESv1_INDEX]=&
gHooks[GLESv1_INDEX][IMPL_SOFTWARE];
hooks[GLESv2_INDEX]=&
gHooks[GLESv2_INDEX][IMPL_SOFTWARE];
dso=loader.open(EGL_DEFAULT_DISPLAY,0,cnx);
dso){
EGLDisplaydpy=cnx->
egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
LOGE_IF(dpy==EGL_NO_DISPLAY,"
NoEGLDisplayforsoftwareEGL!
"
);
d->
disp[IMPL_SOFTWARE].dpy=dpy;
if(dpy==EGL_NO_DISPLAY){
loader.close(cnx->
dso);
dso=NULL;
gEGLImpl[IMPL_HARDWARE];
charvalue[PROPERTY_VALUE_MAX];
property_get("
debug.egl.hw"
value,"
1"
if(atoi(value)!
=0){
gHooks[GLESv1_INDEX][IMPL_HARDWARE];
gHooks[GLESv2_INDEX][IMPL_HARDWARE];
dso=loader.open(EGL_DEFAULT_DISPLAY,1,cnx);
NoEGLDisplayforhardwareEGL!
disp[IMPL_HARDWARE].dpy=dpy;
}else{
LOGD("
3Dhardwareaccelerationisdisabled"
if(!
gEGLImpl[IMPL_SOFTWARE].dso&
&
!
gEGLImpl[IMPL_HARDWARE].dso){
returnEGL_TRUE;
}
由此代码可以看出,egl_init_drivers_locked函数主要的工作就是填充gEGLImp数组变量,这个变量是egl_connection_t类型。
还有一个工作就是填充gDisplay数组(只有一个元素)的disp[IMPL_HARDWARE].dpy以及disp[IMPLSOFTWAREWARE].dpy,填充的来源来自gEGLImpl【softorhard】.egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
在Loader.cpp中的Loader:
open中会加载对应的硬件和软件加速的驱动(动态链接库)。
软件的对应的是/system/lib/egl/libEGL_android.so,没有默认的硬件so,因此在硬件加速时,返回值hnd会指向NULL,在需要硬件加速时这个动态链接库需要进行实现。
LoadDriver函数会根据其第三个参数,决定加载egl/gles,glesv1_cm,glesv2驱动。
。
加载几个动态链接库的过程如下图
由我以上图表可以看出,加载驱动的时候,会尝试先从libGLES_android.so中加载EGL、GLESV1_CM、GLESV2三个部分的函数,如
果加载失败,则会尝试从libEGL_android.so,libGLESV1_cm.so,libGLESV2.so三个动态库中对应的函数。
在这部分代码中,我们可以看到一个非常重要的结构体,egl_connection_t,
structegl_connection_t
void*dso;
gl_hooks_t*hooks[2];
EGLintmajor;
EGLintminor;
egl_tegl;
};
到处都有他的身影,对这几个变量进行一下解释。
structsoinfo
constcharname[SOINFO_NAME_LEN];
Elf32_Phdr*phdr;
intphnum;
unsignedentry;
unsignedbase;
unsignedsize;
//buddy-allocatorindex,negativeforprelinkedlibraries
intba_index;
unsigned*dynamic;
unsignedwrprotect_start;
unsignedwrprotect_end;
soinfo*next;
unsignedflags;
constchar*strtab;
Elf32_Sym*symtab;
unsignednbucket;
unsignednchain;
unsigned*bucket;
unsigned*chain;
unsigned*plt_got;
Elf32_Rel*plt_rel;
unsignedplt_rel_count;
Elf32_Rel*rel;
unsignedrel_count;
unsigned*preinit_array;
unsignedpreinit_array_count;
unsigned*init_array;
unsignedinit_array_count;
unsigned*fini_array;
unsignedfini_array_count;
void(*init_func)(void);
void(*fini_func)(void);
#ifdefANDROID_ARM_LINKER
/*ARMEABIsectionusedforstackunwinding.*/
unsigned*ARM_exidx;
unsignedARM_exidx_count;
#endif
unsignedrefcount;
structlink_maplinkmap;
看一下load_driver中到底做了什么手脚。
1.首先调用dlopen打开动态链接库,返回值是void*,这个void*指向的是什么内容呢?
追踪到bionic/linker/Dlfcn.c中。
其中调用了find_library函数,这个函数是一个奇怪的函数,因为它虽然叫做find_library,在其实现中,不但在系统的so链表中去查找指定的文件名的动态链接库信息,而且对其动态链接库进行加载并返回。
至此我们明白了,这个void*指向的是一个soinfo类型的结构体
这是mandlopen的说明。
一个标准的linux函数。
Thefunctiondlopen()loadsthedynamiclibraryfilenamedbythenull-
terminatedstringfilenameandreturnsanopaque"
handle"
forthe
dynamiclibrary.IffilenameisNULL,thenthereturnedhandleisfor
themainprogram.Iffilenamecontainsaslash("
/"
),thenitis
interpretedasa(relativeorabsolute)pathname.
2.由上一步的分析,我们知道了egl_connection_t的第一个变量dso,是指向的一个soinfo结构体(discover/decompressshared
object的缩写?
?
)
Printf("
HAHALetmeprintthesoinfomation\n"
Printf("
name=%s:
phdr=%x:
entry=%x:
base=%x:
size=%x\n"
soi->
name,soi->
phdr,soi->
entry,soi->
base,soi->
size);
这是上一条语句打印的一些信息。
name=libGLES_android.so:
phdr=acc80034:
entry=0:
base=acc80000:
size=1c000
3.dlsym可以根据dlopen的返回值,查找第二个参数指定的函数名的地址并返回
Thefunctiondlsym()takesa"
ofadynamiclibraryreturnedby
dlopen()andthenull-terminatedsymbolname,returningtheaddress
wherethatsymbolisloadedintomemory.Ifthesymbolisnotfound,
inthespecifiedlibraryoranyofthelibrariesthatwereautomati-
callyloadedbydlopen()whenthatlibrarywasloaded,dlsym()returns
NULL.(Thesearchperformedbydlsym()isbreadthfirstthroughthe
dependencytreeoftheselibraries.)Sincethevalueofthesymbol
couldactuallybeNULL(sothataNULLreturnfromdlsym()neednot
indicateanerror),thecorrectwaytotestforanerroristocall
dlerror()toclearanyolderrorconditions,thencalldlsym(),and
thencalldlerror()again,savingitsreturnvalueintoavariable,and
checkwhetherthissavedvalueisnotNULL.
getProcAddress=(getProcAddressType)dlsym(dso,"
eglGetProcAddress"
eglGetProcAddress'
slocationis%x\n"
getProcAddress);
打印信息如下,可以和刚才的打印信息比较一下。
我们确实找到了一个函数。
slocationisacc930b1
curr=%x,it'
saddressis%x\n"
curr,f);
打印如下
*api=eglGetDisplay
curr=ac708a60,it'
saddressisacc931a5
*api=eglInitialize
curr=ac708a64,it'
saddressisacc93c9d
*api=eglTerminate
curr=ac708a68,it'
saddressisacc93cdd
*api=eglGetConfigs
curr=ac708a6c,it'
saddressisacc93d41
*api=eglChooseConfig
curr=ac708a70,it'
saddressisacc9472d
*api=eglGetConfigAttrib
curr=ac708a74,it'
saddressisacc94325
*api=eglCreateWindowSurface
curr=ac708a78,it'
saddressisacc94689
*api=eglCreatePixmapSurface
curr=ac708a7c,it'
saddressisacc945d5
*api=eglCreatePbufferSurface
curr=ac708a80,it'
saddressisacc9451d
*api=eglDestroySurface
curr=ac708a84,it'
saddressisacc93a1d
*api=eglQuerySurface
curr=ac708a88,it'
saddressisacc94341
*api=eglCreateContext
curr=ac708a8c,it'
saddressisacc9415d
*api=eglDestroyContext
curr=ac708a90,it'
saddressisacc93d09
*api=eglMakeCurrent
curr=ac708a94,it'
saddressisacc93a6d
*api=eglGetCurrentContext
curr=ac708a98,it'
saddressisacc93055
*api=eglGetCurrentSurface
curr=ac708a