Android系统Surface机制的SurfaceFlinger服务对帧缓冲区(Frame Buffer)的管理分析

发表于 5年以前  | 总阅读数:3323 次

在前文中,我们分析了SurfaceFlinger服务的启动过程。SurfaceFlinger服务在启动的过程中,会对系统的硬件帧缓冲区进行初始化。由于系统的硬件帧缓冲区一般只有一个,并且不是谁都可以随便访问的,因此,它就需要由一个服务来统一管理。在Android系统中,这个服务便是SurfaceFlinger。在本文中,我们就详细分析SurfaceFlinger服务是如何管理系统的硬件帧缓冲区的。

从前面Android系统Surface机制的SurfaceFlinger服务简要介绍和学习计划一文可以知道,SurfaceFlinger服务通过一个GraphicPlane对象来描述系统的显示屏,即系统的硬件帧缓冲区。GraphicPlane类内部聚合了一个DisplayHardware对象,通过这个DisplayHardware对象就可以访问系统的硬件帧缓冲区。DisplayHardware类内部又包含了一个FramebufferNativeWindow对象,这个FramebufferNativeWindow对象才是真正用来描述系统的硬件帧缓冲区的。FramebufferNativeWindow类的作用类似于在前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文中所介绍的Surface类,它是连接OpenGL库和Android的UI系统的一个桥梁,OpenGL库就是通过这个桥梁来将Android系统的UI渲染到硬件帧缓冲区中去的。GraphicPlane、DisplayHardware和FramebufferNativeWindow这三个类的关系如图1所示。

图1 GraphicPlane、DisplayHardware和FramebufferNativeWindow的类关系图

接下来,我们就分别介绍GraphicPlane、DisplayHardware和FramebufferNativeWindow这三个类的实现,以便可以理解SurfaceFlinger服务是如何通过它们来管理系统的硬件帧缓冲区的。

从前面Android系统Surface制的SurfaceFlinger服务的启动过程分析一文可以知道,SurfaceFlinger服务在启动的过程中,会对系统的硬件帧缓冲区进行初始化,如下所示:

status_t SurfaceFlinger::readyToRun()  
    {
        ......

        // 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);  
        }  

        ......

        // 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();  
        ......
        hw.makeCurrent();

        ......  
    }

这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。

这个代码段首先创建了一个DisplayHardware对象,用来初始化编号为0的GraphicPlane对象,接着再将这个DisplayHardware对象设置为系统当前活动的DisplayHardware对象,这就相当于是将编号为0的GraphicPlane对象所描述的显示屏设置为系统当前活动的显示屏。

接下来,我们就首先分析编号为0的GraphicPlane对象的初始化过程,接着再分析DisplayHardware对象的创建过程。

编号为0的GraphicPlane对象的初始化过程主要是调用GraphicPlane类的成员函数setDisplayHardware来实现的,如下所示:

void GraphicPlane::setDisplayHardware(DisplayHardware *hw)
    {
        mHw = hw;

        // initialize the display orientation transform.
        // it's a constant that should come from the display driver.
        int displayOrientation = ISurfaceComposer::eOrientationDefault;
        char property[PROPERTY_VALUE_MAX];
        if (property_get("ro.sf.hwrotation", property, NULL) > 0) {
            //displayOrientation
            switch (atoi(property)) {
            case 90:
                displayOrientation = ISurfaceComposer::eOrientation90;
                break;
            case 270:
                displayOrientation = ISurfaceComposer::eOrientation270;
                break;
            }
        }

        const float w = hw->getWidth();
        const float h = hw->getHeight();
        GraphicPlane::orientationToTransfrom(displayOrientation, w, h,
                &mDisplayTransform);
        if (displayOrientation & ISurfaceComposer::eOrientationSwapMask) {
            mDisplayWidth = h;
            mDisplayHeight = w;
        } else {
            mDisplayWidth = w;
            mDisplayHeight = h;
        }

        setOrientation(ISurfaceComposer::eOrientationDefault);
    }

这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。

函数首先设置显示屏的初始大小和旋转方向。GraphicPlane类有三个成员变量mDisplayWidth、mDisplayHeight和mDisplayTransform,前两者的类型为float,分别用描述显示屏的初始宽度和高度,而后者的类型为Transform,用来描述显示屏的初始旋转矩阵。Transform类的作用是来描述变换矩阵,以便后面在渲染UI时可以用来动态地计算显示屏的大小和旋转方向等。

显示屏的初始化宽度和高度是由参数hw所描述的一个DisplayHardware对象来描述的,而显示屏的初始旋转方向则是由名称为"ro.sf.hwrotation"的系统属性来决定的。如果没有设置名称为"ro.sf.hwrotation"的系统属性,那么显示屏的旋转方向就为默认方向,即ISurfaceComposer::eOrientationDefault。

获得了显示屏的初始化宽度w、高度h和旋转方向displayOrientation之后,函数接着就调用GraphicPlane类的静态成员函数orientationToTransfrom来将它们构造成一个变换矩阵,并且保存在GraphicPlane类的成员变量mDisplayTransform中。

函数接下来继续判断显示屏的初始化旋转方向是否将初始化宽度和高度值翻转了。如果翻转了,那么就需要相互调换GraphicPlane类的成员变量mDisplayWidth和mDisplayHeight的值,以便可以正确地反映显示屏的初始化宽度和高度。

注意,显示屏的初始宽度、高度和旋转方向一经初始化之后,就会保持不变,以后显示屏的实际旋转方向计算都是要在此基础上进行计算的,即要在变换矩阵mDisplayTransform的基础上进行计算。从这里还可以看出,通过设置名称为"ro.sf.hwrotation"的系统属性的值,就可以设置系统显示屏的初始化旋转方向,以便匹配实际的硬件帧缓冲区的旋转方向。

函数最后调用GraphicPlane类的成员函数setOrientation来设备显示屏的实际度度、高度以及旋转方向,如下所示:

status_t GraphicPlane::setOrientation(int orientation)
    {
        // If the rotation can be handled in hardware, this is where
        // the magic should happen.

        const DisplayHardware& hw(displayHardware());
        const float w = mDisplayWidth;
        const float h = mDisplayHeight;
        mWidth = int(w);
        mHeight = int(h);

        Transform orientationTransform;
        GraphicPlane::orientationToTransfrom(orientation, w, h,
                &orientationTransform);
        if (orientation & ISurfaceComposer::eOrientationSwapMask) {
            mWidth = int(h);
            mHeight = int(w);
        }

        mOrientation = orientation;
        mGlobalTransform = mDisplayTransform * orientationTransform;
        return NO_ERROR;
    }

这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。

参数orientation的值等于ISurfaceComposer::eOrientationDefault,即SurfaceFlinger服务在初始化系统显示屏时,会将它的旋转方向设置为默认值,以后再根据实际情况来做调整。

GraphicPlane类有三个成员变量mWidth、mHeight和mOrientation,它们的类型均为int,分别用来描述显示屏的实际宽度、高度和旋转方向。与成员变量mDisplayWidth、mDisplayHeight和mDisplayTransform所描述的显示屏初始化宽度、高度和旋转矩阵一经初始化后就保持不变不同,mWidth、mHeight和mOrientation这三个成员变量是会动态变化的。例如,当显示屏由LANDSCAPE变为PORTRAIT模式时,mWidth、mHeight和mOrientation这三个成员变量就会相应地发生改变。

函数首先将显示屏的实际宽度mWidth、高度mHeight和旋转方向mOrientation设置为显示屏的初始宽度mDisplayWidth、高度mDisplayHeight以及参数orientation所描述的旋转方向,接着再调用GraphicPlane类的静态成员函数orientationToTransfrom来将它们构造成一个变换矩阵orientationTransform。

函数接着判断显示屏的实际旋转方向orientation是否将原来的实际宽度和高度值翻转了。如果翻转了,那么就需要相互调换GraphicPlane类的成员变量mWidth和mHeight的值,以便可以正确地反映显示屏的实际宽度和高度。

函数最后将用来描述显示屏的初始化旋转方向的变换矩阵mDisplayTransform和用来描述显示屏的实际旋转方向的变换矩阵orientationTransform相乘,就可以得到一个全局变换矩阵,并且保存在GraphicPlane类的成员变量mGlobalTransform中。这样以后渲染UI时,对于一个任意的点向量,只要将它乘以全局变换矩阵mGlobalTransform,那么就可以得到它所描述的实际位置。

至此,编号为0的GraphicPlane对象的初始化过程就完成了,以后SurfaceFlinger服务就可以调用它的成员函数displayHardware来获得它内部的一个DisplayHardware对象,如下所示:

const DisplayHardware& GraphicPlane::displayHardware() const {
        return *mHw;
    }

这个函数定义在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp文件中。

接下来,回到前面SurfaceFlinger类的成员函数readyToRun中,我们通过DisplayHardware对象的创建过程来分析DisplayHardware类的实现。

在创建DisplayHardware对象的过程中,会调用DisplayHardware类的构造函数,如下所示:

DisplayHardware::DisplayHardware(
            const sp<SurfaceFlinger>& flinger,
            uint32_t dpy)
        : DisplayHardwareBase(flinger, dpy),
          mFlags(0)
    {
        init(dpy);
    }

这个函数定义在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp中。

从这里可以看出,DisplayHardware类是从DisplayHardwareBase类继承下来的。接下来,我们首先继续分析一个DisplayHardware对象的初始化过程,接着再分析这个DisplayHardware对象的父对象DisplayHardwareBase的初始化过程,以便可以了解DisplayHardwareBase类的实现。

一个DisplayHardware对象的初始化过程是通过调用DisplayHardware类的成员函数init来实现的。DisplayHardware类的成员函数init的实现比较长,我们分段来阅读:

void DisplayHardware::init(uint32_t dpy)
    {
        mNativeWindow = new FramebufferNativeWindow();
        framebuffer_device_t const * fbDev = mNativeWindow->getDevice();
        mDpiX = mNativeWindow->xdpi;
        mDpiY = mNativeWindow->ydpi;
        mRefreshRate = fbDev->fps;

        mOverlayEngine = NULL;
        hw_module_t const* module;
        if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {
            overlay_control_open(module, &mOverlayEngine);
        }

这段代码首先是创建了一个FramebufferNativeWindow对象,并且保存在DisplayHardware类的成员变量mNativeWindow中,用来管理硬件帧缓冲区。有了这个FramebufferNativeWindow对象之后,就可以通过它里面的一个fb设备来获得硬件帧缓冲区的点密度以及刷新频率等信息。后面我们再详细分析FramebufferNativeWindow类的实现。

这段代码接着再加载HAL层中的overlay模块,目的是要打开系统的overlay设备。在Android系统中,我们可以将overlay看作是一种特殊的Surface,一般用来显示视频。在这一系列文章中,我们暂时不关心overlay设备的实现。

我们接着往下阅读代码:

    EGLint w, h, dummy;
        EGLint numConfigs=0;
        EGLSurface surface;
        EGLContext context;

        // initialize EGL
        EGLint attribs[] = {
                EGL_SURFACE_TYPE,   EGL_WINDOW_BIT,
                EGL_NONE,           0,
                EGL_NONE
        };

        // debug: disable h/w rendering
        char property[PROPERTY_VALUE_MAX];
        if (property_get("debug.sf.hw", property, NULL) > 0) {
            if (atoi(property) == 0) {
                LOGW("H/W composition disabled");
                attribs[2] = EGL_CONFIG_CAVEAT;
                attribs[3] = EGL_SLOW_CONFIG;
            }
        }

这段代码主要用来设置一个EGL属性数组attribs,以便接下来可以根据这个属性数组的值来正确的初始化EGL库。

我们接着往下阅读代码:

    // TODO: all the extensions below should be queried through
        // eglGetProcAddress().

        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);
        LOGE_IF(err, "couldn't find an EGLConfig matching the screen format");

这段代码首先调用eglGetDisplay和eglInitialize函数来获得和初始化OpengGL库的默认显示屏,接着再调用EGLUtils::selectConfigForNativeWindow函数来获得前面所创建的一个FramebufferNativeWindow对象所描述的系统主绘图表面的配置信息,并且保存在EGLConfig对象config。有了这些配置信息之后,接下来就可以在硬件帧缓冲区上面创建系统的主绘图表面。

我们接着往下阅读代码:

    EGLint r,g,b,a;
        eglGetConfigAttrib(display, config, EGL_RED_SIZE,   &r);
        eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
        eglGetConfigAttrib(display, config, EGL_BLUE_SIZE,  &b);
        eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);

        if (mNativeWindow->isUpdateOnDemand()) {
            mFlags |= PARTIAL_UPDATES;
        }

        if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy) == EGL_TRUE) {
            if (dummy == EGL_SLOW_CONFIG)
                mFlags |= SLOW_CONFIG;
        }

这段代码主要是用来获得系统主绘图表面的一些属性,例如,四个颜色分量R、G、B和A的大小,以及是否支持部分更新、是否使用慢渲染方式等。

我们接着往下阅读代码:

    /*
         * Create our main surface
         */

        surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
        eglQuerySurface(display, surface, EGL_WIDTH,  &mWidth);
        eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight);

        if (mFlags & PARTIAL_UPDATES) {
            // if we have partial updates, we definitely don't need to
            // preserve the backbuffer, which may be costly.
            eglSurfaceAttrib(display, surface,
                    EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
        }

        if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) {
            if (dummy == EGL_BUFFER_PRESERVED) {
                mFlags |= BUFFER_PRESERVED;
            }
        }

这段代码首先调用函数eglCreateWindowSurface来创建系统的主绘图表面。系统的主绘图表面是直接在硬件帧缓冲区上创建的,用来渲染系统的UI,即负责合成和渲染所有应用程序的UI。

这段代码接着还获得系统的主绘图表面的宽度和高度,分别保存在并且保存在DisplayHardware类的成员变量mWidth和mHeight中。

这段代码最后还判断硬件帧缓冲区是否支持部分更新。如果支持的话,就会在调用函数eglSwapBuffers来渲染系统的UI时,不保留后端图形缓冲区的内容,因为保留是有代价的。如果不支持的话,那么就会就会调用函数eglQuerySurface来检查在调用函数eglSwapBuffers来渲染系统的UI时是否需要保留后端图形缓冲区的内容。如果需要的话,那么就会将DisplayHardware类的成员变量mFlags的BUFFER_PRESERVED位设置为1。在保留后端图形缓冲区的内容的情况下,系统就可以支持仅仅渲染那些需要更新的脏区域,这些区域可以是不规则的。然而,实现不规则区域部分更新功能是有代价的,因为每次在渲染UI时,都要将后端图形缓冲区的内容拷贝回那些不在那些需要更新的区域中去,这会导致性能低下。因此,系统一般都不支持不规则区域部分更新功能。

我们接着往下阅读代码:

    /* Read density from build-specific ro.sf.lcd_density property
         * except if it is overridden by qemu.sf.lcd_density.
         */
        if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) {
            if (property_get("ro.sf.lcd_density", property, NULL) <= 0) {
                LOGW("ro.sf.lcd_density not defined, using 160 dpi by default.");
                strcpy(property, "160");
            }
        } else {
            /* for the emulator case, reset the dpi values too */
            mDpiX = mDpiY = atoi(property);
        }
        mDensity = atoi(property) * (1.0f/160.0f);

这段代码用来设备系统的主绘图表面的点密度信息。系统的主绘图表面的点密度信息可以通过名称为"qemu.sf.lcd_density"或者"ro.sf.lcd_density"的系统属性来配置。如果没有配置,那么默认值就为160dpi。

我们接着往下阅读代码:

    /*
         * Create our OpenGL ES context
         */


        EGLint contextAttributes[] = {
    #ifdef EGL_IMG_context_priority
    #ifdef HAS_CONTEXT_PRIORITY
    #warning "using EGL_IMG_context_priority"
            EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG,
    #endif
    #endif
            EGL_NONE, EGL_NONE
        };
        context = eglCreateContext(display, config, NULL, contextAttributes);

        mDisplay = display;
        mConfig  = config;
        mSurface = surface;
        mContext = context;
        mFormat  = fbDev->format;
        mPageFlipCount = 0;

这段代码主要是调用函数eglCreateContext来创建系统的主绘图表面的上下文。有了这个上下文之后,OpenGL库就能够在前面所创建的系统主绘图表面上渲染系统的UI了。

我们接着往下阅读代码:

    /*
         * Gather OpenGL ES extensions
         */

        eglMakeCurrent(display, surface, surface, context);

        GLExtensions& extensions(GLExtensions::getInstance());
        extensions.initWithGLStrings(
                glGetString(GL_VENDOR),
                glGetString(GL_RENDERER),
                glGetString(GL_VERSION),
                glGetString(GL_EXTENSIONS),
                eglQueryString(display, EGL_VENDOR),
                eglQueryString(display, EGL_VERSION),
                eglQueryString(display, EGL_EXTENSIONS));

        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
        glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims);


    #ifdef EGL_ANDROID_swap_rectangle
        if (extensions.hasExtension("EGL_ANDROID_swap_rectangle")) {
            if (eglSetSwapRectangleANDROID(display, surface,
                    0, 0, mWidth, mHeight) == EGL_TRUE) {
                // This could fail if this extension is not supported by this
                // specific surface (of config)
                mFlags |= SWAP_RECTANGLE;
            }
        }
        // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE
        // choose PARTIAL_UPDATES, which should be more efficient
        if (mFlags & PARTIAL_UPDATES)
            mFlags &= ~SWAP_RECTANGLE;
    #endif

这段代码主要用来检查系统的主绘图表面是否支持EGL_ANDROID_swap_rectangle扩展属性。如果支持的话,那么每次在调用函数eglSwapBuffers来渲染UI时,都会使用软件的方式来支持部分更新区域功能,即:先得到不在新脏区域里面的那部分旧脏区域的内容,然后再将得到的这部分旧脏区域的内容拷贝回到要渲染的新图形缓冲区中去,这要求每次在渲染UI时,都要将被渲染的图形缓冲区以及对应的脏区域保存下来。注意,如果系统的主绘图表面同时支持EGL_ANDROID_swap_rectangle扩展属性以及部分更新属性,那么将会优先使用部分更新属性,因为后者是直接在硬件上支持部分更新,因而性能会更好。

我们接着往下阅读最后一段代码:

    LOGI("EGL informations:");
        LOGI("# of configs : %d", numConfigs);
        LOGI("vendor    : %s", extensions.getEglVendor());
        LOGI("version   : %s", extensions.getEglVersion());
        LOGI("extensions: %s", extensions.getEglExtension());
        LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported");
        LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);

        LOGI("OpenGL informations:");
        LOGI("vendor    : %s", extensions.getVendor());
        LOGI("renderer  : %s", extensions.getRenderer());
        LOGI("version   : %s", extensions.getVersion());
        LOGI("extensions: %s", extensions.getExtension());
        LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize);
        LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims);
        LOGI("flags = %08x", mFlags);

        // Unbind the context from this thread
        eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    }

这段代码首先调用日志接口LOGI来显示系统的主绘图表面的属性信息,接着最调用函数eglMakeCurrent来取消 设置OpenGL库在当前线程的绘图表面以及绘图上下文。

从这里就可以看出,一个DisplayHardware对象在初始化完成之后,它还不能直接用来渲染系统的UI,因为它所初始化的的绘图表面以及绘图上下文并没有作为当前线程的绘图表面以及绘图上下文。这是由于SurfaceFlinger服务可以同时支持多个DisplayHardware对象,即同时支持多个显示屏造成的。

从前面SurfaceFlinger类的成员函数readyToRun可以知道,当前正在初始化的DisplayHardware对象的编号为0,并且它是在SurfaceFlinger服务的UI渲染线程中创建的,为了可以将它设置系统的主显示屏,即主绘图表面,SurfaceFlinger类的成员函数readyToRun接下来还会调用它的成员函数makeCurrent来将它所里面的绘图表面以及绘图上下文设置为SurfaceFlinger服务的UI渲染线程的绘图表面以及绘图上下文。

DisplayHardware类的成员函数makeCurrent的实现如下所示:

void DisplayHardware::makeCurrent() const
    {
        eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);
    }

这个函数定义在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp中。

DisplayHardware类的成员函数makeCurrent的实现很简单,它只是通过调用函数eglMakeCurrent来将前面已经创建好的绘图表面以及绘图上下文设置为当前线程的绘图表面以及绘图上下文,即设置为SurfaceFlinger服务的UI渲染线程的绘图表面以及绘图上下文。

系统的硬件帧缓冲区在初始化完成之后,SurfaceFlinger服务以后就可以调用用来描述它的一个DisplayHardware对象的成员函数flip来在它上面渲染系统的UI了,这个成员函数的实现如下所示:

void DisplayHardware::flip(const Region& dirty) const
    {
        checkGLErrors();

        EGLDisplay dpy = mDisplay;
        EGLSurface surface = mSurface;

    #ifdef EGL_ANDROID_swap_rectangle    
        if (mFlags & SWAP_RECTANGLE) {
            const Region newDirty(dirty.intersect(bounds()));
            const Rect b(newDirty.getBounds());
            eglSetSwapRectangleANDROID(dpy, surface,
                    b.left, b.top, b.width(), b.height());
        }
    #endif

        if (mFlags & PARTIAL_UPDATES) {
            mNativeWindow->setUpdateRectangle(dirty.getBounds());
        }

        mPageFlipCount++;
        eglSwapBuffers(dpy, surface);
        checkEGLErrors("eglSwapBuffers");

        // for debugging
        //glClearColor(1,0,0,0);
        //glClear(GL_COLOR_BUFFER_BIT);
    }

这个函数定义在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp中。

这个函数主要就是调用OpenGL库中的函数eglSwapBuffers来将系统的UI渲染到系统的主绘图表面上去的,即渲染到系统的硬件帧缓冲区上去的。在渲染之前,函数会首先判断系统的主绘图表面是否支持EGL_ANDROID_swap_rectangle扩展属性和部分更新属性。如果支持EGL_ANDROID_swap_rectangle扩展属性,即DisplayHardware类的成员变量mFlags的SWAP_RECTANGLE位等于1,那么就需要调用函数eglSetSwapRectangleANDROID来设置要渲染的区域,以便在渲染UI时,可以通过软件的方式来支持部分更新。如果硬件帧缓冲区直接支持部分更新属性,即DisplayHardware类的成员变量mFlags的PARTIAL_UPDATES位等于1,那么就需要调用DisplayHardware类的成员变量mNativeWindow所描述的一个本地窗口的成员函数setUpdateRectangle来设置要更新的那一部分区域。

DisplayHardware类的成员函数flip在调用函数eglSwapBuffers来渲染UI之前,实际上需要通过其成员变量mNativeWindow所描述的一个本地窗口(FramebufferNativeWindow)来获得一个空闲的图形缓冲区,然后才可以将UI数据写入到这个空闲的图形缓冲区中去,最后再渲染到硬件帧缓冲区中去。前面提到,FramebufferNativeWindow类的作用类似于在前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文中所介绍的Surface类,不过它里面所维护的图形缓冲区是直接在硬件帧缓冲区上创建的,后面我们在分析FramebufferNativeWindow类的实现时,再详细分析。

至此,我们就分析完成DisplayHardware类的实现了,接下来我们还需要继续介绍它的父类DisplayHardwareBase的实现,以便可以了解DisplayHardware类的另外一个作用,即它还会创建一个线程来监控硬件帧缓冲区的睡眠和唤醒事件。分析完成DisplayHardwareBase类的实现之后,我们最后再分析FramebufferNativeWindow类的实现。

前面在分析DisplayHardware类的实现时提到,DisplayHardware对象在创建的过程中,会对其父类对象DisplayHardwareBase进行初始化,因此,接下来我们就从DisplayHardwareBase对象的初始化过程入手,来分析DisplayHardwareBase类的实现。不过,在分析DisplayHardwareBase类的实现之前,我们首先看看它的类关系图,如图2所示。

图2 DisplayHardwareBase类关系图

DisplayHardwareBase类一方面用来控制SurfaceFlinger服务当前是否能够访问显示屏。当显示屏处于唤醒状态时,DisplayHardwareBase类的成员变量mScreenAcquired的值就会等于1,表示SurfaceFlinger服务就可以访问显示屏;而当显示屏处于睡眠状态时,DisplayHardwareBase类的成员变量mScreenAcquired的值就会等于0,表示SurfaceFlinger服务不可以访问显示屏。

显示屏的唤醒/睡眠状态切换是由内核来通知DisplayHardwareBase类的,因此,DisplayHardwareBase类会通过一个线程来监控显示屏的唤醒/睡眠状态切换。这个线程是通过DisplayHardwareBase类的成员变量mDisplayEventThread来描述的。DisplayHardwareBase类的成员变量mDisplayEventThread所描述的线程的类型要么是DisplayEventThread,要么是ConsoleManagerThread,这两者均是从DisplayEventThreadBase类继续下来的,而后者又是从Thread类继承下来的。当硬件帧缓冲区的控制台被打开时,DisplayHardwareBase类的成员变量mDisplayEventThread所描述的线程的类型就是DisplayEventThread;当硬件帧缓冲区的控制台没有被打开时,DisplayHardwareBase类的成员变量mDisplayEventThread所描述的线程的类型就是ConsoleManagerThread。这里我们只考虑硬件帧缓冲区的控制台被打开的情况。

用来监控显示屏唤醒/睡眠状态切换的线程是在DisplayHardwareBase对象的初始化过程中创建的,它运行起来之后,就会在一个无限循环中不断地监控显示屏唤醒/睡眠状态切换事件。为了方便描述,我们将这个线程称为控制台事件监控线程。DisplayEventThreadBase类的成员变量mFlinger指向了SurfaceFlinger服务,一旦控制台事件监控线程监控到显示屏发生唤醒/睡眠状态切换,那么就会通过它来通知SurfaceFlinger服务。

控制台事件监控线程的运行过程大概上这样的。在每一次循环中,控制台事件监控线程首先监控显示屏是否要进入睡眠状态了。如果是的话,那么该线程就会通过DisplayEventThreadBase类的成员变量mFlinger来通知SurfaceFlinger服务,并且等待SurfaceFlinger服务处理完成这个通知。SurfaceFlinger服务一旦处理完成显示屏进入睡眠状态的事件,它就会调用DisplayHardwareBase类的成员函数releaseScreen来将其成员变量mScreenAcquired的值设置为0,表示它目前不可以访问显示屏。控制台事件监控线程接下来就会等待显示屏被唤醒过来。一旦显示屏被唤醒过来,那么该线程就会通过DisplayEventThreadBase类的成员变量mFlinger来通知SurfaceFlinger服务。SurfaceFlinger服务得到这个通知之后,就会调用DisplayHardwareBase类的成员函数acquireScreen来将其成员变量mScreenAcquired的值设置为1,表示它目前可以访问显示屏。在下一篇文章分析SurfaceFlinger服务的线程模型时,我们再详细分析这个过程。

DisplayHardwareBase类另一方面用来控制SurfaceFlinger服务当前是否能够在显示屏上渲染UI。当系统的其它组件请求SurfaceFlinger服务关闭显示屏时,SurfaceFlinger服务就会调用DisplayHardwareBase类的成员函数setCanDraw来将其成员变量mCanDraw的值设置为0;而当系统的其它组件请求SurfaceFlinger服务打开显示屏时,SurfaceFlinger服务就会调用DisplayHardwareBase类的成员函数setCanDraw来将其成员变量mCanDraw的值设置为1。只有当DisplayHardwareBase类的成员变量mScreenAcquired和mCanDraw的值均等于1时,SurfaceFlinger服务才可以在显示屏上渲染系统的UI。为了方便SurfaceFlinger服务判断它当前是否可以在显示屏上渲染系统的UI,DisplayHardwareBase类提供了另外一个成员函数canDraw。当DisplayHardwareBase类的成员函数canDraw的返回值等于true时,就表示SurfaceFlinger服务可以在显示屏上渲染系统的UI,否则就不可以。

了解了DisplayHardwareBase类的作用之后,接下来我们就从它的构造函数开始分析它的初始化过程。

DisplayHardwareBase类的构造函数的实现如下所示:

DisplayHardwareBase::DisplayHardwareBase(const sp<SurfaceFlinger>& flinger,
            uint32_t displayIndex)
        : mCanDraw(true), mScreenAcquired(true)
    {
        mDisplayEventThread = new DisplayEventThread(flinger);
        if (mDisplayEventThread->initCheck() != NO_ERROR) {
            // fall-back on the console
            mDisplayEventThread = new ConsoleManagerThread(flinger);
        }
    }

这个函数定义在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp中。

函数首先创建一个类型为DisplayEventThread的线程。如果这个线程能够通过初始化检查,即DisplayEventThread类的成员函数initCheck的返回值等于NO_ERROR,那么SurfaceFlinger服务就会使用这个类型为DisplayEventThread的线程来监控显示屏的睡眠/唤醒状态切换事件,否则的话,函数接下来就会创建另外一个类型为ConsoleManagerThread的线程来监控显示屏的睡眠/唤醒状态切换事件。

DisplayEventThread类的成员函数initCheck的实现如下所示:

status_t DisplayHardwareBase::DisplayEventThread::initCheck() const
    {
        return (((access(kSleepFileName, R_OK) == 0 &&
                access(kWakeFileName, R_OK) == 0) ||
                (access(kOldSleepFileName, R_OK) == 0 &&
                access(kOldWakeFileName, R_OK) == 0)) &&
                access(kFbconSysDir, F_OK) != 0) ? NO_ERROR : NO_INIT;
    }

这个函数定义在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp中。

