理解WebKit和Chromium: Chromium軟件渲染

轉載請註明原文地址:http://blog.csdn.net/milado_nju/article/details/7455348

# Chromium的軟件渲染基礎

## 概述

本章將介紹chromium渲染的最基礎部分,同時也是最常見的部分-軟件渲染。故名思路,軟件渲染就是利用CPU,根據一定的算法來計算生成網頁的內容,其沒有那麼多複雜難懂的概念和架構。如果瞭解了那麼多地關於硬件加速的知識後,你可能會覺得本章的內容非常的簡單明瞭,是的,你沒猜錯。

在絕大多數的情況下,也就是沒有所謂地那些需要硬件加速內容的時候(包括但不限於CSS3 3D transformation, CSS3 3D transition, Canvas 2D, WebGL和Video),Chromium都是用軟件渲染的技術來完成頁面的繪製工作(除非你強行打開硬件加速繪製),基本上你瀏覽地大多數門戶網站,論壇網站,社交網站等等的網頁,都是採用這項技術來完成頁面的生成。可以這麼說,傳統的網頁都是軟件渲染,用到了硬件加速的新網頁通常都是加入了很多眩目視覺效果的網頁(當然,這不是絕對的)。

本章首先詳細瞭解軟件渲染的基礎設施和架構,然後介紹渲染的過程,最後並與硬件加速渲染作一比較,介紹一下各自的優點和侷限性。 

## 軟件渲染基礎和架構

前面介紹了WebKit中的渲染基礎,這些設施在這裏也同樣需要,不過,我不打算在這裏重新詳細介紹它們。下面詳細介紹的是Chromium中支持軟件渲染的基礎設施及其架構。

先來看看Renderer進程。同硬件加速渲染一樣,RenderWidget對象是必不可少的,它不僅負責調度頁面渲染和頁面更新等操作,而且負責同Browser進程的通信。另一個重要的設施是PlatformCanvas,也就是SkiaCanvas,Render樹的繪製操作最後都是調用Canvas的元素操作來實現。

再來看看Browser進程。第一個設施就是RenderWidgetHost對象,一樣的必不可少,負責同Renderer進程的通信。第二個是BackingStore,顧名思義,它就是一個後端的存儲空間,它的大小通常就是viewport也就是網頁可視區域的大小,該空間存儲的就是頁面的繪製結果。

最後來看看這兩個進程是如何傳遞信息和繪製內容地。傳遞消息還是之前介紹過的消息機制,而繪製的結果則是通過TransportDIB類來完成,該類在Linux系統下其實是一個共享內存的實現。對Renderer進程來說,Skia Canvas把內容繪製到bitmap中,該bitmap的後端即是共享內存。對Browser進程來說,當繪製完成後,它會把共享內存的內容複製到自己的backingstore中。

下圖顯示的是軟件渲染的架構圖,其主要來源於chromium的官方網站,但這裏做了一些擴充。


其組成部分上面已經介紹過,那麼一個大致的過程是這樣的:RenderWidget接收到更新請求時,創建一個共享內存區域,然後創建skia canvas,之後RenderWidget會把實際繪製的工作派發到Render樹中來完成,具體上來講,也就是WebKit負責遍歷Render樹,每個RenderObject節點根據需要來繪製自己和子女的內容到目標存儲,也就是SkiaCanvas所對應的共享內存的Bitmap中。之後,RenderWidgetHost把bitmap複製到backingstore的相應區域中,並調用paint來把自己繪製到窗口中。下面詳細介紹這個過程。

 

## 渲染具體過程

依照慣例,我們先來看一個簡單的HTML例子,裏面包含簡單的元素和一小段JavaScript代碼,它會每隔50ms請求更新id爲”content”的元素的內容。


後面我們會講它在哪些時候請求繪製網頁內容,這裏先了解一下很多情況下會發起重新繪製某些區域的請求,大概可以分爲兩類:

1)  前端請求:該類包括從browser進程發起的請求,可能是browser自身的,也有可能是X(或者其他窗口系統)。

2)  後端請求:由頁面自身發起更新部分區域的請求,例如HTML元素或者樣式的改變,動畫等等。上面的例子中, JS代碼每隔50ms更新元素,所以它會觸發更新部分區域。

下面來解釋一下當有繪製或者更新某個區域請求時,Chromium和WebKit如何來處理。過程如下圖所示,下面闡述一下大致的步驟。

1)  Renderer進程的message loop調用處理Invalidation的回調函數,該函數主要調用RenderWidget::DoDeferredUpdate來完成繪製請求。

2)  RenderWidget::DoDeferredUpdate首先調用layout來觸發檢查是否有需要重新計算的佈局和更新請求。

3)  RenderWidget調用TransportDIB來創建共享內存,內存大小爲繪製區域的高×寬×4,同時調用Skia來創建一個canvas,它的繪製目標是一個使用共享內存存儲的bitmap。

4)  當渲染該頁面的全部或者部分時,ScrollView請求按照從前到後順序遍歷並繪製所有的RenderLayer的內容到目標的bitmap中,每個RenderLayer的繪製通過以下步驟來完成:首先計算重繪的區域是否和自己有重疊,如果有,則要求該layer中的所有RenderObject對象繪製自己。這在上圖中省略了該部分的具體內容,詳情參考代碼。

5)  繪製完成後,發送UpdateRect的消息給browser進程,Renderer進程同時返回完成繪製。Browser進程接受到消息後首先由BackingStoreManager來獲取或者創建BackingStoreX(在linux平臺上),BackingStoreX大小是Viewport(可視區域),包含相對於整個網頁的座標信息,它根據UpdateRect的更新區域的位置信息將共享內存的內容繪製到自己的對應存儲區域中。

6)  最後Browser進程發送UpdateRect的ACK消息給renderer進程,完成整個過程。


那麼上述的HTML例子中大概有哪些繪製請求呢?

1)  當初始打開該HTML頁面時,首先會由browser進程發起要求更新整個可視區域的重繪請求,後面的繪製過程如上所述。

2)  後面,每隔50ms,chromium執行JS代碼,JS代碼修改了HTML元素,該動作觸發重新計算佈局,因而發起更新某塊小的區域的請求。

 

## 同硬件加速渲染的對比

我們可以看到,軟件渲染有一個重要的影響性能的地方來自於共享內存後的複製到backingstore的操作,那麼爲什麼需要將共享內存的內容複製到backingstore中,而不是直接使用呢?我認爲這裏面有兩點重要的原因:

第一,  共享內存是很寶貴的資源。通常,操作系統對共享內存的大小和總量有一定的限制,因而可以使用的量比較小(相對於虛擬地址空間)。chromium瀏覽器可能包含多個Renderer進程,這些進程如果都申請這麼多的共享內存,這將是一個極大的浪費,鑑於此,在使用完後,因儘快地釋放共享內存。

第二,  重新繪製網頁內容的需要。當X有請求更新瀏覽器窗口的某個區域時,在此情況下,網頁內容可能沒有任何的改變,因而如果在瀏覽器端有一個backingstore保存網頁的內容,那麼不需要WebKit(Renderer進程)重新渲染,這將會是一個很費時的工作,這在某種程度上可以提高性能。

軟件渲染同硬件加速渲染另外一個很不同的地方就是對更新區域的處理。回憶一下之前介紹的硬件渲染部分,當網頁中有一個更新某個區域的請求時(例如動畫),硬件渲染可能只需要對其中的一層重新繪製,然後重新合成即可。但是,在軟件渲染過程中,因爲它沒有爲每一層提供後端存儲,因而它需要將和這個區域有重疊部分的所有層次重新繪製一遍,因而某些情況下開銷比較大。

上面說了之後,可能會覺得軟件渲染不那麼有用,其實不然,大多數情況下,軟件渲染是有其優勢所在的,

第一,  很多場景適合用軟件渲染,而且速度更快。一般的網頁,用軟件渲染速度更快,用了硬件加速這個“牛刀”可能會造成較大地額外負載,一般網頁的文字和圖片對現代CPU來說不是什麼大問題,處理起來那是相當地快。

第二,  相比較硬件加速渲染,它不需要GPU內存和其他資源。硬件加速渲染需要很多的GPU內存資源,因爲它會每個層分配內存對象,很多時候GPU資源很緊張。

 

## 源文件目錄

content/renderer/

         Renderer進程端與Browser進程通信的設施

content/browser/

         Browser進程端與Renderer進程通信的設施

ui/gfx/surface

         包含TransportDIB類,用來申請和管理共享內存

 

## 參考文獻

1.      GPU加速合成:

http://www.chromium.org/developers/design-documents/gpu-accelerated-compositing-in-chrome

2.      渲染過程圖:

http://www.chromium.org/developers/design-documents/rendering-architecture-diagrams

 

By [email protected]

 

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