我是靠谱客的博主 丰富烧鹅,最近开发中收集的这篇文章主要介绍Suface & Suface Flinger知识,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述


android surfaceflinger研究----显示系统

2011-12-04 18:46 4844人阅读 评论(11) 收藏 举报

   这周抽空研究了一下SurfaceFlinger,发现真正复杂的并不是SurfaceFlinger本身,而是androiddisplay显示系统,网上关于这部分的介绍有不少,本不打算写的,但是发现还是记录一下研究代码的过程比较好,一是能够帮助自己理清思路,另一个原因就是以后当这块内容忘记的时候,能快速的通过这个记录捡起来。

    .  android显示系统的建立

    我们看SurfaceFlinger的定义就知道,它其实是一个Thread,因此SurfaceFlinger的初始化工作就理所当然的放在了SurfaceFlinger线程中,详见readyToRun()@SurfaceFlinger.cpp

   SurfaceFlinger对于显示的管理是通过一个或多个GraphicPlane对象(目前android只实现了一个)来管理的,

@SurfaceFlinger.h

[cpp] view plaincopy

1. GraphicPlane                mGraphicPlanes[1];  

    其实,GraphicPlane类只是一个wrapper层,目的是当android支持多个显示系统时,通过该类来管里各自的图形系统,显示系统真正的初始化工作是通过DisplayHardware类来初始化底层图形系统的管理与显示的。真正的图形显示系统的初始化在init()@DisplayHardware.cpp

    目前,android支持一个图形系统,这个图形系统是全局的,surfaceflinger可以访问,其他不通过surfaceflinger进行图形处理的application也可以对其进行操作。

    1. FrameBuffer的建立

   framebuffer,确切的是说是linux下的framebuffer,,它是linux图形显示系统中一个与图形硬件无关的抽象层,user完全不用考虑我们的硬件设备,而仅仅使用framebuffer就可以实现对屏幕的操作。

   androidframebuffer并没有被SurfaceFlinger直接使用,而是在framebuffer外做了一层包装,这个包装就是FramebufferNativeWindow,我们来看一下FramebufferNativeWindow的创建过程。

  我们的framebuffer是由一个设备符fbDev来表示的,它是FramebufferNativeWindow的一个成员,我们来分析一下对fbDev的处理过程。

    1.1. fbDev设备符

    1.1.1 gralloc library

   在这之前,先介绍一下gralloc library,它的形态如grallocBOARDPLATFORM.so BOARDPLATFORM可以从属性ro.board.platform中获得,这篇文章中我们以Qualcomm msmx7x30为例,也就是gralloc.msm7x30.so中,它的源路径在hardware/msm7k/libgralloc-qsd8k

   framebuffer的初始化需要通过HAL gralloc.msm7x30.so 来完成与底层硬件驱动的适配,但是gralloclibrary并不是平台无关的,不同的vendor可能会实现自己的gralloc library,因此为了保证在创建framebuffer时能够平台无关,android只能是动态的判断并使用当前的gralloc libraryandroid通过从gralloc library中再抽象出一个hw_module_t结构来供使用,它为framebuffer的初始化提供了需要的gralloc.msm7x30.so业务。因此通过这个hw_module_t结构我们就不需要知道当前系统使用的到底是哪个gralloc library。按规定,所有gralloc library中的这个结构体被命名为HAL_MODULE_INFO_SYM(HMI)。当前分析的系统中,HAL_MODULE_INFO_SYMhardware/msm7k/libgralloc-qsd8k/galloc.cpp

    1.1.2 打开fbDev设备符    

   下面看如何打开 打开fbDev设备符。通过HAL_MODULE_INFO_SYM提供的gralloc.msm7x30.so的接口我们调用到了fb_device_open()@hardware/msm7k/libgralloc-qsd8kframebuffer.cpp

 

[cpp] view plaincopy

1. int fb_device_open(hw_module_t const* module, const char* name,  

2.         hw_device_t** device)  

3. {  

4.     int status = -EINVAL;  

5.     if (!strcmp(name, GRALLOC_HARDWARE_FB0)) {  

6.         alloc_device_t* gralloc_device;  

7.         status = gralloc_open(module, &gralloc_device);  

8.   

9.         /* initialize our state here */  

10.         fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev));  

11.         memset(dev, 0, sizeof(*dev));  

12.   

13.         /* initialize the procs */  

14.         dev->device.common.tag = HARDWARE_DEVICE_TAG;  

15.   

16.         private_module_t* m = (private_module_t*)module;  

17.         status = mapFrameBuffer(m);  

18.   

19. }  

 

在这个函数中,主要为fbDev设备符指定一个fb_context_t实例,并通过函数mapFrameBuffer()对设备节点/dev/graphics/fb0进行操作,操作的目的有:

1.获得屏幕设备的信息,并将屏幕信息保存在HAL_MODULE_INFO_SYM(上面代码中的module)中。

 2./dev/graphics/fb0请求page flip模式,page

 flip模式需要至少2个屏幕大小的bufferpage flip模式在后面介绍。目前android系统中设置为2个屏幕大小的buffer。当然屏幕设备可能不支持page flip模式。

mapFrameBufferLocked()@hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp

[cpp] view plaincopy

1. /* 

2.  * Request NUM_BUFFERS screens (at lest 2 for page flipping) 

3.  */  

4. info.yres_virtual = info.yres * NUM_BUFFERS;  

5.   

6.   

7. uint32_t flags = PAGE_FLIP;  

8. if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) {  

9.     info.yres_virtual = info.yres;  

10.     flags &= ~PAGE_FLIP;  

11.     LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported");  

12. }  

 

3. 映射屏幕设备缓存区给fbDev设备符。

mapFrameBufferLocked()@hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp

[cpp] view plaincopy

1. /* 

2.  * map the framebuffer 

3.  */  

4.   

5. int err;  

6. size_t fbSize = roundUpToPageSize(finfo.line_length * info.yres_virtual);  

7. module->framebuffer = new private_handle_t(dup(fd), fbSize,  

8.         private_handle_t::PRIV_FLAGS_USES_PMEM);  

9.   

10. module->numBuffers = info.yres_virtual / info.yres;  

11. module->bufferMask = 0;  

12.   

13. void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);  

14. if (vaddr == MAP_FAILED) {  

15.     LOGE("Error mapping the framebuffer (%s)", strerror(errno));  

16.     return -errno;  

17. }  

18. module->framebuffer->base = intptr_t(vaddr);  

19. memset(vaddr, 0, fbSize);  

 

 

1.2 grDev设备符

在为framebuffer,也就是FramebufferNativeWindow申请内存之前,我们还要介绍一个概念,就是grDev设备符。它虽然也叫设备符,但是它和具体的设备没有直接关系,我们看它的类型就是知道了alloc_device_t,没错,grDev设备符就是为了FramebufferNativeWindow管理内存使用的。为FramebufferNativeWindow提供了申请/释放内存的接口。



    1.3 FramebufferNativeWindow内存管理

    FramebufferNativeWindow维护了2buffer 

[cpp] view plaincopy

1. sp<NativeBuffer> buffers[2];  

 

    1.3.1 屏幕设备支持page filp模式

    目前的android系统默认要求屏幕设备给系统映射2个屏幕大小的缓存区,以便支持pageflip模式,如果屏幕设备支持pageflip模式,那么FramebufferNativeWindowbuffers将分别指向一个屏幕大小的屏幕设备缓存区。

[cpp] view plaincopy

1. // create a "fake" handles for it  

2. intptr_t vaddr = intptr_t(m->framebuffer->base);  

3. private_handle_t* hnd = new private_handle_t(dup(m->framebuffer->fd), size,  

4.                                              private_handle_t::PRIV_FLAGS_USES_PMEM |  

5.                                              private_handle_t::PRIV_FLAGS_FRAMEBUFFER);  

6.   

7. // find a free slot  

8. for (uint32_t i=0 ; i<numBuffers ; i++) {  

9.     if ((bufferMask & (1LU<<i)) == 0) {  

10.         m->bufferMask |= (1LU<<i);  

11.         break;  

12.     }  

13.     vaddr += bufferSize;  

14. }  

15.   

16. hnd->base = vaddr;  

17. hnd->offset = vaddr - intptr_t(m->framebuffer->base);  

18. *pHandle = hnd;  

 

    1.3.2 屏幕设备不支持page flip模式

    mapFrameBufferLocked()@hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp中可以得知,如果屏幕设备不支持pageflip模式,那么numBuffer值将为1而不是2,那么映射过来的屏幕缓存区将只有一个屏幕大小,不够支持pageflip模式,那么此时将不使用这一个屏幕大小的屏幕缓存区,而改为去dev/pmem设备去申请。

gralloc_alloc_framebuffer_locked()@hardware/msm7k/libgralloc-qsd8k/gpu.cpp

[cpp] view plaincopy

1.     const uint32_t bufferMask = m->bufferMask;  

2.     const uint32_t numBuffers = m->numBuffers;  

3.     const size_t bufferSize = m->finfo.line_length * m->info.yres;  

4.     if (numBuffers == 1) {  

5.         // If we have only one buffer, we never use page-flipping. Instead,  

6.         // we return a regular buffer which will be memcpy'ed to the main  

7.         // screen when post is called.  

8.         int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D;  

9.         return gralloc_alloc_buffer(bufferSize, newUsage, pHandle);  

10.     }  

 

    2. 打开Overlay

    同选择gralloc library相似,根据属性值来选择何时的overlay库,如果vendor厂商没有提供overlay库的话,那么系统将使用默认的overlayoverlay.default.so。同样的我们获得overlay库的HAL_MODULE_INFO_SYM结构体,作为系统调用overlay的接口。

 

[cpp] view plaincopy

1. if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) {  

2.     overlay_control_open(module, &mOverlayEngine);  

3. }  

 

    3. 选择OpenGL ES library(也即软/硬件加速)

    OpenGL(Open Graphics Library)[3] is a standard specification defining across-language, cross-platform API for writing applications that produce 2D and3D computer graphics. The interface consists of over 250 different functioncalls which can be used to draw complex three-dimensional scenes from simpleprimitives. OpenGL was developed by Silicon Graphics Inc. (SGI) in 1992[4] andis widely used in CAD, virtual reality, scientific visualization, informationvisualization, flight simulation, and video games. OpenGL is managed by thenon-profit technology consortium Khronos Group.

    android是默认支持OpenGL ES软件加速的,librarylibGLES_android,源码路径为frameworksbaseopengllibagl;如果手机设备支持硬件加速的话,那么复杂的图像处理工作将交由GPU去处理,那么效率将大大提高。但是如果系统真的存在硬件加速,它是如何选择何时用软件加速?何时用硬件加速的呢?

    如何查看是否有GPU来实现硬件加速,很容易查看/system/lib/egl/egl.cfg文件内容

[java] view plaincopy

1. 0 0 android  

2. 0 1 adreno200  

    因此只要我们的移动设备芯片集成了GPU,并提供了对应的GL图形库,那么我们就可以在我们的工程中device目录下的egl.cfg文件中加入类似上面的配置,那么我们的系统就会支持硬件加速。

adreno200 GPU提供的GL图形库:

[cpp] view plaincopy

1. libGLESv1_CM_adreno200.so  

2. libGLESv2_adreno200.so  

3. libEGL_adreno200.so  

    那么假如我们的系统中软硬件加速都支持了,那么我们从代码来看能不能让用户自由的选择加速类型,我们带着问题来研究一下代码。

   3.1 OpenGL初始化

    在调用不管是软件加速的还是硬件加速的OpenGL api之前,我们都需要把软硬两种模式的各自的OpenGL api提取出来,抽象出一个interface来供系统使用,这个过程我称之为OpenGL初始化过程。

    软硬两种模式的OpenGL api被分别指定到了一个全局数组的对应位置。
frameworks/base/opengl/libs/EGL/egl.cpp

[cpp] view plaincopy

1. static egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];  

[cpp] view plaincopy

1. enum {  

2.     IMPL_HARDWARE = 0,  

3.     IMPL_SOFTWARE,  

4.     IMPL_NUM_IMPLEMENTATIONS  

5. };  


gEGLImpl[IMPL_HARDWARE]
中保存着硬件图形设备的OpenGL api地址,从

[cpp] view plaincopy

1. libGLESv1_CM_adreno200.so  

2. libGLESv2_adreno200.so  

3. libEGL_adreno200.so  

3个库中获得;gEGLImpl[IMPL_SOFTWARE]中保存着软件的OpenGL api地址,从libGLES_android.so中获取。

 

这部分代码在egl_init_drivers_locked()@frameworks/base/opengl/libs/EGL/egl.cpp

 

3.2 EGLGLES api

    OpenGL的初始化过程中,OpenGL提供了两套api,分别称为EGLGLESandroidOPENGL初始化过程中,会将两种不同的接口分开管理,从下面代码中我们可以看到EGLGLES api地址被存储到了不同的位置。

@frameworksbaseopengllibsEGLLoader.h

[cpp] view plaincopy

1. enum {  

2.     EGL         = 0x01,  

3.     GLESv1_CM   = 0x02,  

4.     GLESv2      = 0x04  

5. };  

load_driver()@frameworksbaseopengllibsEGLLoader.cpp

 

上面枚举的EGL表示ELG apiGLESvq1_CM表示OpenGL ES 1.0apiGLESv2表示OpenGL ES 2.0api

EGL api地址最终被存储在gEGLImpl[].egl中;

GLESvq1_CM api地址最终被存储在gEGLImpl[].hooks[GLESv1_INDEX]->gl中;

GLESv2 api地址最终被存储在gEGLImpl[].hooks[GLESv2_INDEX]->gl中;

 

3.2.1 EGL api

    EGLis an interface between Khronos rendering APIs such as OpenGL ES or OpenVG andthe underlying native platform window system. It handles graphics contextmanagement, surface/buffer binding, and rendering synchronization and enableshigh-performance, accelerated, mixed-mode 2D and 3D rendering using otherKhronos APIs.

   上面引用了官方的定义,可以看出,EGL是系统和OPENGL ES之间的接口,它的声明在文件frameworksbaseopengllibsEGLegl_entries.in

 

3.2.2 GLES

    GLES才是真正的OpenGL ESapi,它的声明我们可以在frameworksbaseopengllibsentries.in找到。目前的android系统不但将EGL提供给系统使用,同时将GLES也提供给了系统使用,这个我们可以在最开始的显示系统的结构图中可以看到,surfacefligerframeworkopengl模块均可以访问EGLGLES接口。

 

3.3 OpenGL config

    每个OpenGL库都根据不同的像素格式(pixelformat)提供了一系统的configandroid根据framebuffer中设置的像素格式来选择合适的configandroid根据中各config中的属性信息来创建main surfaceopenGL上下文。

3.3.1 系统默认pixel format

    当前的代码分析是基于gingerbread的,在mapFrameBufferLocked()@hardware/msm7k/libgralloc-qsd8k/framebuffer.cpp中我们可以找到framebufferpixel format的类型

[cpp] view plaincopy

1.    if(info.bits_per_pixel == 32) {  

2. /* 

3. * Explicitly request RGBA_8888 

4. */  

5.   

6. /* Note: the GL driver does not have a r=8 g=8 b=8 a=0 config, so if we do 

7. * not use the MDP for composition (i.e. hw composition == 0), ask for 

8. * RGBA instead of RGBX. */  

9. if (property_get("debug.sf.hw", property, NULL) > 0 && atoi(property) == 0)  

10.     module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888;  

11. else if(property_get("debug.composition.type", property, NULL) > 0 && (strncmp(property, "mdp", 3) == 0))  

12.     module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888;  

13. else  

14.     module->fbFormat = HAL_PIXEL_FORMAT_RGBA_8888;  

15.    } else {  

16. /* 

17. * Explicitly request 5/6/5 

18. */  

19. module->fbFormat = HAL_PIXEL_FORMAT_RGB_565;  

20.    }  



目前的移动设备都是真彩色,所以这里我们认为我们的屏幕设备支持的是HAL_PIXEL_FORMAT_RGBA_8888

    

3.3.2config初始化

所有的OpenGL库提供的config,同样需要将软硬两种模式的各自的OpenGL config提取出来供系统使用,如同OpenGL api地址一样。OpenGL config提取出来后保存在另外一个全局变量

[cpp] view plaincopy

1. static egl_display_t gDisplay[NUM_DISPLAYS];  

[cpp] view plaincopy

1. //  EGLDisplay are global, not attached to a given thread  

2. const unsigned int NUM_DISPLAYS = 1;  

中,不同于gEGLImpl分开保存软硬件api,所有的config,不论软硬件的,均保存在gDisplay[0],因为所有的config是以屏幕区分的,同一块屏幕应该保存同一份config信息。



在提取出的openGLconfig时,会保存到gDisplay[0].config中,在这儿有一个很tricky的实现,它保证了硬件加速器的优先使用!



[cpp] view plaincopy

1. <strong>  </strong>      // sort our configurations so we can do binary-searches  

2.         qsort(  dp->configs,  

3.                 dp->numTotalConfigs,  

4.                 sizeof(egl_config_t), cmp_configs);<strong>  

5. </strong>  

最终,上述代码会将gDisplay[0].config中的配置按照先硬件的,后软件的规则做一个总体的排序。

代码在eglInitialize()@frameworks/base/opengl/libs/EGL/egl.cpp



3.3.3config选择

上文说到,android会根据framebufferpixel format信息来获取对应的config,这个过程只选择一个合适的config,选到为止。



3.3.3.1满足属性要求

并不是所有的config都可以被选择,首先这个config的属性需要满足

init()@DisplayHardware.cpp

[cpp] view plaincopy

1. // initialize EGL  

2. EGLint attribs[] = {  

3.         EGL_SURFACE_TYPE,   EGL_WINDOW_BIT,  

4.         EGL_NONE,           0,  

5.         EGL_NONE  

6. };  

3.3.3.2满足RGBA要求

pixelflinger中,为系统提供了各个pixel format的基本信息,RGBA值,字节数/pixel,位数/pixel

system/core/libpixelflinger/format.cpp

[cpp] view plaincopy

1. static GGLFormat const gPixelFormatInfos[] =  

2. {   //          Alpha    Red     Green   Blue  

3.     {  0,  0, {{ 0, 0,   0, 0,   0, 0,   0, 0 }},        0 },   // PIXEL_FORMAT_NONE  

4.     {  4, 32, {{32,24,   8, 0,  16, 8,  24,16 }}, GGL_RGBA },   // PIXEL_FORMAT_RGBA_8888  

android会根据pixelflingerpixel

 format信息,去和openGLconfig比较,得到想要的config



selectConfigForPixelFormat()@frameworks/base/libs/ui/EGLUtils.cpp

[cpp] view plaincopy

1. EGLConfig* const configs = (EGLConfig*)malloc(sizeof(EGLConfig)*numConfigs);  

2. if (eglChooseConfig(dpy, attrs, configs, numConfigs, &n) == EGL_FALSE) {  

3.     free(configs);  

4.     return BAD_VALUE;  

5. }  

6.   

7. const int fbSzA = fbFormatInfo.getSize(PixelFormatInfo::INDEX_ALPHA);  

8. const int fbSzR = fbFormatInfo.getSize(PixelFormatInfo::INDEX_RED);  

9. const int fbSzG = fbFormatInfo.getSize(PixelFormatInfo::INDEX_GREEN);  

10. const int fbSzB = fbFormatInfo.getSize(PixelFormatInfo::INDEX_BLUE);   

11.   

12. int i;  

13. EGLConfig config = NULL;  

14. for (i=0 ; i<n ; i++) {  

15.     EGLint r,g,b,a;  

16.     EGLConfig curr = configs[i];  

17.     eglGetConfigAttrib(dpy, curr, EGL_RED_SIZE,   &r);  

18.     eglGetConfigAttrib(dpy, curr, EGL_GREEN_SIZE, &g);  

19.     eglGetConfigAttrib(dpy, curr, EGL_BLUE_SIZE,  &b);  

20.     eglGetConfigAttrib(dpy, curr, EGL_ALPHA_SIZE, &a);  

21.     if (fbSzA <= a && fbSzR <= r && fbSzG <= g && fbSzB  <= b) {  

22.         config = curr;  

23.         break;  

24.     }  

25. }  

 

    4. 创建main surface

    要让OpenGL进行图形处理,那么需要在OpenGL中创建一个openGL surface。代码在eglCreateWindowSurface()@frameworks/base/opengl/libs/EGL/egl.cpp

调用当前的config所处的openGL库的api来创建surface。通过validate_display_config()方法来获取当前configopenGL api

创建的surface会和FramebufferNativeWindow关联到一起。

    5. 创建 OpenGL ES 上下文

   An OpenGL context represents many things. A context storesall of the state associated with this instance of OpenGL. It represents the(potentially visible) default framebufferthat rendering commands will draw to when not drawing to a framebuffer object. Think of a context as an object that holdsall of OpenGL; when a context is destroyed, OpenGL is destroyed.

  http://www.opengl.org/wiki/OpenGL_context

 具体的创建过程专业术语太多,也没有仔细研究不再介绍。

    6. 绑定contextsurface

    有了surface,有了FramebufferNativeWindow,有了context,基本上与图形系统相关的概念都有了,下一步就是把这几个概念关联起来,在创建surface时已经将surfaceFramebufferNativeWindow关联了起来。

   eglMakeCurrent()@frameworks/base/opengl/libs/EGL/egl.cpp

6.1多线程支持

OpenGL 提供了多线程的支持,有以下2点的支持:

1. 一个Context只能被一个线程使用,不能存在多个线程使用同一个context。因此在多线层操作中使用到了TLS技术,即Thread-local storage,来保证context被唯一使用。

makeCurrent()@frameworks/base/opengl/libs/libagl/egl.cpp

[cpp] view plaincopy

1.     ogles_context_t* current = (ogles_context_t*)getGlThreadSpecific();  

2.     if (gl) {  

3.         egl_context_t* c = egl_context_t::context(gl);  

4.         if (c->flags & egl_context_t::IS_CURRENT) {  

5.             if (current != gl) {  

6.                 // it is an error to set a context current, if it's already  

7.                 // current to another thread  

8.                 return -1;  

9.             }  

10.         } else {  

11.             if (current) {  

12.                 // mark the current context as not current, and flush  

13.                 glFlush();  

14.                 egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT;  

15.             }  

16.         }  

17.         if (!(c->flags & egl_context_t::IS_CURRENT)) {  

18.             // The context is not current, make it current!  

19.             setGlThreadSpecific(gl);  

20.             c->flags |= egl_context_t::IS_CURRENT;  

21.         }  

2. 在同一进程中,对于不同的线程对OpenGL库的访问,可能使用的GLES api version不同,同样可以使用TLS技术来保证多线程过程中,不同线程调用各自的GLESapi

前面我们介绍过GLES api地址被存放在gEGLImpl[].hooks[VERSION]->gl中,因此为保证多线程支持,androidgEGLImpl[].hooks[VERSION]保存到了TLS中,这样就实现了不同线程各自调用各自版本的GLES api

eglMakeCurrent()@frameworks/base/opengl/libs/EGL/egl.cpp

[cpp] view plaincopy

1. // cur_c has to be valid here (but could be terminated)  

2. if (ctx != EGL_NO_CONTEXT) {  

3.     setGlThreadSpecific(c->cnx->hooks[c->version]);  

4.     setContext(ctx);  

5.     _c.acquire();  

6. else {  

7.     setGlThreadSpecific(&gHooksNoContext);  

8.     setContext(EGL_NO_CONTEXT);  

9. }  


尽管openGL 实现了多线程的支持,目前我从代码中别没有找到多线程的使用。

6.2设置surfacecontext之间的关系

由于vendor厂商提供的GPUGLES库是不可见的,因此以libGLES_android.so软件加速为例来说明这个过程。

contex中保存着两个surfacereaddraw,多数情况下这两个surface为同一个surface

设置FramebufferNativeWindowBuffers[2]之一为surface的数据区,通过connect()bindDrawSurface()。最终的形态如下图所示:







init()@DisplayHardware.cpp中,在绑定surfacecontext之后,马上在当前线程中unbindcontext,通过

[cpp] view plaincopy

1. // Unbind the context from this thread  

2. eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);  

这么做的目的应该是支持多display系统中的特殊处理,目的是当系统有多个display系统的话,那么surfaceflinger就会去定义多个DisplayHardware对象,那么为了保证下一个DisplayHardware对象的创建不受影响,在当前的DisplayHardware创建完成后,将context从当前的进程中unbind掉。

不过没关系,在所有的DisplayHardware创建完成之后,surfaceflinger会重新bind Display系统的contextsurface

readyToRun()@SurfaceFlinger.cpp

[cpp] view plaincopy

1. // initialize primary screen  

2. // (other display should be initialized in the same manner, but  

3. // asynchronously, as they could come and go. None of this is supported  

4. // yet).  

5. const GraphicPlane& plane(graphicPlane(dpy));  

6. const DisplayHardware& hw = plane.displayHardware();  

7. const uint32_t w = hw.getWidth();  

8. const uint32_t h = hw.getHeight();  

9. const uint32_t f = hw.getFormat();  

10. hw.makeCurrent();  



下图为这个图形系统的类图结构。

 

android surfaceflinger研究----Surface机制

2011-12-10 20:20 4328人阅读 评论(11) 收藏 举报

    前一篇文章介绍了android的显示系统,这篇文章中,我们把视角往上层移动一下,研究一下framework是如何与surfaceflinger进行业务交互的。如何创建surface,如何显示窗口等等,所有的这一切都是通过系统服务WindowManagerService与surfaceflinger来进行的。

   android中的Surface机制这一块代码写的比较难理解,光叫Surface的类就有3个,因此本篇文章从两部分来分析,首先,想要理解Surface机制,还是需要首先理清各个类之间的关系。其次,在理解了整个Surface机制的类关系之后,到时我们再结合前一篇文章中对显示系统的介绍,研究一下一个Surface是如何和显示系统建立起联系来的,这个联系主要是指Surface的显示buffer的存储管理。在下篇文章中,再分析SurfaceFlinger是如何将已经存储了窗口图形数据的SurfaceBuffer显示到显示系统中。。

1. Surface机制的静态关系

    将这一部分叫做Surface机制,是有别于SurfaceFlinger而言的,android的图形系统中,作为C/S模型两端的WMS和SurfaceFlinger是图形系统业务的核心,但是不把WMS和SurfaceFlinger中间的这层联系搞清楚的话,是很难理解整个图形系统的,在本文中我将两者之间的这个联系关系称之为Surface机制,它的主要任务就是创建一个Surface,ViewRoot在这个Surface上描绘当前的窗口,SurfaceFlinger将这个Surface flinger(扔)给显示系统将其呈现在硬件设备上。其实这里这个Surface在不同的模块中是以不同的形态存在的,唯一不变的就是其对应的显示Buffer。

 

   

 

1.1 ViewRootWMS共享Surface

    我们知道每个Activity都会有一个ViewRoot作为Activity WindowWMS交互的接口,ViewRoot会绘制整个Activity的窗口ViewSurface,因此我们在ViewRoot中就有了创建Surface的需求。看一下代码中的Surface的定义:

relayoutWindow()@ViewRoot.java

[java] view plaincopy

1.  <span style="font-size:13px;">    private final Surface mSurface = new Surface();</span>  

Surface()@Surface.java

[java] view plaincopy

1.  <span style="font-size:13px;">    public Surface() {  

2.          if (DEBUG_RELEASE) {  

3.              mCreationStack = new Exception();  

4.          }  

5.          mCanvas = new CompatibleCanvas();  

6.      }</span>  

    由上面可以看出在ViewRoot中定义的Surface只是一个空壳,那么真正的Surface是在哪里被初始化的呢?大管家WMS中!当ViewRoot请求WMS relayout时,会将ViewSurface中的Surface交给WMS初始化。在WMS中,对应每个WindowState对象,在relayout窗口时,同样会创建一个Surfacewms中的这个Surface会真正的初始化,然后再将这个WMS Surface复制给ViewRoot中的Surface。这么实现的目的就是保证ViewRootWMS共享同一个SurfaceViewRootSurface进行绘制,WMS对这个Surface进行初始化及管理。很和谐!

relayoutWindow()@ViewRoot.java

[java] view plaincopy

1.  <span style="font-size:13px;">        int relayoutResult = sWindowSession.relayout(  

2.                  mWindow, params,  

3.                  (int) (mView.mMeasuredWidth * appScale + 0.5f),  

4.                  (int) (mView.mMeasuredHeight * appScale + 0.5f),  

5.                  viewVisibility, insetsPending, mWinFrame,  

6.                  mPendingContentInsets, mPendingVisibleInsets,  

7.                  mPendingConfiguration, mSurface);</span>  

relayoutWindow()@WindowManagerService.java

[java] view plaincopy

1.  <span style="font-size:13px;">                    Surface surface = win.createSurfaceLocked();  

2.                      if (surface != null) {  

3.                          outSurface.copyFrom(surface);  

4.                          win.mReportDestroySurface = false;  

5.                          win.mSurfacePendingDestroy = false;  

6.                          if (SHOW_TRANSACTIONS) Slog.i(TAG,  

7.                                  "  OUT SURFACE " + outSurface + ": copied");  

8.                      } else {</span>  

1.2 SurfaceSession

   SurfaceSession可以认为是创建Surface过程中,WMSSurfaceFlinger之间的会话层,通过这个SurfaceSession实现了Surface的创建。

   

   SurfaceSessionJAVA层的概念,@SurfaceSession.java。它对应的native实体是一个SurfaceComposerClient对象。

   SurfaceComposerClient通过ComposerService类来获得SurfaceFlingerIBinder接口,但是光获得SurfaceFlingerIBinder接口是不够的,要想请求SurfaceFlinger创建一个Surface,还需要向SurfaceFlinger获得一个IBinder接口ISurfaceComposerClient,通过这个ISurfaceComposerClient来请求SurfaceFlinger创建一个Surface,为什么这么绕呢,为什么不直接让SurfaceFlinger创建Surface呢?

   站在SurfaceFlinger的角度来考虑,对于SurfaceFlinger来说,可能有多个Client来请求SurfaceFlinger的业务,每个Client可能会请求SurfaceFlinger创建多个Surface,那么SurfaceFlinger本地需要提供一套机制来保存每个client请求创建的SurfaceSurfaceFlinger通过为每个client创建一个Client对象实现这个机制,并将这个ClientIBinder接口ISurfaceComposerClient返给SurfaceComposerClient对象。SurfaceComposerClient对象在通过ISurfaceComposerClient去请求创建Surface

@SurfaceFlinger.h

[cpp] view plaincopy

1.  class Client : public BnSurfaceComposerClient  


@SurfaceComposerClient.cpp

[cpp] view plaincopy

1.  void SurfaceComposerClient::onFirstRef()  

2.  {  

3.      sp<ISurfaceComposer> sm(getComposerService());  

4.      if (sm != 0) {  

5.          sp<ISurfaceComposerClient> conn = sm->createConnection();  

6.          if (conn != 0) {  

7.              mClient = conn;  

8.              Composer::addClient(this);  

9.              mPrebuiltLayerState = new layer_state_t;  

10.             mStatus = NO_ERROR;  

11.         }  

12.     }  

13. }  

   下图描述了整个SurfaceSession的内部结构与工作流程。

其中蓝色箭头是SurfaceComposerClient通过ComposerService获得SurfaceFlingerIBinder接口ISurfaceComposer过程;

红色箭头表示SurfaceComposerClient通过IPC请求SurfaceFlinger创建Client的过程,并获得ClientIBinder接口ISurfaceComposerClient

绿色箭头表示SurfaceComposerClient通过IPC请求Client创建Surface

 

1.3 Surface的形态

   上一节我们分析了SurfaceSession的静态结构,得知Surface的创建过程是通过SurfaceSession这个中间会话层去请求SurfaceFlinger去创建的,并且这篇文章中,我们说了半天Surface了,那么究竟我们要创建的Surface究竟是什么样的一个东西呢,它的具体形态是什么呢?这一小节我们就来分析以下Surface的形态。

1.3.1 clientSurface的形态

   首先,我们看一下SurfaceWMS中定义的代码

createSurfaceLocked()@WindowManagerService.java

[java] view plaincopy

1.  mSurface = new Surface(  

2.          mSession.mSurfaceSession, mSession.mPid,  

3.          mAttrs.getTitle().toString(),  

4.          0, w, h, mAttrs.format, flags);  

我们可以看到,它将SurfaceSession对象当作参数传递给了Surface的构造函数。往下看Surface的构造函数。

@Surface.java

[java] view plaincopy

1.  public Surface(SurfaceSession s,  

2.          int pid, int display, int w, int h, int format, int flags)  

3.      throws OutOfResourcesException {  

4.      if (DEBUG_RELEASE) {  

5.          mCreationStack = new Exception();  

6.      }  

7.      mCanvas = new CompatibleCanvas();  

8.      init(s,pid,null,display,w,h,format,flags);  

9.  }  

    这个构造函数,不同于我们在ViewRoot中看到的Surface的构造函数,这个构造函数并不是一个空壳,它做了本地实体的初始化工作,因此这个Surface才是一个真正的Suface

Native函数init回调到SurfaceComposerClientcreateSurface()函数,往下的过程在上一节的图中描述的很清楚,流程就不介绍了,同时我们先不管SurfaceFlingerSurfaceComposerClient创建的Surface到底是一个什么东西,我们先看看SurfaceComposerClientWMS创建的是一个什么东西?

 

@SurfaceComposerClient.cpp

[cpp] view plaincopy

1.  sp<SurfaceControl> SurfaceComposerClient::createSurface(  

2.          int pid,  

3.          const String8& name,  

4.          DisplayID display,  

5.          uint32_t w,  

6.          uint32_t h,  

7.          PixelFormat format,  

8.          uint32_t flags)  

9.  {  

10.     sp<SurfaceControl> result;  

11.     if (mStatus == NO_ERROR) {  

12.         ISurfaceComposerClient::surface_data_t data;  

13.         sp<ISurface> surface = mClient->createSurface(&data, pid, name,  

14.                 display, w, h, format, flags);  

15.         if (surface != 0) {  

16.             result = new SurfaceControl(this, surface, data, w, h, format, flags);  

17.         }  

18.     }  

19.     return result;  

20. }  

   从上面的代码我们可以看出,SurfaceComposerClientWMS返回的是一个SurfaceControl对象,这个SurfaceControl对象包含了surfaceFlingerSurfaceComposerClient创建的surface,这个surfaceFlinge创建的SurfaceClient端的形态为ISurface。这个过程下面分析SurfaceFlinger端的Surface形态时会看到。

   SurfaceControl类中还有一个非常重要的成员,它的类型也叫做Surface,定义在frameworks/base/libs/surfaceflinger/Surface.h。这个Surface提供了显示Buffer的管理。在文章的后面再介绍。

@frameworks/base/libs/surfaceflinger_client/Surface.cpp

[cpp] view plaincopy

1.  sp<Surface> SurfaceControl::getSurface() const  

2.  {  

3.      Mutex::Autolock _l(mLock);  

4.      if (mSurfaceData == 0) {  

5.          mSurfaceData = new Surface(const_cast<SurfaceControl*>(this));  

6.      }  

7.      return mSurfaceData;  

8.  }  

1.3.2 SurfaceFlingerSurface形态

SurfaceFlinger::createSurface@SurfaceFlinger.cpp 

 

[cpp] view plaincopy

1.  sp<Layer> normalLayer;  

2.  switch (flags & eFXSurfaceMask) {  

3.      case eFXSurfaceNormal:  

4.          if (UNLIKELY(flags & ePushBuffers)) {  

5.              layer = createPushBuffersSurface(client, d, w, h, flags);  

6.          } else {  

7.              normalLayer = createNormalSurface(client, d, w, h, flags, format);  

8.              layer = normalLayer;  

9.          }  

10.         break;  

11.     case eFXSurfaceBlur:  

12.         layer = createBlurSurface(client, d, w, h, flags);  

13.         break;  

14.     case eFXSurfaceDim:  

15.         layer = createDimSurface(client, d, w, h, flags);  

16.         break;  

17. }  

18.   

19. if (layer != 0) {  

20.     layer->initStates(w, h, flags);  

21.     layer->setName(name);  

22.     ssize_t token = addClientLayer(client, layer);  

23.   

24.     surfaceHandle = layer->getSurface();  

25.     if (surfaceHandle != 0) {   

26.         params->token = token;  

27.         params->identity = surfaceHandle->getIdentity();  

28.         params->width = w;  

29.         params->height = h;  

30.         params->format = format;  

31.         if (normalLayer != 0) {  

32.             Mutex::Autolock _l(mStateLock);  

33.             mLayerMap.add(surfaceHandle->asBinder(), normalLayer);  

34.         }  

35.     }  

   client请求SurfaceFlinger创建Surface时,SurfaceFlinger首先根据WMS提供的窗口的属性来一个命名为Layer概念的对象,然后再根据Layer创建它的子类对象LayerBaseClient::Surface。此时第三个名为Surface类出现了,下一节我们来介绍一下这个Layer的概念。

    

1.4 Layer      

   

1.4.1 Layer的分类

   目前,android中有4Layer类型,如上图所示。  

    1. Layer, 普通的Layer,它为每个Client端请求的Surface创建显示Buffer

    2. LayerBuffer,这种Layer它并不会创建显示Buffer,它只是使用已有的Buffer作为显示Buffer,如Camerapreview

    3. LayerBlur,这种Layer也不会创建显示Buffer,它只是将通过这个Layer将原来FrameBuffer上的数据进行模糊处理;

    4. LayerDim,这种Layer也不会创建显示Buffer,它只是将通过这个Layer将原来FrameBuffer上的数据进行暗淡处理;

 

    从这中Layer看出,我们分析的重点就是第一种Layer,下面我们着重分析一下普通的LayerLayer的具体业务我们在下一篇文章中分析

1.4.2 Layer的管理

   上文我们在分析SurfaceSession的时候,也分析过,一个Client可能会创建多个Surface,也就是要创建多个Layer,那么SurfaceFlinger端如何管理这个写个Layer呢?SurfaceFlinger维护了2Vector来管理Layer

   第一种方式,我们知道SurfaceFlinger会为每个SurfaceSession创建一个Client对象,这第一种方式就是将所有为某一个SurfacSession创建的Layer保存在它对应的Client对象中。

SurfaceFlinger::createSurface()@SurfaceFlinger.cpp

[cpp] view plaincopy

1.  ssize_t token = addClientLayer(client, layer);  


   
第二种方式,将所有的创建的普通的Layer保存起来,以便Client Surface在请求实现Buffer时能够辨识Client Surface对应的Layer

SurfaceFlinger::createSurface()@SurfaceFlinger.cpp

[cpp] view plaincopy

1.  mLayerMap.add(surfaceHandle->asBinder(), normalLayer);  

 

2. Surface 显示Buffer的存储管理

   在前文介绍Client端的Surface形态的内容时,我们提到SurfaceControl中还会维护一个名为Surface对象,它定义在 frameworks/base/libs/surfaceflinger/Surface.h中,它负责向LayerBaseClient::Surface请求显示Buffer,同时将显示Buffer交给JAVA SurfaceCanvas去绘制窗口,我们称这个SurfaceClient Surface

2.1 窗口绘制

    我们先从ViewRoot中分析一下,它是如何显示窗口View的,如何用到Client Surface请求的显示Buffer的。

draw()@ViewRoot.java

[java] view plaincopy

1.  Canvas canvas;  

2.              try {  

3.                  int left = dirty.left;  

4.                  int top = dirty.top;  

5.                  int right = dirty.right;  

6.                  int bottom = dirty.bottom;  

7.                  canvas = surface.lockCanvas(dirty);  

8.    

9.                  if (left != dirty.left || top != dirty.top || right != dirty.right ||  

10.                         bottom != dirty.bottom) {  

11.                     mAttachInfo.mIgnoreDirtyState = true;  

12.                 }  

13.   

14.                 // TODO: Do this in native  

15.                 canvas.setDensity(mDensity);  

    上面的代码显示,JAVA Surface lock canvas。而Client Surface的创建就在这个过程中,即下面代码中的第一行getSurface().我们先不管Client Surface的创建,先看看Canvas是如何与Client Surface的显示Buffer关联的。

[cpp] view plaincopy

1.  static jobject Surface_lockCanvas(JNIEnv* env, jobject clazz, jobject dirtyRect)  

2.  {  

3.      const sp<Surface>& surface(getSurface(env, clazz));  

4.     if (!Surface::isValid(surface))  

5.         return 0;  

6.   

7.   

8.     SkCanvas* nativeCanvas = (SkCanvas*)env->GetIntField(canvas, no.native_canvas);  

9.     SkBitmap bitmap;  

10.     ssize_t bpr = info.s * bytesPerPixel(info.format);  

11.     bitmap.setConfig(convertPixelFormat(info.format), info.w, info.h, bpr);  

12.     if (info.format == PIXEL_FORMAT_RGBX_8888) {  

13.         bitmap.setIsOpaque(true);  

14.     }  

15.     if (info.w > 0 && info.h > 0) {  

16.         bitmap.setPixels(info.bits);  

17.     } else {  

18.         // be safe with an empty bitmap.  

19.         bitmap.setPixels(NULL);  

20.     }  

21.     nativeCanvas->setBitmapDevice(bitmap);  

22.       

23.     SkRegion clipReg;  

24.     if (dirtyRegion.isRect()) { // very common case  

25.         const Rect b(dirtyRegion.getBounds());  

26.         clipReg.setRect(b.left, b.top, b.right, b.bottom);  

27.     } else {  

28.         size_t count;  

29.         Rect const* r = dirtyRegion.getArray(&count);  

30.         while (count) {  

31.             clipReg.op(r->left, r->top, r->right, r->bottom, SkRegion::kUnion_Op);  

32.             r++, count--;  

33.         }  

34.     }  

35.   

36.     nativeCanvas->clipRegion(clipReg);  

37.       

38.     int saveCount = nativeCanvas->save();  

39.     env->SetIntField(clazz, so.saveCount, saveCount);  

40.   

41.     if (dirtyRect) {  

42.         const Rect& bounds(dirtyRegion.getBounds());  

43.         env->SetIntField(dirtyRect, ro.l, bounds.left);  

44.         env->SetIntField(dirtyRect, ro.t, bounds.top);  

45.         env->SetIntField(dirtyRect, ro.r, bounds.right);  

46.         env->SetIntField(dirtyRect, ro.b, bounds.bottom);  

47.     }  

48.       

49.     return canvas;  

50. }  


   
上面的代码,我们可以看出,CanvasBitmap设备的设置了Client Surface的显示Buffer为其Bitmap pixel存储空间。

[cpp] view plaincopy

1.  bitmap.setPixels(info.bits);  

   这样Canvas的绘制空间就有了。下一步就该绘制窗口了。

draw()@ViewRoot.java

[cpp] view plaincopy

1.  try {  

2.      canvas.translate(0, -yoff);  

3.      if (mTranslator != null) {  

4.          mTranslator.translateCanvas(canvas);  

5.      }  

6.      canvas.setScreenDensity(scalingRequired  

7.              ? DisplayMetrics.DENSITY_DEVICE : 0);  

8.      mView.draw(canvas);  

9.  }  

其中ViewRoot中的mView为整个窗口的DecorView

 

2.2 Client Surface的初始化

   Client Surface的创建是从ViewRoot首次Lock canvas时进行的,这么做的目的可能也是为了节约空间,减少不必要的开支。

   Client Surface的初始化和显示Buffer的管理过程比较复杂,下图给出了这一部分的一个静态结构图,有些东西从图上表现不出来,下面我简单的介绍一下。


2.2.1 SharedClient

   SharedClient是这一部分实现的关键所在,它并不是一个每个Client Surface创建时都会被创建的,整个系统中只有一个SharedClient对象,并且它是在共享内存上创建的,下面代码中可以看出,UserClient在初始化时,提供了一个MemoryHeapBase来供SharedClient创建,MemoryHeapBase是创建的共享内存。

@SurfaceFlinger.cpp

[cpp] view plaincopy

1.  UserClient::UserClient(const sp<SurfaceFlinger>& flinger)  

2.      : ctrlblk(0), mBitmap(0), mFlinger(flinger)  

3.  {  

4.      const int pgsize = getpagesize();  

5.      const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));  

6.    

7.      mCblkHeap = new MemoryHeapBase(cblksize, 0,  

8.              "SurfaceFlinger Client control-block");  

9.    

10.     ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase());  

11.     if (ctrlblk) { // construct the shared structure in-place.  

12.         new(ctrlblk) SharedClient;  

13.     }  

14. }  


    SharedClient
对象的主要目的其实很简单,就是为系统提供了SharedBufferStack::NUM_LAYERS_MAX(GB上为31)SharedBufferStack。也就是目前系统同时支持31Client Surface的创建。关于SharedBufferStack下面再做介绍。

  为什么需要将SharedClient设计为共享内存呢?每个Client Surface需要的SharedBufferStack寄存在SharedClient中,而对于每个SharedBufferStack,一方面,Client Surface需要对它进行一些区域尺寸等的设置;另一方面,在render时,Layer需要获得当前Client Surfce对应的SharedBufferStack中获得区域尺寸等设置信息。

classSharedClient@SharedBufferStack.h

[cpp] view plaincopy

1.  SharedBufferStack surfaces[ SharedBufferStack::NUM_LAYERS_MAX ];  

    

2.2.2 SharedBufferStack

   SharedBufferStack在这个模块中所处的地位在上一小节中介绍了,下面主要介绍一下它的作用。

    1. 设置当前窗口要显示的区域等信息;

classSharedBufferStack@SharedBufferStack.h

[cpp] view plaincopy

1.  status_t setDirtyRegion(int buffer, const Region& reg);  

2.  status_t setCrop(int buffer, const Rect& reg);  

3.  status_t setTransform(int buffer, uint8_t transform);  

    2.android的图形系统中提供了两个显示Buffer,从上图中我们可以看出Client Surface2GraphicBuffer2Buffer其中一个显示,称之为Front Buffer,另外一个交给ViewRoot去绘制窗口,称之为Back Buffer。等BackBuffer绘制完成,SurfaceFlinger在将两者调换,这样就大大提高了显示的效率,具体过程下篇文章介绍。

    SharedBufferStack第二个很重要的作用就是提供了一套机制来实现这个调换的过程,以保证提供给ViewRootBuffer符合当前Buffer轮转的要求。通过SharedBufferClient::tail 

classSharedBufferStack@SharedBufferStack.h

[cpp] view plaincopy

1.  volatile int32_t head;      // server's current front buffer  

2.  volatile int32_t available; // number of dequeue-able buffers  

这几个变量的值来确定Client SurfaceGraphicBuffer的索引,其中SharedBufferClient::tail记录的是BackBuffer的索引;SharedBufferStack::head记录的是FrontBuffer的索引。

 

 

2.2.3 Client Surace GraphicBuffer的请求

   这里将Client SurfaceGraphicBuffer的创建过程以时序图的形式展现出来。

   这里需要注意的是,Client Surface2GraphicBuffer只有在lock()时才会去创建,而不是在Client Surface被创建的时候创建的。

 

android surfaceflinger研究----SurfaceFlinger loop

2011-12-18 01:43 2623人阅读 评论(15) 收藏 举报

   上一篇文章介绍了整个Surface机制(我是这么称呼的,主要是Surface的建立,Surface的显示存储的管理),同时我们也介绍过了整个显示系统,那么这篇文章就介绍一下SurfaceFlinger 这个核心服务层的机制。

   从代码中我们可以看出SurfaceFlinger 是一个thread,运行在system_server进程中,并且其threadLoop()方法的返回值为true,因此它是一个循环的loop。这样保证了SurfaceFlinger业务的循环周期性。

   首先,先来个综述,下图是我总结的一个SurfaceFlinger结构的概括图:

 

1. SurfaceFlinger的同步

   SurfaceFlinger 并不是时时刻刻都在执行业务中,当WMS请求SurfaceFlinger创建Surface,或者WMSSurface进行属性设置时,我们希望此时的SurfaceFlinger并不进行显示操作,以保证对Surface的线程保护,因此SurfaceFlinger loop中实现了同步机制。

[cpp] view plaincopy

1.  waitForEvent();  

   主要的同步情况有如下几种,当然也有其他一些要求SurfaceFlinger同步的情况,不够对于研究SurfaceFlinger就不太重要了

   1. 创建Surface同步

   假如当前只有一个Client,比如WMS请求SufaceFlinger创建一个Surface,那么此时应该保持SurfaceFlinger loop处在block状态,因为这个过程涉及到对一些成员变量的处理,为了保证同步而需要hold住整个loop

   2. 设置Surface属性或SurfaceFlinger属性同步

   创建完Surface之后,WMS会请求SurfaceFlinger对其Layer进行属性设置或者对SurfaceFlinger的属性进行设置,如上面概括图中SurfaceComposerClient中的函数接口。

   3. Surface绘制同步

   ViewRootSurface进行绘制时,同样需要将SurfaceFlinger hold住,当整个窗口绘制完成之后,再向SurfaceFlinger发送signal信号。如下面时序图所示。

   

   4. freeze/unfreeze同步

  当每个Activity启动的时候,AMS都会请求WMS freeze整个屏幕,当Activity启动之后,再unfreeze整个屏幕,我猜测这么做的目的是为了保证在Activity以及Activity的窗口在创建过程中,对Activity窗口的Surface进行的线程保护,以免出现屏幕的闪烁等用户体验较差的现象。

 

2. Layer存储

   SurfaceFlinger中,Layer是怎么样存储的呢?所有的Layer,不论是那个Client创建的Layer,均保存在一个名为layersSortedByZ的变量中,也就是说WMS请求创建的SurfaceLayer和其他Client请求创建的Layer都保存在layersSortedByZ中,但是layersSortedByZ保存过程中则遵守一定的规则。下面代码中的do_compare揭示了这个规则。

@SurfaceFlinger.h

[cpp] view plaincopy

1.  class LayerVector : public SortedVector< sp<LayerBase> > {  

2.  public:  

3.      LayerVector() { }  

4.      LayerVector(const LayerVector& rhs) : SortedVector< sp<LayerBase> >(rhs) { }  

5.      virtual int do_compare(const void* lhs, const void* rhs) const {  

6.          const sp<LayerBase>& l(*reinterpret_cast<const sp<LayerBase>*>(lhs));  

7.          const sp<LayerBase>& r(*reinterpret_cast<const sp<LayerBase>*>(rhs));  

8.          // sort layers by Z order  

9.          uint32_t lz = l->currentState().z;  

10.         uint32_t rz = r->currentState().z;  

11.         // then by sequence, so we get a stable ordering  

12.         return (lz != rz) ? (lz - rz) : (l->sequence - r->sequence);  

13.     }  

14. };  

    每次向layersSortedByZ中添加新的Layer,都会做一次排序,按照规则将其放在合适的位置。

   1. 首先,按照LayerZ-order值来排序,Z-order值小的,放在layersSortedByZ低索引值位置;

   2. 其次,如果两个Layer Z-order值相同,sequence值小的,放在layersSortedByZ低索引值位置;

   Z-order值如何确定?

   WMS根据不同的Window Type来确定Z-order值,Z-order = LAYER*TYPE_LAYER_MULTIPLIER +TYPE_LAYER_OFFSET

根据下面代码中的不同的Window TypeLAYER值,可以确定Z-order值,例如TYPE_APPLICATION窗口,其

Z-order= 2*10000+1000 = 21000

   @PhoneWindowManager.java

[java] view plaincopy

1.  // wallpaper is at the bottom, though the window manager may move it.  

2.   static final int WALLPAPER_LAYER = 2;  

3.   static final int APPLICATION_LAYER = 2;  

4.   static final int PHONE_LAYER = 3;  

5.   static final int SEARCH_BAR_LAYER = 4;  

6.   static final int STATUS_BAR_PANEL_LAYER = 5;  

7.   static final int SYSTEM_DIALOG_LAYER = 6;  

8.   // toasts and the plugged-in battery thing  

9.   static final int TOAST_LAYER = 7;  

10.  static final int STATUS_BAR_LAYER = 8;  

11.  // SIM errors and unlock.  Not sure if this really should be in a high layer.  

12.  static final int PRIORITY_PHONE_LAYER = 9;  

13.  // like the ANR / app crashed dialogs  

14.  static final int SYSTEM_ALERT_LAYER = 10;  

15.  // system-level error dialogs  

16.  static final int SYSTEM_ERROR_LAYER = 11;  

17.  // on-screen keyboards and other such input method user interfaces go here.  

18.  static final int INPUT_METHOD_LAYER = 12;  

19.  // on-screen keyboards and other such input method user interfaces go here.  

20.  static final int INPUT_METHOD_DIALOG_LAYER = 13;  

21.  // the keyguard; nothing on top of these can take focus, since they are  

22.  // responsible for power management when displayed.  

23.  static final int KEYGUARD_LAYER = 14;  

24.  static final int KEYGUARD_DIALOG_LAYER = 15;  

25.  // things in here CAN NOT take focus, but are shown on top of everything else.  

26.  static final int SYSTEM_OVERLAY_LAYER = 16;  

27.  static final int SECURE_SYSTEM_OVERLAY_LAYER = 17;  

    

   sequence值如何确定?

   sequence值是根据Layer的创建的顺序来维护这个序列值,下面代码中的LayerBase的构造函数中的sequence值,每创建一个LayersSequence加一赋值给sequence

@LayerBase.cpp

[cpp] view plaincopy

1.  int32_t LayerBase::sSequence = 1;  

2.    

3.  LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display)  

4.      : dpy(display), contentDirty(false),  

5.        sequence(uint32_t(android_atomic_inc(&sSequence))),  

6.        mFlinger(flinger),  

7.        mNeedsFiltering(false),  

8.        mOrientation(0),  

9.        mLeft(0), mTop(0),  

10.       mTransactionFlags(0),  

11.       mPremultipliedAlpha(true), mName("unnamed"), mDebug(false),  

12.       mInvalidate(0)  

13. {  

14.     const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());  

15.     mFlags = hw.getFlags();  

16.     mBufferCrop.makeInvalid();  

17.     mBufferTransform = 0;  

18. }  

 

3. 属性更新

   这一节的所描述的实现都在函数handleTransactionLocked()中。

   从上面概括图中可以看出,WMS可以对SurfaceFlinger进行属性设置,也可以对当前的Surface对应的Layer进行属性设置,因此handleTransactionLocked()函数就是对SurfaceFlinger属性和设置了新属性的Layer的属性更新。

[cpp] view plaincopy

1.  enum {  

2.      eTransactionNeeded      = 0x01,  

3.      eTraversalNeeded        = 0x02  

4.  };  

    SurfaceFlinger根据这个枚举值来确定handleTransactionLocked()需要更新SurfaceFlinger属性还是layer属性。

   如果SurfaceFlinger属性被设置了新内容,则SurfaceFlinger会记录标志eTransactionNeeded;如果layer属性被设置了新内容,那么
SurfaceFlinger
会记录标志eTraversalNeededhandleTransactionLocked()通过记录的标志来执行各自的属性得更新。

   这里提到的属性的更新,主要是看SurfaceFlinger或者laye新设置的属性与旧的属性相比,哪些属性做了修改,然后

记录下来,在接下来的SurfaceFlinger loop中使用新的属性来显示图形。

   SurfaceFlinger Layer中各自定义了两个属性的变量,其中mCurrentState为新设置属性,mDrawingState为显示图形时用到的属性,一般为旧属性。不过类SurfaceFlinger Layer分别定义了不同的State类。

