该系列文章总纲链接:Android GUI系统之SurfaceFlinger 系列文章目录


本章关键点总结 & 说明:

Android GUI系统之SurfaceFlinger(13)理解Gralloc2 内存分配_android

本章节思维导图如上。主要讲述了gralloc模块 内存分配的概念,这里 主要关注 构造器 和 alloc方法。


Android帧缓冲区硬件抽象层模块Gralloc的实现原理如下:

  1. 分配一个匹配屏幕大小的图形缓冲区
  2. 将分配好的图形缓冲区注册(映射)到当前进程的地址空间来
  3. 将要绘制的画面的内容写入到已经注册好的图形缓冲区中去,并且渲染(拷贝)到系统帧缓冲区中去

为了实现以上三个操作,我们还需要:

  1. 加载Gralloc模块
  2. 打开Gralloc模块中的gralloc设备和fb设备

其中,gralloc设备负责分配图形缓冲区,Gralloc模块负责注册图形缓冲区,而fb设备负责渲染图形缓冲区。在GrafficBuffer中,内存是由Gralloc模块中分配的,基于上一节 FB的工作原理,这里主要分析Gralloc模块如何分配内存。

1 GraphicBufferAllocator分析

在GraficBuffer中,内存申请使用的类是GraphicBufferAllocator,头文件代码如下:

class GraphicBufferAllocator : public Singleton<GraphicBufferAllocator>//单例模式类
{
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<hw_module_t*>(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<buffer_handle_t, alloc_rec_t>& 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<private_module_t*>(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<private_module_t*>(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<<numBuffers)-1)) {//如果缓冲区用完了,则返回错误值ENOMEM
        return -ENOMEM;
    }

    intptr_t vaddr = intptr_t(m->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 ; i<numBuffers ; i++) {
        if ((bufferMask & (1LU<<i)) == 0) {//通过BufferMask判断Buffer是否空闲
            m->bufferMask |= (1LU<<i);
            break;
        }
        vaddr += bufferSize;
    }
    
    hnd->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<gralloc_module_t*>(
                dev->common.module);
        err = mapBuffer(module, hnd);//使用mmap来创建内存,把共享内存的句柄和首地址关联到module
        if (err == 0) {
            *pHandle = hnd;//把创建的结构返回回去
        }
    }
    
    return err;
}

因为Android的图像缓冲区是需要进程间共享访问的,因此使用了匿名共享内存(使用共享内存前必须通过Binder来传递句柄,这里通过private_handle_t结构把共享内存的fd传递出去,这样缓冲区才可以使用)。