您的位置:首页 > 其它

SurfaceFlinger启动过程分析

2012-07-09 22:58 316 查看
转载时请注明出处和作者
文章出处:http://danielwood.cublog.cn
作者:Daniel Wood------------------------------------------------------------
以下将讲讲surfaceflinger的启动过程,可以结合启动过程图来了解。当然最关键是代码(Google
Android 2.2),因为我讲的难免有错误之处。 由于工作需要,所以要了解surfaceflinger这一块,参考的资料也较多,已经无法追溯来源,所以参考文献如下:^_^
参考文献:
[1] www.baidu.com
[2] g.cn
[3] Android display架构分析系列http://hi.baidu.com/leowenj/blog/item/429c2dd6ac1480c851da4b95.html
[4] Android Display System --- Surface Flinger http://blog.csdn.net/yili_xie/archive/2009/11/12/4803527.aspx [5] Android核心分析系列 http://wsqhs.spaces.live.com/blog/cns!94F639580F58209C!668.entry
SurfaceFlinger的启动过程还是从Zygote说起。Zygote起来后会调用SystemServer.java[frameworks\base\services\java\com\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。

char propBuf[PROPERTY_VALUE_MAX];
property_get("system_init.startsurfaceflinger", propBuf, "1");
if(strcmp(propBuf,"1")
== 0){

        // Start the SurfaceFlinger

        SurfaceFlinger::instantiate();

    }

然而,另一方面,有一个可执行文件surfaceflinger,由目录framework/base/cmds/surfaceflinger编译产生,目录下的主要文件main_surfaceflinger.cpp里面就一个main函数:
int main(int argc,char**
 argv)

{

    sp<ProcessState> proc(ProcessState::self());

    sp<IServiceManager> sm= defaultServiceManager();

    LOGI("ServiceManager: %p", sm.get());

    SurfaceFlinger::instantiate();

    ProcessState::self()->startThreadPool();

    IPCThreadState::self()->joinThreadPool();

}

以上两者都会调用SurfaceFlinger.cpp文件的instantiate函数。
void SurfaceFlinger::instantiate(){

    defaultServiceManager()->addService(

            String16("SurfaceFlinger"),new SurfaceFlinger());

}

如果你想在可执行文件中启动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,线程将退出。-->选自参考文献。上节说到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<surface_flinger_cblk_t*>(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();

    const uint32_t w
= hw.getWidth();

    const uint32_t h
= hw.getHeight();

    const uint32_t f
= hw.getFormat();

    hw.makeCurrent();

    // initialize the shared control block

    mServerCblk->connected
|= 1<<dpy;

    display_cblk_t* dcblk 
= mServerCblk->displays
+ 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();

    asm volatile
("":::"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);

    const uint16_t g0
= pack565(0x0F,0x1F,0x0F);

    const uint16_t g1
= pack565(0x17,0x2f,0x17);

    const uint16_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)执行newDisplayHardware(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\surfaceflinger\displayhardware]
DisplayHardware::DisplayHardware(

        const sp<SurfaceFlinger>& 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里面定义
static
inline int 
framebuffer_open(const
struct hw_module_t* module,

        struct framebuffer_device_t** device)
{

    return module->methods->open(module,

            GRALLOC_HARDWARE_FB0,
(struct hw_device_t**)device);

}

static inline
int gralloc_open(const
struct 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]

static struct hw_module_methods_t gralloc_module_methods
= {

        open: gralloc_device_open

};

gralloc.cpp[hardware\libhardware\modules\gralloc]
int
gralloc_device_open(const hw_module_t* module,
const char* 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<hw_module_t*>(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,
const char* 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
< 0) return status; /* initialize our state here */ fb_context_t *dev
= (fb_context_t*)malloc(sizeof(*dev)); 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<hw_module_t*>(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 < 0)

   return status;

然后调用mapFrameBuffer函数,就是将显示缓冲区映射到用户空间,这样在用户空间就可以直接对显示缓冲区进行读写操作。mapFrameBuffer函数的主体功能是在mapFrameBufferLocked函数里面完成的。
关于mapFrameBuffer函数,在下节讲解。内存映射对于framebuffer来说非常重要,因为通常用户是不能直接操作物理地址空间的(也就是物理内存?),然而通过mmap映射之后,将framebuffer的物理地址空间映射到用户空间的一段虚拟地址中,用户就可以通过操作这段虚拟内存而间接操作framebuffer了,你在那段虚拟内存中画了图,相应的图就会显示到屏幕上。
——这段是自己的理解,有错必究!
下面是framebuffer.cpp中的mapFrameBufferLocked函数。
int mapFrameBufferLocked(struct private_module_t*
 module)

{

    // already initialized...

    if (module->framebuffer)
{

        return 0;

    }        

    char const
* 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 
< 0)

        return -errno;

    struct fb_fix_screeninfo finfo;

    if (ioctl(fd, FBIOGET_FSCREENINFO,
&finfo)
== 
-1)

        return -errno;

    struct fb_var_screeninfo info;

    if (ioctl(fd, FBIOGET_VSCREENINFO,
&info)
== 
-1)

        return -errno;

    info.reserved[0]
= 0;

    info.reserved[1]
= 0;

    info.reserved[2]
= 0;

    info.xoffset = 0;

    info.yoffset = 0;

    info.activate 
= FB_ACTIVATE_NOW;

    /*

     * Explicitly request 5/6/5

     */

    info.bits_per_pixel 
= 16;

    info.red.offset
= 11;

    info.red.length
= 5;

    info.green.offset
= 5;

    info.green.length
= 6;

    info.blue.offset
= 0;

    info.blue.length
= 5;

    info.transp.offset
= 0;

    info.transp.length
= 0;

    /*

     * Request NUM_BUFFERS screens (at lest 2 for page flipping)

     */

    info.yres_virtual 
= info.yres 
* NUM_BUFFERS;

    uint32_t flags 
= PAGE_FLIP;

    if (ioctl(fd, FBIOPUT_VSCREENINFO,
&info)
== 
-1) {

        info.yres_virtual 
= info.yres;

        flags &=
~PAGE_FLIP;

        LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");

    }

    if (info.yres_virtual
< info.yres
* 2) 
{

        // we need at least 2 for page-flipping

        info.yres_virtual 
= info.yres;

        flags &=
~PAGE_FLIP;

        LOGW("page flipping not supported (yres_virtual=%d, requested=%d)",

                info.yres_virtual, info.yres*2);

    }

    if (ioctl(fd, FBIOGET_VSCREENINFO,
&info)
== 
-1)

        return -errno;

    int refreshRate 
= 1000000000000000LLU /

    (

            uint64_t( info.upper_margin
+ info.lower_margin
+ info.yres
)

            * ( info.left_margin
+ info.right_margin
+ info.xres
)

            * info.pixclock

    );

    if (refreshRate
== 0)
{

        // bleagh, bad info from the driver

        refreshRate = 60*1000;
// 60 Hz

    }

    if (int(info.width)
<= 0 
|| int(info.height)
<= 0)
{

        // the driver doesn't return that information

        // default to 160 dpi

        info.width 
= ((info.xres
* 25.4f)/160.0f
+ 0.5f);

        info.height 
= ((info.yres
* 25.4f)/160.0f
+ 0.5f);

    }

    float xdpi =
(info.xres 
* 25.4f)
/ info.width;

    float ydpi =
(info.yres 
* 25.4f)
/ info.height;

    float fps = refreshRate
/ 1000.0f;

    LOGI( "using (fd=%d)\n"

            "id = %s\n"

            "xres = %d px\n"

            "yres = %d px\n"

            "xres_virtual = %d px\n"

            "yres_virtual = %d px\n"

            "bpp = %d\n"

            "r = %2u:%u\n"

            "g = %2u:%u\n"

            "b = %2u:%u\n",

            fd,

            finfo.id,

            info.xres,

            info.yres,

            info.xres_virtual,

            info.yres_virtual,

            info.bits_per_pixel,

            info.red.offset, info.red.length,

            info.green.offset, info.green.length,

            info.blue.offset, info.blue.length

    );

    LOGI( "width = %d mm (%f dpi)\n"

            "height = %d mm (%f dpi)\n"

            "refresh rate = %.2f Hz\n",

            info.width, xdpi,

            info.height, ydpi,

            fps

    );

    if (ioctl(fd, FBIOGET_FSCREENINFO,
&finfo)
== 
-1)

        return -errno;

    if (finfo.smem_len
<= 0)

        return -errno;

    module->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();

在加载完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 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; 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 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;}

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 error return 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目录生成。调用readyToRun函数用于初始化整个显示系统。
readyToRun()调用过程如下:
(1)执行new
DisplayHardware(this,dpy),通过DisplayHardware初始化Framebuffer、EGL并获取OpenGL
ES信息。
(2)创建共享的内存控制块。
(3)将EGL与当前屏幕绑定。
(4)初始化共享内存控制块。
(5)初始化OpenGL
ES。
(6)显示开机动画。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: