Android Framebuffer介紹及使用

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

如果想加入Android技術(shù)交流群俱笛,請長按識別二維碼關(guān)注下方公眾號肖爵,點擊“加群”獲取加群方式。

 歡迎關(guān)注公眾號:FutureCoder
歡迎關(guān)注公眾號:FutureCoder

FrameBuffer 介紹

FrameBuffer中文譯名為幀緩沖驅(qū)動绍移,它是出現(xiàn)在2.2.xx內(nèi)核中的一種驅(qū)動程序接口鹊碍。主設(shè)備號為29呵曹,次設(shè)備號遞增。
Linux抽象出FrameBuffer這個設(shè)備來供用戶態(tài)進程實現(xiàn)直接寫屏衬衬。FrameBuffer機制模仿顯卡的功能殖妇,將顯卡硬件結(jié)構(gòu)抽象掉刁笙,可以通過FrameBuffer的讀寫直接對顯存進行操作。用戶可以將FrameBuffer看成是顯示內(nèi)存的一個映像谦趣,將其映射到進程地址空間之后疲吸,就可以直接進行讀寫操作,而寫操作可以立即反應(yīng)在屏幕上前鹅。這種操作是抽象的磅氨,統(tǒng)一的。用戶不必關(guān)心物理顯存的位置嫡纠、換頁機制等等具體細(xì)節(jié),這些都是由FrameBuffer設(shè)備驅(qū)動來完成的延赌。
FrameBuffer實際上就是嵌入式系統(tǒng)中專門為GPU所保留的一塊連續(xù)的物理內(nèi)存除盏,LCD通過專門的總線從framebuffer讀取數(shù)據(jù),顯示到屏幕上挫以。
FrameBuffer本質(zhì)上是一塊顯示緩存者蠕,往顯示緩存中寫入特定格式的數(shù)據(jù)就意味著向屏幕輸出內(nèi)容。所以說FrameBuffer就是一塊白板掐松。

屏幕位置從上到下踱侣,從左至右與內(nèi)存地址是順序的線性關(guān)系

FrameBuffer 使用

framebuffer的設(shè)備文件在Linux下一般是 /dev/fb0/dev/fb1 等大磺,但在Android下面一般為/dev/graphics/fb0,/dev/graphics/fb1
注意: 系統(tǒng)中至少要存在一個顯示屏抡句,因此,名稱為“fb0”的設(shè)備是肯定會存在的杠愧,否則的話待榔,就是出錯了。

操作framebuffer的主要步驟

1流济、打開可用的FrameBuffer設(shè)備锐锣;

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

O_RDWR是已可讀寫的方式打開文件

2、計算映射大小

用ioctrl操作取得當(dāng)前顯示屏幕的參數(shù)绳瘟,如屏幕分辨率雕憔,每個像素點的比特數(shù)。根據(jù)屏幕參數(shù)可計算屏幕緩沖區(qū)的大小糖声。

ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo); // fb_fix_screeninfo 通過fbfd獲取屏幕固定的相關(guān)信息
ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo); // fb_var_screeninfo 通過fbfd獲取可變的信息斤彼,可以調(diào)用參數(shù)為`FBIOPUT_VSCREENINFO`的重新進行設(shè)置

從fb_var_screeninfo中可以獲取xoffset ,yoffset的偏移量分瘦,以及屏幕可見行列像素點(xres,yres),以及一個像素所占用的位數(shù)bits_per_pixel畅卓。
從fb_fix_screeninfo 中可以獲取到framebuffer的內(nèi)存空間大小finfo.smem_len擅腰,每行占用的字節(jié)數(shù)line_length等。

這些信息都是對我們下一步來計算需要映射多大的內(nèi)存空間有很大的幫助翁潘,size 可以直接等于 finfo.smem_len, 或者 xres * yres * bits_per_pixel >> 3

3趁冈、通過mmap映射地址空間

通過mmap函數(shù)把顯卡的物理內(nèi)存空間映射到用戶空間地址上

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

