SurfaceFlinger启动过程分析(四)

转载时请注明出处和作者
文章出处:http://danielwood.cublog.cn
作者:Daniel Wood
------------------------------------------------------------
在加载完framebuffer和gralloc模块之后,我们来看FramebufferNativeWindow构造函数中的代码:

buffers[0]=newNativeBuffer(
fbDev->width,fbDev->height,fbDev->format,GRALLOC_USAGE_HW_FB);
buffers[1]=newNativeBuffer(
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]

EGLDisplayeglGetDisplay(NativeDisplayType display)
{
uint32_tindex=uint32_t(display);
if(index>=NUM_DISPLAYS){
returnsetError(EGL_BAD_PARAMETER,EGL_NO_DISPLAY);
}

if(egl_init_drivers()==EGL_FALSE){
returnsetError(EGL_BAD_PARAMETER,EGL_NO_DISPLAY);
}
EGLDisplay dpy=EGLDisplay(uintptr_t(display)+1LU);
returndpy;
}

它会调用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.
returnEGL_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");
}
}
returnEGL_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()
{
charline[256];
chartag[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)){
intdpy;
intimpl;
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,intimpl,egl_connection_t*cnx)
{
/*
* TODO: if we don't find display/0, then use 0/0
* (0/0 should always work)
*/

void*dso;
charpath[PATH_MAX];
intindex=int(display);
driver_t*hnd=0;
constchar*constformat="/system/lib/egl/lib%s_%s.so";
charconst*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=newdriver_t(dso);
}else{
// Always load EGL first
snprintf(path,PATH_MAX,format,"EGL",tag);
dso=load_driver(path,cnx,EGL);
if(dso){
hnd=newdriver_t(dso);
// TODO: make this more automated
snprintf(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.solibGLESv1_CM_android.solibGLESv2_android.so这三个库,事实上我们的/system/lib/egl目录下面只有libGLES_android.so这一个库,所以加载libGLES_android.so库。

PslibEGL.solibGLESv1_CM.solibGLESv2.so三个库在/system/lib目录下面。

下面简单地分析下EGL的配置。首先在Loader的构造函数中获取了EGL的配置信息0, 0, "android",然后把它放在一个结构体中,这个结构体名为entry_t,定义如下

structentry_t{
entry_t(){}
entry_t(intdpy,intimpl,constchar*tag);
intdpy;
intimpl;
String8 tag;
};

随后在Loader::open中调用getTag(index, impl),其实为getTag(0, 0)。所以getTag返回的是字符串android

constchar*Loader::getTag(intdpy,intimpl)
{
constVector<entry_t>&cfgs(gConfig);
constsize_tc=cfgs.size();
for(size_ti=0;i<c;i++){
if(dpy==cfgs[i].dpy)
if(impl==cfgs[i].impl)
returncfgs[i].tag.string();
}
return0;
}

现在有了库的路径path = /system/lib/egl/libGLES_android.so通过load_driver函数来加载函数库。

Loader::load_driver

void*Loader::load_driver(constchar*driver_absolute_path,
egl_connection_t*cnx,uint32_tmask)
{
if(access(driver_absolute_path,R_OK)){
// this happens often, we don't want to log an error

return0;
}//加载libGLES_android.so

void*dso=dlopen(driver_absolute_path,RTLD_NOW|RTLD_LOCAL);
if(dso==0){
constchar*err=dlerror();
LOGE("load_driver(%s): %s",driver_absolute_path,err?err:"unknown");
return0;
}
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;
charconst*const*api=egl_names;
while(*api){
charconst*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);
}
returndso;
}


通过系统调用dlopen打开一个动态链接库。 以下是百度百科对 dlopen 的解释:

dlopen()
  功能:打开一个动态链接库
  包含头文件:
  #include<dlfcn.h>
  函数定义:
  void*dlopen(const char*pathname,intmode);
  函数描述:
  在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_CMAPI保存到cnx->hooks[GLESv1_INDEX]->gl中,从libGLES_android.so获取GLESv1_CMAPI保存到cnx->hooks[GLESv2_INDEX]->gl

提取EGLAPI地址的方法是首先通过dlsym函数获得一个获取函数地址的函数eglGetProcAddress的地址,然后遍历EGLAPI所在文件frameworks/base/opengl/libs/EGL/egl_entries.in。先通过dlsym获取各个API地址,如果返回NULL再利用eglGetProcAddress去获得,如果依旧为空就把函数地址赋值为0;提取GLESv1_CMGLESv1_CM库中函数地址方法和提取EGL差不多,只是他们的函数文件保存在frameworks/base/opengl/libs/entries.in中。还有它们把函数地址复制给了cnx->hooks[GLESv1_INDEX]->glcnx->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文件里面的同名函数,如eglGetDisplayeglCreateWindowSurfaceeglCreateContext等。因为libGLES_android.so库是由rameworks/base/opengl/libagl目录生成。

你可能感兴趣的:(in)