標籤:wayland weston graphics window manager
原文地址:http://blog.csdn.net/jinzhuojun/article/details/46794479
簡單地說,Wayland是一套display server(Wayland compositor)與client間的通訊協定,而Weston是Wayland compositor的參考實現。其官網為http://wayland.freedesktop.org/。它們定位於在Linux上替換X圖形系統。X圖形系統經曆了30年左右的發展,其設計在今天看來已略顯陳舊。在X系統中,X Server作為中心服務,串連clien和硬體以及compositor。但時至今日,原本在X Server中做的事很多已被移到kernel或者單獨的庫中,因此X Server就顯得比較累贅了。Wayland在架構上去掉了這個中介層,將compositor作為display server,使client與compositor直接通訊,從而在靈活性和效能等方面上能夠比前輩更加出色。
Wayland既可以用於傳統的案頭又適用於行動裝置,已經被用於Tizen,Sailfish OS等商業作業系統,同時越來越多的視窗和圖形系統開始相容Wayland協議。Wayland基於domain socket實現了一套display server與client間通訊的庫(簡單的基於例子的介紹可以參見http://blog.csdn.net/jinzhuojun/article/details/40264449),並且以XML形式定義了一套可擴充通訊協定。這個協議分為Wayland核心協議和擴充協議(位於Weston中)。Weston作為Wayland compositor的參考實現,一般和Wayland同步發布。其它Wayland compositor實現還有如mutter, Kwin, Lipstick, Enlightenment, Clayland等。
下面分別從架構,源碼及模組結構,渲染流水線,視窗和輸入管理幾個方面介紹下Wayland/Weston的實現。
架構
Wayland的系統體系架構可以參見官方文檔,不再累述。Weston從內部體繫結構來看,主要分為視窗管理(shell),合成器(compositor)和輸入管理幾個部分。可見,如果拿Android作類比,從功能上看它約等同於InputManagerService,WindowManagerService和SurfaceFlinger。從大體的流程上來看,輸入管理模組接受使用者輸入,然後一方面shell作出相應的視窗管理操作(如視窗堆棧的改變,focus的變化等),另一方面將該input event傳給之前註冊了相應輸入事件的client。client收到後會在handler中做相應動作,如調整視圖然後重繪。如有重繪發生,新buffer渲染完成後client將其handle傳給server,接著server端產生z-order序的視窗列表,之後compositor用renderer進行合成,最後輸出(比如到framebuffer)。
Weston是主要服務進程,它的事件處理模型採用的是典型的Reactor模式。根據Linux中萬物皆檔案的原則,主迴圈通過epoll機制等待在一系列的檔案fd上。這種模型與基於線程的binder不同,是一種串列的事件處理模型。在此模型上的程序呼叫在不加額外同步機制的情況下是非同步。好處是不會有競爭問題,資料同步開銷較小。缺點是當有一個事件處理比較耗時或者在等待IO,則有可能使整個系統效能下降或響應不及時。
主迴圈上等待的幾個核心fd包括:
? Server/Client通訊:listener fd在Weston啟動時建立,並一直監聽新的client串連。一個client串連後會與Weston建立一對domain socket,Wayland就是基於它來通訊的。
? 輸入處理:一方面通過udev monitor監聽裝置的添加刪除事件。另一方面如有新裝置添加時會將該裝置開啟並監聽該fd來得到輸入事件。
? 其它:監聽如timer(用於如睡眠鎖屏等情境)和signal(如收到SIGINT, SIGTERM, SIGQUIT時退出主迴圈)等事件。timer和signal可以分別用timerfd和signalfd來用fd來表示。另外還有logind的dbus串連等。
除這些外,在event loop中還會維護一個idle list。Weston中需要非同步處理的操作可以放在其中。每輪迴圈都會檢查其中是否有任務,有的話拿出來執行。
下面看下Weston的運行時進程模型。Weston設計時是可以以一般使用者啟動並執行,但就需要用weston-launch來啟動。當需要進行一些需要root許可權的工作,比如關於DRM, TTY, input device的相關操作,就交由weston-launch去做。
Weston會在啟動時或按需起一些子進程,它們本質上是Weston的client,它們會通過專用的協議做一些系統應用的工作。如系統應用weston-desktop-shell負責一些系統全域的介面,比如panel, background, cursor, app launcher, lock screen等。它不作為Weston服務本身的一部分,而是作為一個client。其作用有點類似於Android中的SystemUI。這樣便可方便地替換成定製的介面。weston-keyboard是軟鍵盤面板。weston-screenshooter和weston-screensaver分別用於截屏和屏保,它們都是按需才由Weston啟動的。前者在截屏快速鍵按下時啟動,後者在需要鎖屏時啟動。
另外,Weston啟動時會讀取weston.ini這個設定檔,其中可以配置案頭,動畫和後端等等資訊。詳細配置見http://manpages.ubuntu.com/manpages/raring/man5/weston.ini.5.html。
源碼與模組結構
wayland/weston/libinput等項目源碼位於http://cgit.freedesktop.org/wayland下。
Wayland的協議定義在protocol目錄,通訊協定實現在src目錄。它主要編譯出三個庫,libwayland-client,libwayland-server和libwayland-cursor。第一個為協議的client端實現,第二個為協議的server端實現。第三個是協議中滑鼠相關處理實現。編譯時間會首先編譯出wayland-scanner這個可執行檔,它利用expat這個庫來解析xml檔案,將wayland.xml產生相應的wayland-protocol.c,wayland-client-protocol.h和wayland-server-protocol.h。它們會被Wayland的client和server在編譯時間用到。同時wayland-scanner也需要在產生Weston中的Wayland擴充協議中起同樣作用。
Wayland主要依賴於兩個庫,一個上面提到的expat協議,另一個libffi用於在跨進程程序呼叫中根據函數描述產生相應calling convention的跳板代碼。
Weston的主要實現在src目錄下。與Wayland類似,protocol目錄下放著Wayland協議定義。在clients目錄下是一些client的例子,也包括了desktop-shell和keyboard等核心client的例子,也包含了如simple-egl, simple-shm, simple-touch等針對性的簡單用例。Weston啟動過程中會分別載入幾個backend:shell backend, render backend和compositor backend。它們分別用於視窗管理,合成渲染和合成內容輸出。
由於這些後端都可有不同的實現,為了邏輯上的獨立性和結構上的靈活性,他們都編譯成動態連結程式庫從而可以在Weston初始化時被載入進來。這種方式在Weston中被廣泛採用,一些功能比如螢幕畫面分享等都是以這種形式載入的。
舉例來說,compositor backend主要決定了compositor合成完後的結果怎麼處置。從資料結構上,weston_output是output裝置的抽象,而下面的backend會實現具體的output裝置。
? fbdev:直接輸出至linux的framebuffer裝置。介面通用。
? headless:和noop-renderer配合使用,可以在沒有視窗系統的機子(比如server上)測試邏輯。
? RPI:用於Raspberry Pi平台。
? RDP:合成後通過RDP傳輸到RDP peer顯示,用於遠端桌面。
? DRM:Direct redering manager,案頭上一般用這個。
? x11:Wayland compositor作為X server的client。它可以讓Wayland client運行在X11上。
? wayland:Wayland composiotr作為server同時,也作為另一個Wayland compositor的client。用於nested compositor。
Renderer backend主要用於compositor的合成之用,除去noop-renderer外,有gl-renderer和pixman-renderer兩種。前者為GPU硬體渲染,後者為軟體渲染。shell backend用於實現具體的視窗管理。相應的實現分別在desktop-shell,fullscreen-shell和ivi-shell目錄中。
Wayland/Weston運行時依賴的庫主要有下面幾個,其相互關係大體如下。
? libEGL, libGLES:本地視窗系統與圖形driver的glue layer,mesa提供了開源版本的實現。
? libdrm:封裝KMS,GEM等圖形相關介面。平台相關。
? libffi:用於在運行時根據調用介面描述產生函數跳板並調用。
? pixman:用於像素操作的庫,包括region, box等計算。用了許多平台相關的最佳化。
? cairo:軟體渲染庫,類似於skia。也有OpenGL後端。
? libinput:輸入處理,依賴於mtdev, libudev, libevdev等庫。
? libxkbcommon:主要用於鍵盤處理。
? libjpeg, libpng, libwebp:用於載入各種圖片檔案,像壁紙,面板和滑鼠等都需要。
渲染流水線
一個Wayland client要將記憶體渲染到螢幕上,首先要申請一個graphic buffer,繪製完後傳給Wayland compositor並通知其重繪。Wayland compositor收集所有Wayland client的請求,然後以一定周期把所有提交的graphic buffer進行合成。合成完後進行輸出。本質上,client需要將視窗內容繪製到一個和compositor共用的buffer上。這個buffer可以是普通共用記憶體,也可以是DRM中的GBM或是gralloc提供的可供硬體(如GPU)操作的graphic buffer。在大多數移動平台上,沒有專門的顯存,因此它們最終都來自系統記憶體,區別在於圖形加速硬體一般會要求物理連續且符合對齊要求的記憶體。如果是普通共用記憶體,一般是物理不連續的,多數情況用於軟體渲染。有些圖形驅動也支援用物理不連續記憶體做硬體加速,但效率相對會低一些。根據buffer類型的不同,client可以選擇自己繪製,或是通過Cairo,OpenGL繪製,或是更高層的如Qt,GTK+這些widget庫等繪製。繪製完後client將buffer的handle傳給server,以及需要重繪的地區。在server端,compositor將該buffer轉為紋理(如果是共用記憶體使用量glTexImage2D上傳紋理,硬體加速buffer用GL_OES_EGL_image_external擴充產生外部紋理)。最後將其與其它的視窗內容進行合成。下面是抽象的流程圖。
注意Wayland設計中預設buffer是從client端分配的。這和Android中在server端分配buffer的策略是不同的。這樣,client可以自行設計buffer的管理原則。理論上,client可以始終只用一塊buffer,但因為這塊buffer在client和server同時訪問會產生競爭,所以一般client端都會實現buffer queue。流水線上比較關鍵的一環是buffer跨進程的傳輸,也就是client和server間的有效傳遞。buffer當然不可能通過拷貝傳輸,因此這裡只會傳handle,本質上是傳buffer的fd。我們知道fd是per-process的。而可以傳遞fd的主要IPC機制有binder, domain socket和pipe等。Wayland底層用的是domain socket,因此可以用於傳fd。
在這條流水線上,可以看到,client和server端都會發生繪製。client繪製本地的視窗內容,server端主要用於合成時渲染。注意兩邊都可獨立選擇用軟體或者硬體渲染。現在的商用裝置上,多是硬體加速渲染。和Android上的SurfaceFlinger和Ubuntu上的Mir一樣,Wayland同樣基於EGL介面。EGL用於將本地視窗系統與OpenGL關聯起來,與WGL, GLX等作用類似,只是它是用於Embedded platform的。在Wayland/Weston系統中,Wayland定義了用於EGL的視窗抽象,來作為EGL stack(也就是廠商的圖形驅動)和Wayland協議的glue layer。它對EGL進行了擴充,增加了比如eglBindWaylandDisplayWL, eglUnbindWaylandDisplayWL, eglQueryWaylandBufferWL等介面,對Wayland友好的EGL庫應該提供它們的實現,也就是說要提供Wayland EGL platform,比如mesa(src/egl/main/eglapi.c中)。另一種做法是像libhybris中eglplatform一樣通過EGL wrapper的方式加上這些支援(hybris/egl/platforms/common/eglplatformcommon.cpp)。同時,EGL stack需要提供廠商相關的協議擴充使client能與compositor共用buffer。wayland-egl庫提供了Wayland中surface和EGL粘合的作用。一個要用硬體加速的EGL window可以基於Wayland的surface建立,即通過wayland-egl提供的介面建立wl_egl_window。wl_egl_window結構中包含了wl_surface,然後wl_egl_window就可以被傳入EGL的eglCreateWindowSurface()介面。這樣就將Wayland surface與EGL stack聯絡在一起了。
視窗管理
前面提到,buffer需要有surface為載體,這個surface可以理解為一個視窗的繪製表面。如果一個Wayland client的視窗要被視窗管理器(Shell)所管理,則需要為這個surface建立相應的shell surface。理一下這裡相關的幾個核心概念:surface,view,shell surface。首先,surface是Weston中的核心資料結構之一。Surface代表Wayland client的一個繪圖表面。Client通過將畫好的buffer attach到surface上來更新視窗,因此說surface是buffer的載體。在Weston中,shell是視窗管理器,因此一個surface要想被視窗管理器管理,需要建立相應的shell surface。同時shell surface對應的其實是surface的一個view。view是surface的一個視圖。換句話說,同一個surface理論上可以有多個view,因此weston_surface結構中有view的列表。這裡和我們邏輯上的視窗的概念最近似的是view,因為它對應使用者所看到的一個視窗。而當surface與view是1:1關係時(絕大多數情況下),surface也可近似等同於視窗。在server端它們的資料結構是相互關聯的。
如果從Server/Client角度,它們的相互對應關係如下:
另外subsurface可以作為surface的附屬繪圖表面,它與父surface保持關聯,但擁有獨立的繪圖surface,類似於Android中的SurfaceView,作用也是類似。主要用於Camera,Video等應用。
視窗管理不是直接管理view,而是分為兩層進行管理。Layer是中介層,系統中的layer大體有下面幾個,按從上到下順序為:
? Fade layer
? Lock layer
? Cursor layer
? Input panel layer
? Fullscreen layer
? Panel layer
? Workspace layers
? Background layer
其中的workspace layer是一個數組,預設是一個,也可以有多個,其數量可以在weston.ini中指定。大部分的普通應用視窗就是在這個layer中。這些layer被串成list。每次在要做合成時,會首先將這些layer的view按順序合并到一個view list中。這樣,在composition過程中compositor只需訪問這個view list。
可以看到,這是一個二層有序列表合成一個有序列表的過程。這和Android中的WMS通過為每種類型的視窗定義一段z軸地區的原理類似。WMS中每個類型的視窗對定一個基數(定義在WindowManager.java),它會乘以乘數再加上位移從而得到z軸上的地區邊界。區別在於Weston中不是以數值而是有序列表表示z-order。結合具體的資料結構:
輸入管理
為了提高輸入管理部分的重用性和模組性。Weston將對輸入裝置(鍵盤,滑鼠,觸控螢幕等)的處理分離到一個單獨的庫,也就是libinput中。這樣,其它的圖形處理系統也可以共用這部分,比如X.Org Server和Mir。具體地,它提供了裝置檢測,裝置處理,輸入事件處理等準系統,類似於Android中的EventHub。此外它還有pointer acceleration,touchpad support及gesture recognition等功能。libinput更像是一個架構,它將幾個更底層的庫的功能整合起來。它主要依賴於以下幾個庫:
? mtdev:Multi-touch裝置處理,比如它會將不帶tracking ID的protocol A轉化為protocol B。
? libevdev:處理kernel中evdev模組對接。
? libudev:主要用於和udevd的通訊,從而擷取裝置的增加刪除事件。也可從kernel擷取。
Weston中的輸入管理模組與libinput對接,它實現了兩大部分的功能:一是對輸入裝置的維護,二是對輸入事件的處理。對於輸入事件既會在Weston中做處理,也會傳給相應的client。從事件處理模型上來看,libinput的主迴圈監聽udev monitor fd,它主要用於監聽裝置的添加刪除事件。如果有裝置添加,會開啟該裝置並把fd加入到libinput的主迴圈上。另一方面,Weston中會將libinput的epoll fd加入主迴圈。這樣形成級聯的epoll,無論是udev monitor還是input device的fd有事件來,都會通知到Weston和libinput的主迴圈。這些事件通過libinput中的事件緩衝佇列儲存體,而Weston會作為消費者從中拿事件並根據事件類型進行處理。
Weston中支援三種輸入裝置,分別是鍵盤,觸摸和滑鼠。一套輸入裝置屬於一個seat(嚴格來說,seat中包括一套輸入輸出裝置)。因此,weston_seat中包含weston_keyboard,weston_pointer和weston_touch三個結構。系統中可以有多個seat,它們的結構被串在weston_compositor的seat_list鏈表中。相應的資料結構如下。
可以看到,對於焦點處理,每個裝置有自己的focus,它指向焦點視窗,用於拖拽和輸入等。成員focus_resource_list中包含了焦點視窗所在client中輸入裝置proxy對應的resource對象。在這個list中意味著可以接收到相應的事件。
最後,Wayland/Weston作為正在活躍開發的項目,還有其它很多功能已被加入或正被加入進來。本文只是粗線條的介紹了Wayland/Weston主要結構及功能,具體細節之後再展開。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
Wayland與Weston簡介