Android Framebuffer介紹及使用【轉】


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

來自: Android技術特工隊
作者: Aaron
主頁: http://www.wxtlife.com/
原文連接:http://www.wxtlife.com/2017/06/07/Android-framebuffer/

如果想加入Android技術交流群,請長按識別二維碼關注下方公眾號,點擊“加群”獲取加群方式。

 歡迎關注公眾號:FutureCoder
歡迎關注公眾號: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
FrameBuffer1

framebuffer2
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
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM