Android 系统使用OpenGL的标准接口来支持3D图形功能,包含框架层及本地代码两个主要部分,这里先介绍本地代码部分。
源代码目录为:frameworks\base\opengl\libs
在这个代码路径下面会编译生成三个库: libEGL , libGLESv1_CM.so , libGLESv2.so ,那么这三个库之间是个什么关系呢?
首先说明一下主要实现的功能:
EGL是用来管理绘图表面的(Drawing surfaces),并且提供了如下的机制
(1) 与本地窗口系统进行通信
(2) 查找绘图表面可用的类型和配置信息
(3) 创建绘图表面
(4) 同步OpenGL ES 2.0和其他的渲染API(Open VG、本地窗口系统的绘图命令等)
(5) 管理渲染资源,比如材质
2. EGL 和 OpenGL ES API的联系
(1) 通过解析OpenGL ES API函数库 libGLES_android.so来获取函数指针,进行调用。
(2) 通过线程局部存储机制进行联系
3. libGLESv1_CM.so 及 libGLESv2.so 都是一个简单的wrapper, 针对OpenGl ES API进行封装
这里使用到了线程局部存储机制,这是一个什么东东呢?
概念:线程局部存储(Thread Local Storage,TLS)用来将数据与一个正在执行的指定线程关联起来。
进程中的全局变量与函数内定义的静态(static)变量,是各个线程都可以访问的共享变量。在一个线程修改的内存内容,对所有线程都生效。这是一个优点也是一个缺点。说它是优点,线程的数据交换变得非常快捷。说它是缺点,一个线程死掉了,其它线程也性命不保; 多个线程访问共享数据,需要昂贵的同步开销,也容易造成同步相关的BUG。
如果需要在一个线程内部的各个函数调用都能访问、但其它线程不能访问的变量(被称为static memory local to a thread 线程局部静态变量),就需要新的机制来实现。这就是TLS。
功能:它主要是为了避免多个线程同时访存同一全局变量或者静态变量时所导致的冲突,尤其是多个线程同时需要修改这一变量时。为了解决这个问题,我们可以通过TLS机制,为每一个使用该全局变量的线程都提供一个变量值的副本,每一个线程均可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有该变量。而从全局变量的角度上来看,就好像一个全局变量被克隆成了多份副本,而每一份副本都可以被一个线程独立地改变。
好了,介绍完毕,下面介绍系统中代码如何体现的:
由于OpenGL是一个其于上下文Context 环境开发的,所以每个线程需要保存自已的运行环境,如果没有的话则会报错:
"call to OpenGL ES API with no current context logged once per thread"
如何创建TLS:
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
static int sEarlyInitState = pthread_once(&once_control, &early_egl_init);
这两条语句会先于eglGetDisplay函数执行。第二条语句中将函数指针early_egl_init作为参数传入,会执行回调,并且保证单个线程只会执行一次。在early_egl_init()中,对TLS机制进行初始化。将TLS里放入一个结构体指针,这个指针指向gHooksNoContext(gl_hooks_t类型),这个结构体里的每个函数指针被初始化为了gl_no_context。也就是现在如果通过TLS调用的OpenGL ES API都会调到gl_no_context这个函数中。
综上,这两条语句完成了TLS的初始化。
另一处初始化时在eglInitialize函数中,同样设置成了gHooksNoContext。
TLS的赋值
在eglMakeCurrent中,会将渲染上下文绑定到渲染面。在EGL中首先会处理完一系列和本地窗口系统的变量后,调用libGLES_android.so中的eglMakeCurrent,调用成功的话会设置TLS。将TLS指针指向前面已经初始化化好的gl_hooks_t结构体指针,这个结构体里的成员都已经指向了libGLES_android.so中的OpenGL API函数,至此EGL的大部分初始化工作就已经完成了。基本就可以使用OpenGL ES API进行绘图了。
static inline void setGlThreadSpecific(gl_hooks_t const *value) {
gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
tls_hooks[TLS_SLOT_OPENGL_API] = value;
}
我们来看看在OpenGl中如何使用TLS的: (frameworks\base\opengl\libs\GLES_CM\gl.cpp)
#define GET_TLS(reg) \
"mov " #reg ", #0xFFFF0FFF \n" \
"ldr " #reg ", [" #reg ", #-15] \n"
#define CALL_GL_API(_api, ...) \
asm volatile( \
GET_TLS(r12) \
"ldr r12, [r12, %[tls]] \n" \
"cmp r12, #0 \n" \
"ldrne pc, [r12, %[api]] \n" \
"bx lr \n" \
: \
: [tls] "J"(TLS_SLOT_OPENGL_API*4), \
[api] "J"(__builtin_offsetof(gl_hooks_t, gl._api)) \
: \
);