kSleepFileName、kWakeFileName、kOldSleepFileName、kOldWakeFileName和kFbconSysDir是五个字符串常量,它们的定义如下所示:

static char const * kSleepFileName = "/sys/power/wait_for_fb_sleep";
    static char const * kWakeFileName = "/sys/power/wait_for_fb_wake";
    static char const * const kOldSleepFileName = "/sys/android_power/wait_for_fb_sleep";
    static char const * const kOldWakeFileName = "/sys/android_power/wait_for_fb_wake";

    // This dir exists if the framebuffer console is present, either built into
    // the kernel or loaded as a module.
    static char const * const kFbconSysDir = "/sys/class/graphics/fbcon";

当硬件帧缓冲区的控制台被打开时,帧缓冲区驱动程序就创建一个/sys/class/graphics/fbcon目录,以及创建/sys/power/wait_for_fb_sleep和/sys/power/wait_for_fb_wake或者/sys/android_power/wait_for_fb_sleep和/sys/android_power/wait_for_fb_wake文件,用来通知用户空间显示屏即将要进入睡眠/唤醒状态了。

回到DisplayHardwareBase类的构造函数中,最终创建出来的线程对象保存在其成员变量mDisplayEventThread中。DisplayHardwareBase类的mDisplayEventThread是一个类型为DisplayEventThreadBase的强指针,从前面Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析一文可以知道,当一个强指针第一次引用一个对象的时候,这个对象的成员函数onFirstRef就会被调用,因此,接下来我们就继续分析DisplayEventThreadBase类的成员函数onFirstRef的实现,看看它在里面做了一件什么事情。

DisplayEventThreadBase类的成员函数onFirstRef的实现如下所示:

class DisplayHardwareBase
    {
        ......

    private:
        class DisplayEventThreadBase : public Thread {
            ......

        public:
            ......

            virtual void onFirstRef() {
                run("DisplayEventThread", PRIORITY_URGENT_DISPLAY);
            }

            ......
        };

        ......
    };

这个函数定义在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.h中。

DisplayEventThreadBase类的成员函数onFirstRef主要就调用父类Thread的成员函数run来创建一个名称为"DisplayEventThread"的线程,用来监控显示屏的睡眠/唤醒状态切换事件。这个线程在创建完成之后,首先会调用DisplayEventThread类的成员函数readyToRun来执行一些初始化操作,接下来不断地循环调用DisplayEventThread类的成员函数threadLoop来监控显示屏的睡眠/唤醒状态切换事件。接下来,我们就主要分析这个线程的初始化操作,即DisplayEventThread类的成员函数readyToRun的实现,在接下来的一篇文章中分析SurfaceFlinger服务的线程模型时,再详细分析这个线程监控显示屏的睡眠/唤醒状态切换事件的过程,即DisplayEventThread类的成员函数threadLoop的实现。

DisplayEventThread类的成员函数readyToRun的实现如下所示:

status_t DisplayHardwareBase::DisplayEventThread::readyToRun()
    {
        if (access(kSleepFileName, R_OK) || access(kWakeFileName, R_OK)) {
            if (access(kOldSleepFileName, R_OK) || access(kOldWakeFileName, R_OK)) {
                LOGE("Couldn't open %s or %s", kSleepFileName, kWakeFileName);
                return NO_INIT;
            }
            kSleepFileName = kOldSleepFileName;
            kWakeFileName = kOldWakeFileName;
        }
        return NO_ERROR;
    }

这个函数定义在文件frameworks/base/services/surfaceflinger/DisplayHardware/DisplayHardwareBase.cpp中。

DisplayEventThread类的成员函数readyToRun的实现很简单,它首先判断/sys/power/wait_for_fb_sleep和/sys/power/wait_for_fb_wake文件是否存在。如果存在的话,那么就通过它们来监控显示屏的睡眠/唤醒状态切换事件,否则的话,就通过/sys/android_power/wait_for_fb_sleep和/sys/android_power/wait_for_fb_wake文件来监控显示屏的睡眠/唤醒状态切换事件。如果这四个文件都不存在,那么就说明硬件帧缓冲区的控制台没有被打开了,这时候就不能使用类型为DisplayEventThread的线程来监控显示屏的睡眠/唤醒状态切换事件。

至此,我们就分析完成DisplayHardwareBase类的实现了,接下来我们继续分析FramebufferNativeWindow类的实现,以便可以了解它是如何管理硬件帧缓冲区的。

在分析FramebufferNativeWindow类的实现之前,我们首先看看它的类关系图,如图3所示。

图3 FramebufferNativeWindow类关系

前面提到,FramebufferNativeWindow类与在前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文中提到的Surface类的作用是类似的。FramebufferNativeWindow类一方面用来在OpenGL库和Android本地窗口系统之间建立连接,这样,我们就可以使用它的成员函数dequeueBuffer来为OpenGL库分配空闲图形缓冲区,以及使用它的成员函数queueBuffer来将OpenGL已经填充好UI数据的图形缓冲区渲染到硬件帧缓冲区中去。FramebufferNativeWindow类另一方面还继承了LightRefBase类,因此,从前面Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析一文可以知道,FramebufferNativeWindow类对象可以结合Android系统的轻量级指针sp来使用,以便可以自动维护生命周期。

FramebufferNativeWindow类与Surface类又有不同的地方。从前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文可以知道,Surface类使用的图形缓冲区一般是在匿名共享内存中分配的,并且是由SurfaceFlinger服务来负责分配,然后再传递给应用程序进程使用的,而FramebufferNativeWindow类使用的图形缓冲区是直接在硬件帧缓冲区分配的,并且它可以直接将这些图形缓冲区渲染到硬件帧缓冲区中去。从前面Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文可以知道,要从硬件帧缓冲区中分配和渲染图形缓冲区,就必须要将HAL层中的Gralloc模块加载到当前的进程空间来,并且打开里面的gralloc设备和fb设备,其中,gralloc设备用来分配图形缓冲区,而fb设备用来渲染图形缓冲区。因此,FramebufferNativeWindow类包含了一个类型的alloc_device_t的成员变量grDev和一个类型为framebuffer_device_t的成员变量fbDev,它们分别指向HAL层中的Gralloc模块的gralloc设备和fb设备。

