SurfaceFlinger启动过程分析(一)、(二)、(三)、(四)【转】
2011年04月21日
SurfaceFlinger的启动过程还是从Zygote说起。Zygote起来后会调用SystemServer.java[frameworks\base\services\java\co m\android\server]里面的main函数,然后调用本地函数init1(),然后调用的是JNI的com_android_server_SystemServer.cpp里面的android_server_SystemServer_init1函数。 staticvoid android_server_SystemServer_init1(JNIEnv* env, jobject clazz)
{
system_init();
}
然后调用
System_init.cpp[frameworks\base\cmds\system_server \library]的system_init函数,通过获取属性字段system_init.startsurfaceflinger,如果字段值为1,那么就在这里启动surfaceflinger。 然而,另一方面,有一个可执行文件surfaceflinger,由目录framework/base/cmds/surfaceflinger编译产生,目录下的主要文件main_surfaceflinger.cpp里面就一个main函数: int main(int argc,char** argv)
{
sp proc(ProcessState::self());
sp sm = defaultServiceManager();
LOGI("ServiceManager: %p", sm.get());
SurfaceFlinger::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
以上两者都会调用SurfaceFlinger.cpp文件的instantiate函数。 如果你想在可执行文件中启动SurfaceFlinger,那么你可以在init.rc文件中增加类似如下语句: service surfaceflinger /system/bin/surfaceflinger
user root
onrestart restart zygote
disabled 当然你也必须设置属性字段system_init.startsurfaceflinger为0,这个工作可以在init.rc中完成。
setprop system_init.startsurfaceflinger 0 surfaceflinger构造函数调用init()函数【surfaceflinger.cpp】,init函数主要打印"SurfaceFlinger is starting"的Log信息,并且对一些debug属性进行配置。 surfaceflinger构造函数调用readyToRun函数【surfaceflinger.cpp】,至于为什么会调用readyToRun函数(并没有显式的调用语句),主要是因为surfaceflinger是一个线程类,必须实现并会调用如下两个函数:一是readyToRun(),该函数定义了线程循环前需要初始化的内容;二是threadLoop(),每个线程都必须实现,该函数定义了线程执行的内容,如果该函数返回true,线程会继续调用threadLoop(),如果返回false,线程将退出。-->选自参考文献。
关于readyToRun将在下节分析
[b]SurfaceFlinger启动过程分析(二) [/b]
上节说到SurfaceFlinger的readyToRun函数。先来看看它的代码:
(Google Android 2.2)
SurfaceFlinger.cpp
status_t SurfaceFlinger::readyToRun()
{
LOGI("SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
// we only support one display currently
int dpy = 0;
{
// initialize the main display
GraphicPlane& plane(graphicPlane(dpy));
DisplayHardware* const hw = new DisplayHardware(this, dpy);
plane.setDisplayHardware(hw);
}
// create the shared control-block
mServerHeap =new MemoryHeapBase(4096,
MemoryHeapBase::READ_ONLY,"SurfaceFlinger read-only heap");
LOGE_IF(mServerHeap==0,"can't create shared memory dealer");
mServerCblk =static_cast(mServerHeap->getBase());
LOGE_IF(mServerCblk==0,"can't get to shared control block's address");
new(mServerCblk) surface_flinger_cblk_t;
// initialize primary screen
// (other display should be initialized in the same manner, but
// asynchronously, as they could come and go. None of this is supported
// yet).
const GraphicPlane& plane(graphicPlane(dpy));
const DisplayHardware& hw = plane.displayHardware();
constuint32_t w = hw.getWidth();
constuint32_t h = hw.getHeight();
constuint32_t f = hw.getFormat();
hw.makeCurrent();
// initialize the shared control block
mServerCblk->connected |= 1displays + dpy;
memset(dcblk, 0,sizeof(display_cblk_t));
dcblk->w = plane.getWidth();
dcblk->h = plane.getHeight();
dcblk->format = f;
dcblk->orientation = ISurfaceComposer::eOrientationDefault;
dcblk->xdpi = hw.getDpiX();
dcblk->ydpi = hw.getDpiY();
dcblk->fps = hw.getRefreshRate();
dcblk->density = hw.getDensity();
asmvolatile("":::"memory");
// Initialize OpenGL|ES
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glEnableClientState(GL_VERTEX_ARRAY);
glEnable(GL_SCISSOR_TEST);
glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_CULL_FACE);
constuint16_t g0 = pack565(0x0F,0x1F,0x0F);
constuint16_t g1 = pack565(0x17,0x2f,0x17);
constuint16_t textureData[4]={ g0, g1, g1, g0 };
glGenTextures(1,&mWormholeTexName);
glBindTexture(GL_TEXTURE_2D, mWormholeTexName);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0,
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, textureData);
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(0, w, h, 0, 0, 1);
LayerDim::initDimmer(this, w, h);
mReadyToRunBarrier.open();
/*
* We're now ready to accept clients...
*/
// start boot animation
property_set("ctl.start","bootanim");
return NO_ERROR;
} 调用readyToRun函数用于初始化整个显示系统。
readyToRun()调用过程如下[这部分摘自网上资料]: (1)执行new DisplayHardware(this,dpy),通过DisplayHardware初始化Framebuffer、EGL并获取OpenGL ES信息。
(2)创建共享的内存控制块。
(3)将EGL与当前屏幕绑定。
(4)初始化共享内存控制块。
(5)初始化OpenGL ES。
(6)显示开机动画。
上面的六点作为阅读代码的提纲及参考,下面对照代码进行分析:
(1)创建一个DisplayHardware,通过它的init函数去初始化Framebuffer、EGL并获取OpenGL ES信息。
DisplayHardware.cpp[frameworks\base\libs\surfacefl inger\displayhardware] DisplayHardware::DisplayHardware(
const sp& flinger,
uint32_t dpy)
: DisplayHardwareBase(flinger, dpy)
{
init(dpy);
} init函数的代码狠长,我们一块一块,一句一句地分析:
void DisplayHardware::init(uint32_t dpy)
{
mNativeWindow =new FramebufferNativeWindow(); ... 首先亮相的是第一句(如上),new一个FramebufferNativeWindow。
FramebufferNativeWindow构造函数的代码也不少,我们去掉一些次要的代码,挑重要的关键的说:
FramebufferNativeWindow::FramebufferNativeWindow()
: BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false)
{
hw_module_t const* module;
if(hw_get_module(GRALLOC_HARDWARE_MODULE_ID,&module)== 0){
int stride;
int err;
err = framebuffer_open(module,&fbDev);
LOGE_IF(err,"couldn't open framebuffer HAL (%s)",strerror(-err));
err = gralloc_open(module,&grDev);
LOGE_IF(err,"couldn't open gralloc HAL (%s)",strerror(-err));
// bail out if we can't initialize the modules
if(!fbDev ||!grDev)
return;
mUpdateOnDemand =(fbDev->setUpdateRect != 0);
// initialize the buffer FIFO
mNumBuffers = 2;
mNumFreeBuffers = 2;
mBufferHead = mNumBuffers-1;
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));
...
}else{
LOGE("Couldn't get gralloc module");
} ... } 关键的代码都被我高亮了,从最后一行的else的LOGE中可以看出这里主要是获得gralloc这个模块。模块ID定义在:gralloc.h[hardware\libhardware\include\hardware]
#define GRALLOC_HARDWARE_MODULE_ID "gralloc" ps:有时候代码中的log狠有用,可以帮助我们读懂代码,而且logcat也是我们调试代码的好东西。
首先打开framebuffer和gralloc这两个模块
framebuffer_open和gralloc_open这两个接口在gralloc.h里面定义
staticinlineintframebuffer_open(conststruct hw_module_t* module,
struct framebuffer_device_t** device){
return module->methods->open(module,
GRALLOC_HARDWARE_FB0,(struct hw_device_t**)device);
}
staticinlineintgralloc_open(conststruct hw_module_t* module,
struct alloc_device_t** device){
return module->methods->open(module,
GRALLOC_HARDWARE_GPU0,(struct hw_device_t**)device);
} 两者指定的是gralloc.cpp中同一个函数gralloc_device_open,但是用的是不同的设备名,函数名和设备名分别在gralloc.cpp和gralloc.h中定义。
gralloc.h[hardware\libhardware\include\hardware] #define GRALLOC_HARDWARE_FB0 "fb0"
#define GRALLOC_HARDWARE_GPU0 "gpu0" gralloc.cpp[hardware\libhardware\modules\gralloc]
staticstruct hw_module_methods_t gralloc_module_methods ={
open: gralloc_device_open
}; gralloc.cpp[hardware\libhardware\modules\gralloc]
intgralloc_device_open(const hw_module_t* module,constchar* name,
hw_device_t** device)
{
int status =-EINVAL;
if(!strcmp(name, GRALLOC_HARDWARE_GPU0)){
gralloc_context_t *dev;
dev =(gralloc_context_t*)malloc(sizeof(*dev));
/* initialize our state here */
memset(dev, 0,sizeof(*dev));
/* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module =const_cast(module);
dev->device.common.close= gralloc_close;
dev->device.alloc = gralloc_alloc;
dev->device.free= gralloc_free;
*device =&dev->device.common;
status = 0;
}else{
status =fb_device_open(module, name, device);
}
return status;
}
gralloc_device_open函数通过设备名字来进行相关的初始化工作。打开framebuffer则调用fb_device_open函数。fb_device_open函数定义在framebuffer.cpp中。
int fb_device_open(hw_module_t const* module,constchar* name,
hw_device_t** device)
{
int status =-EINVAL;
if(!strcmp(name, GRALLOC_HARDWARE_FB0)){
alloc_device_t* gralloc_device;
status =gralloc_open(module,&gralloc_device);
if(status device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module =const_cast(module);
dev->device.common.close= fb_close;
dev->device.setSwapInterval = fb_setSwapInterval;
dev->device.post = fb_post;
dev->device.setUpdateRect = 0;
private_module_t* m =(private_module_t*)module;
status =mapFrameBuffer(m);
if(status >= 0){
...
*device =&dev->device.common;
}
}
return status;
}
fb_device_open函数是framebuffer.cpp里面的函数它会再次调用gralloc_open函数,调用gralloc_open并没有什么实际的用途,只是检测模块的正确性,感觉这句话没有必要,还是我哪里理解错了???因为gralloc_device这个变量在后面都没有用到啊。
哈哈,经过测试,把以下几句注释掉,然后make,烧到手机上,手机基本功能仍旧正常,看来这几句代码狠有可能是没有什么特别用处的。 alloc_device_t* gralloc_device;
status = gralloc_open(module, &gralloc_device);
if (status 调用mapFrameBuffer函数,就是将显示缓冲区映射到用户空间,这样在用户空间就可以直接对显示缓冲区进行读写操作。mapFrameBuffer函数的主体功能是在mapFrameBufferLocked函数里面完成的。
关于mapFrameBuffer函数,在下节讲解。 [b]SurfaceFlinger启动过程分析(三) [/b]
内存映射对于framebuffer来说非常重要,因为通常用户是不能直接操作物理地址空间的(也就是物理内存?),然而通过mmap映射之后,将framebuffer的物理地址空间映射到用户空间的一段虚拟地址中,用户就可以通过操作这段虚拟内存而间接操作framebuffer了,你在那段虚拟内存中画了图,相应的图就会显示到屏幕上。
--这段是自己的理解,有错必究!
下面是framebuffer.cpp中的mapFrameBufferLocked函数。
int mapFrameBufferLocked(struct private_module_t* module)
{
// already initialized...
if(module->framebuffer){
return 0;
}
charconst*const device_template[]={
"/dev/graphics/fb%u",
"/dev/fb%u",
0 };
int fd =-1;
int i=0;
char name[64];
while((fd==-1)&& device_template[i]){
snprintf(name, 64, device_template[i], 0);
fd =open(name, O_RDWR, 0);
i++;
}
if(fd flags = flags;
module->info = info;
module->finfo = finfo;
module->xdpi = xdpi;
module->ydpi = ydpi;
module->fps = fps;
/*
* map the framebuffer
*/
int err;
size_t fbSize =roundUpToPageSize(finfo.line_length * info.yres_virtual);//对齐页
module->framebuffer =new private_handle_t(dup(fd), fbSize, 0);
module->numBuffers = info.yres_virtual / info.yres;
module->bufferMask = 0;
void* vaddr =mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(vaddr == MAP_FAILED){
LOGE("Error mapping the framebuffer (%s)",strerror(errno));
return-errno;
}
module->framebuffer->base =intptr_t(vaddr);
memset(vaddr, 0, fbSize);
return 0;
} 这个函数就是和驱动相关的调用,其实结合驱动去看代码是很有意思的,把一路都打通了。
该函数首先通过open函数打开设备结点。
"/dev/graphics/fb%u"和"/dev/fb%u",如果前一个顺利打开的话,那么就不打开第二个。我的Log显示打开的是第一个设备结点/dev/graphics/fb%u。
然后通过ioctl读取设备的固定参数(FBIOGET_FSCREENINFO)和可变参数(FBIOGET_VSCREENINFO)。
【kernel部分的代码在drivers\video\fbmem.c中。】
然后对可变参数进行修改,通过ioctl设置(FBIOPUT_VSCREENINFO)显示屏的可变参数。
设置好以后再ioctl-FBIOGET_VSCREENINFO获得可变参数,然后在log上打出显示屏的各个参数设置,也就是我们开机看到的一长串log。
I/gralloc ( 1620): using (fd=8)
I/gralloc ( 1620): id = truly-ILI9327
I/gralloc ( 1620): xres = 240 px
I/gralloc ( 1620): yres = 400 px
I/gralloc ( 1620): xres_virtual = 240 px
I/gralloc ( 1620): yres_virtual = 800 px
I/gralloc ( 1620): bpp = 16
I/gralloc ( 1620): r = 11:5
I/gralloc ( 1620): g = 5:6
I/gralloc ( 1620): b = 0:5
I/gralloc ( 1620):width= 38 mm (160.421051 dpi)
I/gralloc ( 1620): height = 64 mm (158.750000 dpi)
I/gralloc ( 1620): refresh rate = 60.00 Hz 然后通过mmap完成对显示缓存区的映射。这样mapFrameBufferLocked函数的任务算是完成了。
好了,以上所讲的只是(1)中的第一句话而已
Displayhardware.cpp中的init函数。
mNativeWindow =new FramebufferNativeWindow();
[b]SurfaceFlinger启动过程分析(四) [/b]
在加载完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设备模块。
FramebufferNativeWindow构造函数的事情就算完了。下面继续看DisplayHardware.cpp中init函数接下去的代码。
接下去就获得overlay模块,前提是你的设备支持overlay。
然后就初始化EGL。
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去初始化设备。
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库中所有函数的目的。
上面这两句代码的作用是引用赋值,在loader.open完以后, cnx->hooks[GLESv1_INDEX]会被赋值,而相对应的
gHooks[GLESv1_INDEX][IMPL_SOFTWARE]也会被赋值。
Loader的构造函数先从/system/lib/egl/egl.cfg中读取配置,如果不存在,那就选用默认配置。
Loader::Loader()
{
charline[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);
}
} 默认的配置为(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;
constchar*const format ="/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 =new driver_t(dso);
}else{
// Always load EGL first
snprintf(path, PATH_MAX, format,"EGL", tag);
dso = load_driver(path, cnx, EGL);
if(dso){
hnd =new driver_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;
} Ps:libEGL.so ,libGLESv1_CM.so,libGLESv2.so三个库在/system/lib目录下面。
下面简单地分析下EGL的配置。首先在Loader的构造函数中获取了EGL的配置信息0, 0, "android",然后把它放在一个结构体中,这个结构体名为entry_t,定义如下 随后在Loader::open中调用getTag(index, impl),其实为getTag(0, 0)。所以getTag返回的是字符串android。 现在有了库的路径path = /system/lib/egl/libGLES_android.so,通过load_driver函数来加载函数库。 void*Loader::load_driver(constchar* 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 error
return 0;
}//加载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");
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;
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);
}
return dso;
}
dlopen()
功能:打开一个动态链接库
包含头文件:
#include
函数定义:
void * dlopen( const char * pathname, int mode );
函数描述:
在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。
dlsym()的函数原型是
void* dlsym(void* handle,const char* symbol)
该函数在文件中。
handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数的名称,函数返回值是void*,指向函数的地址,供调用使用。 下面综述一下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目录生成。
本文转自:
2011年04月21日
SurfaceFlinger的启动过程还是从Zygote说起。Zygote起来后会调用SystemServer.java[frameworks\base\services\java\co m\android\server]里面的main函数,然后调用本地函数init1(),然后调用的是JNI的com_android_server_SystemServer.cpp里面的android_server_SystemServer_init1函数。 staticvoid android_server_SystemServer_init1(JNIEnv* env, jobject clazz)
{
system_init();
}
然后调用
System_init.cpp[frameworks\base\cmds\system_server \library]的system_init函数,通过获取属性字段system_init.startsurfaceflinger,如果字段值为1,那么就在这里启动surfaceflinger。 然而,另一方面,有一个可执行文件surfaceflinger,由目录framework/base/cmds/surfaceflinger编译产生,目录下的主要文件main_surfaceflinger.cpp里面就一个main函数: int main(int argc,char** argv)
{
sp proc(ProcessState::self());
sp sm = defaultServiceManager();
LOGI("ServiceManager: %p", sm.get());
SurfaceFlinger::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
以上两者都会调用SurfaceFlinger.cpp文件的instantiate函数。 如果你想在可执行文件中启动SurfaceFlinger,那么你可以在init.rc文件中增加类似如下语句: service surfaceflinger /system/bin/surfaceflinger
user root
onrestart restart zygote
disabled 当然你也必须设置属性字段system_init.startsurfaceflinger为0,这个工作可以在init.rc中完成。
setprop system_init.startsurfaceflinger 0 surfaceflinger构造函数调用init()函数【surfaceflinger.cpp】,init函数主要打印"SurfaceFlinger is starting"的Log信息,并且对一些debug属性进行配置。 surfaceflinger构造函数调用readyToRun函数【surfaceflinger.cpp】,至于为什么会调用readyToRun函数(并没有显式的调用语句),主要是因为surfaceflinger是一个线程类,必须实现并会调用如下两个函数:一是readyToRun(),该函数定义了线程循环前需要初始化的内容;二是threadLoop(),每个线程都必须实现,该函数定义了线程执行的内容,如果该函数返回true,线程会继续调用threadLoop(),如果返回false,线程将退出。-->选自参考文献。
关于readyToRun将在下节分析
[b]SurfaceFlinger启动过程分析(二) [/b]
上节说到SurfaceFlinger的readyToRun函数。先来看看它的代码:
(Google Android 2.2)
SurfaceFlinger.cpp
status_t SurfaceFlinger::readyToRun()
{
LOGI("SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
// we only support one display currently
int dpy = 0;
{
// initialize the main display
GraphicPlane& plane(graphicPlane(dpy));
DisplayHardware* const hw = new DisplayHardware(this, dpy);
plane.setDisplayHardware(hw);
}
// create the shared control-block
mServerHeap =new MemoryHeapBase(4096,
MemoryHeapBase::READ_ONLY,"SurfaceFlinger read-only heap");
LOGE_IF(mServerHeap==0,"can't create shared memory dealer");
mServerCblk =static_cast(mServerHeap->getBase());
LOGE_IF(mServerCblk==0,"can't get to shared control block's address");
new(mServerCblk) surface_flinger_cblk_t;
// initialize primary screen
// (other display should be initialized in the same manner, but
// asynchronously, as they could come and go. None of this is supported
// yet).
const GraphicPlane& plane(graphicPlane(dpy));
const DisplayHardware& hw = plane.displayHardware();
constuint32_t w = hw.getWidth();
constuint32_t h = hw.getHeight();
constuint32_t f = hw.getFormat();
hw.makeCurrent();
// initialize the shared control block
mServerCblk->connected |= 1displays + dpy;
memset(dcblk, 0,sizeof(display_cblk_t));
dcblk->w = plane.getWidth();
dcblk->h = plane.getHeight();
dcblk->format = f;
dcblk->orientation = ISurfaceComposer::eOrientationDefault;
dcblk->xdpi = hw.getDpiX();
dcblk->ydpi = hw.getDpiY();
dcblk->fps = hw.getRefreshRate();
dcblk->density = hw.getDensity();
asmvolatile("":::"memory");
// Initialize OpenGL|ES
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glPixelStorei(GL_PACK_ALIGNMENT, 4);
glEnableClientState(GL_VERTEX_ARRAY);
glEnable(GL_SCISSOR_TEST);
glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_CULL_FACE);
constuint16_t g0 = pack565(0x0F,0x1F,0x0F);
constuint16_t g1 = pack565(0x17,0x2f,0x17);
constuint16_t textureData[4]={ g0, g1, g1, g0 };
glGenTextures(1,&mWormholeTexName);
glBindTexture(GL_TEXTURE_2D, mWormholeTexName);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0,
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, textureData);
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof(0, w, h, 0, 0, 1);
LayerDim::initDimmer(this, w, h);
mReadyToRunBarrier.open();
/*
* We're now ready to accept clients...
*/
// start boot animation
property_set("ctl.start","bootanim");
return NO_ERROR;
} 调用readyToRun函数用于初始化整个显示系统。
readyToRun()调用过程如下[这部分摘自网上资料]: (1)执行new DisplayHardware(this,dpy),通过DisplayHardware初始化Framebuffer、EGL并获取OpenGL ES信息。
(2)创建共享的内存控制块。
(3)将EGL与当前屏幕绑定。
(4)初始化共享内存控制块。
(5)初始化OpenGL ES。
(6)显示开机动画。
上面的六点作为阅读代码的提纲及参考,下面对照代码进行分析:
(1)创建一个DisplayHardware,通过它的init函数去初始化Framebuffer、EGL并获取OpenGL ES信息。
DisplayHardware.cpp[frameworks\base\libs\surfacefl inger\displayhardware] DisplayHardware::DisplayHardware(
const sp& flinger,
uint32_t dpy)
: DisplayHardwareBase(flinger, dpy)
{
init(dpy);
} init函数的代码狠长,我们一块一块,一句一句地分析:
void DisplayHardware::init(uint32_t dpy)
{
mNativeWindow =new FramebufferNativeWindow(); ... 首先亮相的是第一句(如上),new一个FramebufferNativeWindow。
FramebufferNativeWindow构造函数的代码也不少,我们去掉一些次要的代码,挑重要的关键的说:
FramebufferNativeWindow::FramebufferNativeWindow()
: BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false)
{
hw_module_t const* module;
if(hw_get_module(GRALLOC_HARDWARE_MODULE_ID,&module)== 0){
int stride;
int err;
err = framebuffer_open(module,&fbDev);
LOGE_IF(err,"couldn't open framebuffer HAL (%s)",strerror(-err));
err = gralloc_open(module,&grDev);
LOGE_IF(err,"couldn't open gralloc HAL (%s)",strerror(-err));
// bail out if we can't initialize the modules
if(!fbDev ||!grDev)
return;
mUpdateOnDemand =(fbDev->setUpdateRect != 0);
// initialize the buffer FIFO
mNumBuffers = 2;
mNumFreeBuffers = 2;
mBufferHead = mNumBuffers-1;
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));
...
}else{
LOGE("Couldn't get gralloc module");
} ... } 关键的代码都被我高亮了,从最后一行的else的LOGE中可以看出这里主要是获得gralloc这个模块。模块ID定义在:gralloc.h[hardware\libhardware\include\hardware]
#define GRALLOC_HARDWARE_MODULE_ID "gralloc" ps:有时候代码中的log狠有用,可以帮助我们读懂代码,而且logcat也是我们调试代码的好东西。
首先打开framebuffer和gralloc这两个模块
framebuffer_open和gralloc_open这两个接口在gralloc.h里面定义
staticinlineintframebuffer_open(conststruct hw_module_t* module,
struct framebuffer_device_t** device){
return module->methods->open(module,
GRALLOC_HARDWARE_FB0,(struct hw_device_t**)device);
}
staticinlineintgralloc_open(conststruct hw_module_t* module,
struct alloc_device_t** device){
return module->methods->open(module,
GRALLOC_HARDWARE_GPU0,(struct hw_device_t**)device);
} 两者指定的是gralloc.cpp中同一个函数gralloc_device_open,但是用的是不同的设备名,函数名和设备名分别在gralloc.cpp和gralloc.h中定义。
gralloc.h[hardware\libhardware\include\hardware] #define GRALLOC_HARDWARE_FB0 "fb0"
#define GRALLOC_HARDWARE_GPU0 "gpu0" gralloc.cpp[hardware\libhardware\modules\gralloc]
staticstruct hw_module_methods_t gralloc_module_methods ={
open: gralloc_device_open
}; gralloc.cpp[hardware\libhardware\modules\gralloc]
intgralloc_device_open(const hw_module_t* module,constchar* name,
hw_device_t** device)
{
int status =-EINVAL;
if(!strcmp(name, GRALLOC_HARDWARE_GPU0)){
gralloc_context_t *dev;
dev =(gralloc_context_t*)malloc(sizeof(*dev));
/* initialize our state here */
memset(dev, 0,sizeof(*dev));
/* initialize the procs */
dev->device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module =const_cast(module);
dev->device.common.close= gralloc_close;
dev->device.alloc = gralloc_alloc;
dev->device.free= gralloc_free;
*device =&dev->device.common;
status = 0;
}else{
status =fb_device_open(module, name, device);
}
return status;
}
gralloc_device_open函数通过设备名字来进行相关的初始化工作。打开framebuffer则调用fb_device_open函数。fb_device_open函数定义在framebuffer.cpp中。
int fb_device_open(hw_module_t const* module,constchar* name,
hw_device_t** device)
{
int status =-EINVAL;
if(!strcmp(name, GRALLOC_HARDWARE_FB0)){
alloc_device_t* gralloc_device;
status =gralloc_open(module,&gralloc_device);
if(status device.common.tag = HARDWARE_DEVICE_TAG;
dev->device.common.version = 0;
dev->device.common.module =const_cast(module);
dev->device.common.close= fb_close;
dev->device.setSwapInterval = fb_setSwapInterval;
dev->device.post = fb_post;
dev->device.setUpdateRect = 0;
private_module_t* m =(private_module_t*)module;
status =mapFrameBuffer(m);
if(status >= 0){
...
*device =&dev->device.common;
}
}
return status;
}
fb_device_open函数是framebuffer.cpp里面的函数它会再次调用gralloc_open函数,调用gralloc_open并没有什么实际的用途,只是检测模块的正确性,感觉这句话没有必要,还是我哪里理解错了???因为gralloc_device这个变量在后面都没有用到啊。
哈哈,经过测试,把以下几句注释掉,然后make,烧到手机上,手机基本功能仍旧正常,看来这几句代码狠有可能是没有什么特别用处的。 alloc_device_t* gralloc_device;
status = gralloc_open(module, &gralloc_device);
if (status 调用mapFrameBuffer函数,就是将显示缓冲区映射到用户空间,这样在用户空间就可以直接对显示缓冲区进行读写操作。mapFrameBuffer函数的主体功能是在mapFrameBufferLocked函数里面完成的。
关于mapFrameBuffer函数,在下节讲解。 [b]SurfaceFlinger启动过程分析(三) [/b]
内存映射对于framebuffer来说非常重要,因为通常用户是不能直接操作物理地址空间的(也就是物理内存?),然而通过mmap映射之后,将framebuffer的物理地址空间映射到用户空间的一段虚拟地址中,用户就可以通过操作这段虚拟内存而间接操作framebuffer了,你在那段虚拟内存中画了图,相应的图就会显示到屏幕上。
--这段是自己的理解,有错必究!
下面是framebuffer.cpp中的mapFrameBufferLocked函数。
int mapFrameBufferLocked(struct private_module_t* module)
{
// already initialized...
if(module->framebuffer){
return 0;
}
charconst*const device_template[]={
"/dev/graphics/fb%u",
"/dev/fb%u",
0 };
int fd =-1;
int i=0;
char name[64];
while((fd==-1)&& device_template[i]){
snprintf(name, 64, device_template[i], 0);
fd =open(name, O_RDWR, 0);
i++;
}
if(fd flags = flags;
module->info = info;
module->finfo = finfo;
module->xdpi = xdpi;
module->ydpi = ydpi;
module->fps = fps;
/*
* map the framebuffer
*/
int err;
size_t fbSize =roundUpToPageSize(finfo.line_length * info.yres_virtual);//对齐页
module->framebuffer =new private_handle_t(dup(fd), fbSize, 0);
module->numBuffers = info.yres_virtual / info.yres;
module->bufferMask = 0;
void* vaddr =mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(vaddr == MAP_FAILED){
LOGE("Error mapping the framebuffer (%s)",strerror(errno));
return-errno;
}
module->framebuffer->base =intptr_t(vaddr);
memset(vaddr, 0, fbSize);
return 0;
} 这个函数就是和驱动相关的调用,其实结合驱动去看代码是很有意思的,把一路都打通了。
该函数首先通过open函数打开设备结点。
"/dev/graphics/fb%u"和"/dev/fb%u",如果前一个顺利打开的话,那么就不打开第二个。我的Log显示打开的是第一个设备结点/dev/graphics/fb%u。
然后通过ioctl读取设备的固定参数(FBIOGET_FSCREENINFO)和可变参数(FBIOGET_VSCREENINFO)。
【kernel部分的代码在drivers\video\fbmem.c中。】
然后对可变参数进行修改,通过ioctl设置(FBIOPUT_VSCREENINFO)显示屏的可变参数。
设置好以后再ioctl-FBIOGET_VSCREENINFO获得可变参数,然后在log上打出显示屏的各个参数设置,也就是我们开机看到的一长串log。
I/gralloc ( 1620): using (fd=8)
I/gralloc ( 1620): id = truly-ILI9327
I/gralloc ( 1620): xres = 240 px
I/gralloc ( 1620): yres = 400 px
I/gralloc ( 1620): xres_virtual = 240 px
I/gralloc ( 1620): yres_virtual = 800 px
I/gralloc ( 1620): bpp = 16
I/gralloc ( 1620): r = 11:5
I/gralloc ( 1620): g = 5:6
I/gralloc ( 1620): b = 0:5
I/gralloc ( 1620):width= 38 mm (160.421051 dpi)
I/gralloc ( 1620): height = 64 mm (158.750000 dpi)
I/gralloc ( 1620): refresh rate = 60.00 Hz 然后通过mmap完成对显示缓存区的映射。这样mapFrameBufferLocked函数的任务算是完成了。
好了,以上所讲的只是(1)中的第一句话而已
Displayhardware.cpp中的init函数。
mNativeWindow =new FramebufferNativeWindow();
[b]SurfaceFlinger启动过程分析(四) [/b]
在加载完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设备模块。
FramebufferNativeWindow构造函数的事情就算完了。下面继续看DisplayHardware.cpp中init函数接下去的代码。
接下去就获得overlay模块,前提是你的设备支持overlay。
然后就初始化EGL。
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去初始化设备。
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库中所有函数的目的。
上面这两句代码的作用是引用赋值,在loader.open完以后, cnx->hooks[GLESv1_INDEX]会被赋值,而相对应的
gHooks[GLESv1_INDEX][IMPL_SOFTWARE]也会被赋值。
Loader的构造函数先从/system/lib/egl/egl.cfg中读取配置,如果不存在,那就选用默认配置。
Loader::Loader()
{
charline[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);
}
} 默认的配置为(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;
constchar*const format ="/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 =new driver_t(dso);
}else{
// Always load EGL first
snprintf(path, PATH_MAX, format,"EGL", tag);
dso = load_driver(path, cnx, EGL);
if(dso){
hnd =new driver_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;
} Ps:libEGL.so ,libGLESv1_CM.so,libGLESv2.so三个库在/system/lib目录下面。
下面简单地分析下EGL的配置。首先在Loader的构造函数中获取了EGL的配置信息0, 0, "android",然后把它放在一个结构体中,这个结构体名为entry_t,定义如下 随后在Loader::open中调用getTag(index, impl),其实为getTag(0, 0)。所以getTag返回的是字符串android。 现在有了库的路径path = /system/lib/egl/libGLES_android.so,通过load_driver函数来加载函数库。 void*Loader::load_driver(constchar* 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 error
return 0;
}//加载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");
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;
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);
}
return dso;
}
dlopen()
功能:打开一个动态链接库
包含头文件:
#include
函数定义:
void * dlopen( const char * pathname, int mode );
函数描述:
在dlopen的()函数以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程。使用dlclose()来卸载打开的库。
dlsym()的函数原型是
void* dlsym(void* handle,const char* symbol)
该函数在文件中。
handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数的名称,函数返回值是void*,指向函数的地址,供调用使用。 下面综述一下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目录生成。
本文转自: