• android Gui系统之SurfaceFlinger(1)---SurfaceFlinger概论


    GUI 是任何系统都很重要的一块。

    android GUI大体分为4大块。

    1)SurfaceFlinger

    2)WMS

    3)View机制

    4)InputMethod

    这块内容非常之多,但是理解后,可以触类旁通,其实现在主流的系统,包括andorid,ios在构架上,都是有很多相识之处。

    我们先来讲SurfaceFlinger

    1.OpenGL & OpenGL ES

    OPenGL ES 是android系统绘画的基础。关于OpenGL部分,可以百度了解下。

    先来看一个OpenGL & SurfaceFlinger之间的框架图:

    从底层往上看:
    1)linux内核提供统一的设备驱动,/dev/graphics/fb*
    2) Android HAL 提供2个接口 Gralloc & fb
    fb 负责打开framebuffer,提供接口操作。gralloc负责管理帧缓冲区的分配和释放。
    composer是HAL中另一个重要的功能,它主要是给厂商定制UI合成。SurfaceFlinger中负责HWComposer会用到这个功能。
    而且关键是HWComposer还负责产生VSync信号,这是本期SurfaceFlinger的重点。
    3)由于OpenGL是一套通用的库(大部分就是接口),所以它需要一个本地的实现。andorid平台OpenGL有2个本地窗口,FrameBufferNativeWindow & Surface。
    4)OpenGL可以有软件 或者依托于硬件实现,具体的运行状态,就是由EGL来配置。
    5)SurfaceFlinger持有一个成员数组mDisplays来支持各种显示设备。DisplayDevices在初始化的时候调用EGL来搭建OpenGL的环境。
     

    2.Android的硬件接口HAL

    HAL需要满足android系统和厂商的要求

    2.1硬件接口的抽象

    从面向对象角度来讲,接口的概念就是由C++非常容易实现,但是HAL很多代码是C语言描述的。
    这就需要一种技巧来实现面向对象。
    定义一种结构,子类的成员变量第一个类型是父类的结构就可以了。抽象方法可以用函数指针来实现。
    其实这个就是C++多态实现的基本原理,具体可参考《深入理解C++对象模型》

    2.2接口的稳定性

    Android已经把各个硬件都接口都统一定义在:

    libhardware/include/hardware/ 具体代码可以参考:https://github.com/CyanogenMod/android_hardware_libhardware/tree/cm-12.0/include/hardware

     

    3.Android显示设备:Gralloc &  FrameBuffer

    FrameBuffer是linux环境下显示设备的统一接口。从而让用户设备不需要做太多的操作,就可以适配多种显示设备。
    FramwBuffer本质上就是一套接口。android系统不会直接操作显示驱动,而通过HAL层来封装。而HAL中操作驱动的模块就是
    gralloc。

    3.1Gralloc模块的加载

    gralloc通过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;
            int i;
            err = framebuffer_open(module, &fbDev);
            ALOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
            
            err = gralloc_open(module, &grDev);
            ALOGE_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
            if(fbDev->numFramebuffers >= MIN_NUM_FRAME_BUFFERS &&
               fbDev->numFramebuffers <= MAX_NUM_FRAME_BUFFERS){
                mNumBuffers = fbDev->numFramebuffers;
            } else {
                mNumBuffers = MIN_NUM_FRAME_BUFFERS;
            }
            mNumFreeBuffers = mNumBuffers;
            mBufferHead = mNumBuffers-1;
    
            /*
             * This does not actually change the framebuffer format. It merely
             * fakes this format to surfaceflinger so that when it creates
             * framebuffer surfaces it will use this format. It's really a giant
             * HACK to allow interworking with buggy gralloc+GPU driver
             * implementations. You should *NEVER* need to set this for shipping
             * devices.
             */
    #ifdef FRAMEBUFFER_FORCE_FORMAT
            *((uint32_t *)&fbDev->format) = FRAMEBUFFER_FORCE_FORMAT;
    #endif
    
            for (i = 0; i < mNumBuffers; i++)
            {
                    buffers[i] = new NativeBuffer(
                            fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB);
            }
    
            for (i = 0; i < mNumBuffers; i++)
            {
                    err = grDev->alloc(grDev,
                            fbDev->width, fbDev->height, fbDev->format,
                            GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride);
    
                    ALOGE_IF(err, "fb buffer %d allocation failed w=%d, h=%d, err=%s",
                            i, fbDev->width, fbDev->height, strerror(-err));
    
                    if (err)
                    {
                            mNumBuffers = i;
                            mNumFreeBuffers = i;
                            mBufferHead = mNumBuffers-1;
                            break;
                    }
            }
    
            const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags; 
            const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi;
            const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi;
            const_cast<int&>(ANativeWindow::minSwapInterval) = 
                fbDev->minSwapInterval;
            const_cast<int&>(ANativeWindow::maxSwapInterval) = 
                fbDev->maxSwapInterval;
        } else {
            ALOGE("Couldn't get gralloc module");
        }
    
        ANativeWindow::setSwapInterval = setSwapInterval;
        ANativeWindow::dequeueBuffer = dequeueBuffer;
        ANativeWindow::queueBuffer = queueBuffer;
        ANativeWindow::query = query;
        ANativeWindow::perform = perform;
    
        ANativeWindow::dequeueBuffer_DEPRECATED = dequeueBuffer_DEPRECATED;
        ANativeWindow::lockBuffer_DEPRECATED = lockBuffer_DEPRECATED;
        ANativeWindow::queueBuffer_DEPRECATED = queueBuffer_DEPRECATED;
    }
    FramebufferNativeWindow

    我们继续深入看:

    galloc的父类,最终是:

    libhardwareincludehardwarehardware.h

    typedef struct hw_module_methods_t {
        /** Open a specific device */
        int (*open)(const struct hw_module_t* module, const char* id,
                struct hw_device_t** device);
    
    } hw_module_methods_t;

    只有一个open方法,也就是所有的厂商都需要实现开启设备的方法。

    看下fb的打开的代码:

    libhardwaremodulesgrallocframebuffer.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;
    }

    首先check设备名是否正确。

    分配dev的空间,这是一个壳。
    然后初始化dev。
    提供fb的核心接口
    内存映射
    status = mapFrameBuffer(m);

    然后是建立壳 & 核心间的关系。

    这样就打开了fb设备。

    在回到FrameBufferNativeWindow 可以看到:

            err = framebuffer_open(module, &fbDev);
            ALOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));
            
            err = gralloc_open(module, &grDev);
            ALOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err));

    fb打开的驱动信息在fbDev,gralloc打开的信息在grDev中。

    fbDev负责的是主屏幕,grDev负责图形缓冲去的分配和释放。

    所以FrameBufferNativeWindow控制这SurfaceFlinger的基础。

    4.FrameBufferNativeWindow

    4.1FramebufferNativeWindow

    在OpenGL中,我们不断提及本地窗口的概念,在Android中,native window一共由2个。
    一个是面向管理者(SurfaceFlinger)的 FramebufferNativeWindow
    另一个是面像APP的,surface。
    先来看第一种:
    首先看下定义的地方:
    class FramebufferNativeWindow 
        : public ANativeObjectBase<
            ANativeWindow, 
            FramebufferNativeWindow, 
            LightRefBase<FramebufferNativeWindow> >
    {

    ANativeWindow是什么东西?

    ANativeWindow是OpenGL 在android平台的显示类型。

    所以FramebufferNativeWindow就是一种Open GL可以显示的类型。

    FramebufferNativeWindow的构造函数上面已经贴出来了,进一步分析如下:

    1)加载module,上面已经分析过了。

    2)打开fb & gralloc,也已经分析过了。

    3)根据fb的设备属性,获得buffer数。这个buffer后面会解释。

    4)给每个buffer初始化,并分配空间。这里new NativeBuffer只是指定buffer的类型,或者分配了一个指针,但是没有分配内存,所以还需要alloc操作。

    5)为本地窗口属性赋值。

    目前buffer默认值是在2~3,后面会介绍3缓冲技术,就会用到3个buffer。

    双缓冲技术:

    把一组图画,画到屏幕上,画图是需要时间的,如果时间间隔比较长,图片就是一个一个的画在屏幕的,看上去就会卡。

    如果先把图片放在一个缓冲buffer中,待全部画好后,把buffer直接显示在屏幕上,这就是双缓冲技术。

    4.2dequeuebuffer

    int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, 
            ANativeWindowBuffer** buffer, int* fenceFd)
    {
        FramebufferNativeWindow* self = getSelf(window);
        Mutex::Autolock _l(self->mutex);
        framebuffer_device_t* fb = self->fbDev;
    
        int index = self->mBufferHead++;
        if (self->mBufferHead >= self->mNumBuffers)
            self->mBufferHead = 0;
    
        // wait for a free non-front buffer
        while (self->mNumFreeBuffers < 2) {
            self->mCondition.wait(self->mutex);
        }
        ALOG_ASSERT(self->buffers[index] != self->front);
    
        // get this buffer
        self->mNumFreeBuffers--;
        self->mCurrentBufferIndex = index;
    
        *buffer = self->buffers[index].get();
        *fenceFd = -1;
    
        return 0;
    }

    代码不多,但是却是核心功能,通过它来获取一块可渲染的buffer。

    1)获取FramebufferNativeWindow对象。为什么没有使用this 而是使用了传入ANativeWindow的方式,此处我们并不关心。

    2)获得一个Autolock的锁,函数结束,自动解锁。

    3)获取mBufferHead变量,这里自增,也就是使用下一个buffer,一共只有3个,(原因上面已经解释),所以循环取值。

    4)如果没有可用的缓冲区,等待bufferqueue释放。一旦获取后,可用buffer就自减

    5.Surface

    Surface是另一个本地窗口,主要和app这边交互。注意:app层java代码无法直接调用surface,只是概念上surface属于app这一层的。

    首先Surface是ANativeWindow的一个子类。

    可以推测,surface需要解决如下几个问题:

    1)面向上层(java层)提供画板。由谁来分配这块内存

    2)与SurfaceFlinger是什么关系

    Surface::Surface(
            const sp<IGraphicBufferProducer>& bufferProducer,
            bool controlledByApp)

    sp<IGraphicBufferProducer>& bufferProducer 是分配surface内存的。它到底是什么呢?


     先来看看从ViewRootImpl到获取surface的过程。
    ViewRootImpl持有一个java层的surface对象,开始是空的。
    后续的流程见上面的流程图。也就是-说ViewRootImpl持有的surface对象,最终是对SurfaceComposerClient的创建的surface的一个“引用”。
    由此分析可以看到 一个ISurfaceClient->ISurfaceComposerClient->IGraphicBufferProducer.当然binder需要一个实名的server来注册。
    在ServiceManager中可以看到,这些服务查询的是“SurfaceFlinger”。
    也就是,这些东东都是SurfaceFlinger的内容。
    SurfaceFlinger::SurfaceFlinger()
        :   BnSurfaceComposer(),

    SurfaceFlinger是BnSurfaceComposer的一个子类。也就是ISurfaceComposer的一个实现。

    surface虽然是为app层服务的,但是本质上还是由SurfaceFlinger来管理的。

    SurfaceFlinger怎么创建和管理surface,需要通过BufferQueue,将在下一篇讨论。

    参考:

    《深入理解android内核设计思想》 林学森




     
  • 相关阅读:
    (第三周)c#程序理解
    (第三周)使用visual studio 2015进行单元测试
    (第二周)软件工程第二周之四则运算
    (第二周)读《我是一只IT小小鸟》有感
    Jmeter--参数化的两种方法
    接口测试(1)
    自动化测试框架Cucumber和RobotFramework的对比
    selenium2+Python--学习进阶路线图
    Robot Framework--环境搭建(Mac)
    Selenium2+Python--python3.6 安装 xlrd 模块---Mac
  • 原文地址:https://www.cnblogs.com/deman/p/5584198.html
Copyright © 2020-2023  润新知