SurfaceFlinger启动过程分析(一)、(二)、(三)、(四)【转】

时间:2022-11-14 22:50:30
文章出处:http://danielwood.cublog.cn
作者:Daniel Wood

 

SurfaceFlinger的启动过程还是从Zygote说起。Zygote起来后会调用SystemServer.java[frameworks/base/services/java/com/android/server]里面的main函数,然后调用本地函数init1(),然后调用的是JNIcom_android_server_SystemServer.cpp里面的android_server_SystemServer_init1函数。

static void 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,线程将退出。-->选自参考文献。

关于readyToRun将在下节分析

 

 

SurfaceFlinger启动过程分析(二)

上节说到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)执行new DisplayHardware(this,dpy),通过DisplayHardware初始化FramebufferEGL并获取OpenGL ES信息。 

2)创建共享的内存控制块。 

3)将EGL与当前屏幕绑定。 

4)初始化共享内存控制块。 

5)初始化OpenGL ES 

6)显示开机动画。

上面的六点作为阅读代码的提纲及参考,下面对照代码进行分析:

(1)创建一个DisplayHardware,通过它的init函数去初始化FramebufferEGL并获取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_opengralloc_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函数,在下节讲解。


SurfaceFlinger启动过程分析(三)

内存映射对于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();

SurfaceFlinger启动过程分析(一)、(二)、(三)、(四)【转】

SurfaceFlinger启动过程分析(四)

在加载完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.solibGLESv1_CM_android.solibGLESv2_android.so这三个库,事实上我们的/system/lib/egl目录下面只有libGLES_android.so这一个库,所以加载libGLES_android.so库。

PslibEGL.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.so

void* 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_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目录生成。

本文转自:

文章出处:http://danielwood.cublog.cn
作者:Daniel Wood