Android帧缓冲区(FrameBuffer)硬件抽象层(HAL)模块Gralloc的实现原理分析_第1页
Android帧缓冲区(FrameBuffer)硬件抽象层(HAL)模块Gralloc的实现原理分析_第2页
Android帧缓冲区(FrameBuffer)硬件抽象层(HAL)模块Gralloc的实现原理分析_第3页
Android帧缓冲区(FrameBuffer)硬件抽象层(HAL)模块Gralloc的实现原理分析_第4页
Android帧缓冲区(FrameBuffer)硬件抽象层(HAL)模块Gralloc的实现原理分析_第5页
已阅读5页,还剩41页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、Android帧缓冲区(现原理分析FrameBuffer)硬件抽象层(HAL)模块Gralloc的实分类:Android2012-07-2301:251529人阅读评论(16)收藏举报前面在介绍Android系统的开机画面时提到,Android设备的显示屏被抽象为一个帧缓冲区,而Android系统中的SurfaceFlinger务就是通过向这个帧缓冲区写入内容来绘制应用程序的用户界面的。Android系统在硬件抽象层中提供了一个Gralloc模块,封装了对帧缓冲区的所有访问操作。本文将详细分析Gralloc模块的实现,为后续分析SurfaceFlinger务的实现打下基础。在前面Android

2、系统的开机画面显示过程分析一文中提到,LinuX内核在启动的过程中会创建一个类别和名称分别为“graphics:和“fb叩的设备,用来描述系统中的第一个帧缓冲区,即第一个显示屏,其中,数字0表示从设备号。注意,系统中至少要存在一个显示屏,因此,名称为“fb0:的设备是肯定会存在的,否则的话,就是出错了。Android系统和LinuX内核本身的设计都是支持多个显示屏的,不过,在Android目前的实现中,只支持一个显示屏。在前面Android系统的开机画面显示过程分析一个进程ueventd来管理系统的设备文件。当通信,以便可以获得内核中的硬件设备变化通知。而当为“graphics:和“fb0:的

