zoukankan      html  css  js  c++  java
  • Android 显示系统:OpenGL简介和Gralloc代码分析

      一、OpenGL ES与EGL

      Android的GUI系统是基于OpenGL/EGL来实现的。

    1. 由于OpenGL是通用函数库,在不同平台系统上需要被“本土化”——把它与具体平台的窗口系统建立起关联,FramebufferNativeWindow是负责OpenGL ES在Android平台上本地化的中介之一。为OpenGL ES配置本地窗口的是EGL,EGL介于本地窗口系统和Rendering API(即OPenGL ES)之间的一层接口。

    2. 与OpenGL相关的模块可分为:
      (1)配置类。 帮助OpenGL ES完成配置的,包括EGL、DisplayHardware都归为此类。
      (2)依赖类。 OpenGL ES正常运行所依赖的“本地化”的东西,如FramebufferNativeWindow。
      (3)使用类。 使用者也可能是配置者,如DisplayDevice既扮演了构建OpenGL ES环境的角色,同时也是它的用户。
        OpenGL开发查看Android:OpenGL ES开发入门篇(待写)

      

    二、终端显示设备 - Gralloc与Framebuffer

      Linux内核提供了统一的framebuffer显示驱动。Framebuffer是内核系统提供的图形硬件的抽象描述,称为buffer是因为它也占用了系统存储空间的一部分,是一块包含屏幕显示信息的缓冲区。Framebuffer借助于Linux文件系统向上层应用提供了统一而高效的操作接口,让用户空间运行的程序比较容易地适配多种显示设备。
      Android系统中,framebuffer提供的设备节点为/dev/graphics/fb或者/dev/fb,其中fb0表示第一个主显示屏幕,必须存在,当前系统实现中只用到了一个显示屏。显示系统使用HAL层间接引用底层架构,从而操作帧缓冲区。而完成这一中介任务的就是Gralloc.下图为Gralloc的模块简图,描述了gpu0和fb0的主要API.
      

      Gralloc模块是从Android Eclair(android 2.1)開始增加的一个HAL模块,Gralloc的含义为是Graphics Alloc(图形分配)。他对上为libui提供服务,为其分配显存,刷新显示等。对下对framebuffer进行管理。
      Gralloc对应的模块是由FramebufferNativeWindow在构造函数加载的,Gralloc包括fb和gralloc两个设备,前者负责打开内核中的Framebuffer、初始化配置,并提供了post、setSwapInterval等操作接口。后者则管理帧缓冲区的分配和释放。
      gralloc代码通常位于hardware/libhardware/modules/gralloc文件夹下。包括下面几个文件:
      Android.mk  framebuffer.cpp  gralloc.cpp  gralloc_priv.h  gr.h  mapper.cpp

      与其相关的头文件位于hardware/libhardware/include/hardware,涉及fb.h和gralloc.h。


    以下从gralloc的调用开始分析代码(Android8.0):

      FramebufferNativeWindow实现FrameBuffer的管理,它主要被SurfaceFlinger使用。也能够被OpenGL Native程序使用。在本质上,它在Framebuffer之上实现了一个ANativeWindow,眼下它仅仅管理两个buffers:front and back buffer。

      /frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer_hwc1.cpp

    HWComposer::HWComposer(
            const sp<SurfaceFlinger>& flinger,
            EventHandler& handler)
        : mFlinger(flinger),
          mFbDev(0), mHwc(0), mNumDisplays(1),
          mCBContext(new cb_context),
          mEventHandler(handler),
          mDebugForceFakeVSync(false)
    {
        for (size_t i =0 ; i<MAX_HWC_DISPLAYS ; i++) {
            mLists[i] = 0;
        }
    
        for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) {
            mLastHwVSync[i] = 0;
            mVSyncCounts[i] = 0;
        }
    
        char value[PROPERTY_VALUE_MAX];
        property_get("debug.sf.no_hw_vsync", value, "0");
        mDebugForceFakeVSync = atoi(value);
    
        bool needVSyncThread = true;
    
        // Note: some devices may insist that the FB HAL be opened before HWC.
        int fberr = loadFbHalModule();
        loadHwcModule();
    
        if (mFbDev && mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
            // close FB HAL if we don't needed it.
            // FIXME: this is temporary until we're not forced to open FB HAL
            // before HWC.
            framebuffer_close(mFbDev);
            mFbDev = NULL;
        }
    
        // If we have no HWC, or a pre-1.1 HWC, an FB dev is mandatory.
        if ((!mHwc || !hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
                && !mFbDev) {
            ALOGE("ERROR: failed to open framebuffer (%s), aborting",
                    strerror(-fberr));
            abort();
        }
    
        // these display IDs are always reserved
        for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
            mAllocatedDisplayIDs.markBit(i);
        }
    
        if (mHwc) {
            ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
                  (hwcApiVersion(mHwc) >> 24) & 0xff,
                  (hwcApiVersion(mHwc) >> 16) & 0xff);
            if (mHwc->registerProcs) {
                mCBContext->hwc = this;
                mCBContext->procs.invalidate = &hook_invalidate;
                mCBContext->procs.vsync = &hook_vsync;
                if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
                    mCBContext->procs.hotplug = &hook_hotplug;
                else
                    mCBContext->procs.hotplug = NULL;
                memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
                mHwc->registerProcs(mHwc, &mCBContext->procs);
            }
    
            // don't need a vsync thread if we have a hardware composer
            needVSyncThread = false;
            // always turn vsync off when we start
            eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
    
            // the number of displays we actually have depends on the
            // hw composer version
            if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
                // 1.3 adds support for virtual displays
                mNumDisplays = MAX_HWC_DISPLAYS;
            } else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
                // 1.1 adds support for multiple displays
                mNumDisplays = NUM_BUILTIN_DISPLAYS;
            } else {
                mNumDisplays = 1;
            }
        }
    
        if (mFbDev) {
            ALOG_ASSERT(!(mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)),
                    "should only have fbdev if no hwc or hwc is 1.0");
    
            DisplayData& disp(mDisplayData[HWC_DISPLAY_PRIMARY]);
            disp.connected = true;
            disp.format = mFbDev->format;
            DisplayConfig config = DisplayConfig();
            config.width = mFbDev->width;
            config.height = mFbDev->height;
            config.xdpi = mFbDev->xdpi;
            config.ydpi = mFbDev->ydpi;
            config.refresh = nsecs_t(1e9 / mFbDev->fps);
            disp.configs.push_back(config);
            disp.currentConfig = 0;
        } else if (mHwc) {
            // here we're guaranteed to have at least HWC 1.1
            for (size_t i =0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
                queryDisplayProperties(i);
            }
        }
    
        if (needVSyncThread) {
            // we don't have VSYNC support, we need to fake it
            mVSyncThread = new VSyncThread(*this);
        }
    }

    先依据gralloc的module ID来得到hw_module_t结构:

    // Load and prepare the FB HAL, which uses the gralloc module.  Sets mFbDev.
    int HWComposer::loadFbHalModule()
    {
        hw_module_t const* module;
    
        int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
        if (err != 0) {
            ALOGE("%s module not found", GRALLOC_HARDWARE_MODULE_ID);
            return err;
        }
    
        return framebuffer_open(module, &mFbDev);
    }

    在hw_get_module里面调用hw_get_module_by_class,其中hw_module_exists会检测gralloc动态库的位置:

    int hw_get_module_by_class(const char *class_id, const char *inst,
                               const struct hw_module_t **module)
    {
        int i = 0;
        char prop[PATH_MAX] = {0};
        char path[PATH_MAX] = {0};
        char name[PATH_MAX] = {0};
        char prop_name[PATH_MAX] = {0};
    
    
        if (inst)
            snprintf(name, PATH_MAX, "%s.%s", class_id, inst);
        else
            strlcpy(name, class_id, PATH_MAX);
    
        /*
         * Here we rely on the fact that calling dlopen multiple times on
         * the same .so will simply increment a refcount (and not load
         * a new copy of the library).
         * We also assume that dlopen() is thread-safe.
         */
    
        /* First try a property specific to the class and possibly instance */
        snprintf(prop_name, sizeof(prop_name), "ro.hardware.%s", name);
        if (property_get(prop_name, prop, NULL) > 0) {
            if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
                goto found;
            }
        }
    
        /* Loop through the configuration variants looking for a module */
        for (i=0 ; i<HAL_VARIANT_KEYS_COUNT; i++) {
            if (property_get(variant_keys[i], prop, NULL) == 0) {
                continue;
            }
            if (hw_module_exists(path, sizeof(path), name, prop) == 0) {
                goto found;
            }
        }
    
        /* Nothing found, try the default */
        if (hw_module_exists(path, sizeof(path), name, "default") == 0) {
            goto found;
        }
    
        return -ENOENT;
    
    found:
        /* load the module, if this fails, we're doomed, and we should not try
         * to load a different variant. */
        return load(class_id, path, module);
    }

      找到gralloc库的路径后,会调用load函数使用dlopen打开找到的库,并依据HAL_MODULE_INFO_SYM_AS_STR(其值为HMI)获取到hw_module_t(即HAL_MODULE_INFO_SYM)结构体指针。以及把dlopen返回的handle保存在hw_module_t中,而hw_module_t HMI 结构是一个全局结构。

    /**
     * Load the file defined by the variant and if successful
     * return the dlopen handle and the hmi.
     * @return 0 = success, !0 = failure.
     */
    static int load(const char *id,
            const char *path,
            const struct hw_module_t **pHmi)
    {
        int status = -EINVAL;
        void *handle = NULL;
        struct hw_module_t *hmi = NULL;
    
        /*
         * load the symbols resolving undefined symbols before
         * dlopen returns. Since RTLD_GLOBAL is not or'd in with
         * RTLD_NOW the external symbols will not be global
         */
        if (strncmp(path, "/system/", 8) == 0) {
            /* If the library is in system partition, no need to check
             * sphal namespace. Open it with dlopen.
             */
            handle = dlopen(path, RTLD_NOW);
        } else {
            handle = android_load_sphal_library(path, RTLD_NOW);
        }
        if (handle == NULL) {
            char const *err_str = dlerror();
            ALOGE("load: module=%s
    %s", path, err_str?err_str:"unknown");
            status = -EINVAL;
            goto done;
        }
    
        /* Get the address of the struct hal_module_info. */
        const char *sym = HAL_MODULE_INFO_SYM_AS_STR;
        hmi = (struct hw_module_t *)dlsym(handle, sym);
        if (hmi == NULL) {
            ALOGE("load: couldn't find symbol %s", sym);
            status = -EINVAL;
            goto done;
        }
    
        /* Check that the id matches */
        if (strcmp(id, hmi->id) != 0) {
            ALOGE("load: id=%s != hmi->id=%s", id, hmi->id);
            status = -EINVAL;
            goto done;
        }
    
        hmi->dso = handle;
    
        /* success */
        status = 0;
    
        done:
        if (status != 0) {
            hmi = NULL;
            if (handle != NULL) {
                dlclose(handle);
                handle = NULL;
            }
        } else {
            ALOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
                    id, path, *pHmi, handle);
        }
    
        *pHmi = hmi;
    
        return status;
    }

      回到HWComposer的构造函数处,接下来调用了loadFbHalModule() ---> framebuffer_open(module, &mFbDev); framebuffer_open定义在fb.h中,是一个inline函数:

    /** convenience API for opening and closing a supported device */
    static inline int framebuffer_open(const struct hw_module_t* module,
            struct framebuffer_device_t** device) {
        return module->methods->open(module,
                GRALLOC_HARDWARE_FB0, TO_HW_DEVICE_T_OPEN(device));
    }
      其中调用了上面结构体中初始化的open函数,open函数指向gralloc.cpp中的gralloc_device_open函数:
    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函数既可以打开fb设备也可用来打开gpu设备,如果传入的设备名为GRALLOC_HARDWARE_GPU0,即表示当前打开的是gpu设备。该函数主要是为了创建一个gralloc_context_t结构体,并且对它的成员变量device进行初始化,device变量的类型即为alloc_device_t,它用来描述一个gralloc设备,而gralloc设备是用来分配和释放图形缓冲区的,通过调用它成员函数alloc和free来实现。从这里可以看出,函数gralloc_device_open所打开的gralloc设备的成员函数alloc和free分别被设置为Gralloc模块中的函数gralloc_alloc和gralloc_free。

     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)) {
            /* 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) {
                int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);
                int format = (m->info.bits_per_pixel == 32)
                             ? (m->info.red.offset ? HAL_PIXEL_FORMAT_BGRA_8888 : HAL_PIXEL_FORMAT_RGBX_8888)
                             : HAL_PIXEL_FORMAT_RGB_565;
                const_cast<uint32_t&>(dev->device.flags) = 0;
                const_cast<uint32_t&>(dev->device.width) = m->info.xres;
                const_cast<uint32_t&>(dev->device.height) = m->info.yres;
                const_cast<int&>(dev->device.stride) = stride;
                const_cast<int&>(dev->device.format) = format;
                const_cast<float&>(dev->device.xdpi) = m->xdpi;
                const_cast<float&>(dev->device.ydpi) = m->ydpi;
                const_cast<float&>(dev->device.fps) = m->fps;
                const_cast<int&>(dev->device.minSwapInterval) = 1;
                const_cast<int&>(dev->device.maxSwapInterval) = 1;
                *device = &dev->device.common;
            }
        }
        return status;
    }

      接下来的gralloc_open也是调用了gralloc_device_open,仅仅只是name參数一个是GRALLOC_HARDWARE_GPU0,而另外一个是GRALLOC_HARDWARE_FB0,这两个函数分别得到alloc_device_t 和 framebuffer_device_t结构。

      到此,gralloc模块的三个主要结构体,gralloc_module_t,alloc_device_t,framebuffer_device_t都已经获取到了。当中在fb_device_open函数中会获取实际的framebuffer设备(一般是/dev/graphics/fb0)的一些重要參数以及能力,比如分辨率信息以及支持多少个缓冲等,另外会调用mapFrameBuffer--->mapFrameBufferLocked把framebuffer映射到内存的地址保存到gralloc_module_t中。android一般使用的都是双缓冲机制。详细代码例如以下:

    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;
    
        /*
         * Request NUM_BUFFERS screens (at lest 2 for page flipping)
         */
        info.yres_virtual = info.yres * NUM_BUFFERS;
    
    
        uint32_t flags = PAGE_FLIP;
    #if USE_PAN_DISPLAY
        if (ioctl(fd, FBIOPAN_DISPLAY, &info) == -1) {
            ALOGW("FBIOPAN_DISPLAY failed, page flipping not supported");
    #else
        if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) {
            ALOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");
    #endif
            info.yres_virtual = info.yres;
            flags &= ~PAGE_FLIP;
        }
    
        if (info.yres_virtual < info.yres * 2) {
            // we need at least 2 for page-flipping
            info.yres_virtual = info.yres;
            flags &= ~PAGE_FLIP;
            ALOGW("page flipping not supported (yres_virtual=%d, requested=%d)",
                    info.yres_virtual, info.yres*2);
        }
    
        if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1)
            return -errno;
    
        uint64_t  refreshQuotient =
        (
                uint64_t( info.upper_margin + info.lower_margin + info.yres )
                * ( info.left_margin  + info.right_margin + info.xres )
                * info.pixclock
        );
    
        /* Beware, info.pixclock might be 0 under emulation, so avoid a
         * division-by-0 here (SIGFPE on ARM) */
        int refreshRate = refreshQuotient > 0 ? (int)(1000000000000000LLU / refreshQuotient) : 0;
    
        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;
    
        ALOGI(   "using (fd=%d)
    "
                "id           = %s
    "
                "xres         = %d px
    "
                "yres         = %d px
    "
                "xres_virtual = %d px
    "
                "yres_virtual = %d px
    "
                "bpp          = %d
    "
                "r            = %2u:%u
    "
                "g            = %2u:%u
    "
                "b            = %2u:%u
    ",
                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
        );
    
        ALOGI(   "width        = %d mm (%f dpi)
    "
                "height       = %d mm (%f dpi)
    "
                "refresh rate = %.2f Hz
    ",
                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) {
            ALOGE("Error mapping the framebuffer (%s)", strerror(errno));
            return -errno;
        }
        module->framebuffer->base = intptr_t(vaddr);
        memset(vaddr, 0, fbSize);
        return 0;
    }

     由上面函数看出。mapFrameBufferLocked主要做了以下几件事情:

    1.  打开framebuffer设备
    2.  获取 fb_fix_screeninfo and fb_var_screeninfo
    3.  refill fb_var_screeninfo
    4.  推断是否支持PAGE_FLIP
    5.  计算刷新率
    6.  打印gralloc信息
    7.  填充private_module_t
    8.  mmap the framebuffer
        看之前的HAL模块比方Camera模块,有一个hw_module_t结构和一个hw_device_t结构,而这里的gralloc模块却包括了两个hw_device_t结构,一个alloc_device_t和一个framebuffer_device_t结构。
     其中framebuffer_device_t 里面主要保存了framebuffer相关的一些信息,比如分辨率、刷新率、framebuffer的数量等。
    typedef struct framebuffer_device_t {
       /**
        * Common methods of the framebuffer device.  This *must* be the first member of
        * framebuffer_device_t as users of this structure will cast a hw_device_t to
        * framebuffer_device_t pointer in contexts where it's known the hw_device_t references a
        * framebuffer_device_t.
        */
       struct hw_device_t common;
    
       /* flags describing some attributes of the framebuffer */
       const uint32_t  flags;
    
       /* dimensions of the framebuffer in pixels */
       const uint32_t  width;
       const uint32_t  height;
    
       /* frambuffer stride in pixels */
       const int       stride;
    
       /* framebuffer pixel format */
       const int       format;
    
       /* resolution of the framebuffer's display panel in pixel per inch*/
       const float     xdpi;
       const float     ydpi;
    
       /* framebuffer's display panel refresh rate in frames per second */
       const float     fps;
    
       /* min swap interval supported by this framebuffer */
       const int       minSwapInterval;
    
       /* max swap interval supported by this framebuffer */
       const int       maxSwapInterval;
    
       /* Number of framebuffers supported*/
       const int       numFramebuffers;
    
       int reserved[7];
    
       /*
        * requests a specific swap-interval (same definition than EGL)
        *
        * Returns 0 on success or -errno on error.
        */
       int (*setSwapInterval)(struct framebuffer_device_t* window,
               int interval);
    
       /*
        * This hook is OPTIONAL.
        *
        * It is non NULL If the framebuffer driver supports "update-on-demand"
        * and the given rectangle is the area of the screen that gets
        * updated during (*post)().
        *
        * This is useful on devices that are able to DMA only a portion of
        * the screen to the display panel, upon demand -- as opposed to
        * constantly refreshing the panel 60 times per second, for instance.
        *
        * Only the area defined by this rectangle is guaranteed to be valid, that
        * is, the driver is not allowed to post anything outside of this
        * rectangle.
         *
         * The rectangle evaluated during (*post)() and specifies which area
         * of the buffer passed in (*post)() shall to be posted.
         *
         * return -EINVAL if width or height <=0, or if left or top < 0
         */
        int (*setUpdateRect)(struct framebuffer_device_t* window,
                int left, int top, int width, int height);
    
        /*
         * Post <buffer> to the display (display it on the screen)
         * The buffer must have been allocated with the
         *   GRALLOC_USAGE_HW_FB usage flag.
         * buffer must be the same width and height as the display and must NOT
         * be locked.
         *
         * The buffer is shown during the next VSYNC.
         *
         * If the same buffer is posted again (possibly after some other buffer),
         * post() will block until the the first post is completed.
         *
         * Internally, post() is expected to lock the buffer so that a
         * subsequent call to gralloc_module_t::(*lock)() with USAGE_RENDER or
         * USAGE_*_WRITE will block until it is safe; that is typically once this
         * buffer is shown and another buffer has been posted.
         *
         * Returns 0 on success or -errno on error.
         */
        int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);
    
    
        /*
         * The (*compositionComplete)() method must be called after the
         * compositor has finished issuing GL commands for client buffers.
         */
    
        int (*compositionComplete)(struct framebuffer_device_t* dev);
    
        /*
         * This hook is OPTIONAL.
         *
         * If non NULL it will be caused by SurfaceFlinger on dumpsys
         */
        void (*dump)(struct framebuffer_device_t* dev, char *buff, int buff_len);
    
        /*
         * (*enableScreen)() is used to either blank (enable=0) or
         * unblank (enable=1) the screen this framebuffer is attached to.
         *
         * Returns 0 on success or -errno on error.
         */
        int (*enableScreen)(struct framebuffer_device_t* dev, int enable);
    
        void* reserved_proc[6];
    
    } framebuffer_device_t;
    
    
    /** convenience API for opening and closing a supported device */
    
    static inline int framebuffer_open(const struct hw_module_t* module,
            struct framebuffer_device_t** device) {
        return module->methods->open(module,
                GRALLOC_HARDWARE_FB0, TO_HW_DEVICE_T_OPEN(device));
    }
    
    static inline int framebuffer_close(struct framebuffer_device_t* device) {
        return device->common.close(&device->common);
    }
    
    
    __END_DECLS
    
    #endif  // ANDROID_FB_INTERFACE_H

     另外,里面定义了一些操作framebuffer的函数:

    1. int (*setSwapInterval)(struct framebuffer_device_t* window,
               int interval); // 这个函数基本没实用,由于maxSwapInterval=minSwapInterval= 1。
    2. int (*setUpdateRect)(struct framebuffer_device_t* window,
                int left, int top, int width, int height); // 局部刷新用的,默认没有启用,和平台有关。
    3. int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);  // 这个是最关键的函数,用来将图形缓冲区buffer的内容渲染到帧缓冲区中去,即显示在设备的显示屏中去。

     具体看下post函数的实现:

    static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer)
    {
       if (private_handle_t::validate(buffer) < 0)
           return -EINVAL;
    
       fb_context_t* ctx = (fb_context_t*)dev;
    
       private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(buffer);
        private_module_t* m = reinterpret_cast<private_module_t*>(
                dev->common.module);
    
        if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {
            const size_t offset = hnd->base - m->framebuffer->base;
            m->info.activate = FB_ACTIVATE_VBL;
            m->info.yoffset = offset / m->finfo.line_length;
            if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) {
                ALOGE("FBIOPUT_VSCREENINFO failed");
                m->base.unlock(&m->base, buffer);
                return -errno;
            }
            m->currentBuffer = buffer;
    
        } else {
            // If we can't do the page_flip, just copy the buffer to the front
            // FIXME: use copybit HAL instead of memcpy
    
            void* fb_vaddr;
            void* buffer_vaddr;
    
            m->base.lock(&m->base, m->framebuffer,
                    GRALLOC_USAGE_SW_WRITE_RARELY,
                    0, 0, m->info.xres, m->info.yres,
                    &fb_vaddr);
    
            m->base.lock(&m->base, buffer,
                    GRALLOC_USAGE_SW_READ_RARELY,
                    0, 0, m->info.xres, m->info.yres,
                    &buffer_vaddr);
    
            memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres);
    
            m->base.unlock(&m->base, buffer);
            m->base.unlock(&m->base, m->framebuffer);
        }
    
        return 0;
    }

      从fb_post的函数定义能够看出,第一种方式是当private_handle_t结构体hnd所描写叙述的图形缓冲区是在系统帧缓冲区中分配的时候,即这个图形缓冲区的标志值flags的PRIV_FLAGS_FRAMEBUFFER位等于1的时候,是不须要将图形缓冲区的内容复制到系统帧缓冲区去的,由于将内容写入到图形缓冲区的时候,已经相当于是将内容写入到了系统帧缓冲区中去了把Framebuffer的后buffer切为前buffer,然后通过IOCTRL机制告诉FB驱动切换DMA源地地址即可。设置系统帧缓冲区的当前输出图形缓冲区是通过IO控制命令FBIOPUT_VSCREENINFO来进行的,需要一个fb_var_screeninfo结构体作为参数。

     另外一种方式是利用copy的方式来实现,当private_handle_t结构体hnd所描写叙述的图形缓冲区是在内存中分配时,即这个图形缓冲区的标志值flags的PRIV_FLAGS_FRAMEBUFFER位等于0的时候,我们就须要将它的内容复制到系统帧缓冲区中去,这个拷贝的工作是通过调用函数memcpy来完成,相比效率较低。在将一块内存缓冲区的内容复制到系统帧缓冲区中去之前,须要对这两块缓冲区进行锁定,以保证在拷贝的过程中,这两块缓冲区的内容不会被改动,这个锁定的工作是由Gralloc模块中的函数gralloc_lock来实现的。Gralloc模块中的函数gralloc_lock的地址正好就保存在private_module_t结构体m的成员变量base所描写叙述的一个gralloc_module_t结构体的成员函数lock中。将要渲染的图形缓冲区的内容复制到系统帧缓冲区之后,就能够通过Gralloc模块中的函数gralloc_unlock来解锁。

     以上是framebuffer_device_t结构相关的一些内容,其主要作用是渲染图形缓冲区来显示内容。
     以下再看看alloc_device_t的内容:
    typedef struct alloc_device_t {
        struct hw_device_t common;
    
        /*
         * (*alloc)() Allocates a buffer in graphic memory with the requested
         * parameters and returns a buffer_handle_t and the stride in pixels to
         * allow the implementation to satisfy hardware constraints on the width
         * of a pixmap (eg: it may have to be multiple of 8 pixels).
         * The CALLER TAKES OWNERSHIP of the buffer_handle_t.
         *
         * If format is HAL_PIXEL_FORMAT_YCbCr_420_888, the returned stride must be
         * 0, since the actual strides are available from the android_ycbcr
         * structure.
         *
         * Returns 0 on success or -errno on error.
         */
    
        int (*alloc)(struct alloc_device_t* dev,
                int w, int h, int format, int usage,
                buffer_handle_t* handle, int* stride);
    
        /*
         * (*free)() Frees a previously allocated buffer.
         * Behavior is undefined if the buffer is still mapped in any process,
         * but shall not result in termination of the program or security breaches
         * (allowing a process to get access to another process' buffers).
         * THIS FUNCTION TAKES OWNERSHIP of the buffer_handle_t which becomes
         * invalid after the call.
         *
         * Returns 0 on success or -errno on error.
         */
        int (*free)(struct alloc_device_t* dev,
                buffer_handle_t handle);
    
        /* This hook is OPTIONAL.
         *
         * If non NULL it will be caused by SurfaceFlinger on dumpsys
         */
        void (*dump)(struct alloc_device_t *dev, char *buff, int buff_len);
    
        void* reserved_proc[7];
    } alloc_device_t;

     从其结构体成员能够看出,其主要作用是为请求者分配图形缓冲区。先看alloc函数的实现(gralloc.cpp):

    static int gralloc_alloc(alloc_device_t* dev,
            int width, int height, int format, int usage,
            buffer_handle_t* pHandle, int* pStride)
    {
        if (!pHandle || !pStride)
            return -EINVAL;
    
        int bytesPerPixel = 0;
        switch (format) {
            case HAL_PIXEL_FORMAT_RGBA_FP16:
                bytesPerPixel = 8;
                break;
            case HAL_PIXEL_FORMAT_RGBA_8888:
            case HAL_PIXEL_FORMAT_RGBX_8888:
            case HAL_PIXEL_FORMAT_BGRA_8888:
                bytesPerPixel = 4;
                break;
            case HAL_PIXEL_FORMAT_RGB_888:
                bytesPerPixel = 3;
                break;
            case HAL_PIXEL_FORMAT_RGB_565:
            case HAL_PIXEL_FORMAT_RAW16:
                bytesPerPixel = 2;
                break;
            default:
                return -EINVAL;
        }
    
        const size_t tileWidth = 2;
        const size_t tileHeight = 2;
    
        size_t stride = align(width, tileWidth);
        size_t size = align(height, tileHeight) * stride * bytesPerPixel + 4;
    
        int err;
        if (usage & GRALLOC_USAGE_HW_FB) {
            err = gralloc_alloc_framebuffer(dev, size, usage, pHandle);
        } else {
            err = gralloc_alloc_buffer(dev, size, usage, pHandle);
        }
    
        if (err < 0) {
            return err;
        }
    
        *pStride = stride;
        return 0;
    }

      参数w表示要分配的图形缓冲区所保存的图像的宽度,将它乘以bpp,就能够得到保存一行像素所须要使用的字节数。需要将这个字节数对齐到4个字节边界,最后得到一行像素所须要的字节数就保存在变量bpr中。
      参数h表示要分配的图形缓冲区所保存的图像的高度,将它乘以bpr。就能够得到保存整个图像所须要使用的字节数。将变量bpr的值除以变量bpp的值,就得到要分配的图形缓冲区一行包括有多少个像素点,这个结果须要保存在输出參数pStride中,以便能够返回给调用者。
      参数format用来描写叙述要分配的图形缓冲区的颜色格式,描写叙述一个像素须要几个字节来表示。
      参数usage用来描写叙述要分配的图形缓冲区的用途,假设是用来在系统帧缓冲区中渲染的,即参数usage的GRALLOC_USAGE_HW_FB位等于1,那么就必须在系统帧缓冲区中分配,否则的话,就在内存中分配。注意,在内存中分配的图形缓冲区,最终是需要复制到系统帧缓冲区去的,以便能够将它所描写叙述的图形渲染出来。

      函数gralloc_alloc_framebuffer用来在系统帧缓冲区中分配图形缓冲区,而函数gralloc_alloc_buffer用来在内存在分配图形缓冲区,接下来就来看看这两个函数的实现。

      gralloc_alloc_framebuffer最终调用了gralloc_alloc_framebuffer_locked(gralloc.cpp):

    static int gralloc_alloc_framebuffer_locked(alloc_device_t* dev,
            size_t size, int usage, buffer_handle_t* pHandle)
    {
        private_module_t* m = reinterpret_cast<private_module_t*>(
                dev->common.module);
    
        // allocate the framebuffer
        if (m->framebuffer == NULL) {
            // initialize the framebuffer, the framebuffer is mapped once
            // and forever.
            int err = mapFrameBufferLocked(m);
            if (err < 0) {
                return err;
            }
        }
    
        const uint32_t bufferMask = m->bufferMask;
        const uint32_t numBuffers = m->numBuffers;
        const size_t bufferSize = m->finfo.line_length * m->info.yres;
        if (numBuffers == 1) {
            // If we have only one buffer, we never use page-flipping. Instead,
            // we return a regular buffer which will be memcpy'ed to the main
            // screen when post is called.
            int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D;
            return gralloc_alloc_buffer(dev, bufferSize, newUsage, pHandle);
        }
    
        if (bufferMask >= ((1LU<<numBuffers)-1)) {
            // We ran out of buffers.
            return -ENOMEM;
        }
    
        // create a "fake" handles for it
        intptr_t vaddr = intptr_t(m->framebuffer->base);
        private_handle_t* hnd = new private_handle_t(dup(m->framebuffer->fd), size,
                private_handle_t::PRIV_FLAGS_FRAMEBUFFER);
    
        // find a free slot
        for (uint32_t i=0 ; i<numBuffers ; i++) {
            if ((bufferMask & (1LU<<i)) == 0) {
                m->bufferMask |= (1LU<<i);
                break;
            }
            vaddr += bufferSize;
        }
    
        hnd->base = vaddr;
        hnd->offset = vaddr - intptr_t(m->framebuffer->base);
        *pHandle = hnd;
    
        return 0;
    }

      bufferMask用来描述系统帧缓冲区的使用情况,而变量numBuffers用来描述系统帧缓冲区可以划分为多少个图形缓冲区。另外一个变量bufferSize用来描述设备显示屏一屏内容所占用的内存的大小。假设系统帧缓冲区仅仅有一个图形缓冲区大小,即变量numBuffers的值等于1,那么这个图形缓冲区就始终用作系统主图形缓冲区来使用,在这样的情况下,就不可以在系统帧缓冲区中分配图形缓冲区来给用户空间的应用程序使用,因此,这时候就会转向内存中来分配图形缓冲区,即调用函数gralloc_alloc_buffer来分配图形缓冲区。注意,这时候分配的图形缓冲区的大小为一屏内容的大小,即bufferSize。

      假设bufferMask的值大于等于((1LU<<numBuffers)-1)的值,就说明系统帧缓冲区中的图形缓冲区所有都分配出去了,这时候分配图形缓冲区就失败了。比如,假设图形缓冲区的个数为2,那么((1LU<<numBuffers)-1)的值就等于3,即二制制0x11,假设此时bufferMask的值也等于0x11,那么就表示第一个和第二个图形缓冲区都已经分配出去了,此时就不能再在系统帧缓冲区中分配图形缓冲区了。如果此时系统帧缓冲区中尚有空暇的图形缓冲区的,接下来函数就会创建一个private_handle_t结构体hnd来描写叙述这个即将要分配出去的图形缓冲区,注意,这个图形缓冲区的标志值为PRIV_FLAGS_FRAMEBUFFER,即表示这是一块在系统帧缓冲区中分配的图形缓冲区。接下来的for循环从低位到高位检查变量bufferMask的值,而且找到第一个值等于0的位,这样就能够知道在系统帧缓冲区中第几个图形缓冲区的是空暇的。注意,变量vadrr的值开始的时候指向系统帧缓冲区的基地址,在以下的for循环中,每循环一次它的值都会添加bufferSize。

      以上可见每次从系统帧缓冲区中分配出去的图形缓冲区的大小都是刚好等于显示屏一屏内容大小,最后分配出去的图形缓冲区的起始地址就保存在前面所创建的private_handle_t结构体hnd成员变量base中,用户空间的应用程序就能够直接将要渲染的图形内容复制到这个地址上去,这就相当于是直接将图形渲染到系统帧缓冲区中去了。在将private_handle_t结构体hnd返回给调用者之前,还须要设置它的成员变量offset,以便能够知道它所描写叙述的图形缓冲区的起始地址相对于系统帧缓冲区基地址的偏移量。

      gralloc_alloc_buffer(gralloc.cpp)的实现如下:

    static int gralloc_alloc_buffer(alloc_device_t* dev,
            size_t size, int /*usage*/, buffer_handle_t* pHandle)
    {
        int err = 0;
        int fd = -1;
    
        size = roundUpToPageSize(size);
    
        fd = ashmem_create_region("gralloc-buffer", size);
        if (fd < 0) {
            ALOGE("couldn't create ashmem (%s)", strerror(-errno));
            err = -errno;
        }
    
        if (err == 0) {
            private_handle_t* hnd = new private_handle_t(fd, size, 0);
            gralloc_module_t* module = reinterpret_cast<gralloc_module_t*>(
                    dev->common.module);
            err = mapBuffer(module, hnd);
            if (err == 0) {
                *pHandle = hnd;
            }
        }
    
        ALOGE_IF(err, "gralloc failed err=%s", strerror(-err));
    
        return err;
    }

      它首先调用函数ashmem_create_region来创建一块匿名共享内存,接着再在这块匿名共享内存上分配一个图形缓冲区。注意,这个图形缓冲区也是使用一个private_handle_t结构体来描述的,只是这个图形缓冲区的标志值等于0,以区别于在系统帧缓冲区中分配的图形缓冲区。当中mapBuffer又把hnd所描写叙述的一个图形缓冲区映射到当前进程的地址空间来。

      以上内容就是alloc_device_t的相关内容。在private_module_t中有一个registerBuffer的函数指针,此函数是干什么的呢?在Android系统中,全部的图形缓冲区都是由SurfaceFlinger服务分配的,而当一个图形缓冲区被分配的时候,它会同一时候被映射到请求分配的进程的地址空间去,即分配过程同时包含了注册过程,但对用户空间的其他的应用程序来说,它们所须要的图形缓冲区是在由SurfaceFlinger服务分配的,因此,当它们得到SurfaceFlinger服务分配的图形缓冲区之后,还须要将这块图形缓冲区映射到自己的地址空间来,以便能够使用这块图形缓冲区。

      在系统帧缓冲区中分配的图形缓冲区仅仅在SurfaceFlinger服务中使用,而SurfaceFlinger服务在初始化系统帧缓冲区的时候,已经将系统帧缓冲区映射到自己所在的进程中来了,因此,函数gralloc_map假设发现要注册的图形缓冲区是在系统帧缓冲区分配的时候,那么就无需再运行映射图形缓冲区的操作了。假设要注册的图形缓冲区是在内存中分配的,即它的标志值flags的PRIV_FLAGS_FRAMEBUFFER位等于0,那么接下来就需要将它映射到当前进程的地址空间来,因为要注册的图形缓冲区是在文件描述符hnd->fd对应的一块匿名共享内存中分配的,因此,仅仅需要将文件描述符hnd->fd对应的匿名共享内存映射到当前进程的地址空间来。因为映射文件描述符hnd->fd得到的是一整块匿名共享内存在当前进程地址空间的基地址,而要注册的图形缓冲区可能仅仅占领这块匿名共享内存的某一小部分,因此,还需要将要注册的图形缓冲区的在被映射的匿名共享内存中的偏移量hnd->offset加上被映射的匿名共享内存的基地址hnd->base,才能够得到真正要注册的图形缓冲区在当前进程中的访问地址,这个地址最终要被写到hnd->base中去。

  • 相关阅读:
    TyvjP2018 「Nescafé26」小猫爬山
    简化版桶排序
    getchar吸收回车
    fprintf与fscanf
    c语言命令行参数
    bit、byte、位、字节、汉字的关系
    C语言联合体
    结构体
    关于Integer的parseInt(String s, int radix)方法的使用
    java中nextLine()和next()的区别
  • 原文地址:https://www.cnblogs.com/blogs-of-lxl/p/11278413.html
Copyright © 2011-2022 走看看