Android OpenGL ES 分析与实践

 

Android OpenGL ES 分析与实践(1)

2010-05-05 15:30

1.    OpenGL ES 简介

Android 3D引擎采用的是OpenGL ESOpenGL ES是一套为手持和嵌入式系统设计的3D引擎API,由Khronos公司维护。在PC领域,一直有两种标准的3D API进行竞争,OpenGL  DirectX。一般主流的游戏和显卡都支持这两种渲染方式,DirectXWindows平台上有很大的优势,但是OpenGL具有更好的跨平台性。

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

2.    Android OpenGL ES简介

Android系统使用OpenGL的标准接口来支持3D图形功能,android 3D图形系统也分为java框架和本地代码两部分。本地代码主要实现的OpenGL接口的库,在Java框架层,javax.microedition.khronos.openglesjava标准的OpenGL包,android.opengl包提供了OpenGL系统和Android GUI系统之间的联系。

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

3.    OpenGL的本地代码分析

3.1 OpenGL ES测试代码

frameworks/base/opengl/tests下有OpenGL的本地测试代码。包括angelesfillrate14个测试代码,这些代码都可以通过终端进行本地调用测试(模拟器中使用adb shell)。在本文中,主要使用了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.2  OpenGL ES的编译

编译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

Android OpenGL ES 分析与实践(2)

2010-05-05 15:31

3.3 使用OpenGL ES画图必经的步骤  

1、获取DisplayDisplay代表显示器。

    函数原型:

   

    EGLDisplay eglGetDisplay(NativeDisplayType display);

 

    display参数是native系统的窗口显示ID值,一般为 EGL_DEFAULT_DISPLAY 。该参数实际的意义是平台实现相关的,在X-Window下是XDisplay ID,在MS Windows下是Window DC

 

 

2、初始化egl库。

    函数原型:

   

    EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor);

 

    其中dpy应该是一个有效的 EGLDisplay 。函数返回时,majorminor将被赋予当前EGL版本号。

 

 

3、选择一个合适的EGL Configuration FrameBuffer,实际指的是FrameBuffer的参数

    函数原型:

   

    EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list,EGLConfig *configs, EGLint config_size,

                           EGLint *num_config);

 

    参数attrib_list:指定了选择配置时需要参照的属性。

    参数configs    将返回一个按照attrib_list排序的平台有效的所有EGL framebuffer配置列表。

    参数config_size:指定了可以返回到configs的总配置个数。

    参数num_config: 返回了实际匹配的配置总数。

 

 

4、创建一个可实际显示的EGLSurface,实际上就是一个FrameBuffer

    函数原型:

   

    EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config,

                                  NativeWindowType win,

                                  const EGLint *attrib_list);

 

 

5、创建Context

    函数原型:

   

    EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,

                            EGLContext share_context,

                            const EGLint *attrib_list);

 

 

 

6、绑定DisplaySurfaceContext

    函数原型:

   

    EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw,

                          EGLSurface read, EGLContext ctx);

Android OpenGL ES 分析与实践(3)

2010-05-05 15:32

3.4 OpenGL ES 执行过程

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

D/libEGL  ( 1962): egl.cfg not found, using default config

D/libEGL  ( 1962): loaded /system/lib/egl/libGLES_android.so

可以看出,在执行OpenGL调用的过程中,会自动加载libGLES_android.so动态链接库。后面将会通过分析和修改源码的方式,了解OpenGL ES系统的调用过程。

通过3.3中的说明,我们在tritex测试程序中插入一些调试信息,查看OpenGL ES的调用过程。

在调用eglGetDisplay之前会执行early_egl_init函数,这是一个静态的函数。

 

eglGetDisplay中会去初始化驱动,最终调用到egl_init_drivers_locked函数中。这个函数的主要内容如下

 

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 all our EGL implementations for all displays

    // and retrieve the corresponding EGLDisplay

    // if that fails, don't use this driver.

    // TODO: currently we only deal with EGL_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) {

            EGLDisplay dpy = cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);

            LOGE_IF(dpy==EGL_NO_DISPLAY, "No EGLDisplay for software EGL!");

            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) {

        char value[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) {

                EGLDisplay dpy = cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);

                LOGE_IF(dpy==EGL_NO_DISPLAY, "No EGLDisplay for hardware EGL!");

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

                if (dpy == EGL_NO_DISPLAY) {

                    loader.close(cnx->dso);

                    cnx->dso = NULL;

                }

            }

        } else {

            LOGD("3D hardware acceleration is disabled");

        }

    }

 

    if (!gEGLImpl[IMPL_SOFTWARE].dso && !gEGLImpl[IMPL_HARDWARE].dso) {

        return EGL_FALSE;

    }

 

    return EGL_TRUE;

}

由此代码可以看出,egl_init_drivers_locked函数主要的工作就是填充gEGLImp数组变量,这个变量是egl_connection_t类型。还有一个工作就是填充gDisplay数组(只有一个元素)的disp[IMPL_HARDWARE].dpy以及disp[IMPLSOFTWAREWARE].dpy,填充的来源来自gEGLImplsoft or hard.egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);

 

Loader.cpp中的Loader::open中会加载对应的硬件和软件加速的驱动(动态链接库)。软件的对应的是/system/lib/egl/libEGL_android.so,没有默认的硬件so,因此在硬件加速时,返回值hnd会指向NULL,在需要硬件加速时这个动态链接库需要进行实现。

 

LoadDriver函数会根据其第三个参数,决定加载egl/gles,glesv1_cm,glesv2驱动。。。

 

加载几个动态链接库的过程如下图

 

你可能感兴趣的:(android,list,jni,测试,嵌入式,代码分析)