Android图形合成和显示系统---基于高通MSM8k MDP4平台

介绍了Android SurfaceFlinger层次以下的图形合成和显示系统,主要基于高通MSM8k MDP4x平台。

做为Android Display专题。SurfaceFlinger的详细介绍参见链接文章。

Android GDISurfaceFlinger

SurfaceFinger按英文翻译过来就是Surface投递者。SufaceFlinger的构成并不是太复杂,复杂的是他的客户端建构。SufaceFlinger主要功能是:

1LayersSurfaces内容的刷新到屏幕上

2维持LayerZorder序列,并对Layer最终输出做出裁剪计算。

3响应Client要求,创建Layer与客户端的Surface建立连接

4接收Client要求,修改Layer属性(输出大小,Alpha等设定)

但是作为投递者的实际意义,我们首先需要知道的是如何投递,投掷物,投递路线,投递目的地。

1 SurfaceFlinger的基本组成框架

SurfaceFlinger管理对象为:

mClientsMap:管理客户端与服务端的连接。

ISurfaceIsurfaceComposerAIDL调用接口实例

mLayerMap:服务端的Surface的管理对象。

mCurrentState.layersSortedByZ:以SurfaceZ-order序列排列的Layer数组。

graphicPlane缓冲区输出管理

OpenGL ES:图形计算,图像合成等图形库。

gralloc.xxx.so这是个跟平台相关的图形缓冲区管理器。

pmem Device:提供共享内存,在这里只是在gralloc.xxx.so可见,在上层被gralloc.xxx.so抽象了。

2 SurfaceFinger Client和服务端对象关系图

Client端与SurfaceFlinger连接图:

Client对象:一般的在客户端都是通过SurfaceComposerClient来跟SurfaceFlinger打交道。

3主要对象说明

3.1 DisplayHardware &FrameBuffer

首先SurfaceFlinger需要操作到屏幕,需要建立一个屏幕硬件缓冲区管理框架。Android在设计支持时,考虑多个屏幕的情况,引入了graphicPlane的概念。在SurfaceFlinger上有一个graphicPlane数组,每一个graphicPlane对象都对应一个DisplayHardware.在当前的Android2.1)版本的设计中,系统支持一个graphicPlane,所以也就支持一个DisplayHardware

SurfaceFlingerHardware硬件缓冲区的数据结构关系图。

3.2 Layer

method:setBufferSurfaceFlinger端建立显示缓冲区。这里的缓冲区是指的HW性质的,PMEM设备文件映射的内存。

1) layer的绘制

void Layer::onDraw(const Region& clip) const

{

    int index = mFrontBufferIndex;

    GLuint textureName = mTextures[index].name;

  drawWithOpenGL(clip, mTextures[index]);

}

3.2 mCurrentState.layersSortedByZ

SurfaceZ-order序列排列的LayerBase数组,该数组是层显示遮挡的依据。在每个层计算自己的可见区域时,从Z-order顶层开始计算,是考虑到遮挡区域的裁减,自己之前层的可见区域就是自己的不可见区域。而绘制Layer时,则从Z-order底层开始绘制,这个考虑到透明层的叠加。

4 SurfaceFlinger的运行框架

我们从前面的章节<Android Service>的基本原理可以知道,SurfaceFlinger的运行框架存在于:threadLoop,他是SurfaceFlinger的主循环体。SurfaceFlinger在进入主体循环之前会首先运行:SurfaceFlinger::readyToRun()

4.1 SurfaceFlinger::readyToRun()

1)建立GraphicPanle

2)建立FrameBufferHardware(确定输出目标)

初始化:OpenGL ES

建立兼容的mainSurface.利用eglCreateWindowSurface

建立OpenGL ES进程上下文。

建立主SurfaceOpenGL ES)。 DisplayHardwareInit()@DisplayHardware.cpp函数对OpenGL做了初始化,并创建立主Surface。为什么叫主Surface,因为所有的Layer在绘制时,都需要先绘制在这个主Surface上,最后系统才将主Surface的内容投掷到真正的屏幕上。

3Surface的绑定

1)在DisplayHandware初始完毕后,hw.makeCurrent()将主SurfaceOpenGL ES进程上下文绑定到SurfaceFlinger的上下文中,

2)之后所有的SurfaceFlinger进程中使用EGL的所有的操作目的地都是mSurface@DisplayHardware

这样,在OpenGL绘制图形时,主Surface被记录在进程的上下文中,所以看不到显示的主Surfce相关参数的传递。下面是Layer-DrawHardware.flip的动作示意图:

4.2 ThreadLoop

(1)handleTransaction(…):主要计算每个Layer有无属性修改,如果有修改着内用需要重画。

(2)handlePageFlip()

computeVisibleRegions:根据Z-Order序列计算每个Layer的可见区域和被覆盖区域。裁剪输出范围计算-

在生成裁剪区域的时候,根据Z_order依次,每个Layer在计算自己在屏幕的可显示区域时,需要经历如下步骤:

1)以自己的W,H给出自己初始的可见区域

2)减去自己上面窗口所覆盖的区域

在绘制时,Layer将根据自己的可将区域做相应的区域数据Copy

3handleRepaint()

composeSurfaces(需要刷新区域):

根据每个Layer的可见区域与需要刷新区域的交集区域从Z-Order序列从底部开始绘制到主Surface上。

4postFramebuffer()

DisplayHardwarehw.flip(mInvalidRegion);

eglSwapBuffers(display,mSurface) :mSruface投递到屏幕。

5总结

现在SurfaceFlinger干的事情利用下面的示意图表示出来:

更详细地,参考

Android GUISurfaceFlinger系列

Android display架构分析-SW架构分析(1-8)

SurfaceFlinger使用的各组件,参考

Learning about Android Graphics Subsystem by MIPS Engineer

*******************************************************************************

 

 

Copybit HAL Introduction

SurfaceFlinger layercompositionType有三种:

HWC_FRAMEBUFFER的使用OpenGL ES来绘制;

HWC_OVERLAY的使用Overlay Engine来合成;

HWC_USE_COPYBIT的使用Copybit硬件加速绘制;

 

MSM8xxx平台Jellybean代码中没有发现使用HWC_USE_COPYBITlayer,该平台下 Copybit 硬件加速主要有两种:

PPP vpe模块的PPPdirect copy

C2D :可能是2D GPU OpenVG之类的。

PPP驱动实现是做为Framebuffer设备的一个命令MSMFB_BLITC2D是使用c2d hal库;

MSM7627平台下hwcomposer还是使用copybit的。

可能早期系统没有Hardware Composer,又没有GPU的时候,layer draw就要使用Copybit去一层一层一RectRect的拷贝了。

 

Copybit的代码在display/libcopybit下,硬件合成器使用Copybit做的封装代码在display/libhwcomposer/copybitcopybit_c2d中,前者对应PPP,后者对应C2D

*******************************************************************************

 

AndroidGralloc流程分析for msm8960

主要介绍Gralloc/Framebuffer HAL设备,可以籍此考察显示Buffer(Ashmem、Pmem)的拥有者和传递。

平台中内存有ashmenPMEM等多种内存类型,为了VideoGraphicsGPU内存访问的需要,android引入Gralloc模块实现内存的管理。GrallocFrameBuffer的分配也纳入了其中,并且新引入ION做为Gralloc的非FrameBuffer内存的分配器。ION对于内核态内存在用户进程之间的访问和硬件平台模块之间数据流转提供了高效的解决方案。

Android lcd 是一个帧缓冲设备,驱动程序通过处理器的 lcd控制器将物理内存的一段区域设置为显存,如果向这段内存区域写入数据就会马上在 lcd上显示出来。Android HAL 中提供了gralloc模块,封装了用户层对帧缓冲设备的所有操作接口,并通过 SurfaceFlinger服务向应用提供显示支持。在启动过程中系统会加载 gralloc模块,然后打开帧缓冲设备,获取设备的各种参数并完成 gralloc模块的初始化。当应用程序需要把内容显示到 lcd上时,需要通过 gralloc模块申请一块图形缓冲区,然后将这块图形缓冲区映射到自己的地址空间并写入内容即可。当应用程序不再需要这块图形缓冲区时需要通过 gralloc模块释放掉,然后解除对缓冲区的映射。

1、基础数据结构

gralloc模块通过 struct private_module_t来描述,该结构定义如下:

1.  struct private_module_t {  

2.      gralloc_module_t base;  

3.    

4.      private_handle_t* framebuffer;  /* 指向图形缓冲区的句柄 */  

5.      uint32_t flags;                 /* 用来标志系统帧缓冲区是否支持双缓冲 */  

6.      uint32_t numBuffers;            /* 表示系统帧缓冲的个数 */  

7.      uint32_t bufferMask;            /* 记录系统帧缓冲的使用情况 */  

8.      pthread_mutex_t lock;           /* 保护结构体private_module_t的并行访问 */  

9.      buffer_handle_t currentBuffer;  /* 描述当前正在被渲染的图形缓冲区 */  

10.     int pmem_master;                /* pmem设备节点的描述符 */  

11.     void* pmem_master_base;         /* pmem的起始虚拟地址 */  

12.   

13.     struct fb_var_screeninfo info;  /* lcd的可变参数 */  

14.     struct fb_fix_screeninfo finfo; /* lcd的固定参数 */  

15.     float xdpi;                     /* x方向上每英寸的像素数量 */  

16.     float ydpi;                     /* y方向上每英寸的像素数量 */  

17.     float fps;                      /* lcd的刷新率 */  

18.       

19.     int orientation;                /* 显示方向 */  

20.   

21.     enum {  

22.         PRIV_USAGE_LOCKED_FOR_POST = 0x80000000  /* flag to indicate we'll post this buffer */  

23.     };  

24. };

该结构的成员记录了 gralloc模块的各种参数,主要为模块自己使用,应用程序操作的图形缓冲区的数据结构是struct private_handle_t,定义如下:

1.  >#ifdef __cplusplus  

2.  struct private_handle_t : public native_handle {  

3.  #else   

4.  struct private_handle_t {  

5.      struct native_handle nativeHandle;  /* 用来描述一个本地句柄值 */  

6.  #endif   

7.        

8.      enum {  

9.          PRIV_FLAGS_FRAMEBUFFER    = 0x00000001,  

10.         PRIV_FLAGS_USES_PMEM      = 0x00000002,  

11.         PRIV_FLAGS_USES_MMEM      = 0x00000004,  

12.         PRIV_FLAGS_NEEDS_FLUSH    = 0x00000008,  

13.     };  

14.   

15.     enum {  

16.         LOCK_STATE_WRITE     =   1<<31,  

17.         LOCK_STATE_MAPPED    =   1<<30,  

18.         LOCK_STATE_READ_MASK =   0x3FFFFFFF  

19.     };  

20.   

21.     /* 指向一个文件描述符,这个文件描述符要么指向帧缓冲区设备,要么指向一块匿名共享内存 

22.      * 取决于private_handle_t描述的图形缓冲区是在帧缓冲区分配的,还是在内存中分配的 */  

23.     int     fd;  

24.     /* 指向一个魔数,它的值由静态成员变量sMagic来指定,用来标识一个private_handle_t结构体 */  

25.     int     magic;  

26.     /* 用来描述一个图形缓冲区的标志,它的值要么等于0,要么等于PRIV_FLAGS_FRAMEBUFFER 

27.      * 当一个图形缓冲区的标志值等于PRIV_FLAGS_FRAMEBUFFER的时候,就表示它是在帧缓冲区中分配的 */  

28.     int     flags;  

29.     int     size;   /* 描述一个图形缓冲区的大小 */  

30.     int     offset; /* 描述一个图形缓冲区的偏移地址 */  

31.   

32.     int     phys;   /* 图形缓冲区或帧缓冲的起始物理地址 */  

33.     int     base;   /* 图形缓冲区或帧缓冲的起始虚拟地址 */  

34.     int     lockState;  

35.     int     writeOwner;  

36.     int     pid;    /* 描述一个图形缓冲区的创建者的PID */  

37.   

38. #ifdef __cplusplus   

39.     static const int sNumInts = 9;  /* 9个整数变量 */  

40.     static const int sNumFds = 1;   /* 1个文件描述符 */  

41.     static const int sMagic = 0x3141592;  

42.   

43.     private_handle_t(int fd, int size, int flags) :  

44.         fd(fd), magic(sMagic), flags(flags), size(size), offset(0),  

45.         phys(0), base(0), lockState(0), writeOwner(0), pid(getpid())  

46.     {  

47.         version = sizeof(native_handle);  

48.         numInts = sNumInts;  

49.         numFds = sNumFds;  

50.     }  

51.     ~private_handle_t() {  

52.         magic = 0;  

53.     }  

54.   

55.     bool usesPhysicallyContiguousMemory() {  

56.         return (flags & PRIV_FLAGS_USES_PMEM) != 0;  

57.     }  

58.   

59.     /* 用来验证一个native_handle_t指针是否指向了一个private_handle_t结构体 */  

60.     static int validate(const native_handle* h) {  

61.         const private_handle_t* hnd = (const private_handle_t*)h;  

62.         if (!h || h->version != sizeof(native_handle) ||  

63.                 h->numInts != sNumInts || h->numFds != sNumFds ||  

64.                 hnd->magic != sMagic)   

65.         {  

66.             LOGE("invalid gralloc handle (at %p)", h);  

67.             return -EINVAL;  

68.         }  

69.         return 0;  

70.     }  

71.   

72.     static private_handle_t* dynamicCast(const native_handle* in) {  

73.         if (validate(in) == 0) {  

74.             return (private_handle_t*) in;  

75.         }  

76.         return NULL;  

77.     }  

78. #endif   

79. };

图形缓冲区的操作接口由结构struct gralloc_module_t 定义:

80. typedef struct gralloc_module_t {  

81.     struct hw_module_t common;  

82.   

83.     /* 注册一个图形缓冲区,这个指定的图形缓冲区使用一个buffer_handle_t句柄来描述 */  

84.     int (*registerBuffer)(struct gralloc_module_t const* module,  

85.             buffer_handle_t handle);  

86.   

87.     /* 注销一个图形缓冲区 */  

88.     int (*unregisterBuffer)(struct gralloc_module_t const* module,  

89.             buffer_handle_t handle);  

90.   

91.     /* 用来锁定一个图形缓冲区并将缓冲区映射到用户进程 

92.      * 在锁定一块图形缓冲区的时候,可以指定要锁定的图形绘冲区的位置以及大小 

93.      * 这是通过参数ltwh来指定的,其中,参数lt指定的是要访问的图形缓冲区的左上角位置 

94.      * 而参数wh指定的是要访问的图形缓冲区的宽度和长度 

95.      * 锁定之后,就可以获得由参数参数ltwh所圈定的一块缓冲区的起始地址,保存在输出参数vaddr 

96.      * 另一方面,在访问完成一块图形缓冲区之后,需要解除这块图形缓冲区的锁定 */  

97.     int (*lock)(struct gralloc_module_t const* module,  

98.             buffer_handle_t handle, int usage,  

99.             int l, int t, int w, int h,  

100.            void** vaddr);  

101.  

102.    int (*unlock)(struct gralloc_module_t const* module,  

103.            buffer_handle_t handle);  

104.  

105.    int (*perform)(struct gralloc_module_t const* module,  

106.            int operation, ... );  

107.  

108.    /* reserved for future use */  

109.    void* reserved_proc[7];  

110.} gralloc_module_t;

gralloc设备则用结构 struct alloc_device_t来描述,其定义如下:

111.typedef struct alloc_device_t {  

112.    struct hw_device_t common;  

113.  

114.    /* 申请图形缓冲区的内存空间 */      

115.    int (*alloc)(struct alloc_device_t* dev,int w, int h, int format, int usage,buffer_handle_t* handle, int* stride);  

116.  

117.    /* 释放图形缓冲区的内存空间 */  

118.    int (*free)(struct alloc_device_t* dev,buffer_handle_t handle);  

119.} alloc_device_t;

帧缓冲设备则采用结构 struct framebuffer_device_t述:

120.typedef struct framebuffer_device_t {  

121.    struct hw_device_t common;  

122.  

123.    const uint32_t  flags;  /* 用来记录系统帧缓冲区的标志 */  

124.  

125.    const uint32_t  width;  /* lcd显示区域的像素点数 */  

126.    const uint32_t  height;  

127.  

128.    const int       stride; /* 描述设备显示屏的一行有多少个像素点 */  

129.  

130.    /* 描述系统帧缓冲区的像素格式,主要有HAL_PIXEL_FORMAT_RGBX_8888和HAL_PIXEL_FORMAT_RGB_565两种 */  

131.    const int       format;  

132.  

133.    const float     xdpi;  

134.    const float     ydpi;  

135.    const float     fps;              /* lcd刷新率 */  

136.    const int       minSwapInterval;  /* 交换两帧图像的最小间隔时间 */  

137.    const int       maxSwapInterval;  /* 交换两帧图像的最大间隔时间 */  

138.  

139.    int reserved[8];  

140.  

141.    /* 设置帧交换间隔 */  

142.    int (*setSwapInterval)(struct framebuffer_device_t* window,int interval);  

143.  

144.    /* 设置帧缓冲区的更新区域 */  

145.    int (*setUpdateRect)(struct framebuffer_device_t* window,int left, int top, int width, int height);  

146.  

147.    /* 用来将图形缓冲区buffer的内容渲染到帧缓冲区中去,即显示在设备的显示屏中去 */  

148.    int (*post)(struct framebuffer_device_t* dev, buffer_handle_t buffer);  

149.  

150.    /* 用来通知fb设备device,图形缓冲区的组合工作已经完成 */  

151.    int (*compositionComplete)(struct framebuffer_device_t* dev);  

152.  

153.    void* reserved_proc[8];  

154.        } framebuffer_device_t;

中成员函数 post对应用程序来说是最重要的接口,它将完成数据写入显存的工作:

2gralloc模块

HAL中通过 hw_get_module接口加载指定 id的模块,并获得一个 hw_module_t结构来打开设备,流程如下: 

1.  >#define HAL_LIBRARY_PATH1 "/system/lib/hw"  

2.  #define HAL_LIBRARY_PATH2 "/vendor/lib/hw"   

3.    

4.  static const char *variant_keys[] = {  

5.      "ro.hardware",  /* This goes first so that it can pick up a different file on the emulator. */  

6.      "ro.product.board",  

7.      "ro.board.platform",  

8.      "ro.arch"  

9.  };  

10.   

11. static const int HAL_VARIANT_KEYS_COUNT =  

12.     (sizeof(variant_keys)/sizeof(variant_keys[0]));  

13.   

14. int hw_get_module(const char *id, const struct hw_module_t **module)   

15. {  

16.     int status;  

17.     int i;  

18.     const struct hw_module_t *hmi = NULL;  

19.     char prop[PATH_MAX];  

20.     char path[PATH_MAX];  

21.   

22.     /* 

23.      * Here we rely on the fact that calling dlopen multiple times on 

24.      * the same .so will simply increment a refcount (and not load 

25.      * a new copy of the library). 

26.      * We also assume that dlopen() is thread-safe. 

27.      */  

28.   

29.     /* Loop through the configuration variants looking for a module */  

30.     for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {  

31.         if (i < HAL_VARIANT_KEYS_COUNT) {  

32.             if (property_get(variant_keys[i], prop, NULL) == 0) {  /* 读取variant_keys数组指定的属性值 */  

33.                 continue;  

34.             }  

35.             snprintf(path, sizeof(path), "%s/%s.%s.so",  /* 格式化模块名和路径,如:/system/lib/hw/gralloc.xxx.so */  

36.                     HAL_LIBRARY_PATH1, id, prop);  

37.             if (access(path, R_OK) == 0) break;  

38.   

39.             snprintf(path, sizeof(path), "%s/%s.%s.so",  

40.                      HAL_LIBRARY_PATH2, id, prop);  

41.             if (access(path, R_OK) == 0) break;  

42.         } else {  

43.             snprintf(path, sizeof(path), "%s/%s.default.so",  

44.                      HAL_LIBRARY_PATH1, id);  

45.             if (access(path, R_OK) == 0) break;  

46.         }  

47.     }  

48.   

49.     status = -ENOENT;  

50.     if (i < HAL_VARIANT_KEYS_COUNT+1) {  

51.         /* load the module, if this fails, we're doomed, and we should not try to load a different variant. */  

52.         status = load(id, path, module);                 /* 加载模块 */  

53.     }  

54.   

55.     return status;  

56. }

可以看出,是使用id和系统平台的名字组合出so的文件名,去设定的目录动态加载该库文件然后解析特定符号,找到hw_module_t object。函数会在 /system/lib/hw或者 /vendor/lib/hw目录中去寻找gralloc.xxx.so文件,如果找到了就调用load接口完成加载。最终会调用 gralloc_device_open完成 gralloc 设备成员的初始化:

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

2.         hw_device_t** device)  

