王 凯,宁 钰,周 威
(江南计算技术研究所第三处,江苏 无锡 214083)
嵌入式系统是一种“完全嵌入受控器件内部,为特定应用而设计的专用计算机系统”,在电子产品集成度越来越高的今天,广泛应用于工业生产和生活的各个方面。全球生产的CPU(中央处理器),超过80%应用于各类嵌入式系统。随着嵌入式系统的日益发展,嵌入式处理器运算能力的不断增强,越来越多的嵌入式设备开始采用较为复杂的GUI系统,嵌入式设备的GUI系统发展得十分迅速[1]。传统的嵌入式GUI系统,如Microwindows等,由于项目规模较小,功能较薄弱,缺乏第三方支持等诸多原因,已经越来越不能满足用户的需要。
Qt/Embedded是著名的Qt库开发商Trolltech公司开发的面向嵌入式系统的Qt版本。Qt/Embedded与X11版本的Qt接口在最大程度上兼容,大部分基于Qt的X-Window程序移植到Qt/Embedded是非常方便的。Qt/Embedded延续了在X-Window上的强大功能[2-3]。Qt/Embedded类库完全采用C++封装,丰富的控件资源和较好的可移植性是Qt/Embedded最为优秀的特点。Qt是完全面向对象的,易扩展,并且允许真正的组件编程。使用X-Window下的开发工具Qt Creator可以直接开发基于Qt/Embedded的UI(用户操作接口)界面。采用Qt/Embedded开发嵌入式Linux下的应用软件的第三方公司也日益增加。
文中分析了Qt/Embedded的体系结构和底层支持的方式,阐述了Qt/Embedded图形事件服务的源码和架构原理,通过结合帧缓冲驱动体系与Qt/Embedded图形引擎,对在嵌入式系统下如何实现图形硬件加速进行了详细的研究和实现。
Qt/Embedded库是Qt图形界面库的嵌入式版本,为了适应嵌入式操作系统环境,做出了许多改动和调整。它放弃了原本使用的X11也就是X Window体系去构建GUI环境,因此也就不需要使用较大的Xlib库,所以内存占用十分小,能够适应嵌入式开发的需求。在放弃了X Window体系之后,Qt/Embedded库底层使用帧缓冲体系作为底层图形接口,并使用输入事件作为具体的输入设备的抽象[4],比如keyboard和mouse等。在上层,Qt/Embedded库继续使用原本的Qt架构,从而保证用户使用的便利性和一致性,并能够与X Window系统下的程序兼容,使得用户可以方便地进行程序移植。
Qt/Embedded图形引擎的基础是图形帧缓冲体系(framebuffer),它是一种采用mmap系统调用的驱动接口,正是依靠帧缓冲驱动,屏幕才能显示内容,帧缓冲驱动为上层QWS Server提供图形支持,同时也为下层操作系统提供对图形事件访问服务的接口[5]。
Qt/Embedded的GUI系统采用了C/S结构,每个独立的Qt/Embedded程序都允许作为系统中唯一的一个GUI Server存在。在应用程序首次以系统GUI Server的方式加载时,将创建QWS Server实体。QWS Server创建底层显示设备抽象基类QScreen,其中声明了对于显示设备的基本描述和操作方式,如打开、关闭、获得显示能力、创建GFX操作对象等等[6]。同时,QWS Server调用QWSServer::openDisplay()函数创建窗体,在窗体创建过程中调用QWSServer::Data::Init()进行初始化工作,之后创建Qt/Embedded的图形引擎中帧缓冲驱动类QLinuxFbScreen的实体对象,在QLinuxFbScreen对象中获取设置QScreen关键指针qt_screen的信息并调用connect()打开帧缓冲体系中的抽象显示设备(dev/fb0)。在QWSServer中所有对显示设备的调用都由qt_screen发起[7]。
至此,完成了Qt/Embedded中图形引擎的创建,并通过Qt/Embedded的图形引擎中的帧缓冲驱动类与系统帧缓冲驱动直接相关联,由系统帧缓冲驱动提供进一步的支持。
帧缓冲(framebuffer)是Linux系统的显示设备驱动接口,Linux系统通过它屏蔽不同图形硬件的底层差异性,对显示缓冲区进行抽象,实现上层应用在图形模式下直接对显示缓冲区进行I/O操作[8]。用户不需要关心物理显示缓冲区的具体位置及数据存放方式,这些都由帧缓冲设备的驱动本身来完成。
在显示过程中,用户进程的显示数据将会存放在帧缓冲映射的内存中,然后显示设备会从帧缓冲映射的内存中将数据取出,然后显示到屏幕上。通过系统调用mmap将显示设备物理内存抽象为帧缓冲区,使得用户可以直接访问。对用户来说,它即是物理显存的一个映像,帧缓冲系统将其映射到用户虚拟进程地址空间[9]。对于用户而言,帧缓冲区可以直接读、写、映射。这样用户程序就可以对这块内存区域填充任何已经定义的像素及颜色,而屏幕也就可以在帧缓冲系统的控制下显示画面[10]。
对于Linux系统,帧缓冲设备是标准的字符设备,采用“文件层—驱动层”的接口方式,主设备号为29,从设备号为0~31[11]。对应的设备文件为/dev/fb*。在linux中,fb设备驱动的源码主要在Fb.h(linuxincludelinux)和Fbmem.c(linuxdriversvideo)两个文件中,它们是fb设备驱动的中间层,为上层提供系统调用,为底层驱动提供接口。
由于Linux将设备也认为是一个文件,Framebuffer驱动核心Fbmem.c文件中定义了帧缓冲设备的文件层接口,该接口即为file_operations结构体,它是提供给用户空间应用程序使用的。在file_operations中,统一实现了字符设备文件层次上的文件操作接口函数,用户空间的应用程序通过它的接口函数对帧缓冲设备进行访问[12]。在驱动核心层上方的是Framebuffer特定帧缓冲设备层,在这一层中实现具体的Framebuffer驱动接口,例如特定帧缓冲设备fb_info结构体的注册函数,fb_ops结构体成员函数以及使用完毕后的结构体注销函数[13]。fb_ops结构体是特定帧缓冲设备层的核心模块,它的成员函数实现了对应的帧缓冲驱动功能,最终直接控制显示设备的硬件寄存器[14-15]。
通过对Qt/Embedded底层支持和帧缓冲驱动体系结构的分析,提出一种基于Qt/Embedded环境的嵌入式系统GUI图形加速支持体系结构,如图1所示。
图1 基于Qt/Embedded GUI环境的嵌入式系统
图1描述了实现嵌入式系统图形加速方法的体系结构,从上至下可以分为三部分:
(1)Qt/Embedded GUI支持及实现部分:该部分包含图中最上方1,2,3层,在该部分中,通过Qt/Embedded嵌入式图形支持中的QLinuxFbScreen对象,实现了Qt/Embedded库和帧缓冲体系结构内接口的对接,Qt/Embedded库使用Linux操作系统提供的open,write,read,close等文件系统接口函数,基于帧缓冲驱动体系对帧缓冲进行操作。
(2)Framebuffer驱动核心部分:该部分为图中第4,5层,实现了VFS虚拟文件系统与帧缓冲驱动体系间的对接,在这一层实现file_operations结构体成员函数的内部功能,Qt/Embedded应用程序通过访问file_operations结构体统一实现的文件操作函数对帧缓冲设备进行读写等操作,上层使用的文件系统接口在这里得到具体实现。
(3)Framebuffer帧缓冲设备支持部分:该部分为图中第6层,定义了特定帧缓冲设备fb_info结构体,它是特定帧缓冲物理设备的软件实体和驱动层接口的核心数据结构,记录了关于帧缓冲设备属性、参数以及操作函数指针的完整信息。系统中每一个帧缓冲设备对应一个fb_info结构体。在对该部分的具体实现过程中,通过使用硬件GPU的图形加速功能,优化fb_info结构体中对应软件功能接口函数,从而完成Qt/Embedded GUI环境下的图形硬件加速。
在嵌入式Linux的GUI图形硬件加速架构下,framebuffer框架最重要的实体就是fb_info,其中涉及驱动的操作接口fb_ops,这些操作接口是framebuffer框架实现交互的桥梁,也是实现图形硬件的途径和接口。
fb_ops以结构体形式展现,内部接口函数数量繁多,如fb_check_var()检查可修改的屏幕参数并调整到硬件所支持的值,fb_setcolreg()设置颜色寄存器来实现伪颜色表和颜色表的填充等等。fb_ops结构体的成员函数最终与显示适配器和LCD控制器硬件打交道,需要根据显示适配器的硬件设置及LCD显示屏的硬件参数进行编写。
fb_ops结构体图形硬件加速成员函数接口:fb_fillrect()实现屏幕上矩形区域的颜色填充与绘制,fb_copyarea()实现对屏幕区域显示数据的复制与重新绘制,fb_imageblit()实现位图数据的读取与绘制。这3个接口函数是实现GUI图形硬件加速功能的关键函数,它们的函数声明代码如下:
struct fb_ops {
……
/*绘制矩形*/
void(*fb_fillrect) (struct fb_info *info, const struct fb_fillrect *rect);
/*从一个区域复制数据到另一个区域*/
void(*fb_copyarea) (struct fb_info *info, const struct fb_copyarea *region);
/*绘制一幅位图到显示设备 */
void(*fb_imageblit) (struct fb_info *info, const struct fb_image *image);
……
}
通过上述fb_ops图形硬件加速成员函数接口,在函数体中可以使用硬件图形绘制命令和算法,从而释放软件绘制图形占用的CPU时间,实现图形绘制的硬件加速。
Qt/Embedded GUI应用程序通过fbmem.c文件的file_operations结构体统一实现文件操作接口对帧缓冲硬件设备的访问,特定帧缓冲硬件设备的软件抽象与fb_info结构体一一对应,fb_info结构体的注册、注销及其中成员函数的实现工作由内核中对应的xxxfb.c文件来完成。指定的fb_ops结构体成员函数作为驱动层的核心模块实现对应的图形硬件加速操作,最终通过图形硬件控制及加速算法实现图形绘制工作硬件化。
通过前面建立起的嵌入式Linux下的图形硬件加速架构和硬件加速系统的设计,在实现过程中可以充分利用图形硬件加速的优势:2D图形硬件加速没有大量的循环操作占用CPU时间,在硬件加速过程中,只有两个基本的CPU操作—等待和赋值,极大地节约了CPU时间,从而大大地提高了图形操作的效率。其中具体的绘图工作通过DMA(直接内存存取)由GPU(图形处理单元)完成,在使用GPU的过程中,依赖GPU提供的各种功能寄存器对GPU的功能进行控制。等待操作和DMA操作的具体流程如图2所示。
图2 等待操作和DMA操作的流程
图2中一共有两组不同的绘图操作,每组操作内部都是等待DMA命令状态和绘图操作执行交替执行。在DMA等待过程中,释放CPU时间片,CPU处于空闲状态。控制DMA等待和开始执行的寄存器为DMA控制命令寄存器DMA_COMMAND。
在具体的实现过程中,2D图形硬件加速包含如下内容:矩形填充、位块传输BitBLT、图像旋转和地址定位、图像剪切、光栅操作、矩形区域复制等。
现选取较为有代表性的矩形填充、位块传输BitBLT、矩形区域复制三个操作进行具体实现。
3.3.1 矩形填充
矩形填充,指的是对一个矩形区域进行像素的填充。矩形填充功能的实现,依赖于GPU提供的Fast Solid Color Fill(快速固色填充)功能,通过前文提到的fb_fillrect()接口实现。在fb_fillrect()接口的输入参数结构体fb_info中,获得了目标图像的基地址、目标图像颜色以及图像颜色模式和通道信息。在输入参数结构体fb_fillrect中,获得了目标图像的左上角坐标和右下角坐标,确定了图像的位置,在确定目标图像信息后,将这些信息输入到快速固色填充功能寄存器中,启动GPU进行图像的绘制。矩形填充的逻辑流程见图3。
在矩形填充开始前,需等待上一个DMA操作结束,通过访问前文提到的DMA控制命令寄存器DMA_COMMAND来获取当前DMA状态,在DMA可用之后,从结构体fb_info中读出目标图像基地址,将它写入地址寄存器DST_BASE_ADDR_REG,然后从fb_info中读取出目标图像颜色相关信息,从结构体fb_fillrect中读出图像最终显示位置坐标,将它们分别写入每个对应的功能寄存器,最后通过使能Fast Solid Color Fill命令寄存器BITBLT_COMMAND_REG启动GPU的快速固色填充功能,完成图像矩形填充的绘制工作。
图3 矩形填充的逻辑流程
3.3.2 位块传输BitBLT
位块传输BitBLT,指的是将源设备环境区域中的像素进行位块(bit_block)转换,以传送到目标设备环境。位块传输功能依赖于GPU中的BitBLT功能,在前文提到的接口fb_imageblit()中实现。在fb_imageblit()的参数结构体fb_info中,获取目标图像的基地址和左上角和右下角坐标,目标图像的前景色和背景色,目标图像的stride值等信息,在参数结构体fb_image中,获得了源图像的左上角和右下角坐标,源图像的数据指针、颜色模式、通道模式等信息。在获得所有相关信息后,使能位块传输的命令寄存器启动GPU的位块传输功能。位块传输BitBLT的逻辑流程见图4。
图4 位块传输BitBLT的逻辑流程
在实现位块传输的过程中,需要注意的是GPU硬件中的BitBLT操作分为On-Screen Rendering(在屏渲染)和Off-Screen Rendering(离屏渲染)。on-screen BitBLT操作在屏幕上拷贝一个矩形像素块到屏幕上的另一个位置,而off-screen BitBLT操作是将像素数据从off-screen内存放到屏幕内存帧缓冲区,当SRC_COLOR_MODE_REG值和DST_COLOR_MODE_REG不相同时,该操作会自动进行颜色模式转换。在这里实现的是off-screen位块传输BitBLT操作。
3.3.3 矩形区域复制
矩形区域复制,指的是选择一个矩形区域的图像并把它复制到另一个矩形区域显示。实现该功能由GPU提供的Clipping功能以及GPU的BitBLT功能中的On-Screen Rendering共同完成。GPU的Clipping功能提供一个clipping window,使用这个clipping window确定裁剪的图像大小及位置,clipping window的大小必须小于屏幕大小。在前文的fb_copyarea()接口中,通过fb_copyarea结构体获得需要裁剪的区域位置和大小,并将位置和大小参数传到Clipping功能寄存器,然后通过on-screen BitBLT操作将裁剪的图像绘制到屏幕。绘制的目标图像的信息从参数结构体fb_info中获取。矩形区域复制的逻辑流程基本与图4类似,在矩形区域复制的过程中,无需进行图4中的源图像寄存器相关操作。
在矩形区域复制的过程中,源图像的基本信息大部分都由GPU硬件Clipping操作获得,在实现过程中有3点需要注意:源基地址和目标基地址一致;源stride值和目标stride值一致;源颜色模式和目标颜色模式一致。
反映在寄存器对应关系上即为:
SRC_BASE_ADDR_REG = DST_BASE_ADDR_REG
SRC_STRIDE_REG = DST_STRIDE_REG
SRC_COLOR_MODE_REG = DST_COLOR_MODE_
REG
这也是矩形区域赋值的前提要求。
文中使用的测试平台是讯为科技的iTop-4412开发板,iTop-4412开发板精英版搭载三星Exynos 4412处理器,配备1 GB DDR3内存,4 GB固态硬盘EMMC存储。
三星Exynos 4412处理器搭载4核心Cortex-A9,每个核心的主频大小为1.6 GHz,内部图形核心为Mail400MP。
测试平台的软件环境为嵌入式Linux+Qt操作系统,Linux内核版本为3.0.15,Qt/Embedded库版本为4.7.1,使用u-boot-1.3.4引导启动软件环境,交叉编译器为arm-2009q3。
测试以最典型的矩形填充功能为例,编写基于Qt/Embedded库的Qt GUI测试例程。在该例程中,使用Qt/Embedded库自带的矩形绘制功能drawRcet函数进行矩形填充,通过内部计时器计算出Qt/Embedded库的绘图时间,同时在该例程中通过ioctl接口调用前面实现的底层硬件加速功能进行矩形填充,按同样方式计算出绘图时间,并将该时间与Qt/Embedded库的绘制时间进行对比,从而得出测试结果。
测试1,绘制宽度﹡高度为40﹡30像素的矩形,重复次数为10 000,20 000,30 000,40 000,50 000,结果见图5。
图5 不同绘制次数下的加速效果对比
从图中可以看出,随着绘制次数的增加,Qt绘制矩形的时间基本呈线性增长,在使用硬件加速功能后,绘制同样次数矩形的时间大大缩短;同样,随着绘制次数的增加,加速后的绘制时间也单调递增,但增加的幅度略有波动。对图中数据进行计算后可知,加速比(加速前时间/加速后时间)基本在2.3~2.7之间,实现了图形绘制硬件加速的效果。
测试2,绘制50 000次不同像素的矩形,像素宽度*高度分别为40*30,60*45,80*60,100*75,120*90,结果见图6。
图6 绘制不同像素矩形的加速效果对比
从图中可知,随着绘制矩形像素数目的增加,无论是Qt绘制,还是硬件加速后绘制,绘制所需的时间都基本是线性增加的,使用Qt绘制时间的增加基本与像素数目的增加成正比,增量较为平滑,使用硬件加速后的绘制时间的增量略有波动。
综合图5和图6的数据可知,硬件加速后,在矩形填充这个基本图形操作上,硬件加速获得了较好的效果,加速比基本维持在2以上,实现了预期的目的。
通过对Qt/Embedded体系结构和Qt/Embedded图形引擎架构的分析,利用嵌入式Linux下的帧缓冲体系结构,提出一种Qt/Embedded GUI环境下图形硬件加速实现方法和图形加速实现架构。实现了帧缓冲体系对Qt/Embedded GUI体系的底层硬件支持,依赖于Linux帧缓冲体系的加速接口,使用硬件GPU内部加速功能对Qt/Embedded GUI环境下的基本图形绘制进行了硬件加速,取得了较好的加速效果。
参考文献:
[1] 蒋 飞.基于嵌入式Linux系统的数字电视GUI图形加速设计[D].北京:北京邮电大学,2010.
[2] 李升亮,徐剑锋,李峻林.嵌入式系统中的多窗口GUI系统的研究[J].计算机与数字工程,2008,36(10):126-128.
[3] 周 鸿.基于嵌入式系统的智能缝制设备研究[D].西安:西安电子科技大学,2010.
[4] 李军民,祝红军.基于ARMlinux平台的QT/E键盘实现[J].微计算机信息,2008,24(26):27-29.
[5] 周 开,倪 伟.基于Qt/E的嵌入式Linux GUI研究与实现[J].淮阴工学院学报,2015,24(3):10-13.
[6] 严吉国.基于嵌入式Linux的200MHz数字存储示波器的设计与实现[D].南京:东南大学,2009.
[7] CHEN F,FAN X.Embedded system’s performance analysis with RTC and QT[C]//Proceedings of the 7th international conference on advanced parallel processing technologies.Berlin:Springer-Verlag,2007:569-579.
[8] 郭小梅.Linux下的帧缓冲设备驱动研究与应用[J].工业控制计算机,2012,25(6):3-4.
[9] YANG L, SANDER P V, LAWRENCE J. Geometry-aware framebuffer level of detail[J].Computer Graphics Forum,2008,27(4):1183-1188.
[10] MAO C,JOHNSON K M.Fast-switching liquid-crystal-on-silicon microdisplay with framebuffer pixels and surface-mode optically compensated birefringence[J].Optical Engineering,2006,45(12):1269-1278.
[11] 赵 洁,龚 威.嵌入式Linux帧缓冲设备驱动程序[J].计算机系统应用,2010,19(12):208-211.
[12] CHANG C Y,HUANG C H,CHU Y S.Efficient memory access methods for framebuffer-less video processing applications[C]//IEEE international symposium on circuits and systems.[s.l.]:IEEE,2013:3026-3029.
[13] 宋方伟,刘 勇,聂诗良,等.SM502移动多媒体协处理器在嵌入式系统中的应用[J].兵工自动化,2010,29(2):91-92.
[14] 苏哲欣,刘鸿飞,薛 晓.基于嵌入式Linux的LCD驱动分析与实现[J].工业控制计算机,2009,22(2):29-30.
[15] 黄相平,余水宝,夏 灿.基于S3C6410平台的嵌入式Linux系统LCD驱动模块[J].微型机与应用,2013,32(13):9-12.