android的gralloc分析
Android 中 lcd 是一个帧缓冲设备,驱动程序通过处理器的 lcd 控制器将物理内存的一段区域设置为显存,如果向这段内存区域写入数据就会马上在 lcd 上显示出来。Android 在 HAL 中提供了gralloc 模块,封装了用户层对帧缓冲设备的所有操作接口,并通过 SurfaceFlinger 服务向应用提供显示支持。在启动过程中系统会加载 gralloc 模块,然后打开帧缓冲设备,获取设备的各种参数并完成 gralloc 模块的初始化。当应用程序需要把内容显示到 lcd 上时,需要通过 gralloc 模块申请一块图形缓冲区,然后将这块图形缓冲区映射到自己的地址空间并写入内容即可。当应用程序不再需要这块图形缓冲区时需要通过 gralloc 模块释放掉,然后解除对缓冲区的映射。
1、基础数据结构
gralloc 模块通过struct private_module_t 来描述,该结构定义如下:
[cpp] view plain copy- structprivate_module_t{
- gralloc_module_tbase;
- private_handle_t*framebuffer;/*指向图形缓冲区的句柄*/
- uint32_tflags;/*用来标志系统帧缓冲区是否支持双缓冲*/
- uint32_tnumBuffers;/*表示系统帧缓冲的个数*/
- uint32_tbufferMask;/*记录系统帧缓冲的使用情况*/
- pthread_mutex_tlock;/*保护结构体private_module_t的并行访问*/
- buffer_handle_tcurrentBuffer;/*描述当前正在被渲染的图形缓冲区*/
- intpmem_master;/*pmem设备节点的描述符*/
- void*pmem_master_base;/*pmem的起始虚拟地址*/
- structfb_var_screeninfoinfo;/*lcd的可变参数*/
- structfb_fix_screeninfofinfo;/*lcd的固定参数*/
- floatxdpi;/*x方向上每英寸的像素数量*/
- floatydpi;/*y方向上每英寸的像素数量*/
- floatfps;/*lcd的刷新率*/
- intorientation;/*显示方向*/
- enum{
- PRIV_USAGE_LOCKED_FOR_POST=0x80000000/*flagtoindicatewe'llpostthisbuffer*/
- };
- };
[cpp] view plain copy
- #ifdef__cplusplus
- structprivate_handle_t:publicnative_handle{
- #else
- structprivate_handle_t{
- structnative_handlenativeHandle;/*用来描述一个本地句柄值*/
- #endif
- enum{
- PRIV_FLAGS_FRAMEBUFFER=0x00000001,
- PRIV_FLAGS_USES_PMEM=0x00000002,
- PRIV_FLAGS_USES_MMEM=0x00000004,
- PRIV_FLAGS_NEEDS_FLUSH=0x00000008,
- };
- enum{
- LOCK_STATE_WRITE=1<<31,
- LOCK_STATE_MAPPED=1<<30,
- LOCK_STATE_READ_MASK=0x3FFFFFFF
- };
- /*指向一个文件描述符,这个文件描述符要么指向帧缓冲区设备,要么指向一块匿名共享内存
- *取决于private_handle_t描述的图形缓冲区是在帧缓冲区分配的,还是在内存中分配的*/
- intfd;
- /*指向一个魔数,它的值由静态成员变量sMagic来指定,用来标识一个private_handle_t结构体*/
- intmagic;
- /*用来描述一个图形缓冲区的标志,它的值要么等于0,要么等于PRIV_FLAGS_FRAMEBUFFER
- *当一个图形缓冲区的标志值等于PRIV_FLAGS_FRAMEBUFFER的时候,就表示它是在帧缓冲区中分配的*/
- intflags;
- intsize;/*描述一个图形缓冲区的大小*/
- intoffset;/*描述一个图形缓冲区的偏移地址*/
- intphys;/*图形缓冲区或帧缓冲的起始物理地址*/
- intbase;/*图形缓冲区或帧缓冲的起始虚拟地址*/
- intlockState;
- intwriteOwner;
- intpid;/*描述一个图形缓冲区的创建者的PID*/
- #ifdef__cplusplus
- staticconstintsNumInts=9;/*有9个整数变量*/
- staticconstintsNumFds=1;/*有1个文件描述符*/
- staticconstintsMagic=0x3141592;
- private_handle_t(intfd,intsize,intflags):
- fd(fd),magic(sMagic),flags(flags),size(size),offset(0),
- phys(0),base(0),lockState(0),writeOwner(0),pid(getpid())
- {
- version=sizeof(native_handle);
- numInts=sNumInts;
- numFds=sNumFds;
- }
- ~private_handle_t(){
- magic=0;
- }
- boolusesPhysicallyContiguousMemory(){
- return(flags&PRIV_FLAGS_USES_PMEM)!=0;
- }
- /*用来验证一个native_handle_t指针是否指向了一个private_handle_t结构体*/
- staticintvalidate(constnative_handle*h){
- constprivate_handle_t*hnd=(constprivate_handle_t*)h;
- if(!h||h->version!=sizeof(native_handle)||
- h->numInts!=sNumInts||h->numFds!=sNumFds||
- hnd->magic!=sMagic)
- {
- LOGE("invalidgrallochandle(at%p)",h);
- return-EINVAL;
- }
- return0;
- }
- staticprivate_handle_t*dynamicCast(constnative_handle*in){
- if(validate(in)==0){
- return(private_handle_t*)in;
- }
- returnNULL;
- }
- #endif
- };
图形缓冲区的操作接口由结构 struct gralloc_module_t定义:
[cpp] view plain copy- typedefstructgralloc_module_t{
- structhw_module_tcommon;
- /*注册一个图形缓冲区,这个指定的图形缓冲区使用一个buffer_handle_t句柄来描述*/
- int(*registerBuffer)(structgralloc_module_tconst*module,
- buffer_handle_thandle);
- /*注销一个图形缓冲区*/
- int(*unregisterBuffer)(structgralloc_module_tconst*module,
- buffer_handle_thandle);
- /*用来锁定一个图形缓冲区并将缓冲区映射到用户进程
- *在锁定一块图形缓冲区的时候,可以指定要锁定的图形绘冲区的位置以及大小
- *这是通过参数l、t、w和h来指定的,其中,参数l和t指定的是要访问的图形缓冲区的左上角位置
- *而参数w和h指定的是要访问的图形缓冲区的宽度和长度
- *锁定之后,就可以获得由参数参数l、t、w和h所圈定的一块缓冲区的起始地址,保存在输出参数vaddr中
- *另一方面,在访问完成一块图形缓冲区之后,需要解除这块图形缓冲区的锁定*/
- int(*lock)(structgralloc_module_tconst*module,
- buffer_handle_thandle,intusage,
- intl,intt,intw,inth,
- void**vaddr);
- int(*unlock)(structgralloc_module_tconst*module,
- buffer_handle_thandle);
- int(*perform)(structgralloc_module_tconst*module,
- intoperation,...);
- /*reservedforfutureuse*/
- void*reserved_proc[7];
- }gralloc_module_t;
gralloc 设备则用结构struct alloc_device_t 来描述,其定义如下:
[cpp] view plain copy- typedefstructalloc_device_t{
- structhw_device_tcommon;
- /*申请图形缓冲区的内存空间*/
- int(*alloc)(structalloc_device_t*dev,intw,inth,intformat,intusage,buffer_handle_t*handle,int*stride);
- /*释放图形缓冲区的内存空间*/
- int(*free)(structalloc_device_t*dev,buffer_handle_thandle);
- }alloc_device_t;
- typedefstructframebuffer_device_t{
- structhw_device_tcommon;
- constuint32_tflags;/*用来记录系统帧缓冲区的标志*/
- constuint32_twidth;/*lcd显示区域的像素点数*/
- constuint32_theight;
- constintstride;/*描述设备显示屏的一行有多少个像素点*/
- /*描述系统帧缓冲区的像素格式,主要有HAL_PIXEL_FORMAT_RGBX_8888和HAL_PIXEL_FORMAT_RGB_565两种*/
- constintformat;
- constfloatxdpi;
- constfloatydpi;
- constfloatfps;/*lcd刷新率*/
- constintminSwapInterval;/*交换两帧图像的最小间隔时间*/
- constintmaxSwapInterval;/*交换两帧图像的最大间隔时间*/
- intreserved[8];
- /*设置帧交换间隔*/
- int(*setSwapInterval)(structframebuffer_device_t*window,intinterval);
- /*设置帧缓冲区的更新区域*/
- int(*setUpdateRect)(structframebuffer_device_t*window,intleft,inttop,intwidth,intheight);
- /*用来将图形缓冲区buffer的内容渲染到帧缓冲区中去,即显示在设备的显示屏中去*/
- int(*post)(structframebuffer_device_t*dev,buffer_handle_tbuffer);
- /*用来通知fb设备device,图形缓冲区的组合工作已经完成*/
- int(*compositionComplete)(structframebuffer_device_t*dev);
- void*reserved_proc[8];
- }framebuffer_device_t;
2、gralloc 模块
HAL 中通过hw_get_module 接口加载指定 id 的模块,并获得一个hw_module_t 结构来打开设备,流程如下:
[cpp] view plain copy- #defineHAL_LIBRARY_PATH1"/system/lib/hw"
- #defineHAL_LIBRARY_PATH2"/vendor/lib/hw"
- staticconstchar*variant_keys[]={
- "ro.hardware",/*Thisgoesfirstsothatitcanpickupadifferentfileontheemulator.*/
- "ro.product.board",
- "ro.board.platform",
- "ro.arch"
- };
- staticconstintHAL_VARIANT_KEYS_COUNT=
- (sizeof(variant_keys)/sizeof(variant_keys[0]));
- inthw_get_module(constchar*id,conststructhw_module_t**module)
- {
- intstatus;
- inti;
- conststructhw_module_t*hmi=NULL;
- charprop[PATH_MAX];
- charpath[PATH_MAX];
- /*
- *Herewerelyonthefactthatcallingdlopenmultipletimeson
- *thesame.sowillsimplyincrementarefcount(andnotload
- *anewcopyofthelibrary).
- *Wealsoassumethatdlopen()isthread-safe.
- */
- /*Loopthroughtheconfigurationvariantslookingforamodule*/
- for(i=0;i<HAL_VARIANT_KEYS_COUNT+1;i++){
- if(i<HAL_VARIANT_KEYS_COUNT){
- if(property_get(variant_keys[i],prop,NULL)==0){/*读取variant_keys数组指定的属性值*/
- continue;
- }
- snprintf(path,sizeof(path),"%s/%s.%s.so",/*格式化模块名和路径,如:/system/lib/hw/gralloc.xxx.so*/
- HAL_LIBRARY_PATH1,id,prop);
- if(access(path,R_OK)==0)break;
- snprintf(path,sizeof(path),"%s/%s.%s.so",
- HAL_LIBRARY_PATH2,id,prop);
- if(access(path,R_OK)==0)break;
- }else{
- snprintf(path,sizeof(path),"%s/%s.default.so",
- HAL_LIBRARY_PATH1,id);
- if(access(path,R_OK)==0)break;
- }
- }
- status=-ENOENT;
- if(i<HAL_VARIANT_KEYS_COUNT+1){
- /*loadthemodule,ifthisfails,we'redoomed,andweshouldnottrytoloadadifferentvariant.*/
- status=load(id,path,module);/*加载模块*/
- }
- returnstatus;
- }
函数会在/system/lib/hw 或者/vendor/lib/hw 目录中去寻找gralloc.xxx.so 文件,如果找到了就调用load接口完成加载。最终会调用gralloc_device_open完成 gralloc 设备成员的初始化:
[cpp] view plain copy- intgralloc_device_open(consthw_module_t*module,constchar*name,
- hw_device_t**device)
- {
- intstatus=-EINVAL;
- if(!strcmp(name,GRALLOC_HARDWARE_GPU0)){
- gralloc_context_t*dev;
- dev=(gralloc_context_t*)malloc(sizeof(*dev));
- /*initializeourstatehere*/
- memset(dev,0,sizeof(*dev));
- /*initializetheprocs*/
- 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{
- status=fb_device_open(module,name,device);
- }
- returnstatus;
- }
- staticintgralloc_alloc_framebuffer(alloc_device_t*dev,size_tsize,intusage,buffer_handle_t*pHandle)
- {
- private_module_t*m=reinterpret_cast<private_module_t*>(
- dev->common.module);
- pthread_mutex_lock(&m->lock);
- interr=gralloc_alloc_framebuffer_locked(dev,size,usage,pHandle);
- pthread_mutex_unlock(&m->lock);
- returnerr;
- }
- staticintgralloc_alloc_buffer(alloc_device_t*dev,size_tsize,intusage,buffer_handle_t*pHandle)
- {
- interr=0;
- intfd=-1;
- size=roundUpToPageSize(size);
- fd=ashmem_create_region("gralloc-buffer",size);
- if(fd<0){
- LOGE("couldn'tcreateashmem(%s)",strerror(-errno));
- err=-errno;
- }
- if(err==0){
- private_handle_t*hnd=newprivate_handle_t(fd,size,0);
- gralloc_module_t*module=reinterpret_cast<gralloc_module_t*>(
- dev->common.module);
- err=mapBuffer(module,hnd);
- if(err==0){
- *pHandle=hnd;
- }
- }
- LOGE_IF(err,"grallocfailederr=%s",strerror(-err));
- returnerr;
- }
- /*****************************************************************************/
- staticintgralloc_alloc(alloc_device_t*dev,intw,inth,intformat,intusage,
- buffer_handle_t*pHandle,int*pStride)
- {
- if(!pHandle||!pStride)
- return-EINVAL;
- size_tsize,stride;
- intalign=4;
- intbpp=0;
- switch(format){/*一个像素点占用的字节数*/
- caseHAL_PIXEL_FORMAT_RGBA_8888:
- caseHAL_PIXEL_FORMAT_RGBX_8888:
- caseHAL_PIXEL_FORMAT_BGRA_8888:
- bpp=4;
- break;
- caseHAL_PIXEL_FORMAT_RGB_888:
- bpp=3;
- break;
- caseHAL_PIXEL_FORMAT_RGB_565:
- caseHAL_PIXEL_FORMAT_RGBA_5551:
- caseHAL_PIXEL_FORMAT_RGBA_4444:
- bpp=2;
- break;
- default:
- return-EINVAL;
- }
- size_tbpr=(w*bpp+(align-1))&~(align-1);
- size=bpr*h;
- stride=bpr/bpp;
- interr;
- if(usage&GRALLOC_USAGE_HW_FB){
- err=gralloc_alloc_framebuffer(dev,size,usage,pHandle);/*在系统帧缓冲中分配图形缓冲区*/
- }else{
- err=gralloc_alloc_buffer(dev,size,usage,pHandle);/*在内存中分配图形缓冲区*/
- }
- if(err<0){
- returnerr;
- }
- *pStride=stride;
- return0;
- }
在gralloc_device_open 中会根据传递的参数分别初始化两个设备,定义如下:
[cpp] view plain copy- #defineGRALLOC_HARDWARE_FB0"fb0"
- #defineGRALLOC_HARDWARE_GPU0"gpu0"
- intmapFrameBufferLocked(structprivate_module_t*module)
- {
- if(module->framebuffer){
- return0;
- }
- charconst*constdevice_template[]={
- "/dev/graphics/fb%u",
- "/dev/fb%u",
- 0};
- intfd=-1;
- inti=0;
- charname[64];
- while((fd==-1)&&device_template[i]){
- snprintf(name,64,device_template[i],0);
- fd=open(name,O_RDWR,0);
- i++;
- }
- if(fd<0)
- return-errno;
- structfb_fix_screeninfofinfo;
- if(ioctl(fd,FBIOGET_FSCREENINFO,&finfo)==-1)/*获取帧缓冲的固定参数*/
- return-errno;
- structfb_var_screeninfoinfo;
- if(ioctl(fd,FBIOGET_VSCREENINFO,&info)==-1)/*获取帧缓冲的可变参数*/
- return-errno;
- info.reserved[0]=0;
- info.reserved[1]=0;
- info.reserved[2]=0;
- info.xoffset=0;
- info.yoffset=0;
- info.activate=FB_ACTIVATE_NOW;
- info.bits_per_pixel=32;
- info.red.offset=16;
- info.red.length=8;
- info.green.offset=8;
- info.green.length=8;
- info.blue.offset=0;
- info.blue.length=8;
- info.transp.offset=24;
- info.transp.length=8;
- /*
- *RequestNUM_BUFFERSscreens(atlest2forpageflipping)
- */
- info.yres_virtual=info.yres*NUM_BUFFERS;/*帧缓冲总长度*/
- uint32_tflags=PAGE_FLIP;/*支持缓冲交换*/
- if(ioctl(fd,FBIOPAN_DISPLAY,&info)==-1){
- info.yres_virtual=info.yres;
- flags&=~PAGE_FLIP;
- LOGW("FBIOPAN_DISPLAYfailed,pageflippingnotsupported");
- }
- if(info.yres_virtual<info.yres*2){
- /*weneedatleast2forpage-flipping*/
- info.yres_virtual=info.yres;
- flags&=~PAGE_FLIP;
- LOGW("pageflippingnotsupported(yres_virtual=%d,requested=%d)",
- info.yres_virtual,info.yres*2);
- }
- if(ioctl(fd,FBIOGET_VSCREENINFO,&info)==-1)
- return-errno;
- intrefreshRate=1000000000000000LLU/
- (
- uint64_t(info.upper_margin+info.lower_margin+info.yres)
- *(info.left_margin+info.right_margin+info.xres)
- *info.pixclock
- );/*计算lcd刷新率*/
- if(refreshRate==0){
- /*bleagh,badinfofromthedriver*/
- refreshRate=60*1000;//60Hz
- }
- if(int(info.width)<=0||int(info.height)<=0){
- /*thedriverdoesn'treturnthatinformation,defaultto160dpi*/
- info.width=((info.xres*25.4f)/160.0f+0.5f);
- info.height=((info.yres*25.4f)/160.0f+0.5f);
- }
- floatxdpi=(info.xres*25.4f)/info.width;
- floatydpi=(info.yres*25.4f)/info.height;
- floatfps=refreshRate/1000.0f;
- LOGI("using(fd=%d)\n"
- "id=%s\n"
- "xres=%dpx\n"
- "yres=%dpx\n"
- "xres_virtual=%dpx\n"
- "yres_virtual=%dpx\n"
- "bpp=%d\n"
- "r=%2u:%u\n"
- "g=%2u:%u\n"
- "b=%2u:%u\n",
- fd,
- finfo.id,
- info.xres,
- info.yres,
- info.xres_virtual,
- info.yres_virtual,
- info.bits_per_pixel,
- info.red.offset,info.red.length,
- info.green.offset,info.green.length,
- info.blue.offset,info.blue.length
- );
- LOGI("width=%dmm(%fdpi)\n"
- "height=%dmm(%fdpi)\n"
- "refreshrate=%.2fHz\n",
- info.width,xdpi,
- info.height,ydpi,
- fps
- );
- if(ioctl(fd,FBIOGET_FSCREENINFO,&finfo)==-1)
- return-errno;
- if(finfo.smem_len<=0)
- return-errno;
- module->flags=flags;
- module->info=info;
- module->finfo=finfo;
- module->xdpi=xdpi;
- module->ydpi=ydpi;
- module->fps=fps;
- /*
- *maptheframebuffer
- */
- interr;
- size_tfbSize=roundUpToPageSize(finfo.line_length*info.yres_virtual);/*帧缓冲大小*/
- module->framebuffer=newprivate_handle_t(dup(fd),fbSize,
- private_handle_t::PRIV_FLAGS_USES_PMEM);
- module->numBuffers=info.yres_virtual/info.yres;/*计算系统帧缓冲的个数*/
- module->bufferMask=0;
- void*vaddr=mmap(0,fbSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);/*将fb映射到用户空间*/
- if(vaddr==MAP_FAILED){
- LOGE("Errormappingtheframebuffer(%s)",strerror(errno));
- return-errno;
- }
- module->framebuffer->base=intptr_t(vaddr);/*帧缓冲的起始虚拟地址*/
- memset(vaddr,0,fbSize);
- return0;
- }
fb 模块最重要的工作就是将应用程序指定的内容写入显存中,是通过函数fb_post 完成的,流程如下:
[cpp] view plain copy- /*将图形缓冲区buffer的内容渲染到帧缓冲区中去*/
- staticintfb_post(structframebuffer_device_t*dev,buffer_handle_tbuffer)
- {
- unsignedintphys;
- void*virt;
- intpitch;
- intformat;
- /*首先验证参数handle指向的一块图形缓冲区的确是由Gralloc模块分配的*/
- if(private_handle_t::validate(buffer)<0)
- return-EINVAL;
- fb_context_t*ctx=(fb_context_t*)dev;
- private_handle_tconst*hnd=reinterpret_cast<private_handle_tconst*>(buffer);/*图形缓冲区*/
- private_module_t*m=reinterpret_cast<private_module_t*>(dev->common.module);/*帧缓冲区*/
- if(m->currentBuffer){/*当前正在渲染的图形缓冲区*/
- m->base.unlock(&m->base,m->currentBuffer);
- m->currentBuffer=0;
- }
- if(hnd->flags&private_handle_t::PRIV_FLAGS_FRAMEBUFFER){/*如果图形缓冲区是在系统帧缓冲中分配的*/
- m->base.lock(&m->base,buffer,/*锁定图像缓冲区*/
- private_module_t::PRIV_USAGE_LOCKED_FOR_POST,
- 0,0,m->info.xres,m->info.yres,NULL);
- constsize_toffset=hnd->base-m->framebuffer->base;/*计算图形缓冲区与帧缓冲的偏移*/
- /*将作为参数的fb_var_screeninfo结构体的成员变量activate的值设置FB_ACTIVATE_VBL
- *表示要等到下一个垂直同步事件出现时,再将当前要渲染的图形缓冲区的内容绘制出来
- *这样做的目的是避免出现屏幕闪烁,即避免前后两个图形缓冲区的内容各有一部分同时出现屏幕中*/
- m->info.activate=FB_ACTIVATE_VBL;
- m->info.yoffset=offset/m->finfo.line_length;/*得到偏移的起始行*/
- if(ioctl(m->framebuffer->fd,FBIOPAN_DISPLAY,&m->info)==-1){/*刷新显示内容*/
- LOGE("FBIOPAN_DISPLAYfailed");
- m->base.unlock(&m->base,buffer);
- return-errno;
- }
- if(UNLIKELY(mDebugFps)){
- debugShowFPS();
- }
- #ifdefSNAPSHOT
- dumpfile((void*)hnd->base,m->info.xres,m->info.yres);/*dump帧缓冲*/
- #endif
- m->currentBuffer=buffer;/*设置当前图形缓冲区*/
- }else{/*如果图形缓冲区是在内存中分配的*/
- /*Ifwecan'tdothepage_flip,justcopythebuffertothefront
- *FIXME:usecopybitHALinsteadofmemcpy*/
- LOGD("copybit.\n");
- void*fb_vaddr;
- void*buffer_vaddr;
- /*将帧缓冲整个锁定*/
- m->base.lock(&m->base,m->framebuffer,
- GRALLOC_USAGE_SW_WRITE_RARELY,
- 0,0,m->info.xres,m->info.yres,
- &fb_vaddr);
- /*将图形缓冲整个锁定*/
- m->base.lock(&m->base,buffer,
- GRALLOC_USAGE_SW_READ_RARELY,
- 0,0,m->info.xres,m->info.yres,
- &buffer_vaddr);
- /*将图形缓冲的内容拷贝到帧缓冲中即显示到lcd*/
- memcpy(fb_vaddr,buffer_vaddr,m->finfo.line_length*m->info.yres);
- m->base.unlock(&m->base,buffer);/*解锁*/
- m->base.unlock(&m->base,m->framebuffer);
- }
- return0;
- }
更多相关文章
- Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原
- Android(安卓)直接显示yuv数据 通过Awesomeplayer方式直接显示(三
- android-opengles3.0开发-2-绘制图形
- 我是一只忍者神龟.
- Android图形图画学习(11)——颜色相关
- 视频学习笔记:Android(安卓)ffmpeg解码多路h264视频并显示
- android立体图形——三棱锥
- android 解决java.nio.BufferOverflowException 异常
- Android——图形系统