各個參數(shù)的含義如下:

  • start 指向欲映射的內(nèi)存起始地址,通常設(shè)為 NULL拜马,代表讓系統(tǒng)自動選定地址渗勘,映射成功后返回該地址。
  • size 代表將文件中多大的部分映射到內(nèi)存俩莽。
  • PROT_READ | PROT_WRITE 為可讀可寫模式
  • MAP_SHARED 對映射區(qū)域的寫入數(shù)據(jù)會復(fù)制回文件內(nèi)旺坠,而且允許其他映射該文件的進程共享。
  • fbfd 要映射到內(nèi)存中的文件描述符,也就是open文件后的描述符扮超。
  • offsize 文件映射的偏移量

關(guān)于open取刃、mmap的相關(guān)信息可以參考博客:http://www.wxtlife.com/2016/01/17/Android-memory-map/

4、更改內(nèi)存空間里的像素數(shù)據(jù)并顯示出刷;

fbp則是映射framebuffer后的內(nèi)存首地址璧疗,整個framebuffer的地址是線性的,與整個屏幕大小從左到右馁龟,從上到下映射的崩侠。所以操作framebuffer是按照每個像素去操作的,每個像素都需要計算他的偏移量也就是每個像素的內(nèi)存地址空間.
例如在(x坷檩,y)位置寫入顏色 pixel值却音。

4.1 首先先計算偏移量
offset = (x + y * screen_width) * 4; // (4個字節(jié)) 

上面未考慮多buffer切換的地址空間的情況

4.2 給偏移量的內(nèi)存地址賦值
*((uint32_t *)(fbp + offset)) = pixel;

5、退出時關(guān)閉framebuffer設(shè)備矢炼。

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

size需要與mmap時一樣系瓢,會導(dǎo)致內(nèi)存泄露問題。
查看一個完整的示例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 表示算出字節(jié)數(shù)
    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 相關(guān)結(jié)構(gòu)

FrameBuffer設(shè)備驅(qū)動基于如下兩個文件:

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

FrameBuffer 主要包含的結(jié)構(gòu)有以下:fb_info 句灌,fb_ops 八拱,fb_var_screeninfo,fb_fix_screeninfo,上面的結(jié)構(gòu)都定義在fb.h里涯塔。

fb_var_screeninfo

用于記錄用戶可修改的顯示屬性參數(shù)肌稻,包括屏幕分辨率、每個像素點的比特數(shù)等匕荸。
顯卡的顯示屬性,用戶可修改爹谭,此數(shù)據(jù)結(jié)構(gòu)中,定義了偏移量(xoffset ,yoffset)榛搔、可見行列像素點(xres诺凡,yres)东揣、每個像素所占bit位數(shù)(bits_per_pixel), 虛擬分辨率(xres_virtual、yres_virtual)在顯存中包含的分辨率等信息腹泌,這些都是我們常見也是常用的到的嘶卧。
這些數(shù)據(jù)我們是可以通過ioctl函數(shù)來獲取,獲取操作如下:ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo) 凉袱。也可以重新進行賦值芥吟,之后再將設(shè)置進系統(tǒng):設(shè)置如下:ioctl(fbfd, FBIOPUT_VSCREENINFO, &finfo)

數(shù)據(jù)結(jié)構(gòu)如下:

struct fb_var_screeninfo {  
    __u32 xres;         /* 行可見像素*/  
    __u32 yres;         /* 列可見像素*/  
    __u32 xres_virtual; /* 行虛擬像素*/  
    __u32 yres_virtual; /* 列虛擬像素*/  
    __u32 xoffset;      /* 水平偏移量*/  
    __u32 yoffset;      /* 垂直偏移量*/  
    __u32 bits_per_pixel;/*每個像素所占bit位數(shù)*/  
    __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

這個結(jié)構(gòu)在顯卡被設(shè)定模式后創(chuàng)建,它描述顯示卡的屬性专甩,并且系統(tǒng)運行時不能被修改钟鸵;比如FrameBuffer內(nèi)存的起始地址。它依賴于被設(shè)定的模式涤躲,當(dāng)一個模式被設(shè)定后棺耍,內(nèi)存信息由顯示卡硬件給出,內(nèi)存的位置等信息就不可以修改种樱。
顯卡的硬件屬性, 用戶不可修改, 驅(qū)動程序初始化時設(shè)置蒙袍。比如可以獲取內(nèi)存空間大小,起始地址嫩挤,每行占用的字節(jié)數(shù)line_length等等害幅。

數(shù)據(jù)結(jié)構(gòu)如下:

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

是提供給底層設(shè)備驅(qū)動的一個接口,用戶應(yīng)用可以使用 ioctl() 系統(tǒng)調(diào)用來操作設(shè)備俐镐。

fb_cmap

描述設(shè)備無關(guān)的顏色映射信息〔负撸可以通過 FBIOGETCMAP 和 FBIOPUTCMAP 對應(yīng)的 ioctl 操作設(shè)定或獲取顏色映射信息佩抹。主要是顏色映射表,顏色相關(guān)一些映射信息取董。

fb_info

結(jié)構(gòu)僅在內(nèi)核中可見棍苹,在這個結(jié)構(gòu)中有一個fb_ops指針,指向驅(qū)動設(shè)備工作所需的函數(shù)集茵汰,是Linux為幀緩沖設(shè)備定義的驅(qū)動層接口枢里。它不僅包含了底層函數(shù),而且還有記錄設(shè)備狀態(tài)的數(shù)據(jù)蹂午。每個幀緩沖設(shè)備都與一個fb_info結(jié)構(gòu)相對應(yīng)栏豺。

結(jié)構(gòu)如下:


FrameBuffer結(jié)構(gòu)圖

ioctl中request參數(shù):

  • FBIOGET_VSCREENINFO 表示用戶獲取屏幕的可變參數(shù);
  • FBIOPUT_VSCREENINFO 表示用戶設(shè)置可變的屏幕參數(shù)豆胸;
  • FBIOGET_FSCREENINFO 表示用戶獲得屏幕的固定參數(shù)奥洼;
  • FBIOBLANK表示調(diào)用sep4020fb_blank函數(shù)清空液晶屏;
  • FBIOPUTCMAP 表示設(shè)置屏幕的顏色表晚胡;
  • FBIOGETCMAP 表示獲得顏色表灵奖。

雙緩沖機制

Android 使用SurfaceFlinger作為屏幕合成引擎嚼沿。它管理來自各個窗口的Surface objects,然后將其寫入到framebuffer去瓷患。SurfaceFlinger使用前buffer來合成骡尽,后buffer來繪制。一旦繪制完成擅编,Android通過頁翻轉(zhuǎn)操作攀细,交換Y軸坐標(biāo)的偏移量,選擇不同buffer沙咏。在EGL顯示服務(wù)初始化時辨图,如果虛擬Y軸分辨率大于實際Y軸分辨率,說明framebuffer可以直接使用雙緩沖肢藐。否則故河,后buffer要復(fù)制到前buffer,這樣會導(dǎo)致頁交換延遲吆豹。為了提高系統(tǒng)性能鱼的,F(xiàn)ramebuffer驅(qū)動最好提供雙緩沖機制。

雙緩沖機制的原理

所有畫圖操作將它們畫圖的結(jié)果保存在一塊系統(tǒng)內(nèi)存區(qū)域中痘煤,這塊區(qū)域通常被稱作“后緩沖區(qū)(backbuffer)”凑阶,當(dāng)所有的繪圖操作結(jié)束之后,系統(tǒng)通過換頁機制將繪制區(qū)域指向先前的后緩沖區(qū)衷快,然后進行繪制顯示宙橱,而原來的繪制緩沖區(qū)就變?yōu)椤昂缶彌_區(qū)”,之后按照這種情況不停循環(huán)切換蘸拔。這個復(fù)制操作通常要跟顯示器的光棧束同步师郑,以避免撕裂。雙緩沖機制必須要求有比單緩沖更多的顯示內(nèi)存和CPU消耗時間调窍,因為“后緩沖區(qū)”需要顯示內(nèi)存宝冕,而復(fù)制操作和等待同步需要CPU時間。

FrameBuffer1
FrameBuffer1

framebuffer2
framebuffer2

雙緩沖是一種畫圖技術(shù)邓萨,使用這種技術(shù)可以使得畫圖沒有(至少是減少)閃爍地梨、撕裂等不良效果,并減少等待時間缔恳。

緩沖區(qū)切換步驟:

  1. 把fb驅(qū)動的framebuffer通過mmap映射到應(yīng)用空間的內(nèi)存地址map_base宝剖,一般來說framebuffer size是2*framesize或者3*framesize 大小(和平臺相關(guān))
  2. 把第一幀數(shù)據(jù)寫入map_base
  3. 調(diào)用FBIOPAN_DISPLAY顯示
  4. 把第二幀數(shù)據(jù)寫入map_base+framesize處
  5. 調(diào)用FBIOPAN_DISPLAY
  6. 重復(fù)step2~step5

FBIOPAN_DISPLAY 在linux的注釋里是“平移顯示”的意思,調(diào)用FBIOPAN_DISPLAY時歉甚,會傳一個y坐標(biāo)偏移量yoffset給驅(qū)動诈闺,然后驅(qū)動會把當(dāng)前顯存的指針偏移 “yoffset X 屏幕寬度 X 位色字節(jié)數(shù)” 個字節(jié),這樣就好像實現(xiàn)了圖像的y坐標(biāo)平移铃芦,也就是“平移顯示”雅镊。當(dāng)這個yoffset等于屏幕高度的時候襟雷,就實現(xiàn)了顯存的切換。

參考鏈接

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

如果想加入Android技術(shù)交流群仁烹,請長按識別二維碼關(guān)注下方公眾號耸弄,點擊“加群”獲取加群方式。

 歡迎關(guān)注公眾號:FutureCoder
歡迎關(guān)注公眾號:FutureCoder
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卓缰,一起剝皮案震驚了整個濱河市计呈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌征唬,老刑警劉巖捌显,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異总寒,居然都是意外死亡扶歪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進店門摄闸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來善镰,“玉大人,你說我怎么就攤上這事年枕§牌郏” “怎么了?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵熏兄,是天一觀的道長品洛。 經(jīng)常有香客問我,道長摩桶,這世上最難降的妖魔是什么桥状? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮典格,結(jié)果婚禮上岛宦,老公的妹妹穿的比我還像新娘台丛。我一直安慰自己耍缴,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布挽霉。 她就那樣靜靜地躺著防嗡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪侠坎。 梳的紋絲不亂的頭發(fā)上蚁趁,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天,我揣著相機與錄音实胸,去河邊找鬼他嫡。 笑死番官,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钢属。 我是一名探鬼主播徘熔,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼淆党!你這毒婦竟也來了酷师?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤染乌,失蹤者是張志新(化名)和其女友劉穎山孔,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荷憋,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡台颠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了台谊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓉媳。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖锅铅,靈堂內(nèi)的尸體忽然破棺而出酪呻,到底是詐尸還是另有隱情,我是刑警寧澤盐须,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布玩荠,位于F島的核電站,受9級特大地震影響贼邓,放射性物質(zhì)發(fā)生泄漏阶冈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一塑径、第九天 我趴在偏房一處隱蔽的房頂上張望女坑。 院中可真熱鬧,春花似錦统舀、人聲如沸匆骗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碉就。三九已至,卻和暖如春闷串,著一層夾襖步出監(jiān)牢的瞬間瓮钥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留碉熄,地道東北人桨武。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像锈津,于是被迫代替她去往敵國和親玻募。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容