OpenGL ES (OpenGL for Embedded Systems,以下简称OpenGL)
OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。该API由Khronos集团定义推广,Khronos是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。
EGL
EGL™ 是介于诸如OpenGL 或OpenVG的Khronos渲染API与底层本地平台窗口系统的接口。它被用于处理图形管理、表面/缓冲捆绑、渲染同步及支援使用其他Khronos API进行的高效、加速、混合模式2D和3D渲染。
http://www.khronos.cn/index.shtml
Android 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方式调用底层函数。
EGL是用来管理绘图表面(Drawing surfaces),并且提供了如下的机制
1) 与本地窗口系统进行通信
2) 查找绘图表面可用的类型和配置信息
3) 创建绘图表面
4) 同步OpenGL ES 2.0和其他的渲染API(Open VG、本地窗口系统的绘图命令等)
5) 管理渲染资源,比如材质
egl_object_t结构用来描述没一个elg对象。
egl_display_t结构用来存储get_display函数获取的物理显示设备。
egl_surface_t结构用来存储surface对象,系统可以同时拥有多个surface对象。
egl_context_t结构用用来存储OpenGL状态机信息。
egl_image_t结构用来存储EGLImage对象。
以下结构体在libagl中实现
egl_window_surface_v2_t继承egl_surface_t,提供egl_surface_t功能的具体实现,属于可实际显示的Surface。
egl_pixmap_surface_t存储保存在系统内存中的位图。
egl_pbuffer_surface_t存储保存在显存中的帧,以上两种位图属于不可显示的Surface。
EGLDisplay eglGetDisplay()
eglGetDisplay调用egl_display_t::get_display(dpy)获取显示设备的句柄。
EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
初始化EGL,获取EGL版本号。
EGLBoolean eglTerminate()
结束一个EGLDisplay,并不结束EGL本身。
EGLBoolean eglGetConfigs()
获取EGL配置参数。
EGLBoolean eglChooseConfig()
选择EGL配置参数,配置一个期望并尽可能接近一个有效的系统配置。
EGLBoolean eglGetConfigAttrib()
获取EGL配置时需要参照的属性。
EGLSurface eglCreateWindowSurface()
创建一个EGL surface,surface是可以实际显示在屏幕上类型。
EGLSurface eglCreatePixmapSurface()
EGLSurface eglCreatePbufferSurface()
创建EGL PixmapSurface和PbufferSurface类型,这两种类型不可直接显示与屏幕上。
EGLBoolean eglDestroySurface()
销毁一个EGL surface对象
EGLBoolean eglQuerySurface()
查询surface的参数。
EGLContext eglCreateContext()
创建一个OpenGL状态机。
EGLBoolean eglDestroyContext()
销毁一个OpenGL状态机
EGLBoolean eglMakeCurrent()
将OpenGL context与surface绑定。
EGLBoolean eglQueryContext()
查询context的参数。
EGLBoolean eglSwapBuffers()
绘制完图形后用于显示的函数。
const char* eglQueryString()
查询EGL参数字串。
以上仅为EGL 1.0提供的基础功能,后续EGL版本陆续添加了更多的API。
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()。
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厂商的软件开发团队来完成。
libGLES_android.so提供了两种API,一种是egl实现API,另一种是OpenGL标准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函数。
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")))
libagl中包含egl的实现和OpenGL API,虽然OpenGL API属于软件实现,但是已为2D硬件加速预留了接口,主要位于Texture相关函数,调用Android的Copybit和Gralloc模块。
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
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模块。
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参数移入背景。
图中飞鸽这种背景就是这样生成的。
Rockie Cheng