================
本文主要研究DirectFB及其向上向下的接口,順帶講一下QT搭配DirectFB使用的一些筆記。
其他,如DirectFB的內部結構、Qt的內部結構、及其他GUI系統如android不在本文討論範圍內。
本文會設計一些DirectFB的2D加速函數介紹,3D加速不再本文討論範圍內。
================
桌面GUI系統涉及很多圖像運算,比如畫點、畫線、填充、透明度處理、平滑度處理、層的疊加、字體處理、貼圖等等。如果這些運算都由CPU來處理,那這將是對CPU的一個很大的負載。在QT的文檔中曾說到,假如一個圖形運算讓加速設備來做需要1到2個CLK,同樣的圖形運算讓CPU來算則至少需要20個CLK,而且,對於嵌入式SoC來說,CPU的頻率和SDRAM/DRAM的帶寬始終是圖形運算的瓶頸。所以這些芯片的圖形加速能力有無、高低,最終決定其目標GUI的質量。
近兩年嵌入式設備主芯片越來越多的嵌入了2D/3D加速功能,由此軟件可以搭建越來越複雜、炫酷、先進的桌面系統,如Qt/Android等。這些加速硬件,通常也因其是否擁有私有內存、是否支持3D、加速接口是否可編程等特性而分爲三六九等。硬件的加速功能如果要在目標GUI系統中跑起來,還得需要一些軟件中間層爲不同的硬件加速提供同一的接口,並且爲上層GUI提供服務。DirectFB就是這樣一個軟件中間層,它主要爲上層提供2D加速服務。OpenGL/ES 主要爲上層提供3D加速服務。
DirectFB是一個專門針對Linux圖形庫設計的圖形加速中間層。它的上層直接面向圖形庫比如QT,提供如顯示、畫圖、輸入設備控制等服務,他的下層直接面向GFX加速硬件,要求硬件驅動實現全部或部分預定的畫圖函數。
2. DirectFB給上層圖形庫提供的接口調用
2.1 主要接口
DirectFB由超級接口IDirectFB爲上層提供接口調用,其他所有接口都由此接口生成,這些接口內容包括:
=========================
. IDirectFBScreen
. IDirectbFBDisplayLayer
. IDirectFBSurface
=========================
顯示屏幕、顯示層、顯示錶面,三者之間的關係可這樣表述:
顯示屏幕<-Output(connector)<-IDirectFBScreen<-(mixer)-DisplayLayers <-DisplayLayer's IDirectFBSurface<-gfx Accelarator <--顯卡/或CPU運算畫圖
首先,用戶在顯示屏幕上看到一個Screen的輸出,這個Screen由多個畫面組成(比如同時包含:背景圖片,前景視頻/字幕/圖片)。組成Screen的每個畫面,都由一個單獨的DisplayLayer來處理。多個DisplayLayers由Mixer來決定疊加的順序/位置/以及透明度等。單獨一個顯示層DisplayLayer,可自由控制輸出大小(放大/縮小)、背景顏色、翻轉等。DisplayLayer所顯示的數據(比如解析JPEG產生的RGB數據),則需要在DisplayLayer上生成一個Surface來顯示,Surface控制實際的貼圖操作,比如:畫點/線/框/填充/變換位數、色深、分辨率,等等, Surface可直接和硬件加速打交道(gfxcard)。
以上描述的DisplayLayers和Surface都是Screen的後端,Screen的前端(就是輸出部分),還有Encoder,Output等概念。Encoder着重描述輸出信號的編碼參數,Output着重描述輸出接口. 它們有以下重要成員:
Signals :配置輸出信號(VGA/YC/CVBS/RGB/YCBCR/HDMI/656/>>擴展<<)
Connectors :配置輸出接口(VGA/SCART/YC/CVBS/SCART2/COMPONENT/HDMI/>>擴展<<)
Resolution :配置輸出分辨率(directfb.h中所列舉的常見分辨率/>>擴展<<)
以上表述中,關於每個節點功能的詳細表述可能有誤,但其所處的位置和大概的介紹應該是對的。筆者曾參與SigmaDesigns系列解碼芯片和PNX8735系列的研發工作,發現DFB的這種結構設計非常接近於這類芯片已有的硬件設計。
這部分代碼主要分佈在子目錄“src/display”下。
=========================
IDirectFBImageProvider
IDirectFBVideoProvider
IDirectFBFont
=========================
參見源代碼:idirectfbimageprovider_dfiff.c / idirectfbimageprovider_gif.c / idirectfbimageprovider_jpeg.c / idirectfbimageprovider_png.c
DFB對dfiff/gif/jpeg(libjpeg.a)/png(libpng.a)第三方軟件解碼庫做了簡單封裝,以在DFB的框架內在恰當的時候去解碼或貼圖。
在子目錄“interfaces/IDirectFBImageProvider”下有具體圖片格式的實現,具體是否生效取決於configure參數。
舉例JPEG通過imageprovider解碼和貼圖的過程,具體參見以下代碼:
- tests/dfbtest_scale.c
- lib/direct/interface.c
- lib/direct/interface_implementation.h
- interfaces/IDirectFBImageProvider/idirectfbimageprovider_jpeg.c
1. idirectfbimageprovider_jpeg.c會把自己的'funcs'註冊到interface鏈表中,注意,Register的調用是通過GNU的__attribute__((constructor))實現的,即進入main函數之前已經註冊,沒有顯式調用註冊函數。
2. 測試代碼通過CreateImageProvider調用創建provider,此函數會定位到interface.c::DirectGetInterface()中。DirectGetInterface函數遍歷interface鏈表,首先匹配provider類型,再通過具體implementation的Probe函數回調挨個匹配,Probe函數有的是通過文件擴展名匹配,有的需要讀取文件內容進行匹配(這裏JPEG是通過文件擴展名匹配的)。與某種類型的provider完全匹配後,返回provider,調用其Construct函數. test程序再調用GetSurfaceDescription獲取圖片的參數,再用此參數通過函數CreateSurface創建目標顯示錶面,再通過provider的RenderTo函數往目標Surface貼圖。
視頻採集與顯示IDirectFBVideoProvider, VideoProvider分兩種:
- idirectfbvideoprovider_v4l.c: 直接從v4l設備讀取yuv數據,軟件貼圖顯示視頻。
- idirectfbvideoprovider_gif.c: 封裝gif第三方庫,軟件顯示gif動畫。
- idirectfbfont_ft2.c: 軟件封裝freetype2庫,軟件解析貼圖字體矩陣。
- idirectfbfont_dgiff.c: 封裝dgiff.c庫.
每種類型的provider,其貼圖/播放控制函數都位於其結構定義IDirectFBXXXProvider(其中XXX=Image/Video),或者IDirectFBFont中。
=========================
IDirectFBEventBuffer
IDirectFBDataBuffer
IDirectFBInputDevice
=========================
IDirectFBEventBuffer: 事件同步機制,一端接入產生事件的設備,使用PostEvent之類的接口遞交事件,另一端使用WaitForEvent之類的接口等待事件,使用GetEvent之類的接口獲取事件。
常用與輸入設備
IDirectFBDataBuffer: 數據DataBuffer緩衝,提供同步等待功能,並提供CreateXXXProvider(XXX=Image/Video)功能,在原代碼中被廣泛使用.
IDirectFBInputDevice: 輸入設備接口
2.2. QT與DirectFB之間的關係
QT從4.3版本開始嘗試加入DirectFB,直到4.6版本以後才穩定下來。DirectFB至今也到了1.5.0版本(201106),給圖形庫提供的硬件加速接口也在不斷的擴充。
需要說明,DirectFB並不包含QT的所有畫圖函數的實現,這一點在QT的文檔中有表述。
QT中調用的DirectFB代碼,主要分佈在子目錄"src/plugins/gfxdrivers/directfb/"下,上面提到的幾乎所有接口都有涉及。
筆者移植QT+DirectFB過程中,使用了QT的Configure選項:
./configure -embedded mips -qt-gfx-directfb -little-endian -prefix /tftpboot/install -prefix-install -v
運行時敲入命令:
./browser -qws -display directfb
3. DirectFB給底層硬件提供的接口。
3.1 輸入設備接口: InputDrivers
輸入設備的管理位於核心代碼src/core/input.c,輸入設備的接口定義位於src/core/input_driver.h。 DFB對輸入設備的管理、使用,只是簡單的對輸入設備的設備文件進行封裝,並沒有真正的去驅動某種輸入設備。
要添加一個輸入設備,首先要在DFBROOT目錄下的子目錄"inputdrivers/"下添加一個源文件,或者是在"inputdrivers/"目錄下修改一個已有的源文件。 "inputdrivers"的名稱來自於“src/core/input.c”頭上的DEFINE_MODULE_DIRECTORY定義,所以不同DFB版本對應的目錄名稱可能不同。 其次,要在你的源文件中引用頭文件'core/input_driver.h'並實現InputDriverFuncs所定義的函數:
- driver_get_info(): 用於獲取driver的vendor/name/version等信息。
- driver_get_available(): 用於判斷設備是否可用,返回值表示當前driver可驅動的設備數量。
- driver_open_device(): 打開輸入設備文件,並開啓線程監聽設備輸入。
- driver_get_keymap_entry(): 鍵值修復/更改/映射函數,好像只有keyboard才用,用於處理組合鍵/大小寫/小鍵盤等鍵值。
- driver_close_device: 關閉監聽線程,關閉設備文件
這些函數的具體調用路徑如下:
- 涉及源文件: src/core/input.c, src/core/input_driver.h, src/core/core.c,例子inputdrivers/keyboard/keyboard.c
- Core初始化->InputCore初始化->input.c:init_devices():
- 遍歷inputdrivers下所有輸入設備驅動: 調用driver_get_info()獲取驅動信息,調用driver_get_available()判斷當前設備是否可用,調用driver_open_device()打開並監聽設備
- Core關閉/Suspend: 調用driver_close_device()關閉所有已打開設備,然後清空事件緩衝。
- Core Resume: 再次調用driver_open_device()打開Suspend之前處於開啓狀態的設備。Suspend/Resume由IDirectFB.Suspend/Resume接入,由用戶控制可用於休眠操作。
- driver_open_device()中會打開一個線程監聽設備輸入,輸入鍵值通過dfb_input_dispatch()分發,調用堆棧包括driver_get_keymap_entry()用於重新映射鍵值。鍵值最終通過fusion_reactor_dispatch()向上層分發事件。
如果你是新添加的一個.c文件,可能你還需要修改configure文件和Makefile.XXX文件。
3.2 硬件加速gfxcard.
* 涉及源代碼src/core/graphics_driver.h, src/core/gfxcard.h, src/core/gfxcard.c, 舉例:gfxdrivers/pxa3xx/
這一章很複雜,筆者也只能泛泛而談。
個人認爲,DirectFB最重要的功能就是能夠統一不同的硬件顯示加速設備的各種加速功能爲上層圖形庫所用。
gfx加速在DFB中分兩層概念:
- graphics driver : 需要實現gfxcard.h::GraphicsDriverFuncs所定義的所有函數。
- graphics device : 按需實現gfxcard.h::GraphicsDeviceFuncs所定義的函數。
graphics_driver是core的一個模塊,它的函數調用堆棧跟InputDriver很像,依次爲:
GraphicsDriverFuncs的調用堆棧:
src/core/gfxcard.c: dfb_graphics_core_initialize()->
{Driver.Probe()&Driver.GetDriverInfo()}->
Driver.InitDevice() ->
Device.EngineReset()
真正實現加速功能的,是graphics device的函數調用。先看看DirectFB是如何把用戶的畫圖動作,轉化爲gfxcard的硬件實現的。
前面講過Surface控制實際的貼圖操作,所以下面的調用堆棧跟蹤都從IDirectFBSurface開始。
上層用戶可通過IDirectFBSurface.GetAccelerationMask()的返回值判斷所需畫圖動作是否可以加速,具體調用堆棧如下:
src/display/idirectfbsurface.c:IDirectFBSurface_Construct() -> IDirectFBSurface_GetAccelerationMask() -> src/core/gfxcard.c:dfb_gfxcard_state_check()
-> gfxdrivers/pxa3xx/pxa3xx.c & gfxdrivers/pxa3xx/pxa3xx_blt.c: pxa3xxCheckState()
即: Device的CheckState()函數,是用於判斷所需加速功能是否可用的。
一次具體的畫圖操作(比如:填充矩形),調用堆棧如下:
src/display/idirectfbsurface.c:IDirectFBSurface_FillRectangle() -> src/core/gfxcard.c: dfb_gfxcard_fillrectangles()
dfb_gfxcard_state_check( state, DFXL_FILLRECTANGLE ) -> gfxdrivers/pxa3xx/pxa3xx_blt.c: pxa3xxCheckState()
dfb_gfxcard_state_acquire( state, DFXL_FILLRECTANGLE ) -> gfxdrivers/pxa3xx/pxa3xx_blt.c: pxa3xxSetState()
card->funcs.FillRectangle( driver_data, device_data, &rect ) -> gfxdrivers/pxa3xx/pxa3xx_blt.c: pxa3xxFillRectangle()
即: Surface在調用每一個加速畫圖函數之前,都會先調用Device.CheckState()判斷所需函數是否可達,再調用Device.SetState()告知具體device即將使用此類加速函數(pxaxxx是在SetState()中確定接下來該使用哪個加速函數來實現當前畫圖功能),然後調用Device的具體加速函數畫圖。
瞭解了畫圖加速函數的調用堆棧,再來看看gfxcard支持哪些加速函數,(參考源代碼src/core/gfxcard.h):
FillRectangle : 填充矩形
DrawRectangle : 畫矩形
DrawLine : 畫線
FillTriangle : 填充三角形
Blit : 位塊傳送(內存拷貝/移動?)
Blit2 : 位塊傳送2(支持源座標)
StretchBlit : 位塊傳送並拉伸
TextureTriangles : ?
可以看出,列舉的函數都是2D加速函數。
4. DirectFB移植事項
DirectFB是一個軟件中間層,它爲底層的硬件驅動和上層的GUI提供了統一的接口調用,但它不做任何實際畫圖操作。要移植DirectFB到目標嵌入式硬件上,爲了最大限度利用硬件的加速功能,移植的過程中要考慮以下問題:
- 首先要實現DirectFB的顯示路徑。以Screen爲切入點,根據目標硬件的特性,向前實現各種Encoder->Output的顯示通路,向後實現Screen的各種顯示Layer->Surface。
- 如果目標硬件支持JPEG/PNG/GIF的硬件解碼,那麼你可能需要實現對應的ImageProvider以代替軟件解碼。
- 如果目標硬件支持多種Video格式的硬件解碼,那麼你可能需要實現對應的VideoProvider以實現硬件解碼播放的功能。
- 如果目標設備具有自己的輸入小鍵盤(GPIO擴展、或者其他特殊輸入設備),那麼你可能需要實現自己的InputDriver。
- 如果目標硬件具有GFX加速引擎,那麼你可能需要實現對應的gfxdriver以及實現gfxdevice中的各種畫圖函數。