3、设备的时候,就会这个设备创建一个一文中还提到,init程在启动的过程中,会启动另外ueVentd进程启动起来之后,会通过netlink口来LinuX内核ueventd进程发现内核中创建了一个类型和名称分别/deV/graPhicS/fbO文件。这样,用户空间的应用程序就可以通过设备文件/deV/graPhicS/fbO问内核中的帧缓冲区,即在设备的显示屏中绘制指定的画面。注意,用户空间的应用程序一般是通过内存映射的方式来访问设备文件/deV/graPhicS/fbOAndroid系统定义了硬件抽象层模块的编写规范,具体可以参考Android硬件抽象层(HALD00介绍和学习计划一文。本文假设读

4、者已经熟悉Android系统的硬件抽象层编写规范,因此,我们将按照帧缓冲区的使用情景以及硬件抽象层编写规范来介绍Gralloc块的实现。用户空间的应用程序在使用帧缓冲区之间,首先要加载Gralloc模块,并且获得一个一个fb设备。有了grallod备之后,用户空间中的应用程序就可以申请分配一块图形缓冲区,并且将这块图形缓冲区映射到应用程序的地址空间来,以便可以向里面写入要绘制的画面的内容。最后,用户空间中的应用程序就通过fb设备来将前面已经准备好了的图形缓冲区渲染到帧缓冲区中去,即将图形缓冲区的内容绘制到显示屏中去。相应地,当用户空间中的应用程序不再需要使用一块图形缓冲区的时候,就可以通过gr

5、alloc备来释放它,并且将它从地址空间中解除映射。接下来,我们就按照上述使用情景来分析gralloO备和Gralloc模块的实现。1.Gralloc模块的加载过程。每一个HAL模块都有一个ID值,以这些ID值为参数来调用硬件抽象层提供的函数hw_get_module就可以将指定的模块加载到内存来,并且获得一个hw_module_t接口来打开相应的设备。Gralloc模块的ID值定义在hardWare/libhardWare/include/hardWare/grallo文件中,如下所示:cppvieWplaincopy1#defineGRALLOC_HARDWARE_MODULE_IDgra

6、lloc函数hW_get_module实现在hardWare/libhardWare/hardWare.c件中,如下所示:cppvieWplaincopy/*Basepathofthehalmodules*/#defineHAL_LIBRARY_PATH1/system/lib/hW#defineHAL_LIBRARY_PATH2/vendor/lib/hW56/*Thereareasetofvariantfilenameformodules.Theformofthefilename*is.variant.sosofortheledmoduletheDreamvariants*ofbasedu

7、ct.board,ro.board.platformandro.archWouldbe:*1112131415161718192021222324252627282930313233343536373839404142434445464748495051*led.trout.so*led.msm7k.so*led.ARMV6.so*led.default.so*/staticconstchar*variant_keys=ro.hardware,/*Thisgoesfirstsothatitcanpickupadifferentfileontheemulator.*/duct.board,ro.

8、board.platform,ro.arch;staticconstintHAL_VARIANT_KEYS_COUNT=(sizeof(variant_keys)/sizeof(variant_keys0);inthw_get_module(constchar*id,conststructhw_module_t*module)intstatus;inti;conststructhw_module_t*hmi=NULL;charpropPATH_MAX;charpathPATH_MAX;/*Herewerelyonthefactthatcallingdlopenmultipletimeson*t

9、hesame.sowillsimplyincrementarefcount(andnotload*anewcopyofthelibrary).*Wealsoassumethatdlopen()isthread-safe.*/*Loopthroughtheconfigurationvariantslookingforamodule*/for(i=0;iHAL_VARIANT_KEYS_COUNT+1;i+)if(iid)!=0)LOGE(load:id=%s!=hmi-id=%s,id,hmi-id);status=-EINVAL;gotodone;hmi-dso=handle;/*succes

10、s*/status=0;done:if(status!=0)hmi=NULL;8081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120if(handle!=NULL)在系统中,后缀名为的文件为动态链接库文件,可能通过函数来加载到内存中。硬件抽象层模块编写规范规定每一个硬件抽象层模块都必须导出一个符号名称为的符号,而且这个符号必须是用来描述一个类型为的结构体的。是一个宏,定义在文件文件中,如下所示:将模块加载到内存中来之后,就可以调用函数来获得它所

11、导出的符号。由于这个符号指向的是一个结构体,因此,最后函数就可以强制地将这个符号转换为一个结构体指针,并且保存在输出参数中返回给调用者。调用者获得了这个结构体指针之后,就可以创建一个设备或者一个设备。模块实现在目录中,它导出的符号定义在文件文件中,如下所示:也是一个宏,它的值是与宏对也是一个宏,它的值是与宏对应的,它也是定义在文件文件中,如下所示:符号的类型为。前面提到,符号必须指向一个结构体,但是这里它指向的却是一个结构体,是不是有问题呢?为了弄清楚这个问题,我们首先了解一下结构体的定义,如图所示:结构体,而结构体,这意味着,指向结构体提针来继承结构体结构体。这样,我们就可以把在结构体。它的

12、类型为,它用只定义有一个操作方法,用来结构体定义结构体的第一个成员变量指向一个结构体的第一个成员变量又指向了一个一个结构体的指针同时可以用作一个或者使用。事实上,这是使用语言来实现的一种继承关系,等价于结构体,而结构体继承模块中定义的符号看作是一个结构体有一个重要的成员变量来描述一个模块的操作方法列表。结构体打开一个指定的设备。在模块中,用来打开指定设备的函数被指定为,通过这个函数就可以打开模块中的或者设备,后面我们再详细分析。中,它主要结构体定义在文件是定义了四个用来操作图形缓冲区的成员函数,如下所示:成员函数和分别用来注册和注销一个指定的图形缓冲区,这个指定的图形缓冲区使用一个句柄来描述。

13、所谓注册图形缓冲区,实际上就是将一块图形缓冲区映射到一个进程的地址空间去,而注销图形缓冲区就是执行相反的操作。成员函数和分别用来锁定和解锁一个指定的图形缓冲区,这个指定的图形缓冲区同样是使用一个句柄来描述。在访问一块图形缓冲区的时候,例如,向一块图形缓冲写入内容的时候,需要将该图形缓冲区锁定,用来避免访问冲突。在锁定一块图形缓冲区的时候,可以指定要锁定的图形绘冲区的位置以及大小,这是通过参数、和来指定的,其中,参数和指定的是要访问的图形缓冲区的左上角位置,而参数和指定的是要访问的图形缓冲区的宽度和长度。锁定之后,就可以获得由参数参数、和所圈定的一块缓冲区的起始地址,保存在输出参数中。另一方面,

14、在访问完成一块图形缓冲区之后,需要解除这块图形缓冲区的锁定。在模块中,符号指向的结构体的成员函数、t和分别被指定为函数、和,后面我们再详细分析它们的实现。结构体定义在文件中,它主要是用来描述帧缓冲区的属性,如下所示:成员变量的类型为,它是一个指向系统帧缓冲区的句柄,后面我们再分析结构体的定义。成员变量用来标志系统帧缓冲区是否支持双缓冲。如果支持的话,那么它的位就等于1,否则的话,就等于0。成员变量表示系统帧缓冲区包含有多少个图形缓冲区。一个帧缓冲区包含有多少个图形缓冲区是与它的可视分辨率以及虚拟分辨率的大小有关的。例如,如果一个帧缓冲区的可视分辨率为,而虚拟分辨率为,那么这个帧缓冲区就可以包含

15、有两个图形缓冲区。成员变量用来记录系统帧缓冲区中的图形缓冲区的使用情况。例如,假设系统帧缓冲区有两个图形缓冲区,这时候成员变量就有四种取值,分别是二进制的、和,其中,00分别表示两个图缓冲区都是空闲的,01表示第1个图形缓冲区已经分配出去,而第2个图形缓冲区是空闲的,、0表示第、个图形缓冲区是空闲的,而第2个图形缓冲区已经分配出去,、表示两个图缓冲区都已经分配出去。成员变量、曰疋个互斥锁,用来保护结构体成员变量,的类型为们再分析它的定义。成员变量和成员变量和的类型分别为的并行访问。,用来描述当前正在被渲染的图形缓冲区,后面我目前没有使用。和,它们用来保存设备显示屏的属性信息,其中,成员变量保存

16、的属性信息是可以动态设置的,而成员变量保存的属性信息是只读的。这两个成员变量的值可以通过控制命令和成员变量用来描述显示屏的刷新频率,它的单位的fp即每秒帧数。接下来,我们再分析结构体buff,r和的定义。结构体b定义在文件所示:来从帧缓冲区驱动模块中获得。成员变量和分别用来描述设备显示屏在宽度和高度上的密度,即每英寸有多少个像素点。文件中,如下它是一个类型为定义在系统运行时层的文件的指针,而结构体用来描述一个本地句柄值,它文件中,如下所示:的大小,用来标识结构体所包含的文件描述符以及整数值的个数,成员变量的大小被设置为结构体的版本。成员变量和表示结构体这些文件描述符和整数保存在成员变量所指向的

17、一块缓冲区中。我们一般不直接使用结构体来描述一个本地句柄值,而是通过它的子类来描述一个具体的本地句柄值。接下来我们就通过结构体的定义来说明结构体的用法。结构体用来描述一块图形缓冲区,这块图形缓冲区可能是在帧缓冲区中分配的,也可能是在内存中分配的,视具体情况而定,它定义在文件文件中,如下所示:为了方便描述,我们假设我们是在环境中编译文件,即编译环境定义有宏u这样,结构体就是从结构体继承下来的,它包含有个文件描述符以及6个整数,以及三个静态成员变量。成员变量指向一个文件描述符,这个文件描述符要么指向帧缓冲区设备,要么指向一块匿名共享内存,取决于它的宿主结构体描述的一个图形缓冲区是在帧缓冲区分配的,

18、还是在内存中分配的。成员变量指向一个魔数,它的值由静态成员变量来指定,用来标识一个结构体。成员变量用来描述一个图形缓冲区的标志,它的值要么等于0要么等于M当一个图形缓冲区的标志值等于的时候,就表示它是在帧缓冲区中分配的。成员变量用来描述一个图形缓冲区的大小。成员变量用来描述一个图形缓冲区的偏移地址。例如,当一个图形缓冲区是在一块内存中分块的时候,假设这块内存的地址为,那么这个图形缓冲区的起始地址就为。成员变量用来描述一个图形缓冲区的实际地址,它是通过成员变量来计算得到的。例如,上面计算得到的的值就保存在成员变量中。成员变量用来描述一个图形缓冲区的创建者的。例如,如果一个图形缓冲区是在值为结构体

19、的进程中创建的,那么用来描述这个图形缓冲区的结构体的成员变量的值就等于的静态成员变量前面已经描述过了,另外两个静态成员变量和的值分别等于和6表示结构体包含有个文件描述符和个整数,它们是用来初始化结构体的父类的成员变量和的,如结构体的构造函数所示。从这里就可以看出,结构体的父类的成员变量所指向的缓冲区就是由结构体的成员变量、a、f和所占用的连续内存块来组成的,一共包含有个整数。结构体是否指向了一个还定义了一个静态成员函数结构体。用来验证一个指针至此,模块的加载过程以及相关的数据结构体就介绍到这里,接下来我们分别分析定义在模块中的和设备的打开过程。设备的打开过程在模块中,设备的值定义为R是一个宏,

20、定义在文件中,如下所示:有两个成员函数和中,如下设备使用结构体来描述。结构体,分,别用来分配和释放图形缓冲区。结构体也是定义在文件所示:00模块在在文件用来打开设备,如下所示:中定义了一个帮助函数参数所指向的一个函数如下所示:指向的是一个用来描述模块的结构体,它的成员变量定义在文件文件中,结构体的成员函数指向了模块中的函数进行初始化。设备。前面来实现的。从分别被设置为设备的值定义为是一个宏,定义在文件这个函数主要是用来创建一个结构体,并且对它的成员变量结构体的成员变量的类型为,它用来描述一个提到,设备是用来分配和释放图形缓冲区的,这是通过调用它的成员函数和这里可以看出,函数所打开的设备的成员函

21、数和模块中的函数和,后面我们再详细分析它们的实现。至此,设备的打开过程就分析完成了,接下来我们继续分析设备的打开过程。设备的打开过程在模块中,中,如下所示:设备使用结构体来描述。结构体是用来描述系统帧缓冲区的信息,它定义在文件中,如下所示:成员变量用来记录系统帧缓冲区的标志,目前没有使用这成员变量,它的值被设置为。成员变量和分别用来描述设备显示屏的宽度和高度,它们是以像素为单位的。成员变量用来描述设备显示屏的一行有多少个像素点。成员变量用来描述系统帧缓冲区的像素格式,支持的像素格式主要有和两种。表示一个像素使用位来描述,、和分别占位,另外位未使用。表示一个像素使用位来描述,、和分别占、和位。成

22、员变量和分别用来描述设备显示屏在宽度和高度上的密度,即每英寸有多少个像素点。成员变量用来描述设备显示屏的刷新频率,它的单位是帧每秒。成员变量和用来描述帧缓冲区交换前后两个图形缓冲区的最小和最大时间间隔。成员变量是保留给将来使用的。成员函数用来设置帧缓冲区交换前后两个图形缓冲区的最小和最大时间间隔。成员函数用来设置帧缓冲区的更新区域。成员函数用来将图形缓冲区的内容渲染到帧缓冲区中去,即显示在设备的显示屏中去。成员函数用来通知设备,图形缓冲区的组合工作已经完成,目前status=-EINVAL;没有使用这个成员函数。成员变量是一个函数指针数组,它们是保留给将来使用的。在结构体的一系列成员函数中,是

23、最重要的一个成员函数,用户空间的应用程序通过调用这个成员函数就可以在设备的显示屏中渲染指定的画面,后面我们将详细这个函数的实现。模块在在文件中定义了一个帮助函数,用来打开设备,如下所示:结构体,前面提到,它的成员变量指向了模块中的函数参数指向的是一个用来描述模块的所指向的一个结构体的成员函数e这个函数打开设备的代码段如下所示:参数na的值等于,因此,函数接下来会调用另外一个函数来执行打开设备的操作。函数定义在文件中,如下所示:status=-EINVAL;382int383if38438538638738838939039139239339439539639739839940040140240

24、3404405406407408409#ifdef410411#endif412413414415416417418419420421422(!strcmp(name,GRALLOC_HARDWARE_FB0)alloc_device_t*gralloc_device;status=gralloc_open(module,&gralloc_device);if(statusmon.tag=HARDWARE_DEVICE_TAG;dev-mon.version=0;dev-mon.module=const_cast(module);dev-mon.close=fb_close;dev-devic

25、e.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_pixel3);intformat=(m-info.bits_per_pixel=32)?HAL_PIXEL_FORMAT_RGBX_8888:HAL_PIXEL_F

26、ORMAT_RGB_565;NO_32BPPformat=HAL_PIXEL_FORMAT_RGB_565;const_cast(dev-device.flags)=0;const_cast(dev-device.width)=m-info.xres;const_cast(dev-device.height)=m-info.yres;const_cast(dev-device.stride)=stride;const_cast(dev-device.format)=format;const_cast(dev-device.xdpi)=m-xdpi;const_cast(dev-device.y

27、dpi)=m-ydpi;const_cast(dev-device.fps)=m-fps;const_cast(dev-device.minSwapInterval)=1;=1;const_cast(dev-device.maxSwapInterval)*device=&dev-mon;这个函数主要是用来创建一个结构体,并且对它的成员变量进行初始化。结构体的成员变量的类型为,前面提到,它是用来描述设备的。设备主要是用来渲染图形缓冲区的,这是通过调用它的成员函数来实现的。从这里可以看出,函数所打开的设备的成员函数被设置为模块中的函数,后面我们再详细分析它的实现。函数在打开设备的过程中,会调用另外

28、一个函数来获得系统帧缓冲区的信息,并且将这些信息保存在参数所描述的一个结构体的各个成员变量中。有了系统帧缓冲区的信息之后,函数接下来就可以对前面所打开的一个设备的各个成员变量进行初始化。这些成员变量的含义可以参考前面对结构体的介绍。接下来我们只简单介绍一下结构体的成员变量和的初始化过程。变量的成员变量的类型为,它是在函数中被始化的。是在内核中定义的一个结构体,用来描述设备显示屏的固定属性信息,其中,它的成员变量用来描述显示屏一行像素总共所占用的字节数。TOC o 1-5 h z变量的另外一个成员变量的类型为,它也是在函数中被始化的。也是内核中定义的一个结构体,用来描述可以动态设置的显示屏属性信

29、息,其中,它的成员变量用来描述显示屏每一个像素所占用的位数。这样,我们将的值向右移位,就可以得到显示屏每一个像素所占用的字节数。用显示屏每一个像素所占用的字节数去除显示屏一行像素总共所占用的字节数,就可以得到显示屏一行有多少个像素点。这个值最终就可以保存在前面所打开的设备的成员变量中。当显示屏每一个像素所占用的位数等于的时候,那么前面所打开的设备的像素格式就会被设置为,否则的话,就会被设置为T另一方面,如果在编译的时候定义了宏,即不要使用位来描述一个像素,那么函数就会强制将前面所打开的设备的像素格式设置为函数除了用来获得系统帧缓冲区的信息之外,还会将系统帧缓冲区映射到当前进程的地址空间来。在系

30、统中,模块中的设备是由服务来负责打开和管理的,而服是运行进程中的,因此,系统帧缓冲区实际上是映射到进程的地址空间中的。函数实现在文件,如下所示:这个函数调用了同一个文件中的另外一个函数来初始化参数以及将系统帧缓冲区映射到当前进程的地址空间来。函数的实现比较长,我们分段来阅读:这段代码在首先在系统中检查是否存在设备文件或者/如果存在的话,那么就调用函数来打开它,并且将得到的文件描述符保存在变量中。这样,接下来函数就可以通过文件描述符来与内核中的帧缓冲区驱动程序交互。继续往下看函数:这几行代码分别通过控制命令系统帧缓冲区的信息,分别保存在再往下看函数和结构体和结构体来获得中。系统的开机画面显示过程

31、分析这段代码主要是用来设置设备显示屏的虚拟分辨率。在前面文提到,结构体的成员变量和用来描述显示屏的可视分辨率,而成员变量和用来描述显示屏的虚拟分辨率。这里保持可视分辨率以及虚拟分辨率的宽度值不变,而将虚拟分辨率的高度值设置为可视分辨率的高度值的倍。是一个宏,它的值被定义为2。这样,我们就可以将系统帧缓冲区划分为两个图形缓冲区来使用,即可以通过硬件来实现双缓冲技术。在结构体中,与显示屏的可视分辨率和虚拟分辨率相关的另外两个成员变量是和,它们用来告诉帧缓冲区当前要渲染的图形缓冲区是哪一个,它们的使用方法可以参考前面系统的开机画面显示过程分析一文。这段代码在设置设备显示屏的虚拟分辨率之前,还会检查是

32、否定义了宏B如果定义了的话,那么就说明系统显式地要求将帧缓冲区的像素格式设置为T在这种情况下,这段代码就会通过结构体的成员变量、和来通知帧缓冲区驱动程序使用像素格式来渲染显示屏。这段代码最终是通过控制命令来设置设备显示屏的虚拟分辨率以及像素格式的。如果设置失败,即调用函数的返回值等于,那么很可能是因为系统帧缓冲区在硬件上不支持双缓冲,因此,接下来的代码就会重新将显示屏的虚拟分辨率的高度值设置为可视分辨率的高度值,并且将变量的位置为。另一方面,如果调用函数成功,但是最终获得的显示屏的虚拟分辨率的高度值小于可视分辨率的高度值的2倍,那么也说明系统帧缓冲区在硬件上不支持双缓冲。在这种情况下,接下来的

33、代码也会重新将显示屏的虚拟分辨率的高度值设置为可视分辨率的高度值,并且将变量的位置为。再继续往下看函数:这段代码再次通过控制命令来获得系统帧缓冲区的可变属性信息,并且保存在结构体中,接下来再计算设备显示屏的刷新频率。显示屏的刷新频率与显示屏的扫描时序相关。显示屏的扫描时序可以参考内核源代码目录下的文件。我们结合图来简单说明上述代码是如何计算显示屏的刷新频率的。t|A?XuC_fGUT|fOMGL_iygL3TU+斛卅卅卅卅卅卅卅卅卅卅卅卅卅卅卅+4-1-GUJ9LTU4XLG?+湍卅斛斛斛卅卅卅琳冊冊冊冊冊昭昭昭斛+TInbbGLiugLflTu图2显示屏扫描时序示意图中间由和组成的区域即为显

34、示屏的图形绘制区,在绘制区的上、下、左和右分别有四个边距、和。此外,在显示屏的最右边以及最下边还有一个水平同步区域和一个垂直同步区域c电子枪按照从左到右、从上到下的顺序来显示屏中打点,从而可以将要渲染的图形显示在屏幕中。前面所提到的区域信息分别保存在结构体的成员变量电子枪每在和所组成的区域中打一个点所花费的时间记录在结构体的成员变量,单位为,即秒。电子枪从左到右扫描完成一行之后,都会处理关闭状态,并且会重新折回到左边去。由于电子枪在从右到左折回的过程中不需要打点,因此,这个过程会比从左到右扫描屏幕的过程要快,这个折回的时间大概就等于在和所组成的区域扫描(+个点的时间。这样,我们就可以认为每渲染

35、一行需要的时间为()c同样,电子枪从上到下扫描完成显示屏之后,需要从右下角折回到左上角去,折回的时间大概等于在和所组成的区域中扫描()行所需要的时间。这样,我们就可以认为每渲染一屏图形所需要的时间等于在和所组成的区域中扫描()行所需要的时间。由于在和所组成的区域中扫描一行所需要的时间为()c因此,每渲染一屏图形所需要的总时间就等于(TOC o 1-5 h z)r)每渲染一屏图形需要的总时间经过计算之后,就保存在变量中。注意,变量所描述的时间的单位为秒。这样,将变量的值倒过来,就可以得到设备显示屏的刷新频率。将这个频率值乘以次方之后,就得到一个单位为的刷新频率,保存在变量中。当系统在模拟器运行的

36、时候,保存在结构体的成员变量中的值可能等于。在这种情况下,前面计算得到的变量的值就会等于。在这种情况下,接下来的代码会将变量的值设置为3即将显示屏的刷新频率设置为H再往下看函数:这段代码首先计算显示屏的密度,即每英寸有多少个像素点,分别宽度和高度两个维度,分别保存在变量和中。注意,结构体的成员变量和用来描述显示屏的宽度和高度,它们是以毫米()为单位的。这段代码接着再将前面计算得到的显示屏刷新频率的单位由转换为,即帧每秒,并且保存在变量中。再往下看函数mapFram,Buf:f,rLock,d这段代码再次通过控制命令来获得系统帧缓冲区的固定信息,并且保存在结构体帧缓冲区的其它信息来初始化参数最后

37、,函数mapFra中,接下来再使用结构体以及前面得到的系统所描述的一个结构体。就将系统帧缓冲区映射到当前进程的地址空间来:表达式fi,fo.li*数(虚拟分辨率的咼度值,计算的是整个系统帧缓冲区的大小,它的值等于显示屏行e乘以每一行所占用的字节数()。函数用来将整个系统帧缓冲区的大小对齐到页面边界。对齐后的大小保存在变量中。表达式计算的是整个系统帧缓冲区可以划分为多少个图形缓冲区来使用,这个数值保存在参数所描述的一个结构体的成员变量中。参数所描述的一个结构体的另外一个成员变量的值接着被设置为0表示系统帧缓冲区中的所有图形缓冲区都是处于空闲状态,即它们可以分配出去给应用程序使用。系统帧缓冲区是通

38、过调用函数来映射到当前进程的地址空间来的。映射后得到的地址空间使用一个结构体来描述,这个结构体的成员变量保存的即为系统帧缓冲区在当前进程的地址空间中的起始地址。这样,模块以后就可以从这块地址空间中分配图形缓冲区给当前进程使用。至此,设备的打开过程就分析完成了。在打开设备的过程中,模块还完成了对系统帧缓冲区的初始化工作。接下来我们继续分析模块是如何分配图形缓冲区给用户空间的应用程序使用的。4.分配图形缓冲区前面提到,用户空间的应用程序用到的图形缓冲区是由模块中的函数来分配的,这个函数实现在文件中,如下所示:568569570571572573574575576577578579580581582

39、583584585586587588589590591592593594595596597598599600601602603604605606607608intw,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_

40、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)

41、err=gralloc_alloc_framebuffer(dev,size,usage,pHandle);elseerr=gralloc_alloc_buffer(dev,size,usage,pHandle);if(err0)returnerr;参数用来描述要分配的图形缓冲区的颜色格式。当值等于_或者的时候,一个像素需要使用位来表示,即个字节。当值等于的时候,一个像素需要使用位来描述,即个字节。当值等于T或者的时候,一个像需要使用位来描述,即个字节。最终一个像素需要使用的字节数保存在变量中。参数表示要分配的图形缓冲区所保存的图像的宽度,将它乘以,就可以得到保存一行像素所需要使用的字节数。我

42、们需要将这个字节数对齐到4个字节边界,最后得到一行像素所需要的字节数就保存在变量中。参数表示要分配的图形缓冲区所保存的图像的高度,将它乘以,就可以得到保存整个图像所需要使用的字节数。将变量的值除以变量的值,就得到要分配的图形缓冲区一行包含有多少个像素点,这个结果需要保存在输出参数中,以便可以返回给调用者。参数用来描述要分配的图形缓冲区的用途。如果是用来在系统帧缓冲区中渲染的,即参数的位等于1那么就必须要系统帧缓冲区中分配,否则的话,就在内存中分配。注意,在内存中分配的图形缓冲区,最终是需要拷贝到系统帧缓冲区去的,以便可以将它所描述的图形渲染出来。函数用来在系统帧缓冲区中分配图形缓冲区,而函数用

