SurfaceFlinger的啓動過程


轉載於:http://blog.chinaunix.net/uid-22935566-id-3528798.html


分類: Android平臺

文章出處: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->= plane.getWidth();
    dcblk->= 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初始化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_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:
I/gralloc ( 1620): g = 5:
I/gralloc ( 1620): b = 0:
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啓動過程分析(四)

在加載完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=; i<; 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 (== NULL) {
                // couldn't find the entry-point, use eglGetProcAddress()

                f = getProcAddress(name);
                if (== 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目錄生成。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章