1. 概述
fbdev用于为显示图形硬件提供一层软件抽象.它代理了显示图形硬件的帧内存,并且提供了一些良好定义的接口让应用软件去访问图形硬件,而不用去关心底层图形硬件的具体控制细节。
访问fbdev通常通过一些特定的设备节点,例如位于/dev目录下的 /dev/fb*.
一个简单的framebuffer 使用场景:
3个application 通过fb0这个fb device 节点对framebuffer进行修改,然后由图形硬件将修改之后的内容输出到显示端
2. User’s View of /dev/fb*
fbdev 目前是一个字符设备,首设备号29,尾设备号标志frame buffer的编号
方便起见, 设备节点会如下生成(数字表示问设备号):
0 = /dev/fb0 First frame buffer
1 = /dev/fb1 Second frame buffer
…
31 = /dev/fb31 32nd frame buffer
fbdev同时也是一个普通的内存设备。这就意味着,你可以读写它的内容。比如:
1.你可以通过如下操作截屏:
cp /dev/fb0 myfile
2.往屏幕的左上角画一个白色的像素点:
echo -en '\xFF\xFF\xFF\x00' > /dev/fb0
3.mmap /dev/fb0 用于更精细的绘图(这部分会在其它章节详述):
int fb = open("/dev/fb0", O_RDWR);
assert(fb > 0);
struct fb_var_screeninfo info;
assert(0 == ioctl(fb, FBIOGET_VSCREENINFO, &info));
size_t len = 4 * info.xres * info.yres;
uint32_t *buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fb, 0);
assert(buf != MAP_FAILED);
通过给buf[y * info.xres + x] 赋值就可以轻松的修改 (x,y)这个点的像素。
3. Closer look to /dev/fb*
简介提到fbdev代理了图形硬件的显示帧内存,并且提供了一些接口控制这部分内存,对比上文的framebuffer简单应用场景。
下图是framebuffer的memory layout:
- 不同的fbdev的实现,让fbdev代理的图形硬件的显示帧内存(framebuffer)会具备不同的初始属性.有些是可配置的属性(通过ioctl(fb,FBIOGET_VSCREENINFO, &info)获取struct fb_var_screeninfo defined in “linux/fb.h”),有些则是固定属性(通过ioctl(fb, FBIOGET_FSCREENINFO,&info)获取struct fb_fix_screeninfo defined in “linux/fb.h”).
以上图为例我们需要关注几个主要属性:
- 颜色格式(colorformat)
framebuffer的颜色格式可以通过ioctl(fb, FBIOPUT_VSCREENINFO, &info) 设定,通常我们都是在初始化fbdev的时候就决定好. 对fbdev的用户来说,确定了颜色格式用户就知道如何修改对应pixel的内容。
- bitsperpixel
一个pixel的宽度,与颜色格式协调的变量,颜色格式确定了pixel的宽度也确定了。
- xres_virtual/yres_virtual
可通过ioctl FBIOGET_VSCREENINFO/FBIOPUT_VSCREENINFO获取。通常fbdev 代理的framebuffer总大小为:xres_virtualyres_virtualbitsperpixel。
- xres/yres,xoffset/yoffset
(xoffset,yoffset) (xoffset+xres,yoffset+yres) 在framebuffer中标记了一个矩形框,即当前显示的buffer区域.也就是说display buffer 用于显示的有效区域可以是从framebuffer开始地址开始。
为何 display buffer 在 framebuffer内
- 支持不同分辨率的内容.
有时候framebuffer的内存大小在FBDEV初始化的时候已经申请好了. 我们可能需要申请一个能够容纳最大分辨的framebuffer,比如1080p,此时我们使用的display buffer 可能是720p.
- double buffer.
使用单buffer无法避免画面撕裂的问题,因为你可能总是修改未显示的内容,造成已经被graphic hardware显示的部分和被修改将要显示的部分时空错位。使用双buffer,显示一张display buffer#0,修改另一张显示一张display buffer#1,修改完成之后交换.显示一张display buffer#1,修改另一张显示一张display buffer#0.