Showing posts with label dri. Show all posts
Showing posts with label dri. Show all posts

Thursday, June 25, 2009

一千零一夜之 GEM Object

這個系列是讀書筆記,作者可能沒有跟主題有關的開發經驗。


GEM object 是一種 buffer,一種可以在 A 程式被配置,在 B 程式被使用的 buffer。當 userspace 產生一個 GEM object 時,以 i915 為例,它會呼叫 drm_gem_object_alloc。從這個函數的頭幾行

struct drm_gem_object *obj;

BUG_ON((size & (PAGE_SIZE - 1)) != 0);

obj = kcalloc(1, sizeof(*obj), GFP_KERNEL);

obj->dev = dev;
obj->filp = shmem_file_setup("drm mm object", size, VM_NORESERVE);
if (IS_ERR(obj->filp)) {
kfree(obj);
return NULL;
}


可以看出,它只是一塊 shared memory。就這個方式來看,GEM object 其實不是新的東西。

但這塊 buffer 除了 A、B 程式會用到外,它還另外有一個使用者,這個使用者是 GPU。之所以把 shared memory 包裝成 GEM object 最主要的原因是,kernel 需要同步 CPU 和 GPU 對這塊 buffer 的存取。很明顯地,如果讓 CPU 與 GPU 同時寫入同一塊記憶體,出來的結果一定無法預期。在 GEM object 裡,處理同步的方法是引入 domain 的機制。所有人在存取 GEM object 前,必須先把它的 domain 設好。如果是 GPU 要讀取或寫入,那就需要先把 read domain 或 write domain 設成 GPU;相對的,如果是 CPU 要讀取或寫入,則需要把 read/write domain 設成 CPU。DRM 保證的是,只要正確地指定 domain,存取到的資料也會是正確的。

為了解 domain 跟同步的關係,我們可以看一下當我們把 read domain 指定為 CPU 時,kernel 會幫我們做什麼處理。這部份完整的程式碼可以在 i915_gem_object_set_to_cpu_domain 看到。

首先,kernel 要確定之前所下的 GPU 指令已經全部執行完畢,而且 GPU cache 要 flush 掉

i915_gem_object_flush_gpu_write_domain(obj);
ret = i915_gem_object_wait_rendering(obj);


GPU 的運作方式是會依序執行某個指定的 ring buffer 裡面的指令,而開發者要做的是把指令放到這個 buffer。為了達到目的,kernel 會在 ring buffer 最後加上 flush 跟 interrupt 指令,並且開始等待中斷。我們可以想像,當 kernel 收到中斷時,也就表示之前的指令都已經執行,包含它自己加上的 flush 指令。

事實上,CPU 除了直接存取 shared memory 外,也可以從 AGP aperture 去存取,這在 i915 裡稱做 GTT domain。所以 kernel 也要確定這邊沒有資料被 cache 住

i915_gem_object_flush_gtt_write_domain(obj);


這些動作可能造成記憶體上的資料被改變,而,這時候 CPU 還不知道這件事。所以只要再確保 CPU 可以知道這些變動

i915_gem_object_set_to_full_cpu_read_domain(obj);
if ((obj->read_domains & I915_GEM_DOMAIN_CPU) == 0) {
i915_gem_clflush_object(obj);
obj->read_domains |= I915_GEM_DOMAIN_CPU;
}


我們就可以說 GEM object 已經被移到 CPU read domain。

GEM object 是 shared memory 的特性,讓 X server 可以直接把 buffer 傳給 client 做 DRI。它同時也可以當做到 OpenGL 裡頭各種 buffer object 的 storage。除了 Intel 外,在 2.6.31 也可以看到初步的 ATI Radeon KMS 支援。雖然從使用者的經驗來看,似乎只是進入另一個黑暗時期。但是在領袖的帶領下,我們好像也可以看到光了!

Thursday, June 4, 2009

Android 3D acceleration on x86

過去一、二個月的文章都是圍繞著 android x86 與 mesa/dri 在打轉,原因無它,因為這陣子在做的事恰好就是 Android 在 Eee PC 上的 3D 加速。因為 android 跟 mesa 都是第一次接觸,所以確實費了不少工夫在認識它們。

Android 在起來的時候,會試著載入 /system/lib 底下的 libagl.so 與 libhgl.so。前者是 OpenGL ES 與 EGL 的純軟體實作,source code 在 frameworks/base/opengl/libagl/ 底下;後者則與硬體相關,目前沒有開放的實作。所以我做的事基本上就是寫一份 libhgl.so。

之前在 DRI Overview 有提到,只要有適當的 loader,DRI driver 可以在 X 以外的平台使用。目前我的 libhgl.so 實作是基於 eagle,並只支援 intel graphics chipsets。因為 i915_dri.so 實作了 OpenGL,而不是 OpenGL ES,所以定點數與部份 ES 才有的 extension 沒有辦法支援。這個問題最快的解決方式應該是改用 Gallium 搭配 OpenGL ES state tracker,這也是接下來要評估的。

對 Android 本身,主要則有三個部份需要修改。一是 EGLDisplaySurface 要用 kernel modesetting 的實作取代,同時 kernel 也要上 2.6.29;二是 GPUHardware 要改用 i915 的 GEM object 實作;三是 Surface 要能認得 GEM object。

目前整個 Android 系統可以正常運作。SurfaceFlinger 或一些 3D 軟體像是 Android-GL 也可以正確地被加速。不過目前的實作比較接近於驗證這個方式的可行性。程式碼整理好後會放在 Android Eee PC,到時有興趣的開發者都可以一同參與。

Wednesday, April 29, 2009

一千零一夜之 eagle

這個系列是讀書筆記,作者可能沒有跟主題有關的開發經驗。

之前的文章提到過,只要有適當的 loader,DRI driver 也可以用在 X server 以外的平台,eagle 就是一個例子。eagle 提供開發者接近於 EGL 的 API,並且可以運作在支援 DRI2 的 X server 與支援 GEM 的 intel drm 之上。這篇文章主要想討論後者。

eagle 與視窗系統無關的實作,行數落在大約 800 行左右。intel drm 的支援也只在 250 行左右。對它使用到的技術有些許了解後,只要花一個晚上就可以讀完,算是學習 linux 3D 實作不錯的途徑。也因為它的簡單,閱讀過程如果有難以理解的部份,也可以馬上修改跑跑看。如果這兩部份看完後想要再看 DRI2 的部份,eagle 也可以讓人滿足。

intel drm 的支援在 eagle 中稱為 backend,以 EagleBackend 表示。Backend 與視窗系統相關,要提供的主要功能有

  • 建立 EGLSurface

  • 提供 DRI driver 需要的 GEM buffer

  • 更新畫好的內容到螢幕上



intel drm backend 建立 EGLSurface 的函數是 intelCreateSurfaceForName。這邊的 name 型別為 uint32_t,指的是 GEM buffer 的 identifier。如果沒給的話,backend 會產生一塊新的 buffer。client 可以要求 double buffering,這時 surface 的 backBuffer 會設為 EGL_TRUE。

intelGetBuffers 負責提供 DRI driver 需要的 GEM buffer,像是 front buffer,back buffer 或 depth buffer 等。這邊要特別注意,在 double buffering 的情形,也就是 backBuffer 設為 EGL_TRUE 的時候,即便 DRI driver 要求了 front buffer,intelGetBuffers 還是會產生新的 buffer 回傳給 DRI driver,而不是回傳 intelCreateSurfaceForName 時給定或產生的 front buffer。這個新產生的 buffer 也被稱為 fake front buffer。

最後看到 intelSwapBuffers,也就是 eglSwapBuffers 會呼叫的函數。可以看到,當不是 double buffering 時,這個函數會直接 return,因為 DRI driver 本來就是畫在真正的 front buffer 上。若是 double buffering 的話,intelSwapBuffers 會把 DRI driver 以為的 (fake) front buffer 複製到真正的 front buffer 上。

Backend 大致上就處理這些事情。eagle 的核心則負責提供接近 EGL 的 API。它同時也是 DRI loader,選定適當的 EagleBackend 並載入適當的 DRI driver。除了選擇與 dlopen 適當的 .so 檔外,核心其餘的程式碼大多在處理 EGL 的型別與 DRI 型別的對映。這個部份很基礎,也是所有想寫 loader 的開發者都要熟悉的東西。這邊的程式碼比較直接,有興趣的人可以自行閱讀。

