【原創】Linux環境下的圖形系統和AMD R600顯卡編程(2)——Framebuffer、DRM、EXA和Mesa簡介


1. Framebuffer

  Framebuffer驅動提供基本的顯示,framebuffer驅動操作的硬件就是一個顯示控制器和幀緩存(一片位於系統主存或者顯卡顯存)。Framebuffer驅動向應用程序提供/dev/fbx的設備接口,應用程序通過讀寫這個設備節點實現對顯示控制器和幀緩存。

  下面這個程序顯示了應用程序操作操作framebuffer節點的過程。運行這個程序,將在屏幕上方顯示一個正方形(這里省略了錯誤檢查代碼)。

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);

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驅動。

2. DRM驅動

  前一篇博文的圖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命令查看內核模塊,能夠看到類似下面的信息:

  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仍然是分離的。

3. EXA驅動

  早期的2D加速驅動使用的是XAA、KAA架構,但是隨着composite擴展的加入,新的exa框架產生了,EXA刪除掉了原來2D驅動中的三角形繪制、線繪制等一些現在沒什么用處的功能,取而代之的是三個加速功能:矩形填充(Solid)、拷屏操作(Copy/Blt)和混合操作(Composite)。

  “XFree86 server 4.x Design (DRAFT)”文詳細介紹xorg驅動需要提供的接口,EXA驅動通過擴展的形式添加並編譯到xorg驅動中。在后續的blog文章里面將介紹exa驅動接口,並使用radeon的exa驅動來描述顯卡驅動編程過程。

4. 3D驅動

  在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的一些細節。

 


免責聲明!

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



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