这篇文章是对开源项目http://code.google.com/p/flying-on-android/中ToolRotate.cpp的补充说明。
这个程序源于Android平台中的OpenGL库出现的一个bug,我们的板子屏幕分辨率是800*600,当程序竖屏状态时(600*800),屏幕上的横线会出现锯齿。打开"设置"这一类有列表的程序时,可以很显示地看到列表之间的分隔线中间有一段比两边的向下错了一个像素。
Layer.cpp中的mTextures是个长度为2的Texture数组.而两个Texture里面的数据其实是两个GraphicBuffer,分别代表前后台buffer。绘制时,调用OpenGL把前台Texture的渲染到屏幕上。当Texture的宽高分别为600*800时,屏幕就会出现锯齿(这个可以通过Layer.cpp中的onDraw方法中打印需要渲染的Texture的width/height值来确定)。其它尺寸的Layer则尚未发现这个问题。比如,在我的板子上面,状态栏这个Layer的高度为25,输入法为400多,它们都没有出现锯齿。
Texture的width/height只是它的两个int32_t属性。而里面的GraphicBuffer数据其实只是一个void*指针,指向一块儿存放像素数据的内存。OpenGL就是根据width/height的指导把GraphicBuffer的数据渲染到屏幕上去的。
假如一个APP根据重力方向对屏幕进行旋转的话,流程是这样的:
系统计算传感器的输出数据,当需要旋转时,会调用Surfaceflinger层的API来旋转Surface,具体方法可以看ToolRotate.cpp的源码。由于一个Client端的Surface对应一个Server端的Layer,所以,改变Surface的方法,其实就是变的Layer中mTextures代表的那两个Texture的宽高。Surface宽高改变后,绘制在这个Surface上面的View树得到通知,根据Surface新的宽高重新onMeasure,onLayout,onDraw。
补充于2011.7.18
/framework/base/opengl/libagl属于opengles默认的软实现代码,编译出来的库是libGLES_android.so
/framework/base/opengl/libs目录下有三个目录:GLES_CM和GLES2分别是OpenGLES1.x和2.0的接口。EGL包含一个加载器,加载OpenGL时,加载器会根据配置选择使用厂商的实现还是libagl里面的默认实现。下面的代码摘自/frameworks/base/opengl/libs/EGL/Loader.cpp
Loader::Loader() { char line[256]; char tag[256]; FILE* cfg = fopen("/system/lib/egl/egl.cfg", "r"); if (cfg == NULL) { // default config LOGD("egl.cfg not found, using default config"); gConfig.add( entry_t(0, 0, "android") ); } else { while (fgets(line, 256, cfg)) { int dpy; int impl; if (sscanf(line, "%u %u %s", &dpy, &impl, tag) == 3) { //LOGD(">>> %u %u %s", dpy, impl, tag); gConfig.add( entry_t(dpy, impl, tag) ); } } fclose(cfg); } }通过这里我们可以看出来,只要修改配置文件/system/lib/egl/egl.cfg就可以为系统定制OpenGL库了
补充于2011.7.19
/system/lib/egl/egl.cfg文件里面的内容是下面这个样子的:
0,0,android
0,1,xxx
其中,0代表软实现使用的.so库。1代表硬实现使用的.so库。具体可以参考下面代码:
// 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;软实现的库和硬件实现的库分别放在gEGLImpl[IMPL_SOFTWARE],gEGLImpl[IMPL_HARDWARE]里面。
补充于2011.7.20
再深入看一下加载.so库的loader.open()函数。这是它的核心代码:
dso = load_driver("GLES", tag, cnx, EGL | GLESv1_CM | GLESv2); if (dso) { hnd = new driver_t(dso); } else { // Always load EGL first dso = load_driver("EGL", tag, cnx, EGL); if (dso) { hnd = new driver_t(dso); // TODO: make this more automated hnd->set( load_driver("GLESv1_CM", tag, cnx, GLESv1_CM), GLESv1_CM ); hnd->set( load_driver("GLESv2", tag, cnx, GLESv2), GLESv2 ); } }
有一个很有意思的地方,就是egl_names,gl_names,cnx->egl,cnx->hooks[GLESv1_INDEX]->gl,cnx->hooks[GLESv2_INDEX]->gl这几个结构体的定义:
#undef GL_ENTRY #undef EGL_ENTRY #define GL_ENTRY(_r, _api, ...) #_api, #define EGL_ENTRY(_r, _api, ...) #_api, char const * const gl_names[] = { #include "entries.in" NULL }; char const * const egl_names[] = { #include "egl_entries.in" NULL }; #undef GL_ENTRY #undef EGL_ENTRY
#undef GL_ENTRY #undef EGL_ENTRY #define GL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__); #define EGL_ENTRY(_r, _api, ...) _r (*_api)(__VA_ARGS__); struct egl_t { #include "EGL/egl_entries.in" }; struct gl_hooks_t { struct gl_t { #include "entries.in" } gl; struct gl_ext_t { __eglMustCastToProperFunctionPointerType extensions[MAX_NUMBER_OF_GL_EXTENSIONS]; } ext; }; #undef GL_ENTRY #undef EGL_ENTRY