SPICE協議之RedWorker線程

http://blog.csdn.net/haiq2006/article/details/50644506

RedWorker是SPICE協議的架構的核心,該線程處理所有的display/cursor相關消息。RedWorker主函數red_worker_main()使用了異步事件處理框架,事件來源有:

1、驅動消息,包括繪圖指令、Surface創建銷燬、UpdateArea等等。

2、客戶端display/cursor信道連接、斷鏈

3、migrate消息

4、socket事件

5、定時器

red_worker.c中的register_callbacks()函數,這裏是爲控制面和驅動消息註冊回調函數。具體的消息類型可以去看看。最新版本的SPICE對red_worker作了大幅重構,代碼可維護性上升了不少,但是流程、算法和功能不受影響,本文以老版本爲基準來分析。

首先發生的流程是surface_create。顯示驅動加載時觸發primary_surface創建。offscreen創建流程與primary surface略有差異。red_create_surface()是主要流程,初始化了worker->surfaces[],這個RedSurface結構體非常重要,對於一個surface上的所有draw command會順序添加到RedSurface 上的current_list結構上。

第二步客戶端display信道連接,對RED_WORKER_MESSAGE_DISPLAY_CONNECT消息的處理。這裏創建了DCC(display channel client)數據結構,初始化了編解碼器,並在on_new_display_channel_client()傳送了第一副主屏圖像到客戶端。每個DCC都有一個pipe,發往客戶端的所有消息都需要先掛在pipe上,當red_push()時候會從pipe tail取出來編碼後發送。

無論客戶端是否連接,主循環都每次都回調用red_process_commands()中處理來自驅動的繪圖指令。該函數會輪詢來自驅動的DRAW_COMMAND,其中QXL_CMD_DRAW的處理是重點,會進行渲染樹的維護。渲渲染樹的作用是重複數據消除和流媒體維護,它是RedWorker的核心,也是SPICE的核心。渲染樹的數據結構是一個樹型結構,每個surface都有自己的渲染樹。所有的draw command到來時會往渲染樹上添加節點。節點類型分析三種:

drawable:相當於葉節點;

Container:包含多個drawable,相當於父結點;

shadow:當drawable類型是QXL_COPY_BITS(從surface自身的一塊src區域copy到目標區域),創建shadow標識src區域。shadow被覆蓋的部分會加入on_hold區,這個區域用於維護exclude_rgn。on_hold區縮小時,exclude_rgn需要相應擴大,這樣就可以將shadow的區域的drawable正確地保留或者消除。爲簡化起見先不分析shadow。

渲染樹操作的核心函數是red_current_add(),分爲兩個階段:

1、第一階段,主要是Container維護,也就是red_current_add()前半部分。首先進行region_test(),有三種結果。

第一種結果是新來的drawable與舊drawable(後面稱爲sibling)的region完全相同,如果新drawable類型是不透明的(QXL_EFFECT_OPAQUE)就會直接覆蓋sibling,並將其從pipe中刪除,這樣就減少了重複數據傳輸。如果不是OPAQUE的,那麼就會判斷能不能覆蓋刪除了。在這裏還會進行流媒體維護,因爲同樣的地方反覆發生繪製容易進入流。

第二種結果是sibling被完全覆蓋了而新來的drawable還有部分獨立不重疊,且新drawable opaque,那麼就刪除sibling繼續進行判斷。

第三種結果是新drawable被sibling完全包圍,且sibling是Container或是opaque drawable,就要進入Container繼續比較或者創建Container後繼續比較,相當於樹節點向下延伸一級。

exclude_base是比較的起點,exclude_region都需要以此爲起點進行重複數據消除,也就是說前面(sibling鏈表前面的部分以及上一級Container部分)的已經比較完了,後繼渲染樹比較操作從這裏開始。exclude_region()本質上是搜索渲染樹,如果遇到重疊就調用__exclude_region()把重疊部分消除掉。如果舊的drawable被多個新的 drawable疊加在一起才完全覆蓋了,這裏也能進行消除。

2、第二階段是red_current_add() while循環之後的半部分,這裏就只會消除而不會再新建Container了。如果drawable 是opaque的,就要再進行剩餘的節點的搜索消除,然後維護流。如果不是opaque,那麼就需要將與之重疊的stream detach。最後把drawble掛到渲染樹的合適位置。

在渲染樹操作完成之後,就要往pipe中添加了,需要關注red_handle_drawable_surfaces_client_synced(),這個函數會把客戶端缺失的offscreen surface創建傳送過去。我們再往回看red_handle_surfaces_dependencies(),這個函數會把drawable依賴其他surface的關係維護好,並將與主surface中依賴區域中重疊的stream detach掉。

現在再來關注red_push(),它的作用是把DCC級別將pipe數據編碼發送給客戶端。之前push到pipe的item會從隊尾取出來,在display_channel_send_item()中分發到各個處理函數中。研究編解碼先關注red_marshall_qxl_draw_copy(),這個函數編碼出來的數據量是佔據最大流量的地方。具體流程就不再詳細分析了,總體而言,這裏根據客戶端能力配置、測速信息、圖形GRADUAL級別來決定採用有損壓縮還是無損壓縮,構造好消息和壓縮數據,都送入marshaller,然後通過marshaller發送給客戶端。marshaller發送使用的是非阻塞socket,進行異步傳送。marshaller是一個巧妙的零拷貝發送機制,如果有人感興趣想自己編寫一套協議,這個機制也是可以重用的。

另一個關注點是update_area。update_area的作用是將指定surface中的指定區域繪製到“顯存”中,此功能的觸發可能來自於驅動需求,也可能來自於內部繪製的需求。update_area之後,渲染樹上的drawable就會被摘除了,但是pipe可能還沒有發送出去,所以這些drawable後繼就無法通過渲染樹消除重複數據了。

總的來說SPICE很優秀,但是我們看到其重複數據消除過於簡單,僅僅依賴於渲染樹,所以無法降低流量到ICA或者SPLASHTOP的水準,如果要進行優化需要添加其他流控機制
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章