Android Framebuffer介紹及使用

轉載自:http://www.wxtlife.com/2017/06/07/Android-framebuffer/
Android Framebuffer介紹及使用
發表於 2017-06-07 | 閱讀次數 : 1922
字數統計 : 2,890 | 閱讀時長 : 12 分鐘
想第一時間獲取我的最新文章,請關注公衆號: 技術特工隊
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

雙緩衝是一種畫圖技術,使用這種技術可以使得畫圖沒有(至少是減少)閃爍、撕裂等不良效果,並減少等待時間。

緩衝區切換步驟:
把fb驅動的framebuffer通過mmap映射到應用空間的內存地址map_base,一般來說framebuffer size是2framesize或者3framesize 大小(和平臺相關)
把第一幀數據寫入map_base
調用FBIOPAN_DISPLAY顯示
把第二幀數據寫入map_base+framesize處
調用FBIOPAN_DISPLAY
重複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
http://blog.csdn.net/louiswangbing/article/details/6606837
http://lib.csdn.net/article/android/4928
http://www.cnblogs.com/mfryf/archive/2013/05/22/3093912.html
http://www.cnblogs.com/mfryf/archive/2013/05/22/3093912.html
http://blog.csdn.net/molibaobei90/article/details/40826791

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章