轉自:http://www.cnblogs.com/shoemaker/p/linux_graphics02.htmlhtml
Framebuffer驅動提供基本的顯示,framebuffer驅動操做的硬件就是一個顯示控制器和幀緩存(一片位於系統主存或者顯卡顯存)。Framebuffer驅動向應用程序提供/dev/fbx的設備接口,應用程序經過讀寫這個設備節點實現對顯示控制器和幀緩存。linux
下面這個程序顯示了應用程序操做操做framebuffer節點的過程。運行這個程序,將在屏幕上方顯示一個正方形(這裏省略了錯誤檢查代碼)。git
1 #include <stdio.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include <sys/mman.h>
5 #include <sys/ioctl.h>
6 #include <linux/fb.h>
7
8 int main ()
9 {
10 int fd;
11 struct fb_var_screeninfo vinfo;
12 struct fb_fix_screeninfo finfo;
13 size_t screensize = 0;
14 int location;
15 char *fbp = NULL, *ptr;
16 int x, y, x0, y0;
17 int i,j;
18 int ret;
19
20 fd = open("/dev/fb0", O_RDWR);
21 if (fd < 0){
22 fprintf(stderr, "error open fb0\n");
23 return -1;
24 }
25 ret= ioctl(fd, FBIOGET_FSCREENINFO, &finfo ) ;
26 if (ret < 0) {
27 fprintf(stderr, "get fixed screen info error\n");
28 return -1;
29 }
30 ret = ioctl(fd, FBIOGET_VSCREENINFO, &vinfo);
31 if (ret < 0) {
32 fprintf(stderr, "get variable screen info error\n");
33 return -1;
34 }
35 ret = ioctl(fd, FBIOPAN_DISPLAY,&vinfo);github
36 if (ret < 0) {編程
37 fprintf(stderr, "pan display failed\n");
38 return -1;
39 }
40 screensize=vinfo.xres * vinfo.yres * vinfo.bits_per_pixel /8 ;
41 fbp = (char *)mmap(NULL, screensize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
42 if (fbp == MAP_FAILED) {
43 fprintf(stderr, "mapped error\n");
44 return -1;
45 }
46 x0 = 200;
47 y0 = 200;
48 ptr = fbp + y0 * finfo.line_length + x0 * vinfo.bits_per_pixel / 8;
49 for( i = 0; i < 100; i++){
50 char* tmp_ptr = ptr;
51 for(j = 0; j < 100; j++){
52 *tmp_ptr++ = 0;
53 *tmp_ptr++ = 255;
54 *tmp_ptr++ = 0;
55 *tmp_ptr++ = 0;
56 }
57 ptr += finfo.line_length;
58 }
59 munmap(fbp,screensize);
60 close(fd);
61 return 0;
62 }緩存
應用程序對framebuffer的操做主要是經過ioctl和mmap完成的。mmap將顯存映射到用戶空間。25行和30行分別獲取當前framebuffer驅動的「固定參數」和「可變參數」,這兩個參數包含了當前顯示控制其的一些信息,可變參數主要是當前分辨率信息,固定參數主要是當前顯存的地址。35行FBIOPAN_DISPLAY一般用於雙緩存,可是這裏使用還有其它意義,在後面討論drm的framebuffer的時候還會具體討論。41行將顯存映射出來,51-59行操做這片顯存,往上繪製一個左上方座標爲(200,200),邊長爲100的正方形。架構
應用程序可以使用這些ioctl是由於內核提供了相應的接口,Linux內核設備驅動相關的書籍都會討論framebuffer驅動,這裏不討論如何寫一個framebuffer驅動。app
前一篇博文的圖4內核裏面是drm驅動,注意到和圖3的區別,單獨的FB driver已經沒有了,而是被集合到了drm驅動裏面。在一些只要求進行進本顯示的嵌入式系統上,依然會使用單獨的framebuffer驅動,而對於內核中有3D加速的AMD、intel等驅動,內核裏面和顯示有關的功能已經集合到了drm驅動裏面。在AMD/intel顯卡+Xorg+3D這樣配置的開源Linux系統上,Xorg並不使用,可是系統中仍然有/dev/fb0這樣的設備節點,若是咱們在桌面環境下「cat xxx >/dev/fb0」,系統不會有變化。然而若是將xorg.conf的Driver修改爲「fbdev」,重啓Xorg,在執行操做,可以看到有變化(關於Xorg.conf能夠參考"man xorg.conf",或者查看這個頁面)。或者切換到控制檯終端「Ctrl+Alt+Fn」,而後運行一樣的命令,就可以看到屏幕上的內容發生了變化,在這篇博文的第一節的程序有一個FBIOPAN_DISPLAY調用,這個調用會使得顯卡的Crtc指向的顯存地址發生變化而將當前的顯示區域切換到fb0設備節點對應的區域,所以上面的程序運行即便在X桌面環境下運行,也可以看到屏幕發生了變化。這提示咱們在覈外X驅動正常運行的狀況下,核外X驅動並不使用framebuffer驅動(實際上drm驅動註冊的frambuffer設備只是給內核使用),在X啓動運行後,不使用framebuffer 管理的那片內存的內容做爲顯示輸出,而是使用了另一片內存。框架
當前的Linux系統上內核的顯卡驅動稱爲drm驅動,在一般的linux內核發行版上,咱們使用lsmod命令查看內核模塊,可以看到相似下面的信息:spa
radeon 933054 3
ttm 45600 1 radeon
drm_kms_helper 22468 1 radeon
drm 162230 5 radeon,ttm,drm_kms_helper
i2c_algo_bit 5055 2 i2c_gpio,radeon
機器使用的是AMD的radeon顯卡,上面顯示了當前系統內核和顯卡驅動相關的模塊,drm模塊是內核drm驅動的基礎架構,全部drm顯卡驅動都會加載這個內核模塊,ttm是ttm內核管理機制,drm_kms_helper是內核模式的基礎框架代碼,i2c_algo_bit是顯卡上操做i2c設備使用的模塊,顯卡上的i2c設備主要包括了connector,encoder以及pll時鐘芯片。Drm內核驅動的代碼在內核源碼目錄drivers/gpu/drm下面。加載了drm驅動後,在/dev目錄下面會生成以下設備節點:
/dev/char/226:0 -> ../dri/card0
/dev/char/226:64 -> ../dri/controlD64
/dev/dri/card0
/dev/dri/controlD64
其中/dev/dri/card0是操做gpu的接口,發送命令等操做都是經過對這個設備節點進行的。/dev/dri/controlD64是kms相關的設備節點。
一般核外的驅動程序使用ioctl調用同內核進行交互,linux系統上核外對drm的ioctl進行了一層封裝,即libdrm,應用程序經過libdrm調用操做硬件,關於drm的調用,這裏有一些比較好的示例代碼 https://github.com/dvdhrm/docs,這份代碼主要調用libdrm進行模式設置,有詳細的註釋。
Linux下的圖形驅動的主要部分是核外部分,核外部分包括了xorg exa驅動以及mesa 3d 驅動,exa是傳統的2D加速框架,mesa 3d驅動則是針對3D驅動的硬件加速。因爲歷史緣由,在Fedora 16以及更早和稍後的系統上,即便如今硬件上不包含單獨的2D部件2D功能是由3D部件實現的,可是2D驅動和3D仍然是分離的。
早期的2D加速驅動使用的是XAA、KAA架構,可是隨着composite擴展的加入,新的exa框架產生了,EXA刪除掉了原來2D驅動中的三角形繪製、線繪製等一些如今沒什麼用處的功能,取而代之的是三個加速功能:矩形填充(Solid)、拷屏操做(Copy/Blt)和混合操做(Composite)。
「XFree86 server 4.x Design (DRAFT)」文詳細介紹xorg驅動須要提供的接口,EXA驅動經過擴展的形式添加並編譯到xorg驅動中。在後續的blog文章裏面將介紹exa驅動接口,並使用radeon的exa驅動來描述顯卡驅動編程過程。
在linux環境中,咱們能夠經過glxinfo命令插卡3D硬件圖形加速是否可用。好比在radeon顯卡上glxinfo的輸出包含如下內容:
OpenGL renderer string: Gallium 0.4 on AMD CEDAR
這裏顯示使用AMD CEDAR核心的顯卡進行opengl 3d加速,若是硬件加速不可用,則應當是「vmware on llvmpipe」這類字眼。
開源的OpenGL實現是mesa,mesa向上提供OpenGL接口,下層經過硬件的mesa驅動和硬件交互。GLX是X協議的擴展,用於OpenGL和X的交互(GLX規範)。在mesa源碼包中包含了大量的opengl示例程序,包括OpenGL紅寶書中的示例代碼、調用GLUT或者GLX接口的代碼、調用EGL的代碼等。
圖1
圖1顯示了一個3D應用程序運行的過程,OpenGL繪圖程序命令被用戶空間的mesa驅動翻譯成對應GPU的繪圖命令放入命令緩衝區中,其餘的如頂點信息/紋理信息/索引信息放入到相應緩衝中,mesa驅動爲每一個應用程序保存了當前的繪圖狀態,當發生3D程序切換,當前狀態被保存下來,當下次調度該程序運行的時候,先恢復該程序的繪圖狀態到硬件上,而後繼續執行命令緩存中的命令。當用戶空間調用發送命令到內核的時候,內核驅動對硬件進行編程從該程序的命令緩衝區中取命令開始執行。這個過程是在dri框架下實現的,這裏的全部繪製過程並不請求X,OpenGL直接將命令發送給硬件。GLX在初始化窗口,申請buffer和切換buffer的時候纔會和X交互。
圖1來自文獻「Graphic Engine Resource Management」,這篇文章描述了對早期的內核drm驅動作的一些改進,在這篇碩士論文「A Fair-Share Scheduler for the Graphics Processing Unit」對一些問題有更清楚的描述。
其餘參考資料:
DRM and KMS kernel modules 描述了drm驅動和kms的一些細節。