FramebufferNativeWindow类在内部还包含了一个类型为sp的数组buffers,用来描述OpenGL库可以使用的图形缓冲区,数组的大小等于NUM_FRAME_BUFFERS,即等于硬件帧缓冲区能够提供的图形缓冲区的个数。例如,在Android 2.3系统中,硬件帧缓冲区能够提供的图形缓冲区的个数等于2,这意味着Android系统可以使用双缓冲区技术来渲染系统的UI。由于OpenGL库所使用的图形缓冲区必须要实现android_native_buffer_t接口,因此,NativeBuffer类继承了android_native_buffer_t类。此外,NativeBuffer类还继承了LightRefBase类,因此,它的对象就和FramebufferNativeWindow类对象一样,可以结合Android系统的轻量级指针sp来使用,以便可以自动维护生命周期。

了解了FramebufferNativeWindow类的作用之后,接下来我们就从它的构造函数开始分析它的实现,即分析它的类对象的创建过程。从前面DisplayHardware类的成员函数init的实现可以知道,FramebufferNativeWindow对象是在DisplayHardware对象初始化的过程中创建的,并且包含在DisplayHardware对象内部中,用来管理硬件帧缓冲区。

FramebufferNativeWindow类的构造函数定义在文件frameworks/base/libs/ui/FramebufferNativeWindow.cpp中,它的实现比较长,我们分段来阅读:

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);
            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 = NUM_FRAME_BUFFERS;
            mNumFreeBuffers = NUM_FRAME_BUFFERS;
            mBufferHead = mNumBuffers-1;

这段代码首先调用函数hw_get_module来将HAL层中的Gralloc模块加载到当前进程来,并且调用函数framebuffer_open和gralloc_open分别打开Gralloc模块中的fb设备和gralloc设备。这三个过程的实现可以参考前面Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文。Gralloc模块中的fb设备和gralloc设备打开之后,就分别保存在FramebufferNativeWindow类的成员变量fbDev和grDev中。

这段代码接着判断硬件帧缓冲区是否支持部区更新UI。如果支持的话,那么从Gralloc模块中打开的fb设备的成员函数setUpdateRect就不等于0。这时候这段代码就会FramebufferNativeWindow类的成员变量mUpdateOnDemand的值设置为true。

这段代码最后还会将FramebufferNativeWindow类的成员变量mNumBuffers和mNumFreeBuffers的值均设置为NUM_FRAME_BUFFERS,它们分别用来描述硬件帧缓冲区可以提供的图形缓冲区的个数,以及当前可用的空闲图形缓冲区的个数。此外,FramebufferNativeWindow类的成员变量mBufferHead的值还会被设置为(NUM_FRAME_BUFFERS - 1),表示下一个可用的空闲图形缓冲区在FramebufferNativeWindow类的成员变量buffers所描述的一个图形缓冲区数组的位置。

我们接着往下阅读代码:

        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);

                    LOGE_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;
                    }
            }

这段代码用来创建FramebufferNativeWindow类的成员变量buffers所描述的一个图形缓冲区数组。每一个图形缓冲区都使用一个NativeBuffer对象来描述,并且这些图形缓冲区都是通过调用HAL层中的Gralloc模块的gralloc设备的成员函数alloc来分配的。注意,在分配图形缓冲区时,指定的标志,即第5个参数的值为GRALLOC_USAGE_HW_FB。这意味着FramebufferNativeWindow类所管理的图形缓冲区都是直接在硬件帧缓冲区上分配的,而不是在匿名共享内存中分配的。

我们接着往下阅读代码:

        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 {
            LOGE("Couldn't get gralloc module");
        }

这段代码主要就是用来设置FramebufferNativeWindow的父类ANativeWindow的成员变量flags、xdpi、ydpi、minSwapInternal和maxSwapInterval的值,以便OpenGL库可以知道系统当前所使用的硬件帧缓冲区的一些属性,例如,点密度、缓冲区数据交换时间间隔等信息。这些成员变量的具体含义可以参考前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文所提到的Surface类的初始化过程。

我们接着往下阅读代码:

    ANativeWindow::setSwapInterval = setSwapInterval;
        ANativeWindow::dequeueBuffer = dequeueBuffer;
        ANativeWindow::lockBuffer = lockBuffer;
        ANativeWindow::queueBuffer = queueBuffer;
        ANativeWindow::query = query;
        ANativeWindow::perform = perform;
    }

这段代码用来设置FramebufferNativeWindow的父类ANativeWindow的成员函数setSwapInterval、dequeueBuffer、lockBuffer、queueBuffer、query和perform,它们都是OpenGL的回调接口,分别指向FramebufferNativeWindow类的静态成员函数setSwapInterval、dequeueBuffer、lockBuffer、queueBuffer、query和perform。与前面Android应用程序请求SurfaceFlinger服务创建Surface的过程分析一文所提到的Surface类一样,我们只关注FramebufferNativeWindow类的静态成员函数dequeueBuffer和queueBuffer的实现,因为它们负责用来为OpenGL库分配和渲染图形缓冲区。

FramebufferNativeWindow类的成员函数dequeueBuffer的实现如下所示:

int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window,
            android_native_buffer_t** buffer)
    {
        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;

        GraphicLog& logger(GraphicLog::getInstance());
        logger.log(GraphicLog::SF_FB_DEQUEUE_BEFORE, index);

        // wait for a free buffer
        while (!self->mNumFreeBuffers) {
            self->mCondition.wait(self->mutex);
        }
        // get this buffer
        self->mNumFreeBuffers--;
        self->mCurrentBufferIndex = index;

        *buffer = self->buffers[index].get();

        logger.log(GraphicLog::SF_FB_DEQUEUE_AFTER, index);
        return 0;
    }

这个函数定义在文件frameworks/base/libs/ui/FramebufferNativeWindow.cpp中。

参数window虽然是一个类型为ANativeWindow的指针,但是它指向的实际上是一个FramebufferNativeWindow对象,这个FramebufferNativeWindow对象是在DisplayHardware类的成员函数init中创建的,因此,函数在开始的地方就可以将它转换一个FramebufferNativeWindow对象self。

