wayland詳解

 

簡單地說,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主要結構及功能,具體細節之後再展開。

原文地址:http://blog.csdn.net/jinzhuojun/article/details/46794479   

                  http://www.it165.net/pro/html/201507/46537.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章