1 名词解释 OpenGL ES (OpenGL for Embedded Systems,以下简称OpenGL)
OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等设备而设计。该API由Khronos集团定义推广,Khronos是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。EGLEGL™ 是介于诸如OpenGL 或OpenVG的Khronos渲染API与底层本地平台窗口系统的接口。它被用于处理图形管理、表面/缓冲捆绑、渲染同步及支援使用其他Khronos API进行的高效、加速、混合模式2D和3D渲染。2 中的OpenGL 与EGLAndroid 2.0版本之后图形系统的底层渲染均由OpenGL负责,OpenGL除了负责处理3D API调用,还需负责管理显示内存及处理Android SurfaceFlinger或上层应用对其发出的2D API调用请求。
本地代码: framework/base/opengl/libs/egl Android EGL框架,负责加载OpenGL函数库和EGL本地实现。 framework/base/opengl/libagl Android提供的OpenGL软件库 JNI代码: framework/base/core/jni/com_google_android_gles_jni_EGLImpl.cpp EGL本地代码的JNI调用接口 framework/base/core/jni/com_google_android_gles_jni_GLImpl.cpp framework/base/core/jni/android_opengl_GLESXXX.cpp OpenGL功能函数的JNI调用接口 Java代码: framework/base/opengl/java/javax/microedition/khronos/egl framework/base/opengl/java/javax/microedition/khronos/opengles framework/base/opengl/java/com/google/android/gles_jni/ framework/base/opengl/android/opengl EGL和OpenGL的Java层接口,提供给应用开发者,通过JNI方式调用底层函数。3 Android EGL实现3.1 EGL的主要功能
EGL是用来管理绘图表面(Drawing surfaces),并且提供了如下的机制 1) 与本地窗口系统进行通信 2) 查找绘图表面可用的类型和配置信息 3) 创建绘图表面 4) 同步OpenGL ES 2.0和其他的渲染API(Open VG、本地窗口系统的绘图命令等) 5) 管理渲染资源,比如材质3.2 EGL数据结构
egl_object_t结构用来描述没一个elg对象。 egl_display_t结构用来存储get_display函数获取的物理显示设备。 egl_surface_t结构用来存储surface对象,系统可以同时拥有多个surface对象。 egl_context_t结构用用来存储OpenGL状态机信息。 egl_p_w_picpath_t结构用来存储EGLImage对象。 以下结构体在libagl中实现 egl_window_surface_v2_t继承egl_surface_t,提供egl_surface_t功能的具体实现,属于可实际显示的Surface。 egl_pixmap_surface_t存储保存在系统内存中的位图。 egl_pbuffer_surface_t存储保存在显存中的帧,以上两种位图属于不可显示的Surface。3.4 EGL本地调用关系
framework/base/opengl/libs/egl编译生成libEGL.so。 libEGL.so是Android系统的EGL框架,默认通过以下调用关系加载OpenGL库libGLES_android.so: eglGetDisplay()->egl_init_drivers()->egl_init_drivers_locked()-> loader.open()->load_driver()。
4 Android OpenGL的实现4.1 Android 提供的OpenGL库
framework/base/opengl/libagl 编译生成libGLES_android.so。 libGLES_android.so, 这是一个由系统提供的纯软件3D加速库,可以兼容各种环境。libGLES_android.so中包含了一个EGL框架的实现和OpenGL各种API 的实现。OpenGL的API底层是通过libpixelflinger.so库实现的。 针对不同的硬件设计,GPU厂商都会提供相应的硬件3D加速库,需要重写libGLES_android.so并实现相对应的libpixelflinger.so,工程量较大,一般由GPU厂商的软件开发团队来完成。4.2 EGL加载OpenGL API的方法
libGLES_android.so提供了两种API,一种是egl实现API,另一种是OpenGL标准API。 4.2.1 加载EGL API 在函数load_driver中,通过dlsym从动态链接库中获取egl函数指针。其中egl_names包含egl_entries.in 文件,egl_entries.in 文件则是egl API的声明。void *Loader::load_driver(const char* driver_absolute_path, egl_connection_t* cnx, uint32_t mask) { ... ... char const * const * api = egl_names; while (*api) { char const * name = *api; __eglMustCastToProperFunctionPointerType f = (__eglMustCastToProperFunctionPointerType)dlsym(dso, name); if (f == NULL) { // couldn't find the entry-point, use eglGetProcAddress() f = getProcAddress(name); if (f == NULL) { f = (__eglMustCastToProperFunctionPointerType)0; } } *curr++ = f; api++; } ... ... }每次读取的函数地址赋值给了cnx->egl指向的egl_t结构体。
4.2.2 加载OpenGL API(这一段摘自师弟写的文章,要修) Android 2.0系统之前,加载OpenGL API和EGL API方式相同,均是通过dlsym加载。新版本的Android系统采用TLS技术进行动态加载。 TLS 让多线程程序设计更容易。TLS 是一个机制,通过它,程序可以拥有全局变量,但处于“每一线程各不相同”的状态。也就是说,进程中的所有线程都可以拥有全局变量,但这些变量只对某个线程 才有意义。一段代码在任何情况下都是相同的,而从TLS中取出的对每个线程却各不相同。 在EGL的early_egl_init()中,对TLS 机制进行初始化。将TLS里放入一个结构体指针,这个指针指向gHooksNoContext,这个结构体里的每个函数指针被初始化为 gl_no_context。也就是现在如果通过TLS调用的OpenGL ES API都会调到gl_no_context这个函数中。 在 eglMakeCurrent中,会将渲染上下文绑定到渲染面。在EGL中首先会处理完一系列和本地窗口系统的变量后,调用 libGLES_android.so中的eglMakeCurrent,调用成功的话会设置TLS。将TLS指针指向前面已经初始化化好的 gl_hooks_t结构体指针,这个结构体里的成员都已经指向了libGLES_android.so中的OpenGL API函数。4.3动态加载库的优化
Android 2.0以上系统为libagl的Android.mk定义新的编译器参数-fvisibility=hidden。根据GCC编译器定义,使用该参数生成 的动态库将不会导出函数符号表,亦是动态库函数的Vis参数为HIDDEN。这样可以防止导出多余函数,提高动态库的加载速度,对于需要导出的函数,可用 参数__attribute__((visibility("default")))手动指定其为“default”属性。 针对libGLES_android.so库,以egl和gl的API均由手动指定其属性为“default”。在opengl/include/中,定义了API的额外参数GL_API。 #define GL_API KHRONOS_APICALL #define KHRONOS_APICALL __attribute__((visibility("default")))5 Android OpenGL 2D部分libagl中包含egl的实现和OpenGL API,虽然OpenGL API属于软件实现,但是已为2D硬件加速预留了接口,主要位于Texture相关函数,调用Android的Copybit和Gralloc模块。
5.1宏定义 libagl的Android.mk中有如下定义,如果定义了LIBAGL_USE_GRALLOC_COPYBITS宏,编译时将加入libagl的copybit.cpp文件,并链接libui库。 # Set to 1 to use gralloc and copybits LIBAGL_USE_GRALLOC_COPYBITS := 1 ifeq ($(LIBAGL_USE_GRALLOC_COPYBITS),1) LOCAL_CFLAGS += -DLIBAGL_USE_GRALLOC_COPYBITS LOCAL_SRC_FILES += copybit.cpp LOCAL_SHARED_LIBRARIES += libui endif 5.2 2D图形API void glDrawTexsvOES() void glDrawTexivOES() void glDrawTexsOES() void glDrawTexiOES() 以上四个API调用函数 static void drawTexiOES(GLint x, GLint y, GLint z, GLint w, GLint h, ogles_context_t* c) void glDrawTexfvOES()
void glDrawTexxvOES() void glDrawTexfOES() void glDrawTexxOES() 以上四个API调用函数 static void drawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed w, GLfixed h, ogles_context_t* c) 以上8个API的主要不同在于传入参数的类型。 drawTexiOES 函数用于处理×××数据,drawTexxOES函数用于处理浮点转定点后的数据。以上两个函数如果定义了 LIBAGL_USE_GRALLOC_COPYBITS,则直接调用位于copybit.cpp中的 drawTexiOESWithCopybit(x, y, z, w, h, c)进行硬件加速,否则调用drawTexxOESImp()纯软件实现。drawTexiOESWithCopybit调用copybit函数处理 surface以适应copybit模块。5.3 libagl中的copybit函数
copybit函数共有8个参数,分别为x、y坐标,w、h高宽,EGLTextureObject对象, crop_rect对象,transform旋转方式,ogles_context_t OpenGL context上下文。 该函数主要执行以下操作: 判断源是否有alpha值; 判断是否需要进行blending操作; 选择纹理模式,并将纹理转换为copybit模块兼容模式; 确定copybit模块矩形框大小; 如果需要计算alpha通道: 调用copybit模块,将数据从目的地址考出至临时地址; 调用copybit模块,从源地址复制至目的地址; 调用copybit模块,从临时地址复制到目的地址,并带有alpha值。 如果不需要计算alpha值: 调用copybit模块,从源地址复制至目的地址; 针对需要计算alpha通道情况进行进一步解释: 这种情况属于整个图形区域采用相同的alpha值。 需要表现的效果为背景透明效果,前景明显可见。由此得出计算公式为“前景x(1-Alpha)+背景x Alpha”,需要三个步骤,移出背景,移入前景,带Alpha参数移入背景。
图中飞鸽这种背景就是这样生成的。