43、来在内存在分配图形缓冲区,接下来我们就分别分析这两个函数的实现。函数实现在文件中,如下所示:这个函数调用了另外一个函数函数来分配图形缓冲区。也是实现在文件中,如下所示:private_module_t*m=reinterpret_cast(dev-common.module);/allocatetheframebufferif(m-framebuffer=NULL)/initializetheframebuffer,theframebufferismappedonce/err=mapFrameBufferLocked(m);if(errbufferMask;cons

44、tuint32_tnumBuffers=m-numBuffers;constsize_tbufferSize=m-finfo.line_length*m-info.yres;if(numBuffers=1)/Ifwehaveonlyonebuffer,weneveruseflipping.Instead,/wereturnaregularbufferwhichwillbememcpyedtothemain/newUsage=(usage&GRALLOC_USAGE_HW_FB)|GRALLOC_USAGE_HW_2D;returngrallo

45、c_alloc_buffer(dev,bufferSize,newUsage,pHandle);if(bufferMask=(1LUframebuffer-base);private_handle_t*hnd=newprivate_handle_t(dup(m-framebuffer-fd),size,private_handle_t:PRIV_FLAGS_FRAMEBUFFER);/findafreeslotfor(uint32_ti=0;inumBuffers;i+)if(bufferMask&(1LUbufferMask|=(1LUi);break;6266276286296306316

46、32633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666vaddr+=bufferSize;在系统帧缓冲区分配图形缓冲区之前,首先要对系统帧缓冲区进行过初始化,即这里的变量所指向的一个结构体的成员变量的值不能等于。如果等于的话,那么就必须要调用另外一个函数来初始化系统帧缓冲区。初始化系统帧缓冲区的过程可以参考前面第3部分的内容。变量用来描述系统帧缓冲区的使用情况,而变量用来描述系统帧缓冲区可以划分为多少个图形缓冲区来使用,另外一个变量用来描述设

47、备显示屏一屏内容所占用的内存的大小。如果系统帧缓冲区只有一个图形缓冲区大小,即变量的值等于1那么这个图形缓冲区就始终用作系统主图形缓冲区来使用。在这种情况下,我们就不能够在系统帧缓冲区中分配图形缓冲区来给用户空间的应用程序使用,因此,这时候就会转向内存中来分配图形缓冲区,即调用函数来分配图形缓冲区。注意,这时候分配的图形缓冲区的大小为一屏内容的大小,即e如果的值大于等于的值,那么就说明系统帧缓冲区中的图形缓冲区全部都分配出去了,这时候分配图形缓冲区就失败了。例如,假设图形缓冲区的个数为2,那么的值就等于3即二制制。如果这时候的值也等于1那么就表示第一个和第二个图形缓冲区都已经分配出去了。因此,

48、这时候就不能再在系统帧缓冲区中分配图形缓冲区。假设此时系统帧缓冲区中尚有空闲的图形缓冲区的,接下来函数就会创建一个结构体来描述这个即将要分配出去的图形缓冲区。注意,这个图形缓冲区的标志值等于M即表示这是一块在系统帧缓冲区中分配的图形缓冲区。接下来的循环从低位到高位检查变量的值,并且找到第一个值等于的位,这样就可以知道在系统帧缓冲区中,第几个图形缓冲区的是空闲的。注意,变量的值开始的时候指向系统帧缓冲区的基地址,在下面的循环中,每循环一次它的值都会增加e从这里就可以看出,每次从系统帧缓冲区中分配出去的图形缓冲区的大小都是刚好等于显示屏一屏内容大小的。最后分配出去的图形缓冲区的开始地址就保存在前面

49、所创建的结构体的成员变量中,这样,用户空间的应用程序就可以直接将要渲染的图形内容拷贝到这个地址上去,这就相当于是直接将图形渲染到系统帧缓冲区中去。在将结构体返回给调用者之前,还需要设置它的成员变量,以便可以知道它所描述的图形缓冲区的起始地址相对于系统帧缓冲区的基地址的偏移量。至此,在系统帧缓冲区中分配图形缓冲区的过程就分析完成了,接下来我们再分析在内存在分析图形缓冲区的过程,即分析函数的实现。函数也是实现在文件中,如下所示:这个函数的实现很简单,它首先调用函数来创建一块匿名共享内存,接着再在这块匿名共享内存上分配一个图形缓冲区。注意,这个图形缓冲区也是使用一个结构体来描述的,不过这个图形缓冲区

50、的标志值等于0,以区别于在系统帧缓冲区中分配的图形缓冲区。匿名共享内存的相关知识,可以参考前面系统匿名共享内存()简要介绍和学习计划一文,以及系统匿名共享内存()调用接口分析这篇文章。从匿名共享内存中分配的图形缓冲区还需要映射到进程的地址空间来,然后才可以使用,这是通过调用函数来实现的。函数实现在文件中,如下所示:它通过调用另外一个函数来将参数所描述的一个图形缓冲区映射到当前进程的地址空间来。后面在分析图形缓冲区的注册过程时,我们再分析函数的实现。注意,在系统中,在系统帧缓冲区中分配的图形缓冲区是在服务中使用的,而在内存中分配的图形缓冲区既可以在服务中使用,也可以在其它的应用程序中使用。当其它

51、的应用程序需要使用图形缓冲区的时候,它们就会请求服务为它们分配,因此,对于其它的应用程序来说,它们只需要将服务返回来的图形缓冲区映射到自己的进程地址空间来使用就可以了,这就是后面我们所要分析的图形缓冲区的注册过程。至此,图形缓冲区的分配过程就分析完成了,接下来我们继续分析图形缓冲区的释放过程。图形缓冲区的释放过程前面提到,用户空间的应用程序用到的图形缓冲区是由模块中的函数来释放的,这个函数实现在文件中,如下所示:要释放的图形缓冲区使用参数来描述。前面提到,从模块中分配的图形缓冲区是使用结构体来描述的,因此,这里的参数应该指向一个结构体,这是通过调用类的静态成员函数来验证的。类的静态成员函数的实

52、现可以参考前面第部分的内容。要释放的图形缓冲区有可能是在系统帧缓冲区分配的,也有可能是在内存中分配的,这可以通过检查它的标志值的位是否等于来确认。如果要释放的图形缓冲区是在系统帧缓冲区中分配的,那么首先要知道这个图形缓冲区是系统帧缓冲区的第个位置,接着再将变量所描述的一个结构体的成员变量的第位重置为即可。我们只需要将要释放的图形缓冲区的开始地址减去系统帧缓冲区的基地址,再除以一个图形缓冲区的大小,就可以知道要释放的图形缓冲区是系统帧缓冲区的第几个位置。这个过程刚好是在系统帧缓冲区中分配图形缓冲区的逆操作。如果要释放的图形缓冲区是内存中分配的,那么只需要调用另外一个函数来解除要释放的图形缓冲区在

53、当前进程的地址空间中的映射。最后,这个函数还会将用来描述要释放的图形缓冲区的结构体所占用的内存释放掉,并且将要要释放的图形缓冲区所在的系统帧缓冲区或者匿名共享内存的文件描述符关闭掉。函数实现在文件中,如下所示:它通过调用另外一个函数来解除参数所描述的一个图形缓冲区在当前进程的地址空间中的映射。后面在分析图形缓冲区的注销过程时,我们再详细分析函数的实现。至此,图形缓冲区的释放过程就分析完成了,接下来我们继续分析图形缓冲区的注册过程。图形缓冲区的注册过程前面提到,在系统中,所有的图形缓冲区都是由服务分配的,而当一个图形缓冲区被分配的时候,它会同时被映射到请求分配的进程的地址空间去,即分配的过程同时

54、也包含了注册的过程。但是对用户空间的其它的应用程序来说,它们所需要的图形缓冲区是在由服务分配的,因此,当它们得到服务分配的图形缓冲区之后,还需要将这块图形缓冲区映射到自己的地址空间来,以便可以使用这块图形缓冲区。这个映射的过程即为我们接下来要分析的图形缓冲区注册过程。前面还提到,注册图形缓冲区的操作是由模块中的函数来实现的,这个函数实现在文件中,如下所示:这个函数首先验证参数指向的一块图形缓冲区的确是由模块分配的,方法是调用类的静态成员函数来验证,即如果参数指向的是一个结构体,那么它所指向的一块图形缓冲区就是由模块分配的。通过了上面的检查之后,函数还需要检查当前进程是否就是请求模块分配图形缓冲

55、区的进程。如果是的话,那么当前进程在请求模块分配图形缓冲区的时候,就已经将图形缓冲区映射进自己的地址空间来了,因此,这时候就不需要重复在当前进程中注册这个图形缓冲区。真正执行注册图形缓冲区的操作是由函数来实现的,这个函数也是实现文件中,如下所示:由于在系统帧缓冲区中分配的图形缓冲区只在服务中使用,而服务在初始化系统帧缓冲区的时候,已经将系统帧缓冲区映射到自己所在的进程中来了,因此,函数如果发现要注册的图形缓冲区是在系统帧缓冲区分配的时候,那么就不需要再执行映射图形缓冲区的操作了。如果要注册的图形缓冲区是在内存中分配的,即它的标志值的位等于1那么接下来就需要将它映射到当前进程的地址空间来了。由于

56、要注册的图形缓冲区是在文件描述符所描述的一块匿名共享内存中分配的,因此,我们只需要将文件描述符所描述的一块匿名共享内存映射到当前进程的地址空间来,就可以将参数所描述的一个图形缓冲区映射到当前进程的地址空间来。由于映射文件描述符得到的是一整块匿名共享内存在当前进程地址空间的基地址,而要注册的图形缓冲区可能只占据这块匿名共享内存的某一小部分,因此,我们还需要将要注册的图形缓冲区的在被映射的匿名共享内存中的偏移量加上被映射的匿名共享内存的基地址,才可以得到要注册的图形缓冲区在当前进程中的访问地址,这个地址最终又被写入到中去。注册图形缓冲区的过程就是这么简单,接下来我们再分析图形缓冲区的注销过程。图形

57、缓冲区的注销过程图形缓冲区使用完成之后,就需要从当前进程中注销。前面提到,注销图形缓冲区是由模块中的函数来实现的,这个函数实现在文件中,如下所示:这个函数同样是首先调用类的静态成员函数来验证参数指向的一块图形缓冲区的确是由模块分配的,接着再将将参数指向的一块图形缓冲区转换为一个结构体来访问。一块图形缓冲区只有被注册过,即被模块中的函数注册过,才需要注销,而由函数注册的图形缓冲区都不是由当前进程分配的,因此,当前进程在注销一个图形缓冲区的时候,会检查要注销的图形缓冲区是否是由自己分配的。如果是由自己分配的话,那么它什么也不做就返回了。假设要注销的图形缓冲区不是由当前进程分配的,那么接下来就会调用

58、另外一个函数来注销图形缓冲区。函数也是实现在文件中,如下所示:这个函数的实现与前面所分析的函数的实现是类似的,只不过它执行的是相反的操作,即将解除一个指定的图形缓冲区在当前进程的地址空间中的映射,从而完成对这个图形缓冲区的注销工作。这样,图形缓冲区的注销过程就分析完成了,接下来我们再继续分析一个图形缓冲区是如何被渲染到系统帧缓冲区去的,即它的内容是如何绘制在设备显示屏中的。图形缓冲区的渲染过程用户空间的应用程序将画面内容写入到图形缓冲区中去之后,还需要将图形缓冲区渲染到系统帧缓冲区中去,这样才可以把画面绘制到设备显示屏中去。前面提到,渲染图形缓冲区是由模块中的函数来实现的,这个函数实现在文件中

59、,如下所示:参数用来描述要渲染的图形缓冲区,它指向的必须要是一个结构体,这是通过调用类的静态成员函数来验证的。验证通过之后,就可以将参数所描述的一个结构体转换成一个结构体。参数用来描述在模块中的一个设备。从前面第部分的内容可以知道,在打开设备的时候,模块返回给调用者的实际上是一个结构体,因此,这里就可以将参数所描述的一个结构体转换成一个结构体t参数的成员变量指向了一个结构体,这个结构体的成员变量指向了一个模块。从前面第部分的内容可以知道,一个模块是使用一个结构体来描述的,因此,我们可以将转换成一个结构体。由于结构体所描述的图形缓冲区可能是在系统帧缓冲区分配的,也有可能是内存中分配的,因此,我们分两种情况来讨论图形缓冲区渲染过程。当结构体所描述的图形缓冲区是在系统帧缓冲区中分配的时候,即这个图形缓冲区的标志值的位等于的时候,我们是不需要将图形缓冲区的内容拷贝到系统帧缓冲区去的,因为我们将内容写入到图形缓冲区的时候,已经相当于是将

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论