首页 文章 精选 留言 我的

精选列表

搜索[buffer],共3590篇文章
优秀的个人博客,低调大师

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

函数fb_device_open在打开fb设备的过程中,会调用另外一个函数mapFrameBuffer来获得系统帧缓冲区的信息,并且将这些信息保存在参数module所描述的一个private_module_t结构体的各个成员变量中。有了系统帧缓冲区的信息之后,函数fb_device_open接下来就可以对前面所打开的一个fb设备的各个成员变量进行初始化。这些成员变量的含义可以参考前面对结构体framebuffer_device_t的介绍。接下来我们只简单介绍一下结构体framebuffer_device_t的成员变量stride和format的初始化过程。 变量m的成员变量finfo的类型为fb_fix_screeninfo,它是在函数mapFrameBuffer中被始化的。fb_fix_screeninfo是在内核中定义的一个结构体,用来描述设备显示屏的固定属性信息,其中,它的成员变量line_length用来描述显示屏一行像素总共所占用的字节数。 变量m的另外一个成员变量info的类型为fb_var_screeninfo,它也是在函数mapFrameBuffer中被始化的。fb_var_screeninfo也是内核中定义的一个结构体,用来描述可以动态设置的显示屏属性信息,其中,它的成员变量bits_per_pixel用来描述显示屏每一个像素所占用的位数。 这样,我们将m->info.bits_per_pixel的值向右移3位,就可以得到显示屏每一个像素所占用的字节数。用显示屏每一个像素所占用的字节数去除显示屏一行像素总共所占用的字节数m->finfo.line_length,就可以得到显示屏一行有多少个像素点。这个值最终就可以保存在前面所打开的fb设备的成员变量stride中。 当显示屏每一个像素所占用的位数等于32的时候,那么前面所打开的fb设备的像素格式format就会被设置为HAL_PIXEL_FORMAT_RGBX_8888,否则的话,就会被设置为HAL_PIXEL_FORMAT_RGB_565。另一方面,如果在编译的时候定义了NO_32BPP宏,即不要使用32位来描述一个像素,那么函数fb_device_open就会强制将前面所打开的fb设备的像素格式format设置为HAL_PIXEL_FORMAT_RGB_565。 函数mapFrameBuffer除了用来获得系统帧缓冲区的信息之外,还会将系统帧缓冲区映射到当前进程的地址空间来。在Android系统中,Gralloc模块中的fb设备是由SurfaceFlinger服务来负责打开和管理的,而SurfaceFlinger服是运行System进程中的,因此,系统帧缓冲区实际上是映射到System进程的地址空间中的。 函数mapFrameBuffer实现在文件hardware/libhardware/modules/gralloc/framebuffer.cpp,如下所示: staticintmapFrameBuffer(structprivate_module_t*module) { pthread_mutex_lock(&module->lock); interr=mapFrameBufferLocked(module); pthread_mutex_unlock(&module->lock); returnerr; } 这个函数调用了同一个文件中的另外一个函数mapFrameBufferLocked来初始化参数module以及将系统帧缓冲区映射到当前进程的地址空间来。 函数mapFrameBufferLocked的实现比较长,我们分段来阅读: intmapFrameBufferLocked(structprivate_module_t*module) { //alreadyinitialized... 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; 这段代码在首先在系统中检查是否存在设备文件/dev/graphics/fb0或者/dev/fb0。如果存在的话,那么就调用函数open来打开它,并且将得到的文件描述符保存在变量fd中。这样,接下来函数mapFrameBufferLocked就可以通过文件描述符fd来与内核中的帧缓冲区驱动程序交互。 继续往下看函数mapFrameBufferLocked: structfb_fix_screeninfofinfo; if(ioctl(fd,FBIOGET_FSCREENINFO,&finfo)==-1) return-errno; structfb_var_screeninfoinfo; if(ioctl(fd,FBIOGET_VSCREENINFO,&info)==-1) return-errno; 这几行代码分别通过IO控制命令FBIOGET_FSCREENINFO和FBIOGET_VSCREENINFO来获得系统帧缓冲区的信息,分别保存在fb_fix_screeninfo结构体finfo和fb_var_screeninfo结构体info中。 再往下看函数mapFrameBufferLocked: info.reserved[0]=0; info.reserved[1]=0; info.reserved[2]=0; info.xoffset=0; info.yoffset=0; info.activate=FB_ACTIVATE_NOW; #ifdefined(NO_32BPP) /* *Explicitlyrequest5/6/5 */ info.bits_per_pixel=16; info.red.offset=11; info.red.length=5; info.green.offset=5; info.green.length=6; info.blue.offset=0; info.blue.length=5; info.transp.offset=0; info.transp.length=0; #endif /* *RequestNUM_BUFFERSscreens(atlest2forpageflipping) */ info.yres_virtual=info.yres*NUM_BUFFERS; uint32_tflags=PAGE_FLIP; if(ioctl(fd,FBIOPUT_VSCREENINFO,&info)==-1){ info.yres_virtual=info.yres; flags&=~PAGE_FLIP; LOGW("FBIOPUT_VSCREENINFOfailed,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); } 这段代码主要是用来设置设备显示屏的虚拟分辨率。在前面 Android系统的开机画面显示过程分析 一文提到,结构体fb_var_screeninfo的成员变量xres和yres用来描述显示屏的可视分辨率,而成员变量xres_virtual和yres_virtual用来描述显示屏的虚拟分辨率。这里保持可视分辨率以及虚拟分辨率的宽度值不变,而将虚拟分辨率的高度值设置为可视分辨率的高度值的NUM_BUFFERS倍。NUM_BUFFERS是一个宏,它的值被定义为2。这样,我们就可以将系统帧缓冲区划分为两个图形缓冲区来使用,即可以通过硬件来实现双缓冲技术。 在结构体fb_var_screeninfo中,与显示屏的可视分辨率和虚拟分辨率相关的另外两个成员变量是xoffset和yoffset,它们用来告诉帧缓冲区当前要渲染的图形缓冲区是哪一个,它们的使用方法可以参考前面 Android系统的开机画面显示过程分析 一文。 这段代码在设置设备显示屏的虚拟分辨率之前,还会检查是否定义了宏NO_32BPP。如果定义了的话,那么就说明系统显式地要求将帧缓冲区的像素格式设置为HAL_PIXEL_FORMAT_RGB_565。在这种情况下,这段代码就会通过fb_var_screeninfo结构体info的成员变量bits_per_pixel、red、green、blue和transp来通知帧缓冲区驱动程序使用HAL_PIXEL_FORMAT_RGB_565像素格式来渲染显示屏。 这段代码最终是通过IO控制命令FBIOPUT_VSCREENINFO来设置设备显示屏的虚拟分辨率以及像素格式的。如果设置失败,即调用函数ioctl的返回值等于-1,那么很可能是因为系统帧缓冲区在硬件上不支持双缓冲,因此,接下来的代码就会重新将显示屏的虚拟分辨率的高度值设置为可视分辨率的高度值,并且将变量flags的PAGE_FLIP位置为0。 另一方面,如果调用函数ioctl成功,但是最终获得的显示屏的虚拟分辨率的高度值小于可视分辨率的高度值的2倍,那么也说明系统帧缓冲区在硬件上不支持双缓冲。在这种情况下,接下来的代码也会重新将显示屏的虚拟分辨率的高度值设置为可视分辨率的高度值,并且将变量flags的PAGE_FLIP位置为0。 再继续往下看函数mapFrameBufferLocked: if(ioctl(fd,FBIOGET_VSCREENINFO,&info)==-1) return-errno; uint64_trefreshQuotient= ( uint64_t(info.upper_margin+info.lower_margin+info.yres) *(info.left_margin+info.right_margin+info.xres) *info.pixclock ); /*Beware,info.pixclockmightbe0underemulation,soavoida *division-by-0here(SIGFPEonARM)*/ intrefreshRate=refreshQuotient>0?(int)(1000000000000000LLU/refreshQuotient):0; if(refreshRate==0){ //bleagh,badinfofromthedriver refreshRate=60*1000;//60Hz } 这段代码再次通过IO控制命令FBIOGET_VSCREENINFO来获得系统帧缓冲区的可变属性信息,并且保存在fb_var_screeninfo结构体info中,接下来再计算设备显示屏的刷新频率。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967078,如需转载请自行联系原作者

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

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的实现原理分析(1)

前面在介绍Android系统的开机画面时提到,Android设备的显示屏被抽象为一个帧缓冲区,而Android系统中的SurfaceFlinger服务就是通过向这个帧缓冲区写入内容来绘制应用程序的用户界面的。Android系统在硬件抽象层中提供了一个Gralloc模块,封装了对帧缓冲区的所有访问操作。本文将详细分析Gralloc模块的实现,为后续分析SurfaceFlinger服务的实现打下基础。 在前面 Android系统的开机画面显示过程分析 一文中提到,Linux内核在启动的过程中会创建一个类别和名称分别为“graphics”和“fb0”的设备,用来描述系统中的第一个帧缓冲区,即第一个显示屏,其中,数字0表示从设备号。注意,系统中至少要存在一个显示屏,因此,名称为“fb0”的设备是肯定会存在的,否则的话,就是出错了。Android系统和Linux内核本身的设计都是支持多个显示屏的,不过,在Android目前的实现中,只支持一个显示屏。 在前面 Android系统的开机画面显示过程分析 一文中还提到,init进程在启动的过程中,会启动另外一个进程ueventd来管理系统的设备文件。当ueventd进程启动起来之后,会通过netlink接口来Linux内核通信,以便可以获得内核中的硬件设备变化通知。而当ueventd进程发现内核中创建了一个类型和名称分别为“graphics”和“fb0”的设备的时候,就会这个设备创建一个/dev/graphics/fb0设备文件。这样,用户空间的应用程序就可以通过设备文件/dev/graphics/fb0来访问内核中的帧缓冲区,即在设备的显示屏中绘制指定的画面。注意,用户空间的应用程序一般是通过内存映射的方式来访问设备文件/dev/graphics/fb0的。 Android系统定义了硬件抽象层模块的编写规范,具体可以参考 Android硬件抽象层(HAL)概要介绍和学习计划 一文。本文假设读者已经熟悉Android系统的硬件抽象层编写规范,因此,我们将按照帧缓冲区的使用情景以及硬件抽象层编写规范来介绍Gralloc模块的实现。 用户空间的应用程序在使用帧缓冲区之间,首先要加载Gralloc模块,并且获得一个gralloc设备和一个fb设备。有了gralloc设备之后,用户空间中的应用程序就可以申请分配一块图形缓冲区,并且将这块图形缓冲区映射到应用程序的地址空间来,以便可以向里面写入要绘制的画面的内容。最后,用户空间中的应用程序就通过fb设备来将前面已经准备好了的图形缓冲区渲染到帧缓冲区中去,即将图形缓冲区的内容绘制到显示屏中去。相应地,当用户空间中的应用程序不再需要使用一块图形缓冲区的时候,就可以通过gralloc设备来释放它,并且将它从地址空间中解除映射。接下来,我们就按照上述使用情景来分析Gralloc模块的实现。 1. Gralloc模块的加载过程。 每一个HAL模块都有一个ID值,以这些ID值为参数来调用硬件抽象层提供的函数hw_get_module就可以将指定的模块加载到内存来,并且获得一个hw_module_t接口来打开相应的设备。 Gralloc模块的ID值定义在hardware/libhardware/include/hardware/gralloc.h文件中,如下所示: #defineGRALLOC_HARDWARE_MODULE_ID"gralloc" 函数hw_get_module实现在hardware/libhardware/hardware.c文件中,如下所示: /**Basepathofthehalmodules*/ #defineHAL_LIBRARY_PATH1"/system/lib/hw" #defineHAL_LIBRARY_PATH2"/vendor/lib/hw" /** *Thereareasetofvariantfilenameformodules.Theformofthefilename *is"<MODULE_ID>.variant.so"sofortheledmoduletheDreamvariants *ofbase"ro.product.board","ro.board.platform"and"ro.arch"wouldbe: * *led.trout.so *led.msm7k.so *led.ARMV6.so *led.default.so */ staticconstchar*variant_keys[]={ "ro.hardware",/*Thisgoesfirstsothatitcanpickupadifferent fileontheemulator.*/ "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){ continue; } snprintf(path,sizeof(path),"%s/%s.%s.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,andweshouldnottry *toloadadifferentvariant.*/ status=load(id,path,module); } returnstatus; } 函数hw_get_module依次在目录/system/lib/hw和/vendor/lib/hw中查找一个名称为"<MODULE_ID>.variant.so"的文件,其中,<MODULE_ID>是一个模块ID,而variant表示"ro.hardware"、"ro.product.board"、"ro.board.platform"和"ro.arch"四个系统属性值之一。例如,对于Gralloc模块来说,函数hw_get_module依次在目录/system/lib/hw和/vendor/lib/hw中检查是否存在以下四个文件: gralloc.<ro.hardware>.so gralloc.<ro.product.board>.so gralloc.<ro.board.platform>.so gralloc.<ro.arch>.so 只要其中的一个文件存在,函数hw_get_module就会停止查找过程,并且调用另外一个函数load来将这个文件加载到内存中来。另一方面,如果在/system/lib/hw和/vendor/lib/hw中均不存这些文件,那么函数hw_get_module就会在目录/system/lib/hw中查找是否存在一个名称为gralloc.default.so的文件。如果存在的话,那么也会调用函数load将它加载到内存中来。 本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967056,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

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

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册