4.6 Gralloc分配buffer
用户空间的应用程序用到的图形缓冲区是由Gralloc模块中的函数gralloc_alloc来分配的,这个函数实现在文件hardware/qcom/display/msm8974/libgralloc/gpu.cpp。
int gpu_context_t::gralloc_alloc(alloc_device_t* dev, int w, int h, int format, int usage, buffer_handle_t* pHandle, int* pStride) { if (!dev) { return -EINVAL; } gpu_context_t* gpu = reinterpret_cast<gpu_context_t*>(dev); return gpu->alloc_impl(w, h, format, usage, pHandle, pStride, 0); }
|
在同一个类中,完成alloc_impl的实现。
int gpu_context_t::alloc_impl(int w, int h, int format, int usage, buffer_handle_t* pHandle, int* pStride, size_t bufferSize) { if (!pHandle || !pStride) return -EINVAL;
size_t size; int alignedw, alignedh; int grallocFormat = format; int bufferType;
// 参数format用来描述要分配的图形缓冲区的颜色格式。这些格式定义在system/core/include/system/graphic.h中。 //If input format is HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED then based on //the usage bits, gralloc assigns a format. if(format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED || format == HAL_PIXEL_FORMAT_YCbCr_420_888) { if(usage & GRALLOC_USAGE_HW_VIDEO_ENCODER) grallocFormat = HAL_PIXEL_FORMAT_NV12_ENCODEABLE; //NV12 else if((usage & GRALLOC_USAGE_HW_CAMERA_MASK) == GRALLOC_USAGE_HW_CAMERA_ZSL) grallocFormat = HAL_PIXEL_FORMAT_NV21_ZSL; //NV21 ZSL else if(usage & GRALLOC_USAGE_HW_CAMERA_READ) grallocFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP; //NV21 else if(usage & GRALLOC_USAGE_HW_CAMERA_WRITE) grallocFormat = HAL_PIXEL_FORMAT_YCrCb_420_SP; //NV21 }
if (grallocFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED && (usage & GRALLOC_USAGE_HW_COMPOSER )) { //XXX: If we still haven't set a format, default to RGBA8888 grallocFormat = HAL_PIXEL_FORMAT_RGBA_8888; }
// 设置buffertype,BUFFER_TYPE_UI: RGB formats & HAL_PIXEL_FORMAT_R_8 &HAL_PIXEL_FORMAT_RG_88。其他的都为BUFFER_TYPE_VIDEO getGrallocInformationFromFormat(grallocFormat, &bufferType);
// 根据formate & w,h算出buffersize size = getBufferSizeAndDimensions(w, h, grallocFormat, alignedw, alignedh);
if ((ssize_t)size <= 0) return -EINVAL; size = (bufferSize >= size)? bufferSize : size;
// All buffers marked as protected or for external // display need to go to overlay if ((usage & GRALLOC_USAGE_EXTERNAL_DISP) || (usage & GRALLOC_USAGE_PROTECTED)) { bufferType = BUFFER_TYPE_VIDEO; }
// gralloc usage的类型定义在hardware/libhardware/include/hardware/gralloc.h中。 bool useFbMem = false; char property[PROPERTY_VALUE_MAX]; if((usage & GRALLOC_USAGE_HW_FB) && (property_get("debug.gralloc.map_fb_memory", property, NULL) > 0) && (!strncmp(property, "1", PROPERTY_VALUE_MAX ) || (!strncasecmp(property,"true", PROPERTY_VALUE_MAX )))) { useFbMem = true; }
int err = 0; if(useFbMem) { err = gralloc_alloc_framebuffer(size, usage, pHandle); } else { err = gralloc_alloc_buffer(size, usage, pHandle, bufferType, grallocFormat, alignedw, alignedh); }
if (err < 0) { return err; }
// 一行占据的点数,与宽度的差别就在于前者对齐到4字节边界,后者是调用者传进来的,不一定是对齐到4字节边界的。 *pStride = alignedw; return 0; } |
最后根据memory alloc出处,区别调用gralloc_alloc_framebuffer& gralloc_alloc_buffer函数。
首先来看看 gralloc_alloc_framebuffer的实现:
int gpu_context_t::gralloc_alloc_framebuffer(size_t size, int usage, buffer_handle_t* pHandle) { private_module_t* m = reinterpret_cast<private_module_t*>(common.module); pthread_mutex_lock(&m->lock); int err = gralloc_alloc_framebuffer_locked(size, usage, pHandle); pthread_mutex_unlock(&m->lock); return err; }
int gpu_context_t::gralloc_alloc_framebuffer_locked(size_t size, int usage, buffer_handle_t* pHandle) { private_module_t* m = reinterpret_cast<private_module_t*>(common.module);
// we don't support framebuffer allocations with graphics heap flags if (usage & GRALLOC_HEAP_MASK) { return -EINVAL; }
if (m->framebuffer == NULL) { ALOGE("%s: Invalid framebuffer", __FUNCTION__); return -EINVAL; }
// 变量bufferMask用来描述系统帧缓冲区的使用情况 // 变量numBuffers用来描述系统帧缓冲区可以划分为多少个图形缓冲区来使用 // 变量bufferSize用来描述设备显示屏一屏内容所占用的内存的大小,同时高通的硬件要求4K对齐。 const uint32_t bufferMask = m->bufferMask; const uint32_t numBuffers = m->numBuffers; size_t bufferSize = m->finfo.line_length * m->info.yres;
//adreno needs FB size to be page aligned bufferSize = roundUpToPageSize(bufferSize);
// 如果系统帧缓冲区只有一个图形缓冲区大小,即变量numBuffers的值等于1,那么这个图形缓冲区就始终用作系统主图形缓冲区来使用。在这种情况下,我们就不能够在系统帧缓冲区中分配图形缓冲区来给用户空间的应用程序使用,因此,这时候就会转向内存中来分配图形缓冲区,即调用函数gralloc_alloc_buffer来分配图形缓冲区。注意,这时候分配的图形缓冲区的大小为一屏内容的大小,即bufferSize。
if (numBuffers == 1) { // If we have only one buffer, we never use page-flipping. Instead, // we return a regular buffer which will be memcpy'ed to the main // screen when post is called. int newUsage = (usage & ~GRALLOC_USAGE_HW_FB) | GRALLOC_USAGE_HW_2D; return gralloc_alloc_buffer(bufferSize, newUsage, pHandle, BUFFER_TYPE_UI, m->fbFormat, m->info.xres, m->info.yres); }
// 如果bufferMask的值大于等于((1LU<<numBuffers)-1)的值,那么就说明系统帧缓冲区中的图形缓冲区全部都分配出去了,这时候分配图形缓冲区就失败了。例如,假设图形缓冲区的个数为2,那么((1LU<<numBuffers)-1)的值就等于3,即二制制0x11。如果这时候bufferMask的值也等于0x11,那么就表示第一个和第二个图形缓冲区都已经分配出去了。因此,这时候就不能再在系统帧缓冲区中分配图形缓冲区 if (bufferMask >= ((1LU<<numBuffers)-1)) { // We ran out of buffers. return -ENOMEM; }
// 假设此时系统帧缓冲区中尚有空闲的图形缓冲区的,接下来函数就会创建一个private_handle_t结构体hnd来描述这个即将要分配出去的图形缓冲区。注意,这个图形缓冲区的标志值等于PRIV_FLAGS_FRAMEBUFFER,即表示这是一块在系统帧缓冲区中分配的图形缓冲区。 // create a "fake" handle for it intptr_t vaddr = intptr_t(m->framebuffer->base); private_handle_t* hnd = new private_handle_t( dup(m->framebuffer->fd), bufferSize, private_handle_t::PRIV_FLAGS_USES_PMEM | private_handle_t::PRIV_FLAGS_FRAMEBUFFER, BUFFER_TYPE_UI, m->fbFormat, m->info.xres, m->info.yres);
// 接下来的for循环从低位到高位检查变量bufferMask的值,并且找到第一个值等于0的位,这样就可以知道在系统帧缓冲区中,第几个图形缓冲区的是空闲的。注意,变量vadrr的值开始的时候指向系统帧缓冲区的基地址,在下面的for循环中,每循环一次它的值都会增加bufferSize。从这里就可以看出,每次从系统帧缓冲区中分配出去的图形缓冲区的大小都是刚好等于显示屏一屏内容大小的。 // find a free slot for (uint32_t i=0 ; i<numBuffers ; i++) { if ((bufferMask & (1LU<<i)) == 0) { m->bufferMask |= (1LU<<i); break; } vaddr += bufferSize; }
// 将分配的缓冲区的开始地址保存到变量base中,这样用户控件的应用程序可以直接将需要渲染的图形内容拷贝到这个地址上。这样,就相当于是直接将图形渲染到系统帧缓冲区中去。 // offset表示分配到的图形缓冲区的起始地址正对于系统帧缓冲区基地址的偏移量。 hnd->base = vaddr; hnd->offset = vaddr - intptr_t(m->framebuffer->base); *pHandle = hnd; return 0; }
|
上面分析了从framebuffer中分配图形缓冲区的过程。总结下这块buffer的来历。首先在fb设备open的时候,通过mmap从fb0中映射一块内存到用户空间,即一个内存池(module->framebuffer),通过bufferMask来表示该池中内存的使用情况。而alloc做的事情,就是从这个内存池中找到一个空闲的区块,然后返回该区块的hanlder指针pHandle。
我们现在来看看从内存中分配图形缓冲区的情况。从android 4.0ICS开始,Android启动新的内存管理方式ION,以取代PMEM。PMEM需要一个连续的物理内存,同时需要在系统启动的时候,就完成分配。
int gpu_context_t::gralloc_alloc_buffer(size_t size, int usage, buffer_handle_t* pHandle, int bufferType, int format, int width, int height) { int err = 0; int flags = 0; size = roundUpToPageSize(size);
// 首先分配一个data区域。 alloc_data data; data.offset = 0; data.fd = -1; data.base = 0; if(format == HAL_PIXEL_FORMAT_YCbCr_420_SP_TILED) data.align = 8192; else data.align = getpagesize();
/* force 1MB alignment selectively for secure buffers, MDP5 onwards */ #ifdef MDSS_TARGET if (usage & GRALLOC_USAGE_PROTECTED) { data.align = ALIGN(data.align, SZ_1M); size = ALIGN(size, data.align); } #endif
data.size = size; data.pHandle = (unsigned int) pHandle;
// 追查代码可以知道mallocCtrl指向IonController对象,关键代码可以参考hardware/qrom/display/msm8974/libgralloc/alloc_controller.cpp。具体怎么从ion中分配buffer,请参考文献13,14,15。 err = mAllocCtrl->allocate(data, usage);
if (!err) { /* allocate memory for enhancement data */ 在申请一块扩展data alloc_data eData; eData.fd = -1; eData.base = 0; eData.offset = 0; eData.size = ROUND_UP_PAGESIZE(sizeof(MetaData_t)); eData.pHandle = data.pHandle; eData.align = getpagesize(); int eDataUsage = GRALLOC_USAGE_PRIVATE_SYSTEM_HEAP; int eDataErr = mAllocCtrl->allocate(eData, eDataUsage); ALOGE_IF(eDataErr, "gralloc failed for eDataErr=%s", strerror(-eDataErr));
if (usage & GRALLOC_USAGE_PRIVATE_EXTERNAL_ONLY) { flags |= private_handle_t::PRIV_FLAGS_EXTERNAL_ONLY; //The EXTERNAL_BLOCK flag is always an add-on if (usage & GRALLOC_USAGE_PRIVATE_EXTERNAL_BLOCK) { flags |= private_handle_t::PRIV_FLAGS_EXTERNAL_BLOCK; } if (usage & GRALLOC_USAGE_PRIVATE_EXTERNAL_CC) { flags |= private_handle_t::PRIV_FLAGS_EXTERNAL_CC; } }
if (bufferType == BUFFER_TYPE_VIDEO) { if (usage & GRALLOC_USAGE_HW_CAMERA_WRITE) { #ifndef MDSS_TARGET flags |= private_handle_t::PRIV_FLAGS_ITU_R_601_FR; #else // Per the camera spec ITU 709 format should be set only for // video encoding. // It should be set to ITU 601 full range format for any other // camera buffer // if (usage & GRALLOC_USAGE_HW_CAMERA_MASK) { if (usage & GRALLOC_USAGE_HW_VIDEO_ENCODER) flags |= private_handle_t::PRIV_FLAGS_ITU_R_709; else flags |= private_handle_t::PRIV_FLAGS_ITU_R_601_FR; } #endif } else { flags |= private_handle_t::PRIV_FLAGS_ITU_R_601; } }
if (usage & GRALLOC_USAGE_HW_VIDEO_ENCODER ) { flags |= private_handle_t::PRIV_FLAGS_VIDEO_ENCODER; }
if (usage & GRALLOC_USAGE_HW_CAMERA_WRITE) { flags |= private_handle_t::PRIV_FLAGS_CAMERA_WRITE; }
if (usage & GRALLOC_USAGE_HW_CAMERA_READ) { flags |= private_handle_t::PRIV_FLAGS_CAMERA_READ; }
if (usage & GRALLOC_USAGE_HW_COMPOSER) { flags |= private_handle_t::PRIV_FLAGS_HW_COMPOSER; }
if (usage & GRALLOC_USAGE_HW_TEXTURE) { flags |= private_handle_t::PRIV_FLAGS_HW_TEXTURE; }
flags |= data.allocType; int eBaseAddr = int(eData.base) + eData.offset; // new一个新的framebuffer。 private_handle_t *hnd = new private_handle_t(data.fd, size, flags, bufferType, format, width, height, eData.fd, eData.offset, eBaseAddr);
hnd->offset = data.offset; hnd->base = int(data.base) + data.offset; hnd->gpuaddr = 0;
*pHandle = hnd; }
ALOGE_IF(err, "gralloc failed err=%s", strerror(-err));
return err; }
|
注意,在Android系统中,在系统帧缓冲区中分配的图形缓冲区是在SurfaceFlinger服务中使用的,而在内存中分配的图形缓冲区既可以在SurfaceFlinger服务中使用,也可以在其它的应用程序中使用。当其它的应用程序需要使用图形缓冲区的时候,它们就会请求SurfaceFlinger服务为它们分配,因此,对于其它的应用程序来说,它们只需要将SurfaceFlinger服务返回来的图形缓冲区映射到自己的进程地址空间来使用就可以了,这就是后面我们所要分析的图形缓冲区的注册过程。
至此,图形缓冲区的分配过程就分析完成了,接下来我们继续分析图形缓冲区的释放过程。
4.7 Gralloc释放buffer
从代码中的函数定向可以知道,释放buffer本质是调用gralloc_free函数,定义在hardware/qcom/display/msm8974/libgralloc/gpu.cpp中。
具体实现如下:
int gpu_context_t::gralloc_free(alloc_device_t* dev, buffer_handle_t handle) { // 要释放的图形缓冲区使用参数handle来描述。前面提到,从Gralloc模块中分配的图形缓冲区是使用private_handle_t结构体来描述的,因此,这里的参数handle应该指向一个private_handle_t结构体,这是通过调用private_handle_t类的静态成员函数validate来验证的。 if (private_handle_t::validate(handle) < 0) return -EINVAL;
private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(handle); gpu_context_t* gpu = reinterpret_cast<gpu_context_t*>(dev); return gpu->free_impl(hnd); }
|
该函数又调用了free_impl函数。在处理free buffer的时候,也是按照两种情况来分别处理的。如果之前这个buffer是从framebuffer分配的话,就只要把bufferMask中设置成0即可。而对应从内存中申请的,则是调用allocCtrl(ion)中的free_buffer来完成释放。
int gpu_context_t::free_impl(private_handle_t const* hnd) { private_module_t* m = reinterpret_cast<private_module_t*>(common.module); // 要释放的图形缓冲区有可能是在系统帧缓冲区分配的,也有可能是在内存中分配的,这可以通过检查它的标志值flags的PRIV_FLAGS_FRAMEBUFFER位是否等于1来确认。 if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) { const size_t bufferSize = m->finfo.line_length * m->info.yres;
// 需要将要释放的图形缓冲区的开始地址减去系统帧缓冲区的基地址,再除以一个图形缓冲区的大小,就可以知道要释放的图形缓冲区是系统帧缓冲区的第几个位置。 int index = (hnd->base - m->framebuffer->base) / bufferSize; m->bufferMask &= ~(1<<index); } else {
terminateBuffer(&m->base, const_cast<private_handle_t*>(hnd)); IMemAlloc* memalloc = mAllocCtrl->getAllocator(hnd->flags); int err = memalloc->free_buffer((void*)hnd->base, (size_t) hnd->size, hnd->offset, hnd->fd); if(err) return err; // free the metadata space unsigned long size = ROUND_UP_PAGESIZE(sizeof(MetaData_t)); err = memalloc->free_buffer((void*)hnd->base_metadata, (size_t) size, hnd->offset_metadata, hnd->fd_metadata); if (err) return err; } // 这个函数还会将用来描述要释放的图形缓冲区的private_handle_t结构体所占用的内存释放掉. delete hnd; return 0; }
|