您的位置:首页 > 其它

SurfaceFlinger启动过程分析(四)

2012-01-15 11:14 260 查看
转载时请注明出处和作者
文章出处:http://danielwood.cublog.cn
作者:Daniel Wood------------------------------------------------------------
在加载完framebuffer和gralloc模块之后,我们来看FramebufferNativeWindow构造函数中的代码:
buffers[0] = new NativeBuffer(

fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);

buffers[1] = new NativeBuffer(

fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);

err = grDev->alloc(grDev,

fbDev->width, fbDev->height, fbDev->format,

GRALLOC_USAGE_HW_FB, &buffers[0]->handle, &buffers[0]->stride);

LOGE_IF(err, "fb buffer 0 allocation failed w=%d, h=%d, err=%s", fbDev->width, fbDev->height, strerror(-err));

err = grDev->alloc(grDev,

fbDev->width, fbDev->height, fbDev->format,

GRALLOC_USAGE_HW_FB, &buffers[1]->handle, &buffers[1]->stride);

LOGE_IF(err, "fb buffer 1 allocation failed w=%d, h=%d, err=%s",fbDev->width, fbDev->height, strerror(-err));
该构造函数中关键的就剩下这四句高亮代码了,这四句也是framebuffer双缓存机制的关键。
首先新建了两个NativeBuffer,然后通过grDev为它们分配内存空间。这个grDev就是上面gralloc_open的gralloc设备模块。grDev->alloc这个函数在gralloc_device_open函数里面指定了是gralloc.cpp中的gralloc_alloc函数。
dev->device.alloc = gralloc_alloc;
为两个缓冲区分配完内存之后,FramebufferNativeWindow构造函数的事情就算完了。下面继续看DisplayHardware.cpp中init函数接下去的代码。
DisplayHardware.cpp
if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {

overlay_control_open(module, &mOverlayEngine);

}

// initialize EGL
...
接下去就获得overlay模块,前提是你的设备支持overlay。
然后就初始化EGL。DisplayHardware.cpp
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

eglInitialize(display, NULL, NULL);

eglGetConfigs(display, NULL, 0, &numConfigs);

EGLConfig config;

status_t err = EGLUtils::selectConfigForNativeWindow(

display, attribs, mNativeWindow.get(), &config);
eglGetDisplay是EGL用来获取物理屏幕句柄的函数。返回的是EGLDisplay,代表一个物理显示设备。调用这个函数进入的是egl.cpp[frameworks\base\opengl\libs\egl]
EGLDisplay eglGetDisplay(NativeDisplayType display)

{

uint32_t index = uint32_t(display);

if (index >= NUM_DISPLAYS) {

return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);

}

if (egl_init_drivers() == EGL_FALSE) {

return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);

}

EGLDisplay dpy = EGLDisplay(uintptr_t(display) + 1LU);

return dpy;

}
它会调用egl_init_drivers去初始化设备。
egl_init_drivers->egl_init_drivers_locked
下面简单贴一下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());

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

...

} else {

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

}

}

return EGL_TRUE;

}
egl_init_drivers_locked()函数的作用就是填充gEGLImpl[IMPL_SOFTWARE]和gEGLImpl[IMPL_ HARDWARE]两个数组项。达到通过gEGLImpl[IMPL_SOFTWARE]和gEGLImpl[IMPL_ HARDWARE]两个数组项就可以调用libGLES_android.so库中所有函数的目的。
cnx->hooks[GLESv1_INDEX] = &gHooks[GLESv1_INDEX][IMPL_SOFTWARE];

cnx->hooks[GLESv2_INDEX] = &gHooks[GLESv2_INDEX][IMPL_SOFTWARE];

