介绍了Android SurfaceFlinger层次以下的图形合成和显示系统,主要基于高通MSM8k MDP4x平台。
做为Android Display专题。SurfaceFlinger的详细介绍参见链接文章。
SurfaceFinger按英文翻译过来就是Surface投递者。SufaceFlinger的构成并不是太复杂,复杂的是他的客户端建构。SufaceFlinger主要功能是:
1)将Layers(Surfaces)内容的刷新到屏幕上
2)维持Layer的Zorder序列,并对Layer最终输出做出裁剪计算。
3)响应Client要求,创建Layer与客户端的Surface建立连接
4)接收Client要求,修改Layer属性(输出大小,Alpha等设定)
但是作为投递者的实际意义,我们首先需要知道的是如何投递,投掷物,投递路线,投递目的地。
1 SurfaceFlinger的基本组成框架
SurfaceFlinger管理对象为:
mClientsMap:管理客户端与服务端的连接。
ISurface,IsurfaceComposer:AIDL调用接口实例
mLayerMap:服务端的Surface的管理对象。
mCurrentState.layersSortedByZ:以Surface的Z-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.在当前的Android(2.1)版本的设计中,系统支持一个graphicPlane,所以也就支持一个DisplayHardware。
SurfaceFlinger,Hardware硬件缓冲区的数据结构关系图。
3.2 Layer
method:setBuffer在SurfaceFlinger端建立显示缓冲区。这里的缓冲区是指的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
以Surface的Z-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进程上下文。
建立主Surface(OpenGL ES)。 DisplayHardware的Init()@DisplayHardware.cpp函数对OpenGL做了初始化,并创建立主Surface。为什么叫主Surface,因为所有的Layer在绘制时,都需要先绘制在这个主Surface上,最后系统才将主Surface的内容”投掷”到真正的屏幕上。
(3)主Surface的绑定
1)在DisplayHandware初始完毕后,hw.makeCurrent()将主Surface,OpenGL ES进程上下文绑定到SurfaceFlinger的上下文中,
2)之后所有的SurfaceFlinger进程中使用EGL的所有的操作目的地都是mSurface@DisplayHardware。
这样,在OpenGL绘制图形时,主Surface被记录在进程的上下文中,所以看不到显示的主Surfce相关参数的传递。下面是Layer-Draw,Hardware.flip的动作示意图:
4.2 ThreadLoop
(1)handleTransaction(…):主要计算每个Layer有无属性修改,如果有修改着内用需要重画。
(2)handlePageFlip()
computeVisibleRegions:根据Z-Order序列计算每个Layer的可见区域和被覆盖区域。裁剪输出范围计算-
在生成裁剪区域的时候,根据Z_order依次,每个Layer在计算自己在屏幕的可显示区域时,需要经历如下步骤:
1)以自己的W,H给出自己初始的可见区域
2)减去自己上面窗口所覆盖的区域
在绘制时,Layer将根据自己的可将区域做相应的区域数据Copy。
(3)handleRepaint()
composeSurfaces(需要刷新区域):
根据每个Layer的可见区域与需要刷新区域的交集区域从Z-Order序列从底部开始绘制到主Surface上。
(4)postFramebuffer()
(DisplayHardware)hw.flip(mInvalidRegion);
eglSwapBuffers(display,mSurface) :将mSruface投递到屏幕。
5总结
现在SurfaceFlinger干的事情利用下面的示意图表示出来:
更详细地,参考
Android GUI之SurfaceFlinger系列
Android display架构分析-SW架构分析(1-8)
SurfaceFlinger使用的各组件,参考
Learning about Android Graphics Subsystem by MIPS Engineer
*******************************************************************************
Copybit HAL Introduction
SurfaceFlinger layer的compositionType有三种:
HWC_FRAMEBUFFER的使用OpenGL ES来绘制;
HWC_OVERLAY的使用Overlay Engine来合成;
HWC_USE_COPYBIT的使用Copybit硬件加速绘制;
MSM8xxx平台Jellybean代码中没有发现使用HWC_USE_COPYBIT的layer,该平台下 Copybit 硬件加速主要有两种:
PPP :vpe模块的PPP,direct copy;
C2D :可能是2D GPU OpenVG之类的。
PPP驱动实现是做为Framebuffer设备的一个命令MSMFB_BLIT,C2D是使用c2d hal库;
MSM7627平台下hwcomposer还是使用copybit的。
可能早期系统没有Hardware Composer,又没有GPU的时候,layer draw就要使用Copybit去一层一层一Rect一Rect的拷贝了。
Copybit的代码在display/libcopybit下,硬件合成器使用Copybit做的封装代码在display/libhwcomposer/copybit和copybit_c2d中,前者对应PPP,后者对应C2D。
*******************************************************************************
主要介绍Gralloc/Framebuffer HAL设备,可以籍此考察显示Buffer(Ashmem、Pmem)的拥有者和传递。
平台中内存有ashmen、PMEM等多种内存类型,为了Video、Graphics、GPU内存访问的需要,android引入Gralloc模块实现内存的管理。Gralloc把FrameBuffer的分配也纳入了其中,并且新引入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. * 这是通过参数l、t、w和h来指定的,其中,参数l和t指定的是要访问的图形缓冲区的左上角位置
94. * 而参数w和h指定的是要访问的图形缓冲区的宽度和长度
95. * 锁定之后,就可以获得由参数参数l、t、w和h所圈定的一块缓冲区的起始地址,保存在输出参数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对应用程序来说是最重要的接口,它将完成数据写入显存的工作:
2、gralloc模块
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_device和fb_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.