在初次研讀 eagle 的過程中,我最感興趣的其實是這篇文章沒提到的 glapi。這是 mesa 或 mesa 自動產生的程式碼,其中後者佔了絕大多數。在許多功用中,其中一項功用是提供 OpenGL (ES) 的 global symbols,讓 link 到 libeagle.so 的程式可以呼叫 gl* 等函數。這可以算是 mesa build system 的一部份,對熟悉 linux 3D 的組成有相當的幫助。

Monday, April 6, 2009

一千零一夜之 DRI2 Overview

這個系列是讀書筆記,作者可能沒有跟主題有關的開發經驗。

Linux 的圖形加速一直是個問題,不過這個問題在最近幾版的 kernel 隱約可以看到一些希望。從 2.6.28 的 GEM memory manager 到 2.6.29 的 kernel modesetting,由 Intel 與 RedHat 的開發者所提出的加速架構已經進入 kernel,雖然目前採用這些架構的只有 Intel 自身的驅動程式,但其它繪圖晶片的 open source 驅動程式勢必也得跟隨此架構。在 Intel 與 AMD 相繼釋出 datasheet 後,或許有天所有的使用者都可以享用這些成果,不再依賴私有的驅動程式。

去年年中 GEM 公佈後,許多架構迅速翻新,DRI2 最後也環繞著 GEM 在設計。DRI (Direct Rendering Infrastructure) 是各家 open source 驅動程式所依循的架構,雖然處處都可以看見這個名詞,但是要了解它背後的實作卻不太容易。困難的地方在於 DRI 的組成份子很多,而且大部份來頭都不小。以 Intel 的 945GM 為例,在 kernel 有 drm.ko 與 i915.ko 兩個模組,對映到 userspace,有 drm 提供的libdrm.so 與 libdrm_intel.so;在 xserver 有 libglx.so 與 libdri2.so 這兩個 extension,以及 xf86-video-intel 提供的 intel_drv.so;最後在 mesa 也有 libGL.so 以及 i915_dri.so。習慣上,intel_drv.so 被稱作 2D driver 或 DDX driver,i915_dri.so 則是 3D driver 或 DRI driver。從一個 3D 程式被執行時到顯示在螢幕上,參與這個看似普通的過程背後就是上面這些檔案。

DRI 最重要的內涵在於,一個 X client 可以向 X server 取得視窗的 buffer(s),並利用硬體加速在上面直接繪圖,不再透過 X server。Kernel 在 DRI 扮演的主要角色是讓多個 process 可以共享 (GEM) buffer,還有接受與執行 process 傳來的硬體指令。這邊困難的地方之一在於 CPU 跟 GPU 都有各自的 MMU 與 cache,所以有許多同步的問題要處理。

libGL.so 在 DRI 架構裡扮演了 loader 的角色,它跑在 client 這邊。就像 OpenGL 與平台無關一樣,DRI driver 也與平台無關。loader 一方面載入 DRI driver,一方面又提供適當的 hook 讓 DRI driver 呼叫,並實作了平台相關的部份。所以在 DRI driver 真的要用到 buffer 的時候,它會呼叫 loader 的 getBuffers 函數取得視窗的 buffer 與大小。libglx.so 跟 libdri2.so 實作了 GLX 與 DRI2 這兩個 X extensions。在 X 的平台上,對於 DRI driver 的要求,libGL.so 會利用 GLX 與 DRI2 協定取得所需的資訊,並傳回給 DRI driver。

一個快速驗證上面說法的方法是直接檢視系統的 /usr/lib/libGL.so 跟 /usr/lib/dri/i915_dri.so。libGL.so 只是 loader,檔案大小應該要比 i915_dri.so 小不少;而 i915_dri.so 與平台無關,所以 ldd 應該不會看到 X11 的函式庫。如果再進一步想,那是不是只要提供適當的 loader,DRI driver 也能用在 X 以外的平台?答案是肯定的。

在 compiz 剛出來的時候,有很多名詞跟著它一起冒出來。像是 XGL、GLX_EXT_texture_from_pixmap、還有 AIGLX 等。在 libGL.so 無法成功跟 X server 溝通使用 DRI 時,client 的 3D 繪圖指令會改經由 GLX 傳給 server 處理,這被稱作 indirect rendering。在早先,libglx.so 會利用純軟體的方式執行這些 3D 指令,所以 indirect rendering 跟 software rendering 是等價的。這個情形在 AIGLX 引入後有很大的改變。在 AIGLX 下,libglx.so 除了提供 GLX extension 實作外,它同時也是一個 DRI loader,可以利用 DRI driver 進行 3D 加速。因為 AIGLX,indirect rendering 不再直接被視為慢、無法接受,雖然它還是多了 client 到 server 的 overhead。AIGLX 即是 Accelerated Indirect GLX 的縮寫,非常直接。

因為我直接從 DRI2 開始看,無法比較它跟 DRI1 的差異。在 DRI2 作者 Kristian Høgsberg 的 blog 可以找到一些關鍵字。Kristian Høgsberg 同時也是之前文章提到的 Wayland 的作者。

Sunday, December 14, 2008

一千零一夜之 DRI

這個系列是讀書筆記,作者可能沒有跟主題有關的開發經驗。


DRI 讓 X clients 可以直接利用硬體來做繪圖,這點可以利用下面指令看出來:

olvaffe@m500:~$ glxgears &
[1] 24684
olvaffe@m500:~$ cat /proc/24684/maps
08048000-0804b000 r-xp 00000000 08:03 80591 /usr/bin/glxgears
0804b000-0804c000 rw-p 00002000 08:03 80591 /usr/bin/glxgears
...
b38fa000-b58fa000 rw-s e6000000 00:0d 7315 /dev/dri/card0
b58fa000-b62fa000 rw-s e5000000 00:0d 7315 /dev/dri/card0
b62fa000-b6cfa000 rw-s e4000000 00:0d 7315 /dev/dri/card0
b6cfa000-b733a000 rw-s 2c200000 00:0d 7315 /dev/dri/card0
b733a000-b797a000 rw-s 2c200000 00:0d 7315 /dev/dri/card0
...
b79a0000-b7bbd000 r-xp 00000000 08:03 347365 /usr/lib/dri/i915_dri.so
b7bbd000-b7bd1000 rw-p 0021c000 08:03 347365 /usr/lib/dri/i915_dri.so
b7c00000-b7c07000 r-xp 00000000 08:03 66335 /usr/lib/libdrm.so.2.3.1
b7c07000-b7c08000 rw-p 00006000 08:03 66335 /usr/lib/libdrm.so.2.3.1
b7eb5000-b7f11000 r-xp 00000000 08:03 69724 /usr/lib/libGL.so.1.2
b7f11000-b7f17000 rw-p 0005b000 08:03 69724 /usr/lib/libGL.so.1.2
...

我們可以看到,glxgears 把 i915_dri.so (intel 的 driver) 載進來並且開啟了 /dev/dri/card0。而在不使用 DRI 的時候:

olvaffe@m500:~$ LIBGL_ALWAYS_INDIRECT=1 glxgears &
[1] 24834
olvaffe@m500:~$ cat /proc/24834/maps
08048000-0804b000 r-xp 00000000 08:03 80591 /usr/bin/glxgears
0804b000-0804c000 rw-p 00002000 08:03 80591 /usr/bin/glxgears
...
b7bd3000-b7bda000 r-xp 00000000 08:03 66335 /usr/lib/libdrm.so.2.3.1
b7bda000-b7bdb000 rw-p 00006000 08:03 66335 /usr/lib/libdrm.so.2.3.1
...
b7e88000-b7ee4000 r-xp 00000000 08:03 69724 /usr/lib/libGL.so.1.2
b7ee4000-b7eea000 rw-p 0005b000 08:03 69724 /usr/lib/libGL.so.1.2
...

則少了上述的檔案。這是因為繪圖方面都轉送給 X server 去處理。如果用 CPU usage 去看,也可以看出在這兩個情形下,比較忙的可能是 server 或著是 client。而就像 client 可以利用 DRI 做加速一樣,X server 在收到 client 的請求時,也可以利用 DRI 去完成這些請求,這個技術被稱為 AIGLX (Accelerated Indirect GLX)。

從上面的例子我們可以看出 DRI 這個架講的基本組成份子:提供 /dev/dri/card0 的 i915 kernel module、存取這個裝置用到的 libdrm、Mesa 提供的 libGL 與 i915_dri.so、最後 X server 那邊提供了 GLX 跟 DRI server extensions。值得一提的是,X server 這邊雖然沒有用到 libGL,但它其實用到了部份 Mesa 的 source 來實作 GLX 跟 DRI server extensions,在 AIGLX 的情況下,甚至會 dlopen i915_dri.so 來用。