首页 文章 精选 留言 我的

精选列表

搜索[硬件兼容性],共10000篇文章
优秀的个人博客,低调大师

Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(9)

5. 图形缓冲区的释放过程 前面提到,用户空间的应用程序用到的图形缓冲区是由Gralloc模块中的函数gralloc_free来释放的,这个函数实现在文件hardware/libhardware/modules/gralloc/gralloc.cpp中,如下所示: staticintgralloc_free(alloc_device_t*dev, buffer_handle_thandle) { if(private_handle_t::validate(handle)<0) return-EINVAL; private_handle_tconst*hnd=reinterpret_cast<private_handle_tconst*>(handle); if(hnd->flags&private_handle_t::PRIV_FLAGS_FRAMEBUFFER){ //freethisbuffer private_module_t*m=reinterpret_cast<private_module_t*>( dev->common.module); constsize_tbufferSize=m->finfo.line_length*m->info.yres; intindex=(hnd->base-m->framebuffer->base)/bufferSize; m->bufferMask&=~(1<<index); }else{ gralloc_module_t*module=reinterpret_cast<gralloc_module_t*>( dev->common.module); terminateBuffer(module,const_cast<private_handle_t*>(hnd)); } close(hnd->fd); deletehnd; return0; } 要释放的图形缓冲区使用参数handle来描述。前面提到,从Gralloc模块中分配的图形缓冲区是使用private_handle_t结构体来描述的,因此,这里的参数handle应该指向一个private_handle_t结构体,这是通过调用private_handle_t类的静态成员函数validate来验证的。private_handle_t类的静态成员函数validate的实现可以参考前面第1部分的内容。 要释放的图形缓冲区有可能是在系统帧缓冲区分配的,也有可能是在内存中分配的,这可以通过检查它的标志值flags的PRIV_FLAGS_FRAMEBUFFER位是否等于1来确认。 如果要释放的图形缓冲区是在系统帧缓冲区中分配的,那么首先要知道这个图形缓冲区是系统帧缓冲区的第index个位置,接着再将变量m所描述的一个private_module_t结构体的成员变量bufferMask的第index位重置为0即可。我们只需要将要释放的图形缓冲区的开始地址减去系统帧缓冲区的基地址,再除以一个图形缓冲区的大小,就可以知道要释放的图形缓冲区是系统帧缓冲区的第几个位置。这个过程刚好是在系统帧缓冲区中分配图形缓冲区的逆操作。 如果要释放的图形缓冲区是内存中分配的,那么只需要调用另外一个函数terminateBuffer来解除要释放的图形缓冲区在当前进程的地址空间中的映射。 最后,这个函数还会将用来描述要释放的图形缓冲区的private_handle_t结构体所占用的内存释放掉,并且将要要释放的图形缓冲区所在的系统帧缓冲区或者匿名共享内存的文件描述符关闭掉。 函数terminateBuffer实现在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示: intterminateBuffer(gralloc_module_tconst*module, private_handle_t*hnd) { if(hnd->base){ //thisbufferwasmapped,unmapitnow gralloc_unmap(module,hnd); } return0; } 它通过调用另外一个函数gralloc_unmap来解除参数hnd所描述的一个图形缓冲区在当前进程的地址空间中的映射。后面在分析图形缓冲区的注销过程时,我们再详细分析函数gralloc_unmap的实现。 至此,图形缓冲区的释放过程就分析完成了,接下来我们继续分析图形缓冲区的注册过程。 6. 图形缓冲区的注册过程 前面提到,在Android系统中,所有的图形缓冲区都是由SurfaceFlinger服务分配的,而当一个图形缓冲区被分配的时候,它会同时被映射到请求分配的进程的地址空间去,即分配的过程同时也包含了注册的过程。但是对用户空间的其它的应用程序来说,它们所需要的图形缓冲区是在由SurfaceFlinger服务分配的,因此,当它们得到SurfaceFlinger服务分配的图形缓冲区之后,还需要将这块图形缓冲区映射到自己的地址空间来,以便可以使用这块图形缓冲区。这个映射的过程即为我们接下来要分析的图形缓冲区注册过程。 前面还提到,注册图形缓冲区的操作是由Gralloc模块中的函数gralloc_register_buffer来实现的,这个函数实现在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示: intgralloc_register_buffer(gralloc_module_tconst*module, buffer_handle_thandle) { if(private_handle_t::validate(handle)<0) return-EINVAL; //ifthishandlewascreatedinthisprocess,thenwekeepitasis. interr=0; private_handle_t*hnd=(private_handle_t*)handle; if(hnd->pid!=getpid()){ void*vaddr; err=gralloc_map(module,handle,&vaddr); } returnerr; } 这个函数首先验证参数handle指向的一块图形缓冲区的确是由Gralloc模块分配的,方法是调用private_handle_t类的静态成员函数validate来验证,即如果参数handle指向的是一个private_handle_t结构体,那么它所指向的一块图形缓冲区就是由Gralloc模块分配的。 通过了上面的检查之后,函数gralloc_register_buffer还需要检查当前进程是否就是请求Gralloc模块分配图形缓冲区hnd的进程。如果是的话,那么当前进程在请求Gralloc模块分配图形缓冲区hnd的时候,就已经将图形缓冲区hnd映射进自己的地址空间来了,因此,这时候就不需要重复在当前进程中注册这个图形缓冲区。 真正执行注册图形缓冲区的操作是由函数gralloc_map来实现的,这个函数也是实现文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示: staticintgralloc_map(gralloc_module_tconst*module, buffer_handle_thandle, void**vaddr) { private_handle_t*hnd=(private_handle_t*)handle; if(!(hnd->flags&private_handle_t::PRIV_FLAGS_FRAMEBUFFER)){ size_tsize=hnd->size; void*mappedAddress=mmap(0,size, PROT_READ|PROT_WRITE,MAP_SHARED,hnd->fd,0); if(mappedAddress==MAP_FAILED){ LOGE("Couldnotmmap%s",strerror(errno)); return-errno; } hnd->base=intptr_t(mappedAddress)+hnd->offset; //LOGD("gralloc_map()succeededfd=%d,off=%d,size=%d,vaddr=%p", //hnd->fd,hnd->offset,hnd->size,mappedAddress); } *vaddr=(void*)hnd->base; return0; } 由于在系统帧缓冲区中分配的图形缓冲区只在SurfaceFlinger服务中使用,而SurfaceFlinger服务在初始化系统帧缓冲区的时候,已经将系统帧缓冲区映射到自己所在的进程中来了,因此,函数gralloc_map如果发现要注册的图形缓冲区是在系统帧缓冲区分配的时候,那么就不需要再执行映射图形缓冲区的操作了。 如果要注册的图形缓冲区是在内存中分配的,即它的标志值flags的PRIV_FLAGS_FRAMEBUFFER位等于1,那么接下来就需要将它映射到当前进程的地址空间来了。由于要注册的图形缓冲区是在文件描述符hnd->fd所描述的一块匿名共享内存中分配的,因此,我们只需要将文件描述符hnd->fd所描述的一块匿名共享内存映射到当前进程的地址空间来,就可以将参数hnd所描述的一个图形缓冲区映射到当前进程的地址空间来。 由于映射文件描述符hnd->fd得到的是一整块匿名共享内存在当前进程地址空间的基地址,而要注册的图形缓冲区可能只占据这块匿名共享内存的某一小部分,因此,我们还需要将要注册的图形缓冲区的在被映射的匿名共享内存中的偏移量hnd->offset加上被映射的匿名共享内存的基地址hnd->base,才可以得到要注册的图形缓冲区在当前进程中的访问地址,这个地址最终又被写入到hnd->base中去。 注册图形缓冲区的过程就是这么简单,接下来我们再分析图形缓冲区的注销过程。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967092,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(4)

成员变量fd指向一个文件描述符,这个文件描述符要么指向帧缓冲区设备,要么指向一块匿名共享内存,取决于它的宿主结构体private_handle_t描述的一个图形缓冲区是在帧缓冲区分配的,还是在内存中分配的。 成员变量magic指向一个魔数,它的值由静态成员变量sMagic来指定,用来标识一个private_handle_t结构体。 成员变量flags用来描述一个图形缓冲区的标志,它的值要么等于0,要么等于PRIV_FLAGS_FRAMEBUFFER。当一个图形缓冲区的标志值等于PRIV_FLAGS_FRAMEBUFFER的时候,就表示它是在帧缓冲区中分配的。 成员变量size用来描述一个图形缓冲区的大小。 成员变量offset用来描述一个图形缓冲区的偏移地址。例如,当一个图形缓冲区是在一块内存中分块的时候,假设这块内存的地址为start,那么这个图形缓冲区的起始地址就为start + offset。 成员变量base用来描述一个图形缓冲区的实际地址,它是通过成员变量offset来计算得到的。例如,上面计算得到的start + offset的值就保存在成员变量base中。 成员变量pid用来描述一个图形缓冲区的创建者的PID。例如,如果一个图形缓冲区是在ID值为1000的进程中创建的,那么用来描述这个图形缓冲区的private_handle_t结构体的成员变量pid的值就等于1000。 结构体private_handle_t的静态成员变量sMagic前面已经描述过了,另外两个静态成员变量sNumInts和sNumFds的值分别等于1和6,表示结构体private_handle_t包含有1个文件描述符和6个整数,它们是用来初始化结构体private_handle_t的父类native_handle_t的成员变量numInts和numFds的,如结构体private_handle_t的构造函数所示。从这里就可以看出,结构体private_handle_t的父类native_handle_t的成员变量data所指向的缓冲区就是由结构体private_handle_t的成员变量fds、magic、flags、size、offset、base和pid所占用的连续内存块来组成的,一共包含有7个整数。 结构体private_handle_t还定义了一个静态成员函数validate,用来验证一个native_handle_t指针是否指向了一个private_handle_t结构体。 至此,Gralloc模块的加载过程以及相关的数据结构体就介绍到这里,接下来我们分别分析定义在Gralloc模块中的gralloc和fb设备的打开过程。 2. gralloc设备的打开过程 在Gralloc模块中,gralloc设备的ID值定义为GRALLOC_HARDWARE_GPU0。GRALLOC_HARDWARE_GPU0是一个宏,定义在文件hardware/libhardware/include/hardware/gralloc.h中, 如下所示: #defineGRALLOC_HARDWARE_GPU0"gpu0" gralloc设备使用结构体alloc_device_t 来描述。结构体alloc_device_t有两个成员函数alloc和free,分别用来分配和释放图形缓冲区。 结构体alloc_device_t 也是定义在文件hardware/libhardware/include/hardware/gralloc.h中, 如下所示: 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; Gralloc模块在在文件hardware/libhardware/include/hardware/gralloc.h中定义了一个帮助函数gralloc_open,用来打开gralloc设备,如下所示: staticinlineintgralloc_open(conststructhw_module_t*module, structalloc_device_t**device){ returnmodule->methods->open(module, GRALLOC_HARDWARE_GPU0,(structhw_device_t**)device); } 参数module指向的是一个用来描述Gralloc模块的hw_module_t结构体,它的成员变量methods所指向的一个hw_module_methods_t结构体的成员函数open指向了Gralloc模块中的函数gralloc_device_open。 函数gralloc_device_open定义在文件hardware/libhardware/modules/gralloc/gralloc.cpp文件中,如下所示: structgralloc_context_t{ alloc_device_tdevice; /*ourprivatedatahere*/ }; ...... 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; } ...... returnstatus; } 这个函数主要是用来创建一个gralloc_context_t结构体,并且对它的成员变量device进行初始化。结构体gralloc_context_t的成员变量device的类型为gralloc_device_t,它用来描述一个gralloc设备。前面提到,gralloc设备是用来分配和释放图形缓冲区的,这是通过调用它的成员函数alloc和free来实现的。从这里可以看出,函数gralloc_device_open所打开的gralloc设备的成员函数alloc和free分别被设置为Gralloc模块中的函数gralloc_alloc和gralloc_free,后面我们再详细分析它们的实现。 至此,gralloc设备的打开过程就分析完成了,接下来我们继续分析fb设备的打开过程。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967071,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(8)

4. 分配图形缓冲区 前面提到,用户空间的应用程序用到的图形缓冲区是由Gralloc模块中的函数gralloc_alloc来分配的,这个函数实现在文件hardware/libhardware/modules/gralloc/gralloc.cpp中,如下所示: 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; } 参数format用来描述要分配的图形缓冲区的颜色格式。当format值等于HAL_PIXEL_FORMAT_RGBA_8888、HAL_PIXEL_FORMAT_RGBX_8888或者HAL_PIXEL_FORMAT_BGRA_8888的时候,一个像素需要使用32位来表示,即4个字节。当format值等于HAL_PIXEL_FORMAT_RGB_888的时候,一个像素需要使用24位来描述,即3个字节。当format值等于HAL_PIXEL_FORMAT_RGB_565、HAL_PIXEL_FORMAT_RGBA_5551或者HAL_PIXEL_FORMAT_RGBA_4444的时候,一个像需要使用16位来描述,即2个字节。最终一个像素需要使用的字节数保存在变量bpp中。 参数w表示要分配的图形缓冲区所保存的图像的宽度,将它乘以bpp,就可以得到保存一行像素所需要使用的字节数。我们需要将这个字节数对齐到4个字节边界,最后得到一行像素所需要的字节数就保存在变量bpr中。 参数h表示要分配的图形缓冲区所保存的图像的高度,将它乘以bpr,就可以得到保存整个图像所需要使用的字节数。 将变量bpr的值除以变量bpp的值,就得到要分配的图形缓冲区一行包含有多少个像素点,这个结果需要保存在输出参数pStride中,以便可以返回给调用者。 参数usage用来描述要分配的图形缓冲区的用途。如果是用来在系统帧缓冲区中渲染的,即参数usage的GRALLOC_USAGE_HW_FB位等于1,那么就必须要系统帧缓冲区中分配,否则的话,就在内存中分配。注意,在内存中分配的图形缓冲区,最终是需要拷贝到系统帧缓冲区去的,以便可以将它所描述的图形渲染出来。 函数gralloc_alloc_framebuffer用来在系统帧缓冲区中分配图形缓冲区,而函数gralloc_alloc_buffer用来在内存在分配图形缓冲区,接下来我们就分别分析这两个函数的实现。 函数gralloc_alloc_framebuffer实现在文件hardware/libhardware/modules/gralloc/gralloc.cpp中,如下所示: 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; } 这个函数调用了另外一个函数gralloc_alloc_framebuffer_locked来分配图形缓冲区。 函数gralloc_alloc_framebuffer_locked也是实现在文件hardware/libhardware/modules/gralloc/gralloc.cpp中,如下所示: staticintgralloc_alloc_framebuffer_locked(alloc_device_t*dev, size_tsize,intusage,buffer_handle_t*pHandle) { private_module_t*m=reinterpret_cast<private_module_t*>( dev->common.module); //allocatetheframebuffer if(m->framebuffer==NULL){ //initializetheframebuffer,theframebufferismappedonce //andforever. interr=mapFrameBufferLocked(m); if(err<0){ returnerr; } } constuint32_tbufferMask=m->bufferMask; constuint32_tnumBuffers=m->numBuffers; constsize_tbufferSize=m->finfo.line_length*m->info.yres; if(numBuffers==1){ //Ifwehaveonlyonebuffer,weneverusepage-flipping.Instead, //wereturnaregularbufferwhichwillbememcpy'edtothemain //screenwhenpostiscalled. intnewUsage=(usage&~GRALLOC_USAGE_HW_FB)|GRALLOC_USAGE_HW_2D; returngralloc_alloc_buffer(dev,bufferSize,newUsage,pHandle); } if(bufferMask>=((1LU<<numBuffers)-1)){ //Weranoutofbuffers. return-ENOMEM; } //createa"fake"handlesforit intptr_tvaddr=intptr_t(m->framebuffer->base); private_handle_t*hnd=newprivate_handle_t(dup(m->framebuffer->fd),size, private_handle_t::PRIV_FLAGS_FRAMEBUFFER); //findafreeslot for(uint32_ti=0;i<numBuffers;i++){ if((bufferMask&(1LU<<i))==0){ m->bufferMask|=(1LU<<i); break; } vaddr+=bufferSize; } hnd->base=vaddr; hnd->offset=vaddr-intptr_t(m->framebuffer->base); *pHandle=hnd; return0; } 在系统帧缓冲区分配图形缓冲区之前,首先要对系统帧缓冲区进行过初始化,即这里的变量m所指向的一个private_module_t结构体的成员变量framebuffer的值不能等于NULL。如果等于NULL的话,那么就必须要调用另外一个函数mapFrameBufferLocked来初始化系统帧缓冲区。初始化系统帧缓冲区的过程可以参考前面第3部分的内容。 变量bufferMask用来描述系统帧缓冲区的使用情况,而变量numBuffers用来描述系统帧缓冲区可以划分为多少个图形缓冲区来使用,另外一个变量bufferSize用来描述设备显示屏一屏内容所占用的内存的大小。 如果系统帧缓冲区只有一个图形缓冲区大小,即变量numBuffers的值等于1,那么这个图形缓冲区就始终用作系统主图形缓冲区来使用。在这种情况下,我们就不能够在系统帧缓冲区中分配图形缓冲区来给用户空间的应用程序使用,因此,这时候就会转向内存中来分配图形缓冲区,即调用函数gralloc_alloc_buffer来分配图形缓冲区。注意,这时候分配的图形缓冲区的大小为一屏内容的大小,即bufferSize。 如果bufferMask的值大于等于((1LU<<numBuffers)-1)的值,那么就说明系统帧缓冲区中的图形缓冲区全部都分配出去了,这时候分配图形缓冲区就失败了。例如,假设图形缓冲区的个数为2,那么((1LU<<numBuffers)-1)的值就等于3,即二制制0x11。如果这时候bufferMask的值也等于0x11,那么就表示第一个和第二个图形缓冲区都已经分配出去了。因此,这时候就不能再在系统帧缓冲区中分配图形缓冲区。 假设此时系统帧缓冲区中尚有空闲的图形缓冲区的,接下来函数就会创建一个private_handle_t结构体hnd来描述这个即将要分配出去的图形缓冲区。注意,这个图形缓冲区的标志值等于PRIV_FLAGS_FRAMEBUFFER,即表示这是一块在系统帧缓冲区中分配的图形缓冲区。 接下来的for循环从低位到高位检查变量bufferMask的值,并且找到第一个值等于0的位,这样就可以知道在系统帧缓冲区中,第几个图形缓冲区的是空闲的。注意,变量vadrr的值开始的时候指向系统帧缓冲区的基地址,在下面的for循环中,每循环一次它的值都会增加bufferSize。从这里就可以看出,每次从系统帧缓冲区中分配出去的图形缓冲区的大小都是刚好等于显示屏一屏内容大小的。 最后分配出去的图形缓冲区的开始地址就保存在前面所创建的private_handle_t结构体hnd的成员变量base中,这样,用户空间的应用程序就可以直接将要渲染的图形内容拷贝到这个地址上去,这就相当于是直接将图形渲染到系统帧缓冲区中去。 在将private_handle_t结构体hnd返回给调用者之前,还需要设置它的成员变量offset,以便可以知道它所描述的图形缓冲区的起始地址相对于系统帧缓冲区的基地址的偏移量。 至此,在系统帧缓冲区中分配图形缓冲区的过程就分析完成了,接下来我们再分析在内存在分析图形缓冲区的过程,即分析函数gralloc_alloc_buffer的实现。 函数gralloc_alloc_buffer也是实现在文件hardware/libhardware/modules/gralloc/gralloc.cpp中,如下所示: 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; } 这个函数的实现很简单,它首先调用函数ashmem_create_region来创建一块匿名共享内存,接着再在这块匿名共享内存上分配一个图形缓冲区。注意,这个图形缓冲区也是使用一个private_handle_t结构体来描述的,不过这个图形缓冲区的标志值等于0,以区别于在系统帧缓冲区中分配的图形缓冲区。匿名共享内存的相关知识,可以参考前面 Android系统匿名共享内存Ashmem(Anonymous Shared Memory)简要介绍和学习计划 一文,以及 Android系统匿名共享内存(Anonymous Shared Memory)C++调用接口分析 这篇文章。 从匿名共享内存中分配的图形缓冲区还需要映射到进程的地址空间来,然后才可以使用,这是通过调用函数mapBuffer来实现的。 函数mapBuffer实现在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示: intmapBuffer(gralloc_module_tconst*module, private_handle_t*hnd) { void*vaddr; returngralloc_map(module,hnd,&vaddr); } 它通过调用另外一个函数gralloc_map来将参数hnd所描述的一个图形缓冲区映射到当前进程的地址空间来。后面在分析图形缓冲区的注册过程时,我们再分析函数gralloc_map的实现。 注意,在Android系统中,在系统帧缓冲区中分配的图形缓冲区是在SurfaceFlinger服务中使用的,而在内存中分配的图形缓冲区既可以在SurfaceFlinger服务中使用,也可以在其它的应用程序中使用。当其它的应用程序需要使用图形缓冲区的时候,它们就会请求SurfaceFlinger服务为它们分配,因此,对于其它的应用程序来说,它们只需要将SurfaceFlinger服务返回来的图形缓冲区映射到自己的进程地址空间来使用就可以了,这就是后面我们所要分析的图形缓冲区的注册过程。 至此,图形缓冲区的分配过程就分析完成了,接下来我们继续分析图形缓冲区的释放过程。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967089,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(10)

7. 图形缓冲区的注销过程 图形缓冲区使用完成之后,就需要从当前进程中注销。前面提到,注销图形缓冲区是由Gralloc模块中的函数gralloc_unregister_buffer来实现的,这个函数实现在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示: intgralloc_unregister_buffer(gralloc_module_tconst*module, buffer_handle_thandle) { if(private_handle_t::validate(handle)<0) return-EINVAL; //neverunmapbuffersthatwerecreatedinthisprocess private_handle_t*hnd=(private_handle_t*)handle; if(hnd->pid!=getpid()){ if(hnd->base){ gralloc_unmap(module,handle); } } return0; } 这个函数同样是首先调用private_handle_t类的静态成员函数validate来验证参数handle指向的一块图形缓冲区的确是由Gralloc模块分配的,接着再将将参数handle指向的一块图形缓冲区转换为一个private_handle_t结构体hnd来访问。 一块图形缓冲区只有被注册过,即被Gralloc模块中的函数gralloc_register_buffer注册过,才需要注销,而由函数gralloc_register_buffer注册的图形缓冲区都不是由当前进程分配的,因此,当前进程在注销一个图形缓冲区的时候,会检查要注销的图形缓冲区是否是由自己分配的。如果是由自己分配的话,那么它什么也不做就返回了。 假设要注销的图形缓冲区hnd不是由当前进程分配的,那么接下来就会调用另外一个函数galloc_unmap来注销图形缓冲区hnd。 函数galloc_unmap也是实现在文件hardware/libhardware/modules/gralloc/mapper.cpp中,如下所示: staticintgralloc_unmap(gralloc_module_tconst*module, buffer_handle_thandle) { private_handle_t*hnd=(private_handle_t*)handle; if(!(hnd->flags&private_handle_t::PRIV_FLAGS_FRAMEBUFFER)){ void*base=(void*)hnd->base; size_tsize=hnd->size; //LOGD("unmappingfrom%p,size=%d",base,size); if(munmap(base,size)<0){ LOGE("Couldnotunmap%s",strerror(errno)); } } hnd->base=0; return0; } 这个函数的实现与前面所分析的函数gralloc_map的实现是类似的,只不过它执行的是相反的操作,即将解除一个指定的图形缓冲区在当前进程的地址空间中的映射,从而完成对这个图形缓冲区的注销工作。 这样,图形缓冲区的注销过程就分析完成了,接下来我们再继续分析一个图形缓冲区是如何被渲染到系统帧缓冲区去的,即它的内容是如何绘制在设备显示屏中的。 8. 图形缓冲区的渲染过程 用户空间的应用程序将画面内容写入到图形缓冲区中去之后,还需要将图形缓冲区渲染到系统帧缓冲区中去,这样才可以把画面绘制到设备显示屏中去。前面提到,渲染图形缓冲区是由Gralloc模块中的函数fb_post来实现的,这个函数实现在文件hardware/libhardware/modules/gralloc/framebuffer.cpp中,如下所示: staticintfb_post(structframebuffer_device_t*dev,buffer_handle_tbuffer) { 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(hnd->flags&private_handle_t::PRIV_FLAGS_FRAMEBUFFER){ constsize_toffset=hnd->base-m->framebuffer->base; m->info.activate=FB_ACTIVATE_VBL; m->info.yoffset=offset/m->finfo.line_length; if(ioctl(m->framebuffer->fd,FBIOPUT_VSCREENINFO,&m->info)==-1){ LOGE("FBIOPUT_VSCREENINFOfailed"); m->base.unlock(&m->base,buffer); return-errno; } m->currentBuffer=buffer; }else{ //Ifwecan'tdothepage_flip,justcopythebuffertothefront //FIXME:usecopybitHALinsteadofmemcpy 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); 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; } 参数buffer用来描述要渲染的图形缓冲区,它指向的必须要是一个private_handle_t结构体,这是通过调用private_handle_t类的静态成员函数validate来验证的。验证通过之后,就可以将参数buffer所描述的一个buffer_handle_t结构体转换成一个private_handle_t结构体hnd。 参数dev用来描述在Gralloc模块中的一个fb设备。从前面第3部分的内容可以知道,在打开fb设备的时候,Gralloc模块返回给调用者的实际上是一个fb_context_t结构体,因此,这里就可以将参数dev所描述的一个framebuffer_device_t结构体转换成一个fb_context_t结构体ctx。 参数dev的成员变量common指向了一个hw_device_t结构体,这个结构体的成员变量module指向了一个Gralloc模块。从前面第1部分的内容可以知道,一个Gralloc模块是使用一个private_module_t结构体来描述的,因此,我们可以将dev->common.moudle转换成一个private_module_t结构体m。 由于private_handle_t结构体hnd所描述的图形缓冲区可能是在系统帧缓冲区分配的,也有可能是内存中分配的,因此,我们分两种情况来讨论图形缓冲区渲染过程。 当private_handle_t结构体hnd所描述的图形缓冲区是在系统帧缓冲区中分配的时候,即这个图形缓冲区的标志值flags的PRIV_FLAGS_FRAMEBUFFER位等于1的时候,我们是不需要将图形缓冲区的内容拷贝到系统帧缓冲区去的,因为我们将内容写入到图形缓冲区的时候,已经相当于是将内容写入到了系统帧缓冲区中去了。虽然在这种情况下,我们不需要将图形缓冲区的内容拷贝到系统帧缓冲区去,但是我们需要告诉系统帧缓冲区设备将要渲染的图形缓冲区作为系统当前的输出图形缓冲区,这样才可以将要渲染的图形缓冲区的内容绘制到设备显示屏来。例如,假设系统帧缓冲区有2个图形缓冲区,当前是以第1个图形缓冲区作为输出图形缓冲区的,这时候如果我们需要渲染第2个图形缓冲区,那么就必须告诉系统帧绘冲区设备,将第2个图形缓冲区作为输出图形缓冲区。 设置系统帧缓冲区的当前输出图形缓冲区是通过IO控制命令FBIOPUT_VSCREENINFO来进行的。IO控制命令FBIOPUT_VSCREENINFO需要一个fb_var_screeninfo结构体作为参数。从前面第3部分的内容可以知道,private_module_t结构体m的成员变量info正好保存在我们所需要的这个fb_var_screeninfo结构体。有了个m->info这个fb_var_screeninfo结构体之后,我们只需要设置好它的成员变量yoffset的值(不用设置成员变量xoffset的值是因为所有的图形缓冲区的宽度是相等的),就可以将要渲染的图形缓冲区设置为系统帧缓冲区的当前输出图形缓冲区。fb_var_screeninfo结构体的成员变量yoffset保存的是当前输出图形缓冲区在整个系统帧缓冲区的纵向偏移量,即Y偏移量。我们只需要将要渲染的图形缓冲区的开始地址hnd->base的值减去系统帧缓冲区的基地址m->framebuffer->base的值,再除以图形缓冲区一行所占据的字节数m->finfo.line_length,就可以得到所需要的Y偏移量。 在执行IO控制命令FBIOPUT_VSCREENINFO之前,还会将作为参数的fb_var_screeninfo结构体的成员变量activate的值设置FB_ACTIVATE_VBL,表示要等到下一个垂直同步事件出现时,再将当前要渲染的图形缓冲区的内容绘制出来。这样做的目的是避免出现屏幕闪烁,即避免前后两个图形缓冲区的内容各有一部分同时出现屏幕中。 成功地执行完成IO控制命令FBIOPUT_VSCREENINFO之后,函数还会将当前被渲染的图形缓冲区保存在private_module_t结构体m的成员变量currentBuffer中,以便可以记录当前被渲染的图形缓冲区是哪一个。 当private_handle_t结构体hnd所描述的图形缓冲区是在内存中分配的时候,即这个图形缓冲区的标志值flags的PRIV_FLAGS_FRAMEBUFFER位等于0的时候,我们就需要将它的内容拷贝到系统帧缓冲区中去了。这个拷贝的工作是通过调用函数memcpy来完成的。在拷贝之前,我们需要三个参数。第一个参数是要渲染的图形缓冲区的起址地址,这个地址保存在参数buffer所指向的一个private_handle_t结构体中。第二个参数是要系统帧缓冲区的基地址,这个地址保存在private_module_t结构体m的成员变量framebuffer所指向的一个private_handle_t结构体中。第三个参数是要拷贝的内容的大小,这个大小就刚好是一个屏幕像素所占据的内存的大小。屏幕高度由m->info.yres来描述,而一行屏幕像素所占用的字节数由m->finfo.line_length来描述,将这两者相乘,就可以得到一个屏幕像素所占据的内存的大小。 在将一块内存缓冲区的内容拷贝到系统帧缓冲区中去之前,需要对这两块缓冲区进行锁定,以保证在拷贝的过程中,这两块缓冲区的内容不会被修改。这个锁定的工作是由Gralloc模块中的函数gralloc_lock来实现的。从前面第1部分的内容可以知道,Gralloc模块中的函数gralloc_lock的地址正好就保存在private_module_t结构体m的成员变量base所描述的一个gralloc_module_t结构体的成员函数lock中。 在调用函数gralloc_lock来锁定一块缓冲区之后,还可以通过最后一个输出参数来获得被锁定的缓冲区的开始地址,因此,通过调用函数gralloc_lock来锁定要渲染的图形缓冲区以及系统帧缓冲区,就可以得到前面所需要的第一个和第二个参数。 将要渲染的图形缓冲区的内容拷贝到系统帧缓冲区之后,就可以解除前面对它们的锁定了,这个解锁的工作是由Gralloc模块中的函数gralloc_unlock来实现的。从前面第1部分的内容可以知道,Gralloc模块中的函数gralloc_unlock的地址正好就保存在private_module_t结构体m的成员变量base所描述的一个gralloc_module_t结构体的成员函数unlock中。 这样,一个图形缓冲区的渲染过程就分析完成了。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967093,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(7)

显示屏的刷新频率与显示屏的扫描时序相关。显示屏的扫描时序可以参考Linux内核源代码目录下的Documentation/fb/framebuffer.txt文件。我们结合图2来简单说明上述代码是如何计算显示屏的刷新频率的。图 2 显示屏扫描时序示意图 中间由xres和yres组成的区域即为显示屏的图形绘制区,在绘制区的上、下、左和右分别有四个边距upper_margin、lower_margin、left_margin和right_margin。此外,在显示屏的最右边以及最下边还有一个水平同步区域hsync_len和一个垂直同步区域vsync_len。电子枪按照从左到右、从上到下的顺序来显示屏中打点,从而可以将要渲染的图形显示在屏幕中。前面所提到的区域信息分别保存在fb_var_screnninfo结构体info的成员变量xres、yres、upper_margin、lower_margin、left_margin、right_margin、hsync_len和vsync_len。 电子枪每在xres和yres所组成的区域中打一个点所花费的时间记录在fb_var_screnninfo结构体info的成员变量pixclock,单位为pico seconds,即10E-12秒。 电子枪从左到右扫描完成一行之后,都会处理关闭状态,并且会重新折回到左边去。由于电子枪在从右到左折回的过程中不需要打点,因此,这个过程会比从左到右扫描屏幕的过程要快,这个折回的时间大概就等于在xres和yres所组成的区域扫描(left_margin+right_margin)个点的时间。这样,我们就可以认为每渲染一行需要的时间为(xres + left_margin + right_margin)* pixclock。 同样,电子枪从上到下扫描完成显示屏之后,需要从右下角折回到左上角去,折回的时间大概等于在xres和yres所组成的区域中扫描(upper_margin +lower_margin)行所需要的时间。这样,我们就可以认为每渲染一屏图形所需要的时间等于在xres和yres所组成的区域中扫描(yres + upper_margin +lower_margin)行所需要的时间。由于在xres和yres所组成的区域中扫描一行所需要的时间为(xres + left_margin + right_margin)* pixclock,因此,每渲染一屏图形所需要的总时间就等于(yres +upper_margin +lower_margin)*(xres + left_margin + right_margin)* pixclock。 每渲染一屏图形需要的总时间经过计算之后,就保存在变量refreshQuotient中。注意,变量refreshQuotient所描述的时间的单位为1E-12秒。这样,将变量refreshQuotient的值倒过来,就可以得到设备显示屏的刷新频率。将这个频率值乘以10E15次方之后,就得到一个单位为10E-3 HZ的刷新频率,保存在变量refreshRate中。 当Android系统在模拟器运行的时候,保存在fb_var_screnninfo结构体info的成员变量pixclock中的值可能等于0。在这种情况下,前面计算得到的变量refreshRate的值就会等于0。在这种情况下,接下来的代码会将变量refreshRate的值设置为60 * 1000 * 10E-3 HZ,即将显示屏的刷新频率设置为60HZ。 再往下看函数mapFrameBufferLocked: 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; 这段代码首先计算显示屏的密度,即每英寸有多少个像素点,分别宽度和高度两个维度,分别保存在变量xdpi和ydpi中。注意,fb_var_screeninfo结构体info的成员变量width和height用来描述显示屏的宽度和高度,它们是以毫米(mm)为单位的。 这段代码接着再将前面计算得到的显示屏刷新频率的单位由10E-3 HZ转换为HZ,即帧每秒,并且保存在变量fps中。 再往下看函数mapFrameBufferLocked: 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; 这段代码再次通过IO控制命令FBIOGET_FSCREENINFO来获得系统帧缓冲区的固定信息,并且保存在fb_fix_screeninfo结构体finfo中,接下来再使用fb_fix_screeninfo结构体finfo以及前面得到的系统帧缓冲区的其它信息来初始化参数module所描述的一个private_module_t结构体。 最后,函数mapFrameBufferLocked就将系统帧缓冲区映射到当前进程的地址空间来: /* *maptheframebuffer */ interr; size_tfbSize=roundUpToPageSize(finfo.line_length*info.yres_virtual); module->framebuffer=newprivate_handle_t(dup(fd),fbSize,0); module->numBuffers=info.yres_virtual/info.yres; module->bufferMask=0; void*vaddr=mmap(0,fbSize,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); if(vaddr==MAP_FAILED){ LOGE("Errormappingtheframebuffer(%s)",strerror(errno)); return-errno; } module->framebuffer->base=intptr_t(vaddr); memset(vaddr,0,fbSize); return0; } 表达式finfo.line_length * info.yres_virtual计算的是整个系统帧缓冲区的大小,它的值等于显示屏行数(虚拟分辨率的高度值,info.yres_virtual)乘以每一行所占用的字节数(finfo.line_length)。函数roundUpToPageSize用来将整个系统帧缓冲区的大小对齐到页面边界。对齐后的大小保存在变量fbSize中。 表达式finfo.yres_virtual / info.yres计算的是整个系统帧缓冲区可以划分为多少个图形缓冲区来使用,这个数值保存在参数module所描述的一个private_module_t结构体的成员变量nmBuffers中。参数module所描述的一个private_module_t结构体的另外一个成员变量bufferMask的值接着被设置为0,表示系统帧缓冲区中的所有图形缓冲区都是处于空闲状态,即它们可以分配出去给应用程序使用。 系统帧缓冲区是通过调用函数mmap来映射到当前进程的地址空间来的。映射后得到的地址空间使用一个private_handle_t结构体来描述,这个结构体的成员变量base保存的即为系统帧缓冲区在当前进程的地址空间中的起始地址。这样,Gralloc模块以后就可以从这块地址空间中分配图形缓冲区给当前进程使用。 至此,fb设备的打开过程就分析完成了。在打开fb设备的过程中,Gralloc模块还完成了对系统帧缓冲区的初始化工作。接下来我们继续分析Gralloc模块是如何分配图形缓冲区给用户空间的应用程序使用的。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967084,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(5)

3. fb设备的打开过程 在Gralloc模块中,fb设备的ID值定义为GRALLOC_HARDWARE_FB0。GRALLOC_HARDWARE_FB0是一个宏,定义在文件hardware/libhardware/include/hardware/gralloc.h中, 如下所示: #defineGRALLOC_HARDWARE_FB0"fb0" fb设备使用结构体framebuffer_device_t 来描述。结构体framebuffer_device_t是用来描述系统帧缓冲区的信息,它定义在文件hardware/libhardware/include/hardware/gralloc.h中, 如下所示: typedefstructframebuffer_device_t{ structhw_device_tcommon; /*flagsdescribingsomeattributesoftheframebuffer*/ constuint32_tflags; /*dimensionsoftheframebufferinpixels*/ constuint32_twidth; constuint32_theight; /*frambufferstrideinpixels*/ constintstride; /*framebufferpixelformat*/ constintformat; /*resolutionoftheframebuffer'sdisplaypanelinpixelperinch*/ constfloatxdpi; constfloatydpi; /*framebuffer'sdisplaypanelrefreshrateinframespersecond*/ constfloatfps; /*minswapintervalsupportedbythisframebuffer*/ constintminSwapInterval; /*maxswapintervalsupportedbythisframebuffer*/ constintmaxSwapInterval; intreserved[8]; int(*setSwapInterval)(structframebuffer_device_t*window, intinterval); int(*setUpdateRect)(structframebuffer_device_t*window, intleft,inttop,intwidth,intheight); int(*post)(structframebuffer_device_t*dev,buffer_handle_tbuffer); int(*compositionComplete)(structframebuffer_device_t*dev); void*reserved_proc[8]; }framebuffer_device_t; 成员变量flags用来记录系统帧缓冲区的标志,目前没有使用这成员变量,它的值被设置为0。 成员变量width和height分别用来描述设备显示屏的宽度和高度,它们是以像素为单位的。 成员变量stride用来描述设备显示屏的一行有多少个像素点。 成员变量format用来描述系统帧缓冲区的像素格式,支持的像素格式主要有HAL_PIXEL_FORMAT_RGBX_8888和HAL_PIXEL_FORMAT_RGB_565两种。HAL_PIXEL_FORMAT_RGBX_8888表示一个像素使用32位来描述,R、G和B分别占8位,另外8位未使用。HAL_PIXEL_FORMAT_RGB_565表示一个像素使用16位来描述,R、G和B分别占5、6和5位。 成员变量xdpi和ydpi分别用来描述设备显示屏在宽度和高度上的密度,即每英寸有多少个像素点。 成员变量fps用来描述设备显示屏的刷新频率,它的单位是帧每秒。 成员变量minSwapInterval和maxSwapInterval用来描述帧缓冲区交换前后两个图形缓冲区的最小和最大时间间隔。 成员变量reserved是保留给将来使用的。 成员函数setSwapInterval用来设置帧缓冲区交换前后两个图形缓冲区的最小和最大时间间隔。 成员函数setUpdateRect用来设置帧缓冲区的更新区域。 成员函数post用来将图形缓冲区buffer的内容渲染到帧缓冲区中去,即显示在设备的显示屏中去。 成员函数compositionComplete用来通知fb设备device,图形缓冲区的组合工作已经完成,目前没有使用这个成员函数。 成员变量reserved是一个函数指针数组,它们是保留给将来使用的。 在结构体framebuffer_device_t的一系列成员函数中,post是最重要的一个成员函数,用户空间的应用程序通过调用这个成员函数就可以在设备的显示屏中渲染指定的画面,后面我们将详细这个函数的实现。 Gralloc模块在在文件hardware/libhardware/include/hardware/gralloc.h中定义了一个帮助函数framebuffer_open,用来打开fb设备,如下所示: staticinlineintframebuffer_open(conststructhw_module_t*module, structframebuffer_device_t**device){ returnmodule->methods->open(module, GRALLOC_HARDWARE_FB0,(structhw_device_t**)device); } 参数module指向的是一个用来描述Gralloc模块的hw_module_t结构体,前面提到,它的成员变量methods所指向的一个hw_module_methods_t结构体的成员函数open指向了Gralloc模块中的函数gralloc_device_open,这个函数打开fb设备的代码段如下所示: intgralloc_device_open(consthw_module_t*module,constchar*name, hw_device_t**device) { intstatus=-EINVAL; if(!strcmp(name,GRALLOC_HARDWARE_GPU0)){ ...... }else{ status=fb_device_open(module,name,device); } returnstatus; } 参数name的值等于GRALLOC_HARDWARE_FB0,因此,函数gralloc_device_open接下来会调用另外一个函数fb_device_open来执行打开fb设备的操作。 函数fb_device_open定义在文件hardware/libhardware/modules/gralloc/framebuffer.cpp中,如下所示: structfb_context_t{ framebuffer_device_tdevice; }; ...... intfb_device_open(hw_module_tconst*module,constchar*name, hw_device_t**device) { intstatus=-EINVAL; if(!strcmp(name,GRALLOC_HARDWARE_FB0)){ alloc_device_t*gralloc_device; status=gralloc_open(module,&gralloc_device); if(status<0) returnstatus; /*initializeourstatehere*/ fb_context_t*dev=(fb_context_t*)malloc(sizeof(*dev)); 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=fb_close; dev->device.setSwapInterval=fb_setSwapInterval; dev->device.post=fb_post; dev->device.setUpdateRect=0; private_module_t*m=(private_module_t*)module; status=mapFrameBuffer(m); if(status>=0){ intstride=m->finfo.line_length/(m->info.bits_per_pixel>>3); intformat=(m->info.bits_per_pixel==32) ?HAL_PIXEL_FORMAT_RGBX_8888 :HAL_PIXEL_FORMAT_RGB_565; #ifdefNO_32BPP format=HAL_PIXEL_FORMAT_RGB_565; #endif const_cast<uint32_t&>(dev->device.flags)=0; const_cast<uint32_t&>(dev->device.width)=m->info.xres; const_cast<uint32_t&>(dev->device.height)=m->info.yres; const_cast<int&>(dev->device.stride)=stride; const_cast<int&>(dev->device.format)=format; const_cast<float&>(dev->device.xdpi)=m->xdpi; const_cast<float&>(dev->device.ydpi)=m->ydpi; const_cast<float&>(dev->device.fps)=m->fps; const_cast<int&>(dev->device.minSwapInterval)=1; const_cast<int&>(dev->device.maxSwapInterval)=1; *device=&dev->device.common; } } returnstatus; } 这个函数主要是用来创建一个fb_context_t结构体,并且对它的成员变量device进行初始化。结构体fb_context_t的成员变量device的类型为framebuffer_device_t,前面提到,它是用来描述fb设备的。fb设备主要是用来渲染图形缓冲区的,这是通过调用它的成员函数post来实现的。从这里可以看出,函数fb_device_open所打开的fb设备的成员函数post被设置为Gralloc模块中的函数fb_post,后面我们再详细分析它的实现。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967075,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(3)

结构体gralloc_module_t定义在文件hardware/libhardware/include/hardware/gralloc.h中,它主要是定义了四个用来操作图形缓冲区的成员函数,如下所示: typedefstructgralloc_module_t{ ...... int(*registerBuffer)(structgralloc_module_tconst*module, buffer_handle_thandle); int(*unregisterBuffer)(structgralloc_module_tconst*module, buffer_handle_thandle); 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); ...... } 成员函数registerBuffer和unregisterBuffer分别用来注册和注销一个指定的图形缓冲区,这个指定的图形缓冲区使用一个buffer_handle_t句柄来描述。所谓注册图形缓冲区,实际上就是将一块图形缓冲区映射到一个进程的地址空间去,而注销图形缓冲区就是执行相反的操作。 成员函数lock和unlock分别用来锁定和解锁一个指定的图形缓冲区,这个指定的图形缓冲区同样是使用一个buffer_handle_t句柄来描述。在访问一块图形缓冲区的时候,例如,向一块图形缓冲写入内容的时候,需要将该图形缓冲区锁定,用来避免访问冲突。在锁定一块图形缓冲区的时候,可以指定要锁定的图形绘冲区的位置以及大小,这是通过参数l、t、w和h来指定的,其中,参数l和t指定的是要访问的图形缓冲区的左上角位置,而参数w和h指定的是要访问的图形缓冲区的宽度和长度。锁定之后,就可以获得由参数参数l、t、w和h所圈定的一块缓冲区的起始地址,保存在输出参数vaddr中。另一方面,在访问完成一块图形缓冲区之后,需要解除这块图形缓冲区的锁定。 在Gralloc模块中,符号HAL_MODULE_INFO_SYM指向的gralloc结构体的成员函数registerBuffer、unregisterBuffer、lock和unlock分别被指定为函数gralloc_register_buffer、gralloc_unregister_buffer、gralloc_lock和gralloc_unlock,后面我们再详细分析它们的实现。 结构体private_module_t定义在文件hardware/libhardware/modules/gralloc/gralloc_priv.h中,它主要是用来描述帧缓冲区的属性,如下所示: structprivate_module_t{ gralloc_module_tbase; private_handle_t*framebuffer; uint32_tflags; uint32_tnumBuffers; uint32_tbufferMask; pthread_mutex_tlock; buffer_handle_tcurrentBuffer; intpmem_master; void*pmem_master_base; structfb_var_screeninfoinfo; structfb_fix_screeninfofinfo; floatxdpi; floatydpi; floatfps; }; 成员变量framebuffer的类型为private_handle_t,它是一个指向系统帧缓冲区的句柄,后面我们再分析结构体private_handle_t的定义。 成员变量flags用来标志系统帧缓冲区是否支持双缓冲。如果支持的话,那么它的PAGE_FLIP位就等于1,否则的话,就等于0。 成员变量numBuffers表示系统帧缓冲区包含有多少个图形缓冲区。一个帧缓冲区包含有多少个图形缓冲区是与它的可视分辨率以及虚拟分辨率的大小有关的。例如,如果一个帧缓冲区的可视分辨率为800 x 600,而虚拟分辨率为1600 x 600,那么这个帧缓冲区就可以包含有两个图形缓冲区。 成员变量bufferMask用来记录系统帧缓冲区中的图形缓冲区的使用情况。例如,假设系统帧缓冲区有两个图形缓冲区,这时候成员变量bufferMask就有四种取值,分别是二进制的00、01、10和11,其中,00分别表示两个图缓冲区都是空闲的,01表示第1个图形缓冲区已经分配出去,而第2个图形缓冲区是空闲的,10表示第1个图形缓冲区是空闲的,而第2个图形缓冲区已经分配出去,11表示两个图缓冲区都已经分配出去。 成员变量lock是一个互斥锁,用来保护结构体private_module_t的并行访问。 成员变量currentBuffer的类型为buffer_handle_t,用来描述当前正在被渲染的图形缓冲区,后面我们再分析它的定义。 成员变量pmem_master和pmem_master_base目前没有使用。 成员变量info和finfo的类型分别为fb_var_screeninfo和fb_fix_screeninfo,它们用来保存设备显示屏的属性信息,其中,成员变量info保存的属性信息是可以动态设置的,而成员变量finfo保存的属性信息是只读的。这两个成员变量的值可以通过IO控制命令FBIOGET_VSCREENINFO和FBIOGET_FSCREENINFO来从帧缓冲区驱动模块中获得。 成员变量xdpi和ydpi分别用来描述设备显示屏在宽度和高度上的密度,即每英寸有多少个像素点。 成员变量fps用来描述显示屏的刷新频率,它的单位的fps,即每秒帧数。 接下来, 我们再分析结构体buffer_handle_t和private_handle_t的定义。 结构体buffer_handle_t定义在文件hardware/libhardware/include/hardware/gralloc.h文件中,如下所示: typedefconstnative_handle*buffer_handle_t; 它是一个类型为native_handle_t的指针,而结构体native_handle_t用来描述一个本地句柄值,它定义在系统运行时层的文件system/core/include/cutils/native_handle.h文件中,如下所示: typedefstruct { intversion;/*sizeof(native_handle_t)*/ intnumFds;/*numberoffile-descriptorsat&data[0]*/ intnumInts;/*numberofintsat&data[numFds]*/ intdata[0];/*numFds+numIntsints*/ }native_handle_t; 成员变量version的大小被设置为结构体native_handle_t的大小,用来标识结构体native_handle_t的版本。 成员变量numFds和numInts表示结构体native_handle_t所包含的文件描述符以及整数值的个数,这些文件描述符和整数保存在成员变量data所指向的一块缓冲区中。 我们一般不直接使用native_handle_t结构体来描述一个本地句柄值,而是通过它的子类来描述一个具体的本地句柄值。接下来我们就通过结构体private_handle_t的定义来说明native_handle_t结构体的用法。 结构体private_handle_t用来描述一块图形缓冲区,这块图形缓冲区可能是在帧缓冲区中分配的,也可能是在内存中分配的,视具体情况而定,它定义在文件hardware/libhardware/modules/gralloc/gralloc_priv.h文件中,如下所示: #ifdef__cplusplus structprivate_handle_t:publicnative_handle{ #else structprivate_handle_t{ structnative_handlenativeHandle; #endif enum{ PRIV_FLAGS_FRAMEBUFFER=0x00000001 }; //file-descriptors intfd; //ints intmagic; intflags; intsize; intoffset; //FIXME:theattributesbelowshouldbeout-of-line intbase; intpid; #ifdef__cplusplus staticconstintsNumInts=6; staticconstintsNumFds=1; staticconstintsMagic=0x3141592; private_handle_t(intfd,intsize,intflags): fd(fd),magic(sMagic),flags(flags),size(size),offset(0), base(0),pid(getpid()) { version=sizeof(native_handle); numInts=sNumInts; numFds=sNumFds; } ~private_handle_t(){ magic=0; } 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; } #endif }; 为了方便描述,我们假设我们是在C++环境中编译文件gralloc_priv.h,即编译环境定义有宏__cplusplus。这样,结构体private_handle_t就是从结构体native_handle_t继承下来的,它包含有1个文件描述符以及6个整数,以及三个静态成员变量。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967065,如需转载请自行联系原作者

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册