转自:https://www.jianshu.com/p/df1213e5a0ed

来自: Android技术特工队
作者: Aaron
主页: http://www.wxtlife.com/
原文连接:http://www.wxtlife.com/2017/06/07/Android-framebuffer/

如果想加入Android技术交流群,请长按识别二维码关注下方公众号,点击“加群”获取加群方式。

欢迎关注公众号:FutureCoder

FrameBuffer 介绍

FrameBuffer中文译名为帧缓冲驱动,它是出现在2.2.xx内核中的一种驱动程序接口。主设备号为29,次设备号递增。
Linux抽象出FrameBuffer这个设备来供用户态进程实现直接写屏。FrameBuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过FrameBuffer的读写直接对显存进行操作。用户可以将FrameBuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节,这些都是由FrameBuffer设备驱动来完成的。
FrameBuffer实际上就是嵌入式系统中专门为GPU所保留的一块连续的物理内存,LCD通过专门的总线从framebuffer读取数据,显示到屏幕上。
FrameBuffer本质上是一块显示缓存,往显示缓存中写入特定格式的数据就意味着向屏幕输出内容。所以说FrameBuffer就是一块白板。

屏幕位置从上到下,从左至右与内存地址是顺序的线性关系

FrameBuffer 使用

framebuffer的设备文件在Linux下一般是 /dev/fb0/dev/fb1 等,但在Android下面一般为/dev/graphics/fb0,/dev/graphics/fb1
注意: 系统中至少要存在一个显示屏,因此,名称为“fb0”的设备是肯定会存在的,否则的话,就是出错了。

操作framebuffer的主要步骤

1、打开可用的FrameBuffer设备;

    fbfd = open("/dev/graphics/fb0", O_RDWR);

O_RDWR是已可读写的方式打开文件

2、计算映射大小

用ioctrl操作取得当前显示屏幕的参数,如屏幕分辨率,每个像素点的比特数。根据屏幕参数可计算屏幕缓冲区的大小。

ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo); // fb_fix_screeninfo 通过fbfd获取屏幕固定的相关信息
ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo); // fb_var_screeninfo 通过fbfd获取可变的信息,可以调用参数为`FBIOPUT_VSCREENINFO`的重新进行设置

从fb_var_screeninfo中可以获取xoffset ,yoffset的偏移量,以及屏幕可见行列像素点(xres,yres),以及一个像素所占用的位数bits_per_pixel。
从fb_fix_screeninfo 中可以获取到framebuffer的内存空间大小finfo.smem_len,每行占用的字节数line_length等。

这些信息都是对我们下一步来计算需要映射多大的内存空间有很大的帮助,size 可以直接等于 finfo.smem_len, 或者 xres * yres * bits_per_pixel >> 3

3、通过mmap映射地址空间

通过mmap函数把显卡的物理内存空间映射到用户空间地址上

char *fbp = (char *)mmap(start, size, PROT_READ | PROT_WRITE, MAP_SHARED,fbfd, offsize);

各个参数的含义如下:

  • start 指向欲映射的内存起始地址,通常设为 NULL,代表让系统自动选定地址,映射成功后返回该地址。
  • size 代表将文件中多大的部分映射到内存。
  • PROT_READ | PROT_WRITE 为可读可写模式
  • MAP_SHARED 对映射区域的写入数据会复制回文件内,而且允许其他映射该文件的进程共享。
  • fbfd 要映射到内存中的文件描述符,也就是open文件后的描述符。
  • offsize 文件映射的偏移量

关于open、mmap的相关信息可以参考博客:http://www.wxtlife.com/2016/01/17/Android-memory-map/

4、更改内存空间里的像素数据并显示;

fbp则是映射framebuffer后的内存首地址,整个framebuffer的地址是线性的,与整个屏幕大小从左到右,从上到下映射的。所以操作framebuffer是按照每个像素去操作的,每个像素都需要计算他的偏移量也就是每个像素的内存地址空间.
例如在(x,y)位置写入颜色 pixel值。

4.1 首先先计算偏移量
offset = (x + y * screen_width) * 4; // (4个字节)

上面未考虑多buffer切换的地址空间的情况

4.2 给偏移量的内存地址赋值
*((uint32_t *)(fbp + offset)) = pixel;

5、退出时关闭framebuffer设备。

munmap(fbp, size);
close(fbfd);

size需要与mmap时一样,会导致内存泄露问题。
查看一个完整的示例demo :

int main() {
int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;
char *fbp = 0;
long int location = 0;
// Open the file for reading and writing
fbfd = open("/dev/graphics/fb0", O_RDWR);
if (!fbfd) {
printf("Error: cannot open framebuffer device.\n");
exit(1);
}
printf("The framebuffer device was opened successfully.\n");
// Get fixed screen information
if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
printf("Error reading fixed information.\n");
exit(2);
}
// Get variable screen information
if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
printf("Error reading variable information.\n");
exit(3);
}
screensize = finfo.smem_len;
// screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel >> 3 // >>3 表示算出字节数
fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,fbfd, 0); if ((int)fbp == -1) {
printf("Error: failed to map framebuffer device to memory.\n");
exit(4);
}

FrameBuffer 相关结构

FrameBuffer设备驱动基于如下两个文件:

  1. linux/include/linux/fb.h
  2. linux/drivers/video/fbmem.c

FrameBuffer 主要包含的结构有以下:fb_info ,fb_ops ,fb_var_screeninfo,fb_fix_screeninfo,上面的结构都定义在fb.h里。

fb_var_screeninfo

用于记录用户可修改的显示属性参数,包括屏幕分辨率、每个像素点的比特数等。
显卡的显示属性,用户可修改,此数据结构中,定义了偏移量(xoffset ,yoffset)、可见行列像素点(xres,yres)、每个像素所占bit位数(bits_per_pixel), 虚拟分辨率(xres_virtual、yres_virtual)在显存中包含的分辨率等信息,这些都是我们常见也是常用的到的。
这些数据我们是可以通过ioctl函数来获取,获取操作如下:ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) 。也可以重新进行赋值,之后再将设置进系统:设置如下:ioctl(fbfd, FBIOPUT_VSCREENINFO, &finfo)

数据结构如下:

struct fb_var_screeninfo {
__u32 xres; /* 行可见像素*/
__u32 yres; /* 列可见像素*/
__u32 xres_virtual; /* 行虚拟像素*/
__u32 yres_virtual; /* 列虚拟像素*/
__u32 xoffset; /* 水平偏移量*/
__u32 yoffset; /* 垂直偏移量*/
__u32 bits_per_pixel;/*每个像素所占bit位数*/
__u32 grayscale; /* 灰色刻度*/
struct fb_bitfield red; /* bitfield in fb mem if true color, */
struct fb_bitfield green; /* else only length is significant */
struct fb_bitfield blue;
struct fb_bitfield transp; /* transparency */
__u32 nonstd; /* != 0 Non standard pixel format */
__u32 activate; /* see FB_ACTIVATE_* */
__u32 height; /* 图像高度*/
__u32 width; /* 图像宽度*/
__u32 accel_flags; /* (OBSOLETE) see fb_info.flags */
__u32 pixclock; /* pixel clock in ps (pico seconds) */
__u32 left_margin; /* time from sync to picture */
__u32 right_margin; /* time from picture to sync */
__u32 upper_margin; /* time from sync to picture */
__u32 lower_margin;
__u32 hsync_len; /* length of horizontal sync */
__u32 vsync_len; /* length of vertical sync */
__u32 sync; /* see FB_SYNC_* */
__u32 vmode; /* see FB_VMODE_* */
__u32 rotate; /* angle we rotate counter clockwise */
__u32 reserved[5]; /* Reserved for future compatibility */
};

fb_fix_screeninfo

这个结构在显卡被设定模式后创建,它描述显示卡的属性,并且系统运行时不能被修改;比如FrameBuffer内存的起始地址。它依赖于被设定的模式,当一个模式被设定后,内存信息由显示卡硬件给出,内存的位置等信息就不可以修改。
显卡的硬件属性, 用户不可修改, 驱动程序初始化时设置。比如可以获取内存空间大小,起始地址,每行占用的字节数line_length等等。

数据结构如下:

struct fb_fix_screeninfo {
char id[16]; /* identification string eg "TT Builtin" */
unsigned long smem_start;/* Start of frame buffer mem */
__u32 smem_len; /* Length of frame buffer mem */
__u32 type; /* see FB_TYPE_* */
__u32 type_aux; /* Interleave for interleaved Planes */
__u32 visual; /* see FB_VISUAL_* */
__u16 xpanstep; /* zero if no hardware panning */
__u16 ypanstep; /* zero if no hardware panning */
__u16 ywrapstep; /* zero if no hardware ywrap */
__u32 line_length; /* length of a line in bytes */
unsigned long mmio_start;/* Start of Memory Mapped I/O */
__u32 mmio_len; /* Length of Memory Mapped I/O */
__u32 accel; /* Indicate to driver which */
__u16 reserved[3]; /* Reserved for future compatibility */
};

