SurfaceFlinger启动过程分析(四)

OpenGL ES与EGL的关系(二十一),android高级面试 //调用eglCreateWindowSurface将Surface s转换为本地窗口, surface = eglCreateWindowSurface(display, config, s.get(), NULL); context = eglCreateContext(display, config, NULL, NULL); eglQuerySurface(display, surface, EGL_WIDTH, &w); eglQuerySurface(display, surface, 阅读详情
文章出处:http://danielwood.cublog.cn
作者:Daniel Wood

在加载完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.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_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目录生成。


Android 图形驱动初始化(二十三),移动开发者升职加薪的8项技能 这里通过调用 EGL 库的 eglGetDisplay() 获得 Display。eglGetDisplay() 的定义位于 frameworks/native/opengl/libs/EGL/eglApi.cpp : 12345678910111213141516EGLDisplay eglGetDisplay(EGLNativeDisplayType display){clearError();uintptr_t index = reinterpret_ 阅读详情

相关推荐

Android GUI 系列之Surfaceflinger

Android回炉系列之Surfaceflinger 前言 很早开始就有想写写博客的想法,毕竟好记心不如烂笔头,这段时间公司业务要求,对音视频、渲染等要有深度理解,突然之间发现自己脑袋是空空的,没办法就只有系统性的回炉一遍,同时也做好存档,以便于后期的复习、升华做铺垫!好了废话不多说了。我们首先从界面绘制渲染开始,竟然是绘制那么针对我们Android系统而言我们是怎么把视图呈现给用户的呢?带着这个疑惑,我们就从surfaceflinger开始一一解析。 一、Surfaceflinger是什么? 通过查阅

Super627890115的博客 4743

Android OpenGL库加载过程源码分析

Android系统采用OpenGL绘制3D图形,使用skia来绘制二维图形;OpenGL源码位于: frameworks/native/opengl frameworks/base/opengl 本文简单介绍OpenGL库的加载过程。OpenGL以动态库的方式提供,因此在使用OpenGL的接口函数绘图前,需要加载OpenGL库,并得到接口函数指针。函数EGLBoolean egl_ini...

架构设计 535

Android14 SurfaceFlinger的启动

由init进程启动,从main函数开始主要是初始化了Surflinger, 并启动了线程。

u012627628的博客 2845

Android OpenGL ES 分析与实践(5)

看一下load_driver中到底做了什么手脚。 1.首先调用dlopen打开动态链接库,返回值是void*,这个void*指向的是什么内容呢?追踪到bionic/linker/Dlfcn.c中。其中调用了find_library函数,这个函数是一个奇怪的函数,因为它虽然叫做find_library,在其实现中,不但在系统的so链表中去查找指定的文件名的动态链接库信息,而且对其动态链接库进行加载

1246

Android 图形架构 之二—— SurfaceFlinger 启动和连接

上一篇文章从全局来分析了Android 图形架构,本篇文章来分析SurfaceFlinger,surface的创建、图像的处理,管理设备的帧缓冲区等等,它是图像流的消费者,是本系列文章的核心 SurfaceFlinger的启动过程 启动概述 SurfaceFlinger服务是一个独立进程。 关于硬件方面的服务都在 frameworks/native/services/ 文件夹下,例如:audiomanager、powermanager、inputflinger、sensorservice、surfacefl

薛瑄的博客 7186

EGL 1.4阅读理解

OPENGL ES 2.0开发,EGL接口使用说明。

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

文章出处:http://danielwood.cublog.cn作者:Daniel Wood<br /> <br /><br />SurfaceFlinger的启动过程还是从Zygote说起。Zygote起来后会调用SystemServer.java[frameworks/base/services/java/com/android/server]里面的main函数,然后调用本地函数init1(),然后调用的是JNI的com_android_server_SystemServer.cpp里面的android

lightsoure的专栏 7509

libepoxy:Epoxy是一个为您处理OpenGL函数指针管理的库

Epoxy是一个为您处理OpenGL函数指针管理的库。 它向应用程序开发人员隐藏了dlopen() , dlsym() , glXGetProcAddress() , eglGetProcAddress()等的复杂性,而他们几乎不需要什么知识。 他们可以阅读GL规范并使用未修饰的函数名称(例如glCompileShader()编写代码。 像以前一样,不要忘了在使用扩展程序或版本之前检查它们是否存在! 我们会告诉您您忘记检查的内容,而不仅仅是段错误。 特征 使用新的GL功能时自动进行初始化。 GL 4.6核心和兼容性上下文支持。 GLES 1/2/3上下文支持。 了解函数别名,因此(例如) glBufferData()可以与GL_ARB_vertex_buffer_object实现以及GL 1.5+实现一起使用。 EGL,GLX和WGL支持。 可与非环氧GL混合使用。 建筑 m

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

SurfaceFlinger启动过程分析(一)、(二)、(三)、(四)【转】 2011年04月21日   SurfaceFlinger的启动过程还是从Zygote说起。Zygote起来后会调用SystemServer.java[frameworks\base\services\java\co m\android\server]里面的main函数,然后调用本地函数init1(),然后调用的...

lms09lms 217

医药领域知识图谱快速及医药问答项目(项目全过程)

该项目是中科院软件所刘焕勇老师在github上的开源项目,基于知识图谱的医药领域问答项目 Github项目地址:mirrors / liuhuanyong / qasystemonmedicalkg · GitCode 一、项目环境搭建 该部分要十分注意,由于版本升级的问题,一些用法及格式已经不兼容了。 python3.8 下载地址:Python Release Python 3.8.10 | Python.orght...

qq_52062182的博客 9915

为ESXI Host添加v3700 FC存储

物理环境: ESXI Host version 5.1.0 Brocade SAN Switch v3700 Storage 存储和ESXI Host分别用光纤线连接在SAN交换机上 1.查询ESXI Host 的WWN号: 2. SAN交换机上查询存储的WWPN号 SAN交换机: 3.建立zone,添加到cfg文件中 brocade8Gb

haoshuwei531024的专栏 7561

Android系统Surface机制的SurfaceFlinger服务的启动过程分析

在前面一篇文章中,我们简要介绍了Android系统Surface机制中的SurfaceFlinger服务。SurfaceFlinger服务是在System进程中启动的,并且负责统一管理设备的帧缓冲区。SurfaceFlinger服务在启动的过程中,会创建两个线程,其中一个线程用来监控控制台事件,而另外一个线程用来渲染系统的UI。在本文中,我们就将详细分析SurfaceFlinger服务的启动过程。

老罗的Android之旅 5万+

手把手教 - VSCode 开发 GD32F450 单片机

系统版本:rt-thread v5.2.2单片机型号:GD32F450ZK基于文章进行开发。

weixin_46158019的博客 838

surfaceflinger启动过程分析

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

carltraveler的专栏 1102

使用protege推理小例子---关系链

使用protege推理小例子---关系链关系链推理 SubPropertityChain在Protege中使用关系链推理1、创建Person类2、创建对象关系3、添加实例4、进行推理5、推理结果 关系链推理 SubPropertityChain 关系链简单的说就是将多个关系表达式串联起来,如下图 1-1 图1-1 小明的父亲是大明,大明的父亲是老明。这就是一个由两组关系连接的关系链,根据关系链可以推理出,小明和老明之间的关系是爷孙关系。 在Protege中使用关系链推理 1、创建Person类 在Entit

weixin_45237780的博客 4457

深入LSM6DSL的FIFO连续模式:基于STM32和MEMS库的数据流高效处理指南

本文深入探讨了LSM6DSL的FIFO连续模式,结合STM32硬件平台和ST官方MEMS库,提供了高效的数据流处理方案。通过优化I2C通信时序、中断驱动设计以及动态ODR调整,实现低功耗数据采集与处理,显著提升嵌入式传感器应用的性能和续航能力。

weixin_30387799的博客 348
上一篇: SurfaceFlinger启动过程分析(三)
下一篇: 刚下载的Android 内核编译出错解决方法
learnerz
博客等级 码龄16年 51粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值