AndroidOpenGLES分析与实践.docx

上传人:b****5 文档编号:12026892 上传时间:2023-04-16 格式:DOCX 页数:19 大小:100.27KB
下载 相关 举报
AndroidOpenGLES分析与实践.docx_第1页
第1页 / 共19页
AndroidOpenGLES分析与实践.docx_第2页
第2页 / 共19页
AndroidOpenGLES分析与实践.docx_第3页
第3页 / 共19页
AndroidOpenGLES分析与实践.docx_第4页
第4页 / 共19页
AndroidOpenGLES分析与实践.docx_第5页
第5页 / 共19页
点击查看更多>>
下载资源
资源描述

AndroidOpenGLES分析与实践.docx

《AndroidOpenGLES分析与实践.docx》由会员分享,可在线阅读,更多相关《AndroidOpenGLES分析与实践.docx(19页珍藏版)》请在冰豆网上搜索。

AndroidOpenGLES分析与实践.docx

AndroidOpenGLES分析与实践

AndroidOpenGLES分析与实践

作者:

雪夜刀手

1.OpenGLES简介

Android3D引擎采用的是OpenGLES。

OpenGLES是一套为手持和嵌入式系统设计的3D引擎API,由Khronos公司维护。

在PC领域,一直有两种标准的3DAPI进行竞争,OpenGL和DirectX。

一般主流的游戏和显卡都支持这两种渲染方式,DirectX在Windows平台上有很大的优势,但是OpenGL具有更好的跨平台性。

由于嵌入式系统和PC相比,一般说来,CPU、内存等都比PC差很多,而且对能耗有着特殊的要求,许多嵌入式设备并没有浮点运算协处理器,针对嵌入式系统的以上特点,Khronos对标准的OpenGL系统进行了维护和改动,以期望满足嵌入式设备对3D绘图的要求。

2.AndroidOpenGLES简介

Android系统使用OpenGL的标准接口来支持3D图形功能,android3D图形系统也分为java框架和本地代码两部分。

本地代码主要实现的OpenGL接口的库,在Java框架层,javax.microedition.khronos.opengles是java标准的OpenGL包,android.opengl包提供了OpenGL系统和AndroidGUI系统之间的联系。

Android的本地代码位于frameworks/base/opengl下,JNI代码位于frameworks/base/core/com_google_android_gles_jni_GLImpl.cpp和frameworks/base/core/com_google_android_gles_jni_EGLImpl.cpp,java类位于opengl/java/javax/microedition/khronos下

3.OpenGL的本地代码分析

3.1OpenGLES测试代码

frameworks/base/opengl/tests下有OpenGL的本地测试代码。

包括angeles、fillrate等14个测试代码,这些代码都可以通过终端进行本地调用测试(模拟器中使用adbshell)。

在本文中,主要使用了tritex这个测试用例。

在tests文件夹中执行mm,打印出以下信息

Install:

out/target/product/generic/system/bin/angeles

Install:

out/target/product/generic/system/bin/test-opengl-tritex

由以上信息可知,测试用例被安装在了out/target/product/generic/system/bin/目录下,将之拷贝到nfs文件系统中,以便测试。

我把这些测试用例都单独放在android的根文件系统的gltest文件夹中了。

3.2OpenGLES的编译

编译libagl下的源码生成Install:

out/target/product/generic/system/lib/egl/libGLES_android.so

编译libs下的生成了

Install:

out/target/product/generic/system/lib/libGLESv2.so

Install:

out/target/product/generic/system/lib/libGLESv1_CM.so

Install:

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,

constEGLint*attrib_list);

 

6、绑定Display、Surface、Context

函数原型:

EGLBooleaneglMakeCurrent(EGLDisplaydpy,EGLSurfacedraw,

EGLSurfaceread,EGLContextctx);

3.4OpenGLES执行过程

运行android操作系统之后,输入logcat命令,然后执行gltest中的test-opengl-tritex,屏幕上打印了以下信息

D/libEGL(1962):

egl.cfgnotfound,usingdefaultconfig

D/libEGL(1962):

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];

cnx->hooks[GLESv2_INDEX]=&gHooks[GLESv2_INDEX][IMPL_SOFTWARE];

cnx->dso=loader.open(EGL_DEFAULT_DISPLAY,0,cnx);

if(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);

cnx->dso=NULL;

}

}

}

cnx=&gEGLImpl[IMPL_HARDWARE];

if(cnx->dso==0){

charvalue[PROPERTY_VALUE_MAX];

property_get("debug.egl.hw",value,"1");

if(atoi(value)!

=0){

cnx->hooks[GLESv1_INDEX]=&gHooks[GLESv1_INDEX][IMPL_HARDWARE];

cnx->hooks[GLESv2_INDEX]=&gHooks[GLESv2_INDEX][IMPL_HARDWARE];

cnx->dso=loader.open(EGL_DEFAULT_DISPLAY,1,cnx);

if(cnx->dso){

EGLDisplaydpy=cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);

LOGE_IF(dpy==EGL_NO_DISPLAY,"NoEGLDisplayforhardwareEGL!

");

d->disp[IMPL_HARDWARE].dpy=dpy;

if(dpy==EGL_NO_DISPLAY){

loader.close(cnx->dso);

cnx->dso=NULL;

}

}

}else{

LOGD("3Dhardwareaccelerationisdisabled");

}

}

if(!

gEGLImpl[IMPL_SOFTWARE].dso&&!

gEGLImpl[IMPL_HARDWARE].dso){

returnEGL_FALSE;

}

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"handle"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");

Printf("eglGetProcAddress'slocationis%x\n",getProcAddress);

打印信息如下,可以和刚才的打印信息比较一下。

我们确实找到了一个函数。

eglGetProcAddress'slocationisacc930b1

Printf("curr=%x,it'saddressis%x\n",curr,f);

打印如下

eglGetProcAddress'slocationisacc930b1

*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

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 总结汇报 > 学习总结

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1