fb_ops

是提供给底层设备驱动的一个接口,用户应用可以使用 ioctl() 系统调用来操作设备。

fb_cmap

描述设备无关的颜色映射信息。可以通过 FBIOGETCMAP 和 FBIOPUTCMAP 对应的 ioctl 操作设定或获取颜色映射信息。主要是颜色映射表,颜色相关一些映射信息。

fb_info

结构仅在内核中可见,在这个结构中有一个fb_ops指针,指向驱动设备工作所需的函数集,是Linux为帧缓冲设备定义的驱动层接口。它不仅包含了底层函数,而且还有记录设备状态的数据。每个帧缓冲设备都与一个fb_info结构相对应。

结构如下:

 
FrameBuffer结构图

ioctl中request参数:

  • FBIOGET_VSCREENINFO 表示用户获取屏幕的可变参数;
  • FBIOPUT_VSCREENINFO 表示用户设置可变的屏幕参数;
  • FBIOGET_FSCREENINFO 表示用户获得屏幕的固定参数;
  • FBIOBLANK表示调用sep4020fb_blank函数清空液晶屏;
  • FBIOPUTCMAP 表示设置屏幕的颜色表;
  • FBIOGETCMAP 表示获得颜色表。

双缓冲机制

Android 使用SurfaceFlinger作为屏幕合成引擎。它管理来自各个窗口的Surface objects,然后将其写入到framebuffer去。SurfaceFlinger使用前buffer来合成,后buffer来绘制。一旦绘制完成,Android通过页翻转操作,交换Y轴坐标的偏移量,选择不同buffer。在EGL显示服务初始化时,如果虚拟Y轴分辨率大于实际Y轴分辨率,说明framebuffer可以直接使用双缓冲。否则,后buffer要复制到前buffer,这样会导致页交换延迟。为了提高系统性能,Framebuffer驱动最好提供双缓冲机制。

双缓冲机制的原理

所有画图操作将它们画图的结果保存在一块系统内存区域中,这块区域通常被称作“后缓冲区(backbuffer)”,当所有的绘图操作结束之后,系统通过换页机制将绘制区域指向先前的后缓冲区,然后进行绘制显示,而原来的绘制缓冲区就变为“后缓冲区”,之后按照这种情况不停循环切换。这个复制操作通常要跟显示器的光栈束同步,以避免撕裂。双缓冲机制必须要求有比单缓冲更多的显示内存和CPU消耗时间,因为“后缓冲区”需要显示内存,而复制操作和等待同步需要CPU时间。

FrameBuffer1

framebuffer2

双缓冲是一种画图技术,使用这种技术可以使得画图没有(至少是减少)闪烁、撕裂等不良效果,并减少等待时间。

缓冲区切换步骤:

  1. 把fb驱动的framebuffer通过mmap映射到应用空间的内存地址map_base,一般来说framebuffer size是2*framesize或者3*framesize 大小(和平台相关)
  2. 把第一帧数据写入map_base
  3. 调用FBIOPAN_DISPLAY显示
  4. 把第二帧数据写入map_base+framesize处
  5. 调用FBIOPAN_DISPLAY
  6. 重复step2~step5

FBIOPAN_DISPLAY 在linux的注释里是“平移显示”的意思,调用FBIOPAN_DISPLAY时,会传一个y坐标偏移量yoffset给驱动,然后驱动会把当前显存的指针偏移 “yoffset X 屏幕宽度 X 位色字节数” 个字节,这样就好像实现了图像的y坐标平移,也就是“平移显示”。当这个yoffset等于屏幕高度的时候,就实现了显存的切换。

参考链接

http://www.cnblogs.com/armlinux/archive/2012/02/25/2396760.html
http://blog.csdn.net/yangwen123/article/details/12096483

如果想加入Android技术交流群,请长按识别二维码关注下方公众号,点击“加群”获取加群方式。