有了FramebufferNativeWindow对象self之后,我们就可以在它内部的图形缓冲区数组buffers中获取下一个空闲图形缓冲区。前面提到,下一个空闲图形缓冲区的在数组buffer中的位置就保存在FramebufferNativeWindow对象self的成员变量mBufferHead中。因此,函数就可以将FramebufferNativeWindow对象self的成员变量mBufferHead的值取出来保存在变量index中,以便接下来可以从FramebufferNativeWindow对象self内部的图形缓冲区数组buffers中取出一个图形缓冲区。此外,函数还需要将FramebufferNativeWindow对象self的成员变量mBufferHead增加1,以便它可以指向下一个空闲的图形缓冲区。注意,FramebufferNativeWindow对象self内部的图形缓冲区数组buffers是循环使用的,因此,在将它的成员变量mBufferHead增加1之后,要判断它的值是否已经大于等于数组的大小,如果大于等于的话,就需要将它的值设置为0,即绕回到前面去。

从前面的分析可以知道,FramebufferNativeWindow对象self内部可用的空闲图形缓冲区的个数保存在其成员变量mNumFreeBuffers中,因此,当这个成员变量的值等于0的时候,就表示FramebufferNativeWindow对象self没有空闲的空闲图形缓冲区可用,这时候当前线程就会通过FramebufferNativeWindow对象self的成员变量mCondition所描述的一个条件变量进入到睡眠等待状态,直到有可用的空闲图形缓冲区为止。什么时候FramebufferNativeWindow对象self内部才会有可用的空闲图形缓冲区呢?当OpenGL库请求FramebufferNativeWindow对象self将一个图形缓冲区的内容渲染到硬件帧缓冲区之后,FramebufferNativeWindow对象self就会获得一个可用的空闲图形缓冲区了,后面我们分析FramebufferNativeWindow类的成员函数queueBuffer的实现时就会看到这个逻辑。

一旦FramebufferNativeWindow对象self内部有可用的空闲图形缓冲区,那么函数就会将这个空闲图形缓冲区就会返回给OpenGL库,即保存在输出参数buffer,并且将FramebufferNativeWindow对象self内部可用的空闲图形缓冲区的个数减1,以及将OpenGL库当前正前正在使用的图形缓冲区在数组buffers中的位置保存在FramebufferNativeWindow对象self的成员变量mCurrentBufferIndex中。

至此,我们就分析完成FramebufferNativeWindow类的成员函数dequeueBuffer的实现了,接下来我们继续分析FramebufferNativeWindow类的成员函数queueBuffer的实现,如下所示:

int FramebufferNativeWindow::queueBuffer(ANativeWindow* window,
            android_native_buffer_t* buffer)
    {
        FramebufferNativeWindow* self = getSelf(window);
        Mutex::Autolock _l(self->mutex);
        framebuffer_device_t* fb = self->fbDev;
        buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle;

        const int index = self->mCurrentBufferIndex;
        GraphicLog& logger(GraphicLog::getInstance());
        logger.log(GraphicLog::SF_FB_POST_BEFORE, index);

        int res = fb->post(fb, handle);

        logger.log(GraphicLog::SF_FB_POST_AFTER, index);

        self->front = static_cast<NativeBuffer*>(buffer);
        self->mNumFreeBuffers++;
        self->mCondition.broadcast();
        return res;
    }

这个函数定义在文件frameworks/base/libs/ui/FramebufferNativeWindow.cpp中。

参数window指向的实际上也是一个FramebufferNativeWindow对象,这个FramebufferNativeWindow对象是在DisplayHardware类的成员函数init中创建的,因此,函数在开始的地方同样是先将它转换一个FramebufferNativeWindow对象self。

参数buffer指向的是一个实际类型为NativeBuffer的图形缓冲区,这个图形缓冲区是在FramebufferNativeWindow类的成员函数dequeueBuffer中分配的,如前所述。

FramebufferNativeWindow类的成员函数queueBuffer目标就是要将参数buffer所描述的图形缓冲区渲染到硬件帧缓冲区中去,因此,我们就需要获得FramebufferNativeWindow对象self的成员变量fbDev所描述的一个fb设备。有了这个fb设备之后, 我们就可以调用它的成员函数post来将参数buffer所描述的图形缓冲区渲染到硬件帧缓冲区中去,这个过程可以参考Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析一文。

参数buffer所描述的图形缓冲区被渲染到硬件帧缓冲区中去之后,它就变成一个空闲的图形缓冲区了,因此,我们就需要将它返回给FramebufferNativeWindow对象self内部的图形缓冲区数组buffers中去,并且将可用的空闲图形缓冲区的个数增加1,最后通过FramebufferNativeWindow对象self的成员变量mCondition所描述的一个条件变量将前面正在等待从FramebufferNativeWindow对象self内部分配空闲图形缓的线程唤醒。

至此,FramebufferNativeWindow类的成员函数queueBuffer的实现就分析完成了,FramebufferNativeWindow类的实现也分析完成了。通过GraphicPlane、DisplayHardware和FramebufferNativeWindow这三个类的实现,我们就可以知道SurfaceFlinger服务是如何管理系统的显示屏,即系统的硬件帧缓冲区的了。

从SurfaceFlinger服务创建一个DisplayHardwareBase对象来管理系统的显示屏的过程可以知道,这个DisplayHardwareBase对象会创建一个控制台事件监控线程来监控硬件帧缓冲区的睡眠/唤醒状态切换事件,而从前面Android系统Surface制的SurfaceFlinger服务的启动过程分析一文又可以知道,System进程在启动SurfaceFlinger服务过程中,又会创建一个Binder线程池,以及为SurfaceFlinger服务创建一个UI渲染线程,这样在SurfaceFlinger服务中,就存在三种不同类型的线程,在接下来的一篇文章中,我们就将分析详细SurfaceFlinger服务的线程模型,以便最后我们就可以更好地分析SurfaceFlinger服务的实现,敬请关注!

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 目录