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

简介:

在前文中,我们分析了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服务在启动的过程中,会对系统的硬件帧缓冲区进行初始化,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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来实现的,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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来设备显示屏的实际度度、高度以及旋转方向,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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对象,如下所示:

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

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


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

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

1
2
3
4
5
6
7
8
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的实现比较长,我们分段来阅读:

1
2
3
4
5
6
7
8
9
10
11
12
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设备的实现。

       我们接着往下阅读代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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库。


       我们接着往下阅读代码:

1
2
3
4
5
6
7
8
9
// 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。有了这些配置信息之后,接下来就可以在硬件帧缓冲区上面创建系统的主绘图表面。

       我们接着往下阅读代码:

1
2
3
4
5
6
7
8
9
10
11
12
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的大小,以及是否支持部分更新、是否使用慢渲染方式等。


       我们接着往下阅读代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
  * 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时,都要将后端图形缓冲区的内容拷贝回那些不在那些需要更新的区域中去,这会导致性能低下。因此,系统一般都不支持不规则区域部分更新功能。

       我们接着往下阅读代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 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。

       我们接着往下阅读代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
     /*
      * 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了。


       我们接着往下阅读代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
     /*
      * 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扩展属性以及部分更新属性,那么将会优先使用部分更新属性,因为后者是直接在硬件上支持部分更新,因而性能会更好。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
     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的实现如下所示:

1
2
3
4
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了,这个成员函数的实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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类的构造函数的实现如下所示:

1
2
3
4
5
6
7
8
9
10
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的实现如下所示:

1
2
3
4
5
6
7
8
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;
}

      当硬件帧缓冲区的控制台被打开时,帧缓冲区驱动程序就创建一个/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的实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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的实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
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<NativeBuffer>的数组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中,它的实现比较长,我们分段来阅读:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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所描述的一个图形缓冲区数组的位置。

        我们接着往下阅读代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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类所管理的图形缓冲区都是直接在硬件帧缓冲区上分配的,而不是在匿名共享内存中分配的。


       我们接着往下阅读代码:

1
2
3
4
5
6
7
8
9
10
     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类的初始化过程。


       我们接着往下阅读代码:

1
2
3
4
5
6
7
     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的实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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的实现,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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服务的实现,敬请关注!





本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/1242680,如需转载请自行联系原作者
目录
相关文章
|
13天前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
13天前
|
算法 JavaScript Android开发
|
14天前
|
安全 搜索推荐 程序员
深入探索Android系统的碎片化问题及其解决方案
在移动操作系统的世界中,Android以其开放性和灵活性赢得了广泛的市场份额。然而,这种开放性也带来了一个众所周知的问题——系统碎片化。本文旨在探讨Android系统碎片化的现状、成因以及可能的解决方案,为开发者和用户提供一种全新的视角来理解这一现象。通过分析不同版本的Android系统分布、硬件多样性以及更新机制的影响,我们提出了一系列针对性的策略,旨在减少碎片化带来的影响,提升用户体验。
|
14天前
|
安全 Android开发 iOS开发
深入探索iOS与Android系统的差异性及优化策略
在当今数字化时代,移动操作系统的竞争尤为激烈,其中iOS和Android作为市场上的两大巨头,各自拥有庞大的用户基础和独特的技术特点。本文旨在通过对比分析iOS与Android的核心差异,探讨各自的优势与局限,并提出针对性的优化策略,以期为用户提供更优质的使用体验和为开发者提供有价值的参考。
|
4天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
6天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
8天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。
|
6天前
|
存储 API 开发工具
探索安卓开发:从基础到进阶
【10月更文挑战第37天】在这篇文章中,我们将一起探索安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和建议。我们将从安卓开发的基础开始,逐步深入到更复杂的主题,如自定义组件、性能优化等。最后,我们将通过一个代码示例来展示如何实现一个简单的安卓应用。让我们一起开始吧!
|
7天前
|
存储 XML JSON
探索安卓开发:从新手到专家的旅程
【10月更文挑战第36天】在这篇文章中,我们将一起踏上一段激动人心的旅程,从零基础开始,逐步深入安卓开发的奥秘。无论你是编程新手,还是希望扩展技能的老手,这里都有适合你的知识宝藏等待发掘。通过实际的代码示例和深入浅出的解释,我们将解锁安卓开发的关键技能,让你能够构建自己的应用程序,甚至贡献于开源社区。准备好了吗?让我们开始吧!
20 2
|
8天前
|
Android开发
布谷语音软件开发:android端语音软件搭建开发教程
语音软件搭建android端语音软件开发教程!