Android(安卓)GUI系统之SurfaceFlinger(13)理解Gralloc2 内存分配
该系列文章总纲链接:Android GUI系统之SurfaceFlinger 系列文章目录
本章关键点总结 & 说明:
本章节思维导图如上。主要讲述了gralloc模块 内存分配的概念,这里 主要关注 构造器 和 alloc方法。
Android帧缓冲区硬件抽象层模块Gralloc的实现原理如下:
- 分配一个匹配屏幕大小的图形缓冲区
- 将分配好的图形缓冲区注册(映射)到当前进程的地址空间来
- 将要绘制的画面的内容写入到已经注册好的图形缓冲区中去,并且渲染(拷贝)到系统帧缓冲区中去
为了实现以上三个操作,我们还需要:
- 加载Gralloc模块
- 打开Gralloc模块中的gralloc设备和fb设备
其中,gralloc设备负责分配图形缓冲区,Gralloc模块负责注册图形缓冲区,而fb设备负责渲染图形缓冲区。在GrafficBuffer中,内存是由Gralloc模块中分配的,基于上一节 FB的工作原理,这里主要分析Gralloc模块如何分配内存。
1 GraphicBufferAllocator分析
在GraficBuffer中,内存申请使用的类是GraphicBufferAllocator,头文件代码如下:
class GraphicBufferAllocator : public Singleton//单例模式类{public: ... //主要用来获取GraphicBufferAllocator对象 static inline GraphicBufferAllocator& get() { return getInstance(); } //分配图像缓冲区 status_t alloc(uint32_t w, uint32_t h, PixelFormat format, int usage,buffer_handle_t* handle, int32_t* stride); //释放图像缓冲区 status_t free(buffer_handle_t handle); ...};
继续查看cpp文件中的构造方法,代码如下:
GraphicBufferAllocator::GraphicBufferAllocator(): mAllocDev(0){ hw_module_t const* module; int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);//打开Gralloc模块 if (err == 0) { gralloc_open(module, &mAllocDev); }}
继续分析gralloc_open函数,代码如下:
static inline int gralloc_open(const struct hw_module_t* module, struct alloc_device_t** device) { return module->methods->open(module, GRALLOC_HARDWARE_GPU0, (struct hw_device_t**)device);}
但是与FB相比,这一次传递的参数是GRALLOC_HARDWARE_GPU0,open函数在Gralloc模块中对应的函数是gralloc_device_open,代码如下:
int gralloc_device_open(const hw_module_t* module, const char* name,hw_device_t** device){ int status = -EINVAL; if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) { gralloc_context_t *dev; dev = (gralloc_context_t*)malloc(sizeof(*dev));//创建结构体dev memset(dev, 0, sizeof(*dev)); //初始化结构体 dev->device.common.tag = HARDWARE_DEVICE_TAG; dev->device.common.version = 0; dev->device.common.module = const_cast(module); dev->device.common.close = gralloc_close; dev->device.alloc = gralloc_alloc;//分配内存的函数 dev->device.free = gralloc_free;//释放内存的函数 *device = &dev->device.common; status = 0; } else { ... //走if分支,忽略此处FB操作 } return status;}
说明:在调用完open函数后,GraphicBufferAllocator和Gralloc函数就建立了联系。
2 GraphicBufferAllocator的alloc方法分析
接下来从GraphicBufferAllocator的alloc方法开始分析,代码如下:
status_t GraphicBufferAllocator::alloc(uint32_t w, uint32_t h, PixelFormat format, int usage, buffer_handle_t* handle, int32_t* stride){ ATRACE_CALL(); if (!w || !h)//如果制定的w或者h其中一个为0,则分配一个1*1大小的buffer w = h = 1; // we have a h/w allocator and h/w buffer is requested status_t err; err = mAllocDev->alloc(mAllocDev, w, h, format, usage, handle, stride); if (err == NO_ERROR) { Mutex::Autolock _l(sLock); KeyedVector& list(sAllocList); int bpp = bytesPerPixel(format); if (bpp < 0) { // probably a HAL custom format. in any case, we don't know what its pixel size is. bpp = 0; } //创建并初始化结构体变量rec alloc_rec_t rec; rec.w = w; rec.h = h; rec.s = *stride; rec.format = format; rec.usage = usage; rec.size = h * stride[0] * bpp; list.add(*handle, rec); } return err;}
这里alloc调用了Gralloc函数中分配的函数指针,即Gralloc模块中的函数gralloc_alloc,代码如下:
static int gralloc_alloc(alloc_device_t* dev,int w, int h, int format, int usage,buffer_handle_t* pHandle, int* pStride){ if (!pHandle || !pStride) return -EINVAL; size_t size, stride; int align = 4; //对齐字节树 int bpp = 0; //每个像素字节数 switch (format) { //通过图像格式来去判断每个像素的字节数。 case HAL_PIXEL_FORMAT_RGBA_8888: case HAL_PIXEL_FORMAT_RGBX_8888: case HAL_PIXEL_FORMAT_BGRA_8888: bpp = 4; break; case HAL_PIXEL_FORMAT_RGB_888: bpp = 3; break; case HAL_PIXEL_FORMAT_RGB_565: case HAL_PIXEL_FORMAT_RAW_SENSOR: bpp = 2; break; default: return -EINVAL; } size_t bpr = (w*bpp + (align-1)) & ~(align-1);//计算每行字节数,因为要对其字节,要保证每行大小都是4的倍数 size = bpr * h;//总内存大小 stride = bpr / bpp;//每行的像素数(>=w*h),方法原理:每行的总字节数/每个像素的字节数==一行的像素 int err; if (usage & GRALLOC_USAGE_HW_FB) {//分析usage标志中是否有GRALLOC_USAGE_HW_FB err = gralloc_alloc_framebuffer(dev, size, usage, pHandle);//从硬件缓冲区中分配内存,关键点1 } else { err = gralloc_alloc_buffer(dev, size, usage, pHandle);//从内存中分配缓冲区,关键点2 } if (err < 0) { return err; } *pStride = stride; return 0;}
2.1 gralloc_alloc_framebuffer分析
如果从硬件缓冲区中分配内存,会调用gralloc_alloc_framebuffer方法,代码如下:
static int gralloc_alloc_framebuffer(alloc_device_t* dev,size_t size, int usage, buffer_handle_t* pHandle){ private_module_t* m = reinterpret_cast(dev->common.module); pthread_mutex_lock(&m->lock);//加锁 int err = gralloc_alloc_framebuffer_locked(dev, size, usage, pHandle); pthread_mutex_unlock(&m->lock);//解锁 return err;}
继续分析gralloc_alloc_framebuffer_locked的实现,代码如下:
static int gralloc_alloc_framebuffer_locked(alloc_device_t* dev, size_t size, int usage, buffer_handle_t* pHandle){ private_module_t* m = reinterpret_cast(dev->common.module); if (m->framebuffer == NULL) {//第一次调用会走这里 int err = mapFrameBufferLocked(m);//分配一大块内存 if (err < 0) { return err; } } const uint32_t bufferMask = m->bufferMask; const uint32_t numBuffers = m->numBuffers; const size_t bufferSize = m->finfo.line_length * m->info.yres;//一屏的大小 if (numBuffers == 1) { //单缓冲的FrameBuffer,使用普通内存来分配 int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D; return gralloc_alloc_buffer(dev, bufferSize, newUsage, pHandle); } //对于多缓冲区,采取这样的方法 if (bufferMask >= ((1LU<framebuffer->base); private_handle_t* hnd = new private_handle_t(dup(m->framebuffer->fd), size, private_handle_t::PRIV_FLAGS_FRAMEBUFFER); //因为FB缓冲区块数是有限的,因此需要遍历找到一个空闲块 for (uint32_t i=0 ; ibufferMask |= (1LU<base = vaddr; hnd->offset = vaddr - intptr_t(m->framebuffer->base); *pHandle = hnd; return 0;}
2.2 gralloc_alloc_buffer分析
如果从内存缓冲区中分配内存,会调用gralloc_alloc_buffer方法,代码如下:
static int gralloc_alloc_buffer(alloc_device_t* dev, size_t size, int /*usage*/, buffer_handle_t* pHandle){ int err = 0; int fd = -1; size = roundUpToPageSize(size);//进行页对齐 fd = ashmem_create_region("gralloc-buffer", size);//创建匿名共享内存,使用ashmem机制 if (fd < 0) { ALOGE("couldn't create ashmem (%s)", strerror(-errno)); err = -errno; } if (err == 0) { private_handle_t* hnd = new private_handle_t(fd, size, 0); gralloc_module_t* module = reinterpret_cast( dev->common.module); err = mapBuffer(module, hnd);//使用mmap来创建内存,把共享内存的句柄和首地址关联到module if (err == 0) { *pHandle = hnd;//把创建的结构返回回去 } } return err;}
因为Android的图像缓冲区是需要进程间共享访问的,因此使用了匿名共享内存(使用共享内存前必须通过Binder来传递句柄,这里通过private_handle_t结构把共享内存的fd传递出去,这样缓冲区才可以使用)。
更多相关文章
- android 控件之ProgressBar实现双进度条(通常用于流媒体的缓冲区
- 360面试总结(Android)
- Android实现弱引用AsyncTask,将内存泄漏置之度外。
- 在这白驹过隙的时代,Android开发者如何先人一步?
- 使用 Memory Profiler查看Java堆和内存分配
- Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原
- Android系统堆内存限制浅析
- java/Android内存泄漏和内存溢出详解
- Android内存泄漏终极解决篇(下)