[cpp] view plaincopy

1.  State                   mCurrentState;  

2.  State                   mDrawingState;  

4. 图形缓存

   这一部分的的实现在函数handlePageFlip()中。

   有这么一种可能,当前显示到显示设备上的layer不止一个,而且layer是按照Z-Order的顺序来叠加到OpenGLsurface上的,那么这就需要layerZ-Order值和坐标来确定每个layer能够被显示的区域。

4.1 page flip

   前面一篇文章中介绍过,每个surface均有2buffer供使用,一个作为FronteBufferSurfaceFlinger去显示,另外一个作为BackBufferViewRoot去绘制窗口。因此在显示各个layer之前,我们需要做一个page flip过程,将当前的已经绘制了应用窗口的BackBuffer选择为FrontBuffer,用于显示;将之前的已经显示完成的FrontBuffer在重置为BackBufferViewRoot去绘制。

  而实现这个page flip的过程很简单

lockPageFlip()@Layer.cpp

[cpp] view plaincopy

1.  ssize_t buf = lcblk->retireAndLock();  

SharedBufferServer::RetireUpdate::operator()@SharedBufferStack.cpp

[cpp] view plaincopy

1.  head = (head + 1) % numBuffers;  

   

 

4.2 纹理初始化

   为每个Buffer的纹理进行初始化,为当前的纹理创建一个EGLImageKHR,将当前的Buffer最为该EGLImageKHR的源。这样OpenGL就可以进行纹理映射。

lockPageFlip()@Layer.cpp

[cpp] view plaincopy

1.  /* a buffer was posted, so we need to call reloadTexture(), which 

2.   * will update our internal data structures (eg: EGLImageKHR or 

3.   * texture names). we need to do this even if mPostedDirtyRegion is 

4.   * empty -- it's orthogonal to the fact that a new buffer was posted, 

5.   * for instance, a degenerate case could be that the user did an empty 

6.   * update but repainted the buffer with appropriate content (after a 

7.   * resize for instance). 

8.   */  

9.  reloadTexture( mPostedDirtyRegion );  

 

4.3 计算显示区域

   通过layer的叠加,我们可以计算出总的显示区域以及每个layer需要显示的区域,它的实现在computeVisibleRegions()函数中。这个函数主要计算了layer叠加后的总的显示区域,以及每个layer需要显示的区域。整个的计算过程比较简单,只是需要注意不透明区域的处理,computeVisibleRegions()需要计算出一个不透明区域,通过这个不透明区域验证WMS提供给layer的区域是否正确。即下面代码中的mWormholeRegion计算,mWormholeRegion为屏幕区域减去不透明区域,正常情况mWormholeRegion应该为空,即不透明区域范围应该为屏幕区域,如果不透明区域小雨屏幕区域,那么说明当前的应用程序出现了设置的错误。今天有个网友就出现了这个问题。

handlePageFlip()

[cpp] view plaincopy

1.  const Region screenRegion(hw.bounds());  

2.  if (visibleRegions) {  

3.      Region opaqueRegion;  

4.      computeVisibleRegions(currentLayers, mDirtyRegion, opaqueRegion);  

5.    

6.      /* 

7.       *  rebuild the visible layer list 

8.       */  

9.      mVisibleLayersSortedByZ.clear();  

10.     const LayerVector& currentLayers(mDrawingState.layersSortedByZ);  

11.     size_t count = currentLayers.size();  

12.     mVisibleLayersSortedByZ.setCapacity(count);  

13.     for (size_t i=0 ; i<count ; i++) {  

14.         if (!currentLayers[i]->visibleRegionScreen.isEmpty())  

15.             mVisibleLayersSortedByZ.add(currentLayers[i]);  

16.     }  

17.   

18.     mWormholeRegion = screenRegion.subtract(opaqueRegion);  

19.     mVisibleRegionsDirty = false;  

20. }  

   computeVisibleRegions()叠加计算总的显示范围,layer的计算顺序从上到下的过程计算的,也就是先计算Z-Order值较大的,显示在最上层的layer开始往下计算。这么做的好处就是能够很好的计算出不透明区域的范围。

   SurfaceFlinger的区域相互之间的操作处理如下:

 

   

   

   

 

   

4.4 图形缓存

   前面选择了FrontBuffer、初始化了纹理、计算了layer的显示区域,那么下一步就该将Buffer内容进行图形处理并保存到OpenGL缓存中。

   调用每个layerdraw函数来进行这个操作。如下面代码所示。具体的图形处理过程很复杂,完全交给OpenGL去处理,这里我们就不去关心了。我们只需要知道最终经过图形处理的内容会被缓存到OpenGL的缓存区中。

[cpp] view plaincopy

1.  void SurfaceFlinger::composeSurfaces(const Region& dirty)  

2.  {  

3.      if (UNLIKELY(!mWormholeRegion.isEmpty())) {  

4.          // should never happen unless the window manager has a bug  

5.          // draw something...  

6.          drawWormhole();  

7.      }  

8.      const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);  

9.      const size_t count = layers.size();  

10.     for (size_t i=0 ; i<count ; ++i) {  

11.         const sp<LayerBase>& layer(layers[i]);  

12.         const Region clip(dirty.intersect(layer->visibleRegionScreen));  

13.         if (!clip.isEmpty()) {  

14.             layer->draw(clip);  

15.         }  

16.     }  

17. }  

    从前面的显示系统中,介绍过,Surface的缓存Buffer就是FramebufferNativeWindow中定义的2Buffer,如果/dev/fb0读取设备信息,如果设备支持pageflip,那么Surface的缓存Buffer即从/dev/fb0设备中申请;如果不支持,我们则需要从/dev/pmem中申请,同时/dev/fb0还会提供一个Buffer以便图形最终的显示。

   /dev/fb0不支持page flip模式

   

   /dev/fb0支持page flip模式

   

5. 图形显示

   当图形内容被缓存到frameBuffer中后,最后的一步就是图形显示。代码中很明确就是SurfaceFlinger loop中的postFramebuffer()函数了。

   这个函数最终回调到OpenGLeglSwapBuffers()函数,这个函数主要有2个步骤(由于硬件加速代码不可见,我们仍然以软件加速为例)

   1. 显示当前缓存buffer中内容;

    首先,将原来的屏幕上的内容与最新需要显示的内容进行区域相减,将原来的内容copy到当前的缓存buffer中;

EGLBooleanegl_window_surface_v2_t::swapBuffers()@frameworksbaseopengllibaglegl.cpp

[cpp] view plaincopy

1.  /* 

2.   * Handle eglSetSwapRectangleANDROID() 

3.   * We copyback from the front buffer  

4.   */  

5.  if (!dirtyRegion.isEmpty()) {  

6.      dirtyRegion.andSelf(Rect(buffer->width, buffer->height));  

7.      if (previousBuffer) {  

8.          const Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));  

9.          if (!copyBack.isEmpty()) {  

10.             void* prevBits;  

11.             if (lock(previousBuffer,   

12.                     GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) {  

13.                 // copy from previousBuffer to buffer  

14.                 copyBlt(buffer, bits, previousBuffer, prevBits, copyBack);  

15.                 unlock(previousBuffer);  

16.             }  

17.         }  

18.     }  

19.     oldDirtyRegion = dirtyRegion;  

20. }  

    其次,如果当前的缓存buffer是申请自/dev/fb0,那么直接去显示这个缓存区中内容;如果缓存buffer是申请自/dev/pmem,那么需要将缓存buffer中内容拷贝到/dev/fb0 buffer中去,其结构如上一节所示。

   2. 2个缓存buffer进行page flip(swap)操作。

   通过 queueBuffer()操作将将当前Buffer交还给FramebufferNativeWindow,同时调用fb_post进行图形显示。然后通过dequeueBuffer()操作获得另外一个FramebufferNativeWindow的缓存Buffer,实现page flip(swap)操作。


 

   至此,整个的SurfaceFlinger的机制就分析完了。

 


最后

以上就是丰富烧鹅为你收集整理的Suface & Suface Flinger知识的全部内容,希望文章能够帮你解决Suface & Suface Flinger知识所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(41)

评论列表共有 0 条评论

立即
投稿
返回
顶部