转自: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设备;

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

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

2、计算映射大小

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

  1. ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo); // fb_fix_screeninfo 通过fbfd获取屏幕固定的相关信息
  2. 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函数把显卡的物理内存空间映射到用户空间地址上

  1. 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 首先先计算偏移量
  1. offset = (x + y * screen_width) * 4; // (4个字节)

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

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

5、退出时关闭framebuffer设备。

  1. munmap(fbp, size);
  2. close(fbfd);

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

  1. int main() {
  2. int fbfd = 0;
  3. struct fb_var_screeninfo vinfo;
  4. struct fb_fix_screeninfo finfo;
  5. long int screensize = 0;
  6. char *fbp = 0;
  7. long int location = 0;
  8. // Open the file for reading and writing
  9. fbfd = open("/dev/graphics/fb0", O_RDWR);
  10. if (!fbfd) {
  11. printf("Error: cannot open framebuffer device.\n");
  12. exit(1);
  13. }
  14. printf("The framebuffer device was opened successfully.\n");
  15. // Get fixed screen information
  16. if (ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
  17. printf("Error reading fixed information.\n");
  18. exit(2);
  19. }
  20. // Get variable screen information
  21. if (ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
  22. printf("Error reading variable information.\n");
  23. exit(3);
  24. }
  25. screensize = finfo.smem_len;
  26. // screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel >> 3 // >>3 表示算出字节数
  27. fbp = (char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED,fbfd, 0);
  28. if ((int)fbp == -1) {
  29. printf("Error: failed to map framebuffer device to memory.\n");
  30. exit(4);
  31. }

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)

数据结构如下:

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

fb_fix_screeninfo

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

数据结构如下:

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

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. MySQL复制 -- binlog(2)

    MySQL复制是使用最为广泛的一套组建,上一节已经简单说了一下复制的一些用途和复制的原理,知道了这些我们能够快速的搭建起复制的平台,但是仅知道这些还是不够的,很多时候并不是一帆风顺的,总会有那么一小段 ...

  2. BZOJ5321 JXOI2017加法(二分答案+贪心+堆+树状数组)

    二分答案后得到每个位置需要被加的次数.考虑贪心.从左到右考虑每个位置,将以该位置为左端点的区间按右端点从大到小加进堆.看该位置还需要被加多少次,如果不需要加了就不管,否则取堆顶区间将其选择,BIT实现 ...

  3. 【CF835D】Palindromic characteristics 加强版 解题报告

    [CF835D]Palindromic characteristics 加强版 Description 给你一个串,让你求出\(k\)阶回文子串有多少个.\(k\)从\(1\)到\(n\). \(k\ ...

  4. 《Linux内核设计与实现》第17章读书笔记

    第十七章  设备与模块 一.四种内核成分 设备类型:在所有 Unix 系统中为了统一普通设备的操作所采用的分类. 模块: Linux 内核中用于按需加载和卸载目标码的机制. 内核对象:内核数据结构中支 ...

  5. phpredis用法笔记

    项目中用到redis集群, 发现phpredis对集群,分布式是有支持的.翻译下相关资料备用. redis扩展地址:https://github.com/phpredis/phpredis, 看到如下 ...

  6. 解题:Poetize6 IncDec Sequence

    题面 差分原数列得到差分数组$dif$,这样对于$dif[2]->dif[n]$会多出来两个“空位置”$1$和$n+1$.然后区间加减就变成了使一个位置$+1$,另一个位置$-1$(可以对“空位 ...

  7. (转)Tomcat version 7.0 only supports J2EE 1.2, 1.3, 1.4, and Java EE 5 and 6 Web mod

    背景:在sts中导入web项目,却不能通过sts中的tomcat来加载该服务,并报出如下错误. “Tomcat version 7.0 only supports J2EE 1.2, 1.3, 1.4 ...

  8. CIDR 无类别域间路由

    参考百度百科 1.全称 CIDR的全称是Classless Inter-Domain Routing 2.作用 CIDR将路由集中起来,使一个IP地址代表主要骨干提供商服务的几千个IP地址,从而减轻I ...

  9. android listview使用自定义的adapter没有了OnItemClickListener事件解决办法

    在使用listview的时用使用自定义的adapter的时候,如果你的item布局中包含有Button,Checkable继承来的所有控件,那么你将无法获取listview的onItemClickLi ...

  10. 科学计算三维可视化---TVTK管线与数据加载(数据集)

    一:数据集 三维可视化的第一步是选用合适的数据结构来表示数据,TVTK提供了多种表示不同种类数据的数据集 (一)数据集--ImageData >>> from tvtk.api im ...