3. {  

4. 98    int status = -EINVAL;  

5. 99    if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {  

6. 100        const private_module_t* m = reinterpret_cast<const private_module_t*>(  

7. 101            module);  

8. 102        gpu_context_t *dev;  

9. 103        IAllocController* alloc_ctrl = IAllocController::getInstance();  

10.104        dev = new gpu_context_t(m, alloc_ctrl);  

11.105        *device = &dev->common;  

12.106        status = 0;

13. else {  

14.         status = fb_device_open(module, name, device);  

15.     }  

16.   

17.     return status;  

18. }

可以认为Gralloc module中有两个设备gpu_alloc_devicefb_device,前者用于分配GPU0使用的内存和FB内存,GPU0内存管理使用ION allocator;后者用于获取分配Framebuffer Info并操作fb

android系统中,所有的图形缓冲区都是由SurfaceFlinger服务分配的,在系统帧缓冲区中分配的图形缓冲区只在 SurfaceFlinger服务中使用,而在内存中分配的图形缓冲区既可以在 SurfaceFlinger服务中使用,也可以在其它的应用程序中使用,当应用程序请求 SurfaceFlinger服务分配图形缓冲区时会发生两次映射:服务所在的进程首先会将申请到的缓冲区映射至服务的地址空间,然后应用程序使用这个图形缓冲时再将其映射至应用程序的地址空间。分配函数的实现如下:

1.  static int gralloc_alloc_framebuffer(alloc_device_t* dev,size_t size, int usage, buffer_handle_t* pHandle)  

2.  {  

3.      private_module_t* m = reinterpret_cast<private_module_t*>(  

4.              dev->common.module);  

5.      pthread_mutex_lock(&m->lock);  

6.      int err = gralloc_alloc_framebuffer_locked(dev, size, usage, pHandle);  

7.      pthread_mutex_unlock(&m->lock);  

8.      return err;  

9.  }  

10.   

11. static int gralloc_alloc_buffer(alloc_device_t* dev,size_t size, int usage, buffer_handle_t* pHandle)  

12. {  

13. 127    int err = 0;  

14. 128    int flags = 0;  

15. 129    size = roundUpToPageSize(size);  

16. 130    alloc_data data;  

17. 131    data.offset = 0;  

18. 132    data.fd = -1;  

19. 133    data.base = 0;  

20. 134    data.size = size;  

21. 135    if(format == HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED)  

22. 136        data.align = 8192;  

23. 137    else  

24. 138        data.align = getpagesize();  

25. 139    data.pHandle = (unsigned int) pHandle;  

26. 140    err = mAllocCtrl->allocate(data, usage);  

27. 141  

28. 142    if (!err) {  

29. 143        /* allocate memory for enhancement data */  

30. 144        alloc_data eData;  

31. 145        eData.fd = -1;  

32. 146        eData.base = 0;  

33. 147        eData.offset = 0;  

34. 148        eData.size = ROUND_UP_PAGESIZE(sizeof(MetaData_t));  

35. 

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值