作者:技术特工队
链接:https://www.jianshu.com/p/df1213e5a0ed
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Android Framebuffer介绍及使用【转】的更多相关文章

  1. android Animation介绍

    Animation介绍: 在Android SDK介绍了2种Animation模式: 1. Tween Animation:间动画,通过对场景里的对象不断做图像变换(平移.缩放.旋转)产生动画效果,即 ...

  2. android AsyncTask介绍(转)

    android AsyncTask介绍 AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接 ...

  3. Android monkey介绍

    Android monkey介绍 原文地址 1 简略 monkey是android下自动化测试比较重要的的一个工具,该工具可以运行在host端或者设备(模拟器或真实设备).它会向系统发送随机事件流(即 ...

  4. [Learn Android Studio 汉化教程]第一章 : Android Studio 介绍

    注:为了看上去比较清晰这里只转载了中文 原地址:  [Learn Android Studio 汉化教程]第一章 : Android Studio 介绍 本章将引导您完成安装和设置开发环境,然后你就可 ...

  5. 【转】Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析

    原文网址:http://blog.csdn.net/xubin341719/article/details/38584469 关键词:蓝牙blueZ  A2DP.SINK.sink_connect.s ...

  6. Android bluetooth介绍(四): a2dp connect流程分析

    关键词:蓝牙blueZ  A2DP.SINK.sink_connect.sink_disconnect.sink_suspend.sink_resume.sink_is_connected.sink_ ...

  7. android动画介绍之 自己定义Animation动画实现qq抖一抖效果

    昨天我们介绍了Animation的基本使用方法.小伙伴们了解的怎么样了?假设还没有了解过Animation的小伙伴能够看看这篇博客 android动画介绍--Animation 实现loading动画 ...

  8. android动画介绍之 自定义Animation动画实现qq抖一抖效果

    昨天我们介绍了Animation的基本用法.小伙伴们了解的怎么样了?如果还没有了解过Animation的小伙伴可以看看这篇博客 android动画介绍--Animation 实现loading动画效果 ...

  9. android动画介绍--Animation 实现loading动画效果

    Animation的使用方法并不难.这里简单的介绍一下使用方法. 先看效果图: 效果还是不错的吧. 下面来看看使用方法. 动画效果是通过Animation来实现的,一共有四种,分别为: AlphaAn ...

随机推荐

  1. springmvc接收date类型参数

    springmvc在表单提交接收date类型参数的时候会报错:Cannot convert value of type [java.lang.String] to required type [jav ...

  2. 腾讯下载的视频qlv格式转化为MP4格式

    最近在看腾讯视频的时候发现下载下来的视频格式都是qlv格式,且不能用其他播放器播放,甚是恼怒,网上找了很多方法都很繁琐,于是自己写了一个小程序来处理这个问题.把下载下来的qlv格式转化为MP4格式 首 ...

  3. hbase 原子操作cas

    在高并发的情况下,对数据row1  column=cf1:qual1, timestamp=1, value=val1的插入或者更新可能会导致非预期的情况, 例如:原本客户端A需要在value=val ...

  4. java10 新特性 详解

    引言: 点击-->java9 新特性 详解 点击-->java8 新特性 详解 正题: 1.局部变量var 将前端思想var关键字引入java后段,自动检测所属于类型,一种情况除外,不能为 ...

  5. 【BZOJ2655】Calc(拉格朗日插值,动态规划)

    [BZOJ2655]Calc(多项式插值,动态规划) 题面 BZOJ 题解 考虑如何\(dp\) 设\(f[i][j]\)表示选择了\(i\)个数并且值域在\([1,j]\)的答案. \(f[i][j ...

  6. Java之使用链表实现队列

    import java.util.Iterator; import java.util.NoSuchElementException; /** * 使用链表来实现队列 * 1.考虑结点的结构,包括当前 ...

  7. java线程池赏析

    1.线程池的顶级接口(Executor) 线程池的顶级接口(jdk > 1.5).仅仅定义了方法execute(Runnable). 该方法接收一个Runnable实例,用来执行一个任务,该任务 ...

  8. 团体程序设计天梯赛 L3-016. 二叉搜索树的结构

    #include <cstdio> #include <cstdlib> #include <string.h> #include <math.h> # ...

  9. bzoj 1208

    1208: [HNOI2004]宠物收养所 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 9775  Solved: 3918[Submit][Sta ...

  10. [Java] 理解JVM之二:类加载步骤及内存分配

    一.类加载器 ClassLoader 能根据需要将 class 文件加载到 JVM 中,它使用双亲委托模型,在加载类的时候会判断如果类未被自己加载过,就优先让父加载器加载.另外在使用 instance ...