[关闭]
@ltlovezh 2019-12-22T08:57:05.000000Z 字数 11008 阅读 715

Android图形系统系统篇之Gralloc

图形系统


gralloc是Android中负责申请和释放GraphicBuffer的HAL层模块,由硬件驱动提供实现,为BufferQueue机制提供了基础。gralloc分配的图形Buffer是进程间共享的,且根据其Flag支持不同硬件设备的读写。

从系统层级来看,gralloc属于最底层的HAL层模块,为上层的libui库提供服务,整个层级结构如下所示:

本篇文章主要关注gralloclibui层。

gralloc

grallocHAL模块结构体定义在gralloc.h

  1. // gralloc的模块ID
  2. #define GRALLOC_HARDWARE_MODULE_ID "gralloc"
  3. // gralloc的设备ID
  4. #define GRALLOC_HARDWARE_GPU0 "gpu0"
  5. // gralloc扩展的HAL层模块结构体
  6. typedef struct gralloc_module_t {
  7. // hw_module_t表示一个通用的硬件模块,是HAL层的灵魂,相当于继承了hw_module_t
  8. struct hw_module_t common;
  9. // 当其他进程分配的GraphicBuffer传递到当前进程后,需要通过该方法映射到当前进程,为后续的lock做好准备
  10. int (*registerBuffer)(struct gralloc_module_t const* module,
  11. buffer_handle_t handle);
  12. // 取消GraphicBuffer在当前进程的映射,后续不能调用lock了
  13. int (*unregisterBuffer)(struct gralloc_module_t const* module,
  14. buffer_handle_t handle);
  15. // 调用lock后,才能访问图形Buffer,假如usage指定了GRALLOC_USAGE_SW_* flag,vaddr将被填充成图形Buffer在虚拟内存中的地址,使用方可以直接向该地址写入像素数据
  16. int (*lock)(struct gralloc_module_t const* module,
  17. buffer_handle_t handle, int usage,
  18. int l, int t, int w, int h,
  19. void** vaddr);
  20. // 对图形Buffer写之后,需要unlock提交数据
  21. int (*unlock)(struct gralloc_module_t const* module,
  22. buffer_handle_t handle);
  23. //其他函数......
  24. } gralloc_module_t;
  25. // gralloc扩展的HAL层设备结构体
  26. typedef struct alloc_device_t {
  27. // hw_device_t表示一个通用的硬件设备,相当于继承了hw_device_t结构体
  28. struct hw_device_t common;
  29. // 申请一块graphic buffer,并通过buffer_handle_t标识该graphic buffer
  30. int (*alloc)(struct alloc_device_t* dev,
  31. int w, int h, int format, int usage,
  32. buffer_handle_t* handle, int* stride);
  33. // 释放buffer_handle_t标识的一块graphic buffer
  34. int (*free)(struct alloc_device_t* dev,
  35. buffer_handle_t handle);
  36. //其他函数......
  37. }

之前介绍HWC模块时有提到:每个HAL层模块实现都要定义一个HAL_MODULE_INFO_SYM数据结构,并且该结构的第一个字段必须是hw_module_tgralloc.cpp提供了gralloc的默认实现,对应的共享库是gralloc.default.so高通MSM8994也提供了实现,对应的共享库是gralloc.msm8994.so,下面是default定义:

  1. struct private_module_t HAL_MODULE_INFO_SYM = {
  2. // base表示gralloc_module_t结构体
  3. .base = {
  4. // common表示hw_module_t结构体
  5. .common = {
  6. .tag = HARDWARE_MODULE_TAG,
  7. .version_major = 1,
  8. .version_minor = 0,
  9. .id = GRALLOC_HARDWARE_MODULE_ID,
  10. .name = "Graphics Memory Allocator Module",
  11. .author = "The Android Open Source Project",
  12. // 打开gralloc设备alloc_device_t的函数
  13. .methods = &gralloc_module_methods
  14. },
  15. // gralloc_module_t的扩展字段
  16. .registerBuffer = gralloc_register_buffer,
  17. .unregisterBuffer = gralloc_unregister_buffer,
  18. .lock = gralloc_lock,
  19. .unlock = gralloc_unlock,
  20. },
  21. .framebuffer = 0,
  22. .flags = 0,
  23. .numBuffers = 0,
  24. .bufferMask = 0,
  25. .lock = PTHREAD_MUTEX_INITIALIZER,
  26. .currentBuffer = 0,
  27. };

libui

libui库主要封装了对grallocHAL模块的调用,管理GraphicBuffer的分配释放以及在不同进程间的映射,主要包含3个核心类,类图如下所示:
libui类图

接下来,我们看下在当前进程申请和释放GraphicBuffer的时序逻辑:
申请和释放GraphicBuffer

申请成功的图形Buffer的属性会保存在GraphicBuffer父类ANativeWindowBuffer对应字段中:

  1. // 图形Buffer的Size = stride * height * 每像素字节数
  2. typedef struct ANativeWindowBuffer {
  3. // 图形Buffer的宽度
  4. int width;
  5. // 图形Buffer的高度
  6. int height;
  7. // 图形Buffer的步长,为了处理对齐问题,与height可能不同
  8. int stride;
  9. // 图形Buffer的像素格式
  10. int format;
  11. // 图形Buffer的使用规则(gralloc会分配不同属性的图形Buffer)
  12. int usage;
  13. // 指向一块图形Buffer
  14. buffer_handle_t handle;
  15. } ANativeWindowBuffer_t;

图形Buffer的Size = stride * height * 每像素字节数

除了直接申请一块图形Buffer外,还可以基于已有图形Buffer的不同形式来创建GraphicBuffer

逻辑比较简单,可直接参考GraphicBuffer重载的构造函数,此处不再赘述。

上图中,最终通过GraphicBufferAllocator.mAllocDev(alloc_device_t)分配图形Buffer,mAllocDev是在GraphicBufferAllocator构造函数中初始化的:

  1. // GraphicBufferAllocator是进程内单例
  2. GraphicBufferAllocator::GraphicBufferAllocator(): mAllocDev(0)
  3. {
  4. hw_module_t const* module;
  5. // 跟HWC一样,打开gralloc模块
  6. int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
  7. if (err == 0) {
  8. // 打开gralloc设备,保存在mAllocDev
  9. gralloc_open(module, &mAllocDev);
  10. }
  11. }

与打开HWC设备一样,先打开gralloc模块,再打开gralloc设备,这是操作HAL模块的通用流程。

申请图形Buffer时,除了宽、高、像素格式,还有一个usage参数,它表示申请方使用GraphicBuffer的行为,gralloc可以根据usage做对应优化,gralloc.h定义了usage枚举值:

图形Buffer的申请方可以根据场景,使用不同的usage组合。

上述分析了在同一个进程分配和释放图形Buffer的场景,那么GraphicBuffer怎么在进程间共享那?可以通过一个流程图概括:

  1. 首先,创建进程通过GraphicBuffer::flattenANativeWindowBuffer关键属性保存在两个数组中:bufferfds
  2. 其次,跨进程传输bufferfds
  3. 然后,使用进程通过GraphicBuffer::unflatten在自己进程重建ANativeWindowBuffer,关键是重建ANativeWindowBuffer.handle结构,相当于把创建进程的GraphicBuffer映射到了使用进程。
  4. 最后,遵循registerBuffer->lock->读写GraphicBuffer->unlock->unregisterBuffer的基本流程操作GraphicBuffer就行了。

Binder只是传输ANativeWindowBuffer属性,真正的底层图形显存(内存)是进程间共享的。
从上下文可以看出,GraphicBufferAllocator负责在创建进程申请和释放GraphicBufferGraphicBufferMapper负责在使用进程操作GraphicBuffer

GraphicBufferMapperGraphicBuffer的所有操作最后都是通过grallocHAL模块实现的,具体一点就是通过gralloc_module_t实现,感兴趣的可以参考GraphicBufferMapper.cpp,这里仅看一下gralloc模块的初始化逻辑:

  1. GraphicBufferMapper::GraphicBufferMapper(): mAllocMod(0) {
  2. hw_module_t const* module;
  3. // 跟HWC一样,打开gralloc模块
  4. int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
  5. if (err == 0) {
  6. mAllocMod = reinterpret_cast<gralloc_module_t const*>(module);
  7. }
  8. }

最后,结合上述流程图我们看下GraphicBuffer跨进程传输的关键代码:

  1. // 计算传输GraphicBuffer需要的Size
  2. size_t GraphicBuffer::getFlattenedSize() const {
  3. return static_cast<size_t>(11 + (handle ? handle->numInts : 0)) * sizeof(int);
  4. }
  5. // 获取文件描述符数量
  6. size_t GraphicBuffer::getFdCount() const {
  7. return static_cast<size_t>(handle ? handle->numFds : 0);
  8. }
  9. // 把GraphicBuffer关键属性保存在buffer和fds中,以进行Binder传输
  10. // size表示buffer数组可用长度,count表示fds数组可用长度
  11. status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const {
  12. // 判断buffer可用长度是否足够
  13. size_t sizeNeeded = GraphicBuffer::getFlattenedSize();
  14. if (size < sizeNeeded) return NO_MEMORY;
  15. // 判断fds数组长度是否足够
  16. size_t fdCountNeeded = GraphicBuffer::getFdCount();
  17. if (count < fdCountNeeded) return NO_MEMORY;
  18. // 把当前GraphicBuffer的关键属性存储在buffer中
  19. int32_t* buf = static_cast<int32_t*>(buffer);
  20. // 存储标识符
  21. buf[0] = 'GBFR';
  22. buf[1] = width;
  23. buf[2] = height;
  24. buf[3] = stride;
  25. buf[4] = format;
  26. buf[5] = usage;
  27. buf[6] = static_cast<int32_t>(mId >> 32);
  28. buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
  29. buf[8] = static_cast<int32_t>(mGenerationNumber);
  30. buf[9] = 0;
  31. buf[10] = 0;
  32. if (handle) {
  33. // 存储文件描述符数量
  34. buf[9] = handle->numFds;
  35. // 存储int数组长度
  36. buf[10] = handle->numInts;
  37. // copy文件描述符数组到fds
  38. memcpy(fds, handle->data,
  39. static_cast<size_t>(handle->numFds) * sizeof(int));
  40. // copy int数组到buffer
  41. memcpy(&buf[11], handle->data + handle->numFds,
  42. static_cast<size_t>(handle->numInts) * sizeof(int));
  43. }
  44. // 修改buffer地址和可用长度
  45. buffer = static_cast<void*>(static_cast<uint8_t*>(buffer) + sizeNeeded);
  46. size -= sizeNeeded;
  47. if (handle) {
  48. // 修改fds地址和可用长度
  49. fds += handle->numFds;
  50. count -= static_cast<size_t>(handle->numFds);
  51. }
  52. return NO_ERROR;
  53. }
  54. // 根据Binder传输的buffer和fds中,把创建进程的GraphicBuffer映射到使用进程
  55. status_t GraphicBuffer::unflatten(
  56. void const*& buffer, size_t& size, int const*& fds, size_t& count) {
  57. // 判断buffer的正确性
  58. if (size < 11 * sizeof(int)) return NO_MEMORY;
  59. int const* buf = static_cast<int const*>(buffer);
  60. if (buf[0] != 'GBFR') return BAD_TYPE;
  61. // 取出文件描述符和int数组的长度
  62. const size_t numFds = static_cast<size_t>(buf[9]);
  63. const size_t numInts = static_cast<size_t>(buf[10]);
  64. // 判断buffer长度是否正确
  65. const size_t sizeNeeded = (11 + numInts) * sizeof(int);
  66. if (size < sizeNeeded) return NO_MEMORY;
  67. // 判断fds长度是否正确
  68. size_t fdCountNeeded = numFds;
  69. if (count < fdCountNeeded) return NO_MEMORY;
  70. if (handle) {
  71. // 如果有,先释放之前的ANativeWindowBuffer.handle
  72. free_handle();
  73. }
  74. if (numFds || numInts) {
  75. width = buf[1];
  76. height = buf[2];
  77. stride = buf[3];
  78. format = buf[4];
  79. usage = buf[5];
  80. // 创建ANativeWindowBuffer.handle,native_handle_create定义在native_handle.c
  81. native_handle* h = native_handle_create(static_cast<int>(numFds), static_cast<int>(numInts));
  82. if (!h) {
  83. width = height = stride = format = usage = 0;
  84. handle = NULL;
  85. ALOGE("unflatten: native_handle_create failed");
  86. return NO_MEMORY;
  87. }
  88. // 从fds和buffer中copy文件描述符和int数组到ANativeWindowBuffer.handle结构体
  89. memcpy(h->data, fds, numFds * sizeof(int));
  90. memcpy(h->data + numFds, &buf[11], numInts * sizeof(int));
  91. handle = h;
  92. } else {
  93. width = height = stride = format = usage = 0;
  94. handle = NULL;
  95. }
  96. // 从buffer中恢复其他字段
  97. mId = static_cast<uint64_t>(buf[6]) << 32;
  98. mId |= static_cast<uint32_t>(buf[7]);
  99. mGenerationNumber = static_cast<uint32_t>(buf[8]);
  100. // 表示GraphicBuffer是从其他创建进程映射过来的,决定了释放GraphicBuffer的逻辑
  101. mOwner = ownHandle;
  102. if (handle != 0) {
  103. // register到当前线程
  104. status_t err = mBufferMapper.registerBuffer(handle);
  105. }
  106. // 调整buffer和fds数组的地址和可用长度
  107. buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded);
  108. size -= sizeNeeded;
  109. fds += numFds;
  110. count -= numFds;
  111. return NO_ERROR;
  112. }
  113. // ANativeWindowBuffer.handle结构体
  114. typedef struct native_handle
  115. {
  116. /* sizeof(native_handle_t) */
  117. int version;
  118. /* number of file-descriptors at &data[0] */
  119. int numFds;
  120. /* number of ints at &data[numFds] */
  121. int numInts;
  122. /* numFds + numInts ints */
  123. int data[0];
  124. } native_handle_t;
  125. typedef const native_handle_t* buffer_handle_t

上述代码虽然很长,但都是关键代码,感兴趣的可以参考注释阅读。ANativeWindowBuffer.handle是GraphicBuffer的核心字段,定义在handle.h中。native_handle.c则定义了create、close和delete native_handle的方法,感兴趣的可自行查看。

前面我们看了GraphicBuffer申请和释放的时序图,可知在创建进程GraphicBuffer是通过GraphicBufferAllocator进行申请和释放的,那么在使用进程那?很明显,是通过unflatten在使用进程重建了GraphicBuffer,那么使用进程是如何释放GraphicBuffer的?毕竟真正的图形Buffer并不是在当前进程创建的。我们可以直接看下GraphicBuffer的释放代码:

  1. // GraphicBuffer析构函数会调用到这里
  2. void GraphicBuffer::free_handle()
  3. {
  4. if (mOwner == ownHandle) { // 表示图形Buffer不是自己创建的,而是从创建进程映射过来的,即处于使用进程
  5. mBufferMapper.unregisterBuffer(handle);
  6. // 关闭删除ANativeWindowBuffer.handle,具体方法实现可参见native_handle.c
  7. native_handle_close(handle);
  8. native_handle_delete(const_cast<native_handle*>(handle));
  9. } else if (mOwner == ownData) { //表示图形Buffer是自己创建的,需要自己释放,即处于创建进程
  10. GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
  11. allocator.free(handle);
  12. }
  13. handle = NULL;
  14. mWrappedBuffer = 0;
  15. }

指向同一块图形Buffer的GraphicBuffer可以存在多个实例,但是底层的图形Buffer是同一个。

此外,GraphicBufferAllocator会记录所有分配的GraphicBuffer,通过adb shell dumpsys SurfaceFlinger查看:

  1. // GraphicBufferAllocator的dump信息
  2. Allocated buffers:
  3. // 分别表示buffer_handle_t地址,图形Buffer的大小,宽(stride)*高、像素格式,usage等
  4. 0x76fd25a070: 1546.00 KiB | 459 ( 512) x 773 | 1 | 1 | 0x10000900 | PopupWindow:e2334a2#0
  5. 0x76fd25a7e0: 9945.00 KiB | 1080 (1088) x 2340 | 1 | 1 | 0x10000900 | StatusBar#0
  6. 0x76fd837220: 9945.00 KiB | 1080 (1088) x 2340 | 1 | 1 | 0x10001a00 | FramebufferSurface
  7. // 表示分配的图形Buffer的总大小
  8. Total allocated (estimate): 112834.50 KB

这里再提出一个疑问:GraphicBuffer是在哪个进程分配的?
要解释这个问题,需要了解BufferQueue逻辑,这里不做深入介绍,后面会单独文章分析。直接给出结论:BufferQueueProducer通过BufferQueueCore持有的IGraphicBufferAlloc创建GraphicBuffer,而IGraphicBufferAlloc的实现类GraphicBufferAlloc则运行在SurfaceFlinger进程。也就是说,真正分配GraphicBuffer的只有Surfaceflinger进程,其他进程只不过是映射操作。

总结

本篇文章分析了grallocHAL模块,以及libui库主要逻辑。下一篇文章继续向上看下libgui库主要逻辑。

参考文章

  1. Android 图形系统之gralloc
  2. Android图形显示系统——下层显示2:图形内存的申请与显示
  3. Android P 图像显示系统(二)GraphicBuffer和Gralloc分析
  4. BufferQueue和Gralloc
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注