上面这两句代码的作用是引用赋值,在loader.open完以后,
cnx->hooks[GLESv1_INDEX]会被赋值,而相对应的
gHooks[GLESv1_INDEX][IMPL_SOFTWARE]也会被赋值。Loader的构造函数先从/system/lib/egl/egl.cfg中读取配置,如果不存在,那就选用默认配置。
Loader::Loader(){char line[256];char tag[256];FILE* cfg = fopen("/system/lib/egl/egl.cfg", "r");if (cfg == NULL) {// default configLOGD("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);}}
默认的配置为(0, 0, "android")并把它放在gConfig中,以备在调用Loader.open的时候使用。
void* Loader::open(EGLNativeDisplayType display, int impl, egl_connection_t* cnx){/** TODO: if we don't find display/0, then use 0/0* (0/0 should always work)*/void* dso;char path[PATH_MAX];int index = int(display);driver_t* hnd = 0;const char* const format = "/system/lib/egl/lib%s_%s.so";char const* tag = getTag(index, impl);if (tag) {snprintf(path, PATH_MAX, format, "GLES", tag);dso = load_driver(path, cnx, EGL | GLESv1_CM | GLESv2);if (dso) {hnd = new driver_t(dso);} else {// Always load EGL firstsnprintf(path, PATH_MAX, format, "EGL", tag);dso = load_driver(path, cnx, EGL);if (dso) {hnd = new driver_t(dso);// TODO: make this more automatedsnprintf(path, PATH_MAX, format, "GLESv1_CM", tag);hnd->set( load_driver(path, cnx, GLESv1_CM), GLESv1_CM );snprintf(path, PATH_MAX, format, "GLESv2", tag);hnd->set( load_driver(path, cnx, GLESv2), GLESv2 );}}}LOG_FATAL_IF(!index && !impl && !hnd,"couldn't find the default OpenGL ES implementation ""for default display");return (void*)hnd;}
Loader::open这个函数首先去加载/system/lib/egl/libGLES_android.so,如果加载成功,那么对EGL | GLESv1_CM | GLESv2三个函数库,进行初始化。如果加载不成功,那么就加载libEGL_android.so,libGLESv1_CM_android.so,libGLESv2_android.so这三个库,事实上我们的/system/lib/egl目录下面只有libGLES_android.so这一个库,所以加载libGLES_android.so库。Ps:libEGL.so ,libGLESv1_CM.so, libGLESv2.so三个库在/system/lib目录下面。下面简单地分析下EGL的配置。首先在Loader的构造函数中获取了EGL的配置信息0, 0, "android",然后把它放在一个结构体中,这个结构体名为entry_t,定义如下
struct entry_t {entry_t() { }entry_t(int dpy, int impl, const char* tag);int dpy;int impl;String8 tag;};
随后在Loader::open中调用getTag(index, impl),其实为getTag(0, 0)。所以getTag返回的是字符串android。
const char* Loader::getTag(int dpy, int impl){const Vector<entry_t>& cfgs(gConfig);const size_t c = cfgs.size();for (size_t i=0 ; i<c ; i++) {if (dpy == cfgs[i].dpy)if (impl == cfgs[i].impl)return cfgs[i].tag.string();}return 0;}
现在有了库的路径path = /system/lib/egl/libGLES_android.so,通过load_driver函数来加载函数库。

Loader::load_driver

void *Loader::load_driver(const char* driver_absolute_path,egl_connection_t* cnx, uint32_t mask){if (access(driver_absolute_path, R_OK)) {// this happens often, we don't want to log an errorreturn 0;}//加载libGLES_android.sovoid* dso = dlopen(driver_absolute_path, RTLD_NOW | RTLD_LOCAL);if (dso == 0) {const char* err = dlerror();LOGE("load_driver(%s): %s", driver_absolute_path, err?err:"unknown");return 0;}LOGD("loaded %s", driver_absolute_path);if (mask & EGL) {//加载EGL函数库getProcAddress = (getProcAddressType)dlsym(dso, "eglGetProcAddress");LOGE_IF(!getProcAddress,"can't find eglGetProcAddress() in %s", driver_absolute_path);egl_t* egl = &cnx->egl;//把函数赋值到cnx->egl中__eglMustCastToProperFunctionPointerType* curr =(__eglMustCastToProperFunctionPointerType*)egl;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++;}}if (mask & GLESv1_CM) {//加载GLESv1_CM函数库init_api(dso, gl_names,(__eglMustCastToProperFunctionPointerType*)&cnx->hooks[GLESv1_INDEX]->gl,getProcAddress);}if (mask & GLESv2) {//加载GLESv2函数库init_api(dso, gl_names,(__eglMustCastToProperFunctionPointerType*)&cnx->hooks[GLESv2_INDEX]->gl,getProcAddress);}return dso;}
通过系统调用dlopen打开一个动态链接库。以下是百度百科对dlopen的解释:
dlopen()  功能:打开一个动态链接库  包含头文件:  #include <dlfcn.h>  函数定义:  void * dlopen( const char * pathname, int mode );  函数描述:  在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。

然后通过dlsym函数获得指向函数地址指针。以下是百度百科对dlsym的解释:
dlsym()的函数原型是  void* dlsym(void* handle,const char* symbol)  该函数在<dlfcn.h>文件中。  handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数的名称,函数返回值是void*,指向函数的地址,供调用使用。
dlsym首先去得到eglGetProcAddress的函数指针,这个函数的原型:void (*eglGetProcAddress(const char *procname)) ();该函数的作用是返回由procname指定的扩展函数地址。下面综述一下load_driver函数所做的工作:首先通过dlopen加载libGLES_android.so库,库所在路径为/system/lib/egl/libGLES_android.so,然后从libGLES_android.so库中提取EGL的各个API函数的地址放到cnx->egl中,从libGLES_android.so获取GLESv1_CM的API保存到cnx->hooks[GLESv1_INDEX]->gl中,从libGLES_android.so获取GLESv1_CM的API保存到cnx->hooks[GLESv2_INDEX]->gl。提取EGLAPI地址的方法是首先通过dlsym函数获得一个获取函数地址的函数eglGetProcAddress的地址,然后遍历EGL的API所在文件frameworks/base/opengl/libs/EGL/egl_entries.in。先通过dlsym获取各个API地址,如果返回NULL再利用eglGetProcAddress去获得,如果依旧为空就把函数地址赋值为0;提取GLESv1_CM和GLESv1_CM库中函数地址方法和提取EGL差不多,只是他们的函数文件保存在frameworks/base/opengl/libs/entries.in中。还有它们把函数地址复制给了cnx->hooks[GLESv1_INDEX]->gl和cnx->hooks[GLESv2_INDEX]->gl。等加载完库以后在libs\egl\egl.cpp里面的egl_init_drivers_locked就通过cnx->egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);调用eglGetDisplay函数,其实就是调用libGLES_android.so里面的eglGetDisplay函数,libGLES_android.so库是由目录frameworks/base/opengl/libagl生成的,所以libGLES_android.so里面的eglGetDisplay函数是文件libagl/egl.cpp里面的。其实libs\egl\egl.cpp中的函数,大多是调用libGLES_android.so库里面的,是对其的一种封装,也就是说调用libagl/egl.cpp文件里面的同名函数,如eglGetDisplay,eglCreateWindowSurface,eglCreateContext等。因为libGLES_android.so库是由rameworks/base/opengl/libagl目录生成。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: