@ltlovezh
2019-12-22T16:57:05.000000Z
字数 11008
阅读 3008
图形系统
gralloc
是Android中负责申请和释放GraphicBuffer
的HAL层模块,由硬件驱动提供实现,为BufferQueue
机制提供了基础。gralloc
分配的图形Buffer是进程间共享的,且根据其Flag支持不同硬件设备的读写。
从系统层级来看,gralloc
属于最底层的HAL层模块,为上层的libui
库提供服务,整个层级结构如下所示:
gralloc
HAL模块。libui
库,主要功能是封装对gralloc
HAL层的调用。代码目录是frameworks/native/include/ui和frameworks/native/libs/ui。libgui
库,主要功能是封装连接SF客户端和服务端的BufferQueue
,向下依赖于于libui
。代码目录是frameworks/native/include/gui和frameworks/native/libs/gui。Skia
、Hwui
和OpenGL ES
是BufferQueue
的生产方,SurfaceFlinger
是BufferQueue
的消费方。本篇文章主要关注gralloc
和libui
层。
gralloc
HAL模块结构体定义在gralloc.h:
// gralloc的模块ID
#define GRALLOC_HARDWARE_MODULE_ID "gralloc"
// gralloc的设备ID
#define GRALLOC_HARDWARE_GPU0 "gpu0"
// gralloc扩展的HAL层模块结构体
typedef struct gralloc_module_t {
// hw_module_t表示一个通用的硬件模块,是HAL层的灵魂,相当于继承了hw_module_t
struct hw_module_t common;
// 当其他进程分配的GraphicBuffer传递到当前进程后,需要通过该方法映射到当前进程,为后续的lock做好准备
int (*registerBuffer)(struct gralloc_module_t const* module,
buffer_handle_t handle);
// 取消GraphicBuffer在当前进程的映射,后续不能调用lock了
int (*unregisterBuffer)(struct gralloc_module_t const* module,
buffer_handle_t handle);
// 调用lock后,才能访问图形Buffer,假如usage指定了GRALLOC_USAGE_SW_* flag,vaddr将被填充成图形Buffer在虚拟内存中的地址,使用方可以直接向该地址写入像素数据
int (*lock)(struct gralloc_module_t const* module,
buffer_handle_t handle, int usage,
int l, int t, int w, int h,
void** vaddr);
// 对图形Buffer写之后,需要unlock提交数据
int (*unlock)(struct gralloc_module_t const* module,
buffer_handle_t handle);
//其他函数......
} gralloc_module_t;
// gralloc扩展的HAL层设备结构体
typedef struct alloc_device_t {
// hw_device_t表示一个通用的硬件设备,相当于继承了hw_device_t结构体
struct hw_device_t common;
// 申请一块graphic buffer,并通过buffer_handle_t标识该graphic buffer
int (*alloc)(struct alloc_device_t* dev,
int w, int h, int format, int usage,
buffer_handle_t* handle, int* stride);
// 释放buffer_handle_t标识的一块graphic buffer
int (*free)(struct alloc_device_t* dev,
buffer_handle_t handle);
//其他函数......
}
之前介绍HWC
模块时有提到:每个HAL层模块实现都要定义一个HAL_MODULE_INFO_SYM
数据结构,并且该结构的第一个字段必须是hw_module_t
。gralloc.cpp提供了gralloc
的默认实现,对应的共享库是gralloc.default.so
。高通MSM8994也提供了实现,对应的共享库是gralloc.msm8994.so
,下面是default定义:
struct private_module_t HAL_MODULE_INFO_SYM = {
// base表示gralloc_module_t结构体
.base = {
// common表示hw_module_t结构体
.common = {
.tag = HARDWARE_MODULE_TAG,
.version_major = 1,
.version_minor = 0,
.id = GRALLOC_HARDWARE_MODULE_ID,
.name = "Graphics Memory Allocator Module",
.author = "The Android Open Source Project",
// 打开gralloc设备alloc_device_t的函数
.methods = &gralloc_module_methods
},
// gralloc_module_t的扩展字段
.registerBuffer = gralloc_register_buffer,
.unregisterBuffer = gralloc_unregister_buffer,
.lock = gralloc_lock,
.unlock = gralloc_unlock,
},
.framebuffer = 0,
.flags = 0,
.numBuffers = 0,
.bufferMask = 0,
.lock = PTHREAD_MUTEX_INITIALIZER,
.currentBuffer = 0,
};
libui
库主要封装了对gralloc
HAL模块的调用,管理GraphicBuffer
的分配释放以及在不同进程间的映射,主要包含3个核心类,类图如下所示:
GraphicBuffer
:对应gralloc
分配的图形Buffer(也可能是普通内存,具体要看gralloc实现),继承ANativeWindowBuffer
结构体,核心是指向图形显存的指针(buffer_handle_t
),并且图形Buffer本身是多进程共享的,跨进程传输的是GraphicBuffer
的关键属性,这样在使用进程可以重建GraphicBuffer
,同时指向同一块图形Buffer。GraphicBufferAllocator
:向下对接gralloc
HAL模块的alloc_device_t
设备,是进程内单例,负责分配进程间共享的图形Buffer,对外即GraphicBuffer
。GraphicBufferMapper
:向下对接gralloc
HAL模块的gralloc_module_t
模块,是进程内单例,负责把GraphicBufferAllocator
分配的GraphicBuffer
映射到当前进程空间。接下来,我们看下在当前进程申请和释放GraphicBuffer的时序逻辑:
申请成功的图形Buffer的属性会保存在GraphicBuffer
父类ANativeWindowBuffer
对应字段中:
// 图形Buffer的Size = stride * height * 每像素字节数
typedef struct ANativeWindowBuffer {
// 图形Buffer的宽度
int width;
// 图形Buffer的高度
int height;
// 图形Buffer的步长,为了处理对齐问题,与height可能不同
int stride;
// 图形Buffer的像素格式
int format;
// 图形Buffer的使用规则(gralloc会分配不同属性的图形Buffer)
int usage;
// 指向一块图形Buffer
buffer_handle_t handle;
} ANativeWindowBuffer_t;
图形Buffer的Size = stride * height * 每像素字节数
除了直接申请一块图形Buffer外,还可以基于已有图形Buffer的不同形式来创建GraphicBuffer
:
ANativeWindowBuffer
native_handle_t
(即buffer_handle_t
)逻辑比较简单,可直接参考GraphicBuffer重载的构造函数,此处不再赘述。
上图中,最终通过GraphicBufferAllocator.mAllocDev
(alloc_device_t)分配图形Buffer,mAllocDev
是在GraphicBufferAllocator构造函数中初始化的:
// GraphicBufferAllocator是进程内单例
GraphicBufferAllocator::GraphicBufferAllocator(): mAllocDev(0)
{
hw_module_t const* module;
// 跟HWC一样,打开gralloc模块
int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
if (err == 0) {
// 打开gralloc设备,保存在mAllocDev
gralloc_open(module, &mAllocDev);
}
}
与打开HWC
设备一样,先打开gralloc
模块,再打开gralloc
设备,这是操作HAL模块的通用流程。
申请图形Buffer时,除了宽、高、像素格式,还有一个usage参数,它表示申请方使用GraphicBuffer
的行为,gralloc
可以根据usage做对应优化,gralloc.h定义了usage枚举值:
图形Buffer的申请方可以根据场景,使用不同的usage组合。
上述分析了在同一个进程分配和释放图形Buffer的场景,那么GraphicBuffer
怎么在进程间共享那?可以通过一个流程图概括:
GraphicBuffer::flatten
把ANativeWindowBuffer
关键属性保存在两个数组中:buffer
和fds
。buffer
和fds
。GraphicBuffer::unflatten
在自己进程重建ANativeWindowBuffer
,关键是重建ANativeWindowBuffer.handle
结构,相当于把创建进程的GraphicBuffer
映射到了使用进程。GraphicBuffer
->unlock->unregisterBuffer的基本流程操作GraphicBuffer
就行了。Binder只是传输
ANativeWindowBuffer
属性,真正的底层图形显存(内存)是进程间共享的。
从上下文可以看出,GraphicBufferAllocator
负责在创建进程申请和释放GraphicBuffer
,GraphicBufferMapper
负责在使用进程操作GraphicBuffer
。
GraphicBufferMapper
对GraphicBuffer
的所有操作最后都是通过gralloc
HAL模块实现的,具体一点就是通过gralloc_module_t
实现,感兴趣的可以参考GraphicBufferMapper.cpp,这里仅看一下gralloc
模块的初始化逻辑:
GraphicBufferMapper::GraphicBufferMapper(): mAllocMod(0) {
hw_module_t const* module;
// 跟HWC一样,打开gralloc模块
int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
if (err == 0) {
mAllocMod = reinterpret_cast<gralloc_module_t const*>(module);
}
}
最后,结合上述流程图我们看下GraphicBuffer
跨进程传输的关键代码:
// 计算传输GraphicBuffer需要的Size
size_t GraphicBuffer::getFlattenedSize() const {
return static_cast<size_t>(11 + (handle ? handle->numInts : 0)) * sizeof(int);
}
// 获取文件描述符数量
size_t GraphicBuffer::getFdCount() const {
return static_cast<size_t>(handle ? handle->numFds : 0);
}
// 把GraphicBuffer关键属性保存在buffer和fds中,以进行Binder传输
// size表示buffer数组可用长度,count表示fds数组可用长度
status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const {
// 判断buffer可用长度是否足够
size_t sizeNeeded = GraphicBuffer::getFlattenedSize();
if (size < sizeNeeded) return NO_MEMORY;
// 判断fds数组长度是否足够
size_t fdCountNeeded = GraphicBuffer::getFdCount();
if (count < fdCountNeeded) return NO_MEMORY;
// 把当前GraphicBuffer的关键属性存储在buffer中
int32_t* buf = static_cast<int32_t*>(buffer);
// 存储标识符
buf[0] = 'GBFR';
buf[1] = width;
buf[2] = height;
buf[3] = stride;
buf[4] = format;
buf[5] = usage;
buf[6] = static_cast<int32_t>(mId >> 32);
buf[7] = static_cast<int32_t>(mId & 0xFFFFFFFFull);
buf[8] = static_cast<int32_t>(mGenerationNumber);
buf[9] = 0;
buf[10] = 0;
if (handle) {
// 存储文件描述符数量
buf[9] = handle->numFds;
// 存储int数组长度
buf[10] = handle->numInts;
// copy文件描述符数组到fds
memcpy(fds, handle->data,
static_cast<size_t>(handle->numFds) * sizeof(int));
// copy int数组到buffer
memcpy(&buf[11], handle->data + handle->numFds,
static_cast<size_t>(handle->numInts) * sizeof(int));
}
// 修改buffer地址和可用长度
buffer = static_cast<void*>(static_cast<uint8_t*>(buffer) + sizeNeeded);
size -= sizeNeeded;
if (handle) {
// 修改fds地址和可用长度
fds += handle->numFds;
count -= static_cast<size_t>(handle->numFds);
}
return NO_ERROR;
}
// 根据Binder传输的buffer和fds中,把创建进程的GraphicBuffer映射到使用进程
status_t GraphicBuffer::unflatten(
void const*& buffer, size_t& size, int const*& fds, size_t& count) {
// 判断buffer的正确性
if (size < 11 * sizeof(int)) return NO_MEMORY;
int const* buf = static_cast<int const*>(buffer);
if (buf[0] != 'GBFR') return BAD_TYPE;
// 取出文件描述符和int数组的长度
const size_t numFds = static_cast<size_t>(buf[9]);
const size_t numInts = static_cast<size_t>(buf[10]);
// 判断buffer长度是否正确
const size_t sizeNeeded = (11 + numInts) * sizeof(int);
if (size < sizeNeeded) return NO_MEMORY;
// 判断fds长度是否正确
size_t fdCountNeeded = numFds;
if (count < fdCountNeeded) return NO_MEMORY;
if (handle) {
// 如果有,先释放之前的ANativeWindowBuffer.handle
free_handle();
}
if (numFds || numInts) {
width = buf[1];
height = buf[2];
stride = buf[3];
format = buf[4];
usage = buf[5];
// 创建ANativeWindowBuffer.handle,native_handle_create定义在native_handle.c
native_handle* h = native_handle_create(static_cast<int>(numFds), static_cast<int>(numInts));
if (!h) {
width = height = stride = format = usage = 0;
handle = NULL;
ALOGE("unflatten: native_handle_create failed");
return NO_MEMORY;
}
// 从fds和buffer中copy文件描述符和int数组到ANativeWindowBuffer.handle结构体
memcpy(h->data, fds, numFds * sizeof(int));
memcpy(h->data + numFds, &buf[11], numInts * sizeof(int));
handle = h;
} else {
width = height = stride = format = usage = 0;
handle = NULL;
}
// 从buffer中恢复其他字段
mId = static_cast<uint64_t>(buf[6]) << 32;
mId |= static_cast<uint32_t>(buf[7]);
mGenerationNumber = static_cast<uint32_t>(buf[8]);
// 表示GraphicBuffer是从其他创建进程映射过来的,决定了释放GraphicBuffer的逻辑
mOwner = ownHandle;
if (handle != 0) {
// register到当前线程
status_t err = mBufferMapper.registerBuffer(handle);
}
// 调整buffer和fds数组的地址和可用长度
buffer = static_cast<void const*>(static_cast<uint8_t const*>(buffer) + sizeNeeded);
size -= sizeNeeded;
fds += numFds;
count -= numFds;
return NO_ERROR;
}
// ANativeWindowBuffer.handle结构体
typedef struct native_handle
{
/* sizeof(native_handle_t) */
int version;
/* number of file-descriptors at &data[0] */
int numFds;
/* number of ints at &data[numFds] */
int numInts;
/* numFds + numInts ints */
int data[0];
} native_handle_t;
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
的释放代码:
// GraphicBuffer析构函数会调用到这里
void GraphicBuffer::free_handle()
{
if (mOwner == ownHandle) { // 表示图形Buffer不是自己创建的,而是从创建进程映射过来的,即处于使用进程
mBufferMapper.unregisterBuffer(handle);
// 关闭删除ANativeWindowBuffer.handle,具体方法实现可参见native_handle.c
native_handle_close(handle);
native_handle_delete(const_cast<native_handle*>(handle));
} else if (mOwner == ownData) { //表示图形Buffer是自己创建的,需要自己释放,即处于创建进程
GraphicBufferAllocator& allocator(GraphicBufferAllocator::get());
allocator.free(handle);
}
handle = NULL;
mWrappedBuffer = 0;
}
指向同一块图形Buffer的GraphicBuffer可以存在多个实例,但是底层的图形Buffer是同一个。
此外,GraphicBufferAllocator
会记录所有分配的GraphicBuffer
,通过adb shell dumpsys SurfaceFlinger
查看:
// GraphicBufferAllocator的dump信息
Allocated buffers:
// 分别表示buffer_handle_t地址,图形Buffer的大小,宽(stride)*高、像素格式,usage等
0x76fd25a070: 1546.00 KiB | 459 ( 512) x 773 | 1 | 1 | 0x10000900 | PopupWindow:e2334a2#0
0x76fd25a7e0: 9945.00 KiB | 1080 (1088) x 2340 | 1 | 1 | 0x10000900 | StatusBar#0
0x76fd837220: 9945.00 KiB | 1080 (1088) x 2340 | 1 | 1 | 0x10001a00 | FramebufferSurface
// 表示分配的图形Buffer的总大小
Total allocated (estimate): 112834.50 KB
这里再提出一个疑问:GraphicBuffer
是在哪个进程分配的?
要解释这个问题,需要了解BufferQueue
逻辑,这里不做深入介绍,后面会单独文章分析。直接给出结论:BufferQueueProducer
通过BufferQueueCore
持有的IGraphicBufferAlloc
创建GraphicBuffer
,而IGraphicBufferAlloc
的实现类GraphicBufferAlloc
则运行在SurfaceFlinger
进程。也就是说,真正分配GraphicBuffer
的只有Surfaceflinger
进程,其他进程只不过是映射操作。
本篇文章分析了gralloc
HAL模块,以及libui
库主要逻辑。下一篇文章继续向上看下libgui
库主要逻辑。