DirectFB (Direct Frame Buffer)是一组建立在Linux framebuffer 设备(fbdev)抽象层之上实现的图形api。DirectFB运行在Frame Buffer Device (/dev/fb)之上,并利用芯片组驱动程序的硬件加速,实现对图形图像的高效处理。
它的架构如下图所示:
注意图中的蓝色箭头,它是实现硬件加速的关键,我们以代码为例,分析一下DirectFB硬件加速的实现原理。
获取代码:
DirectFB的源很多,随便在gitee上找到一个仓库下载,看名字是在新唐的SOC上的directfb加速实现。
git clone https://gitee.com/ufbycd/NUC970_DirectFB.git
代码目录结构:
核心实现代码:
数据源模块实现:
interface目录中实现的模块,可以作为数据源,输出图像供DIRECTFB贴图实现,以./interfaces/IDirectFBVideoProvider/idirectfbvideoprovider_v4l.c为例,它可以直接从v4l设备读取yuv数据,软件贴图显示视频,idirectfbvideoprovider_gif.c 则同样道理,只不过提供的是GIF数据源。
V4L2的核心是QBUF和DQBUF两个调用,我们可以根据这两个调用抓到脉络。
帧缓冲区实现:
核心实现代码在src目录下,主要是其中的core目录。
system目录则是不同的不同的framebuffer 驱动实现,可以看到这里有最常见的fbdev,除了fbdev,framebuffer还可以是android的帧缓冲机制,它是基于egl 实现,还可以是x11/sdl等其它的帧缓冲区实现。
以fbdev为例,我们可以看它的关键实现是:
android 基于egl 实现:
Framebuffer的PAN DISPLAY,当平移显示框时,要设置var,以便和内核就使用哪个显示区域达成一致。
我们知道用户空间的显示管理程序(比如xserver)都是直接映射fb设备的显存进入用户空间,然后直接操作这块内存。但是有的fb设备实现了双缓冲,那显示管理程序怎么在这两块缓存间切换呢?就是用这个FBIOPAN_DISPLAY操作。
FBIOPAN_DISPLAY在linux的注释里是“平移显示”的意思。怎么理解呢?就是按照y坐标平移显示缓存中的内容。调用FBIOPAN_DISPLAY时,会传一个y坐标偏移量yoffset给驱动,然后驱动会把当前显存的指针偏移 “yoffset X 屏幕宽度 X 位色字节数” 个字节,这样就好像实现了图像的y坐标平移,也就是“平移显示”。当这个yoffset等于屏幕高度的时候,就实现了显存的切换。
加速器实现:
加速器的实现在另外一个目录,这个目录下有各个支持G2D加速的厂家的针对自己加速器的porting.比如allwinner也可以自己写一个针对自家G2D的加速实现代码,放到这里。
我们看一下核心实现机制:
以AW为例,google到一套开源社区的实现,它是基于vmware的实现改的,地址在这里
lichee_buildroot/directfb-1.4.11-vmware.patch at master · allwinner-ics/lichee_buildroot · GitHub
可以看到,它的实现中调用了AW G2D接口API。
回到代码,我们顺着这套思路继续找,首先找到新塘的FillRectangle,可见,AW的叫G2D,而新唐的叫GE2D,从功能上来讲,其实指的都是同一个东西。
可以看到,对于对接一个新的G2D加速设备,需要实现的接口有:Blit, StrechBlit, DrawLine, DrawRectangle, FillRectangle, SetState, CheckState,EmitCommands, EngineReset, EngineSync几个接口。更具体的要看typedef struct _GraphicsDeviceFuncs 函数表结构体中的函数有哪些。
Nvidia的实现更加复杂
接下来我们看driver_init_driver是如何被调用的,流程比较简单,就不过多介绍了。
所以,基本上DirectFB的工作原理可以摸清楚了。
DirectFB 和framebuffer之间的关系:
Framebuffer的总体架构如下图所示:
在V833 Tina SDK上,framebuffer的实现基于Display Engine的channel 2 的第 0 个图层.
在framebuffer架构的实现中,struct fb_info *info中的成员info->screen_base起者至关重要的作用,它即是framebuffer,也是DE中对应的图层,所对应的真实的内核虚拟地址,显示的最终目的,就是将这片的buffer在DE中生效.
info->screen_base的分配:
fb_read的实现,直接读取info->screen_base的内容:
fb_write的实现,直接写到screen_base中.
用户态怎么写这个图层呢?当然是通过mmap来实现:
可以看到,fb_mmap的本质仍然是将info->screen_base的地址映射到用户空间去.
所以,可以看到,通过内核,DE硬件以及用户态指针都可以访问到fb这个图层,framebuffer的工作原理就是通过修改完图层之后,在刷新率中断作用下,将framebuffer中的内容显示出来。