瀏覽器渲染流程

瀏覽器渲染頁面主要分為如下幾步

1.根據HTML構建 DOM 樹

由於瀏覽器無法直接理解和使用 HTML,所以需要將 HTML 轉換為瀏覽器能夠理解的結構-DOM 樹。在此步驟中,HTML解析器會將輸入的HTML文檔解析為對應的DOM樹。

image.png

 

2.樣式計算(Recalculate Style)

這個過程分為如下三個步驟

將CSS 文本轉換為瀏覽器可以理解的結構-styleSheets(即CSSOM樹)

由於瀏覽器也無法直接理解這些純文本的 CSS 樣式,所以當渲染引擎接收到 CSS 文本時,會執行轉換操作。styleSheets同時具備了查詢和修改功能,這會為後面的樣式操作提供基礎。

image.png

轉換樣式表中的屬性值,使其標準化。

如 2em、blue、bold,這些類型數值不容易被渲染引擎理解,則會轉換。將所有值轉換為渲染引擎容易理解的、標準化的計算值,這個過程就是屬性值標準化。

image.png

計算出 DOM 樹中每個節點的具體樣式(即結合DOM樹和CSSOM樹形成渲染樹)

樣式計算階段的目的是為了計算出 DOM 節點中每個元素的具體樣式,在計算過程中需要遵守 CSS 的繼承層疊兩個規則。這個階段最終輸出的內容是每個 DOM 節點的樣式,並被保存在 ComputedStyle 的結構內。

image.png

3.布局階段

計算出 DOM 樹中可見元素的幾何位置,我們把這個計算過程叫做布局。

1.創建布局樹

創建建一棵只包含可見元素布局樹。瀏覽器會做如下操作: 遍歷 DOM 樹中的所有可見節點,並把這些節點加到布局樹中;而不可見的節點會被布局樹忽略掉

image.png

2.布局計算

計算布局樹節點的坐標位置。在執行布局操作的時候,會把布局運算的結果重新寫回布局樹中,所以布局樹既是輸入內容也是輸出內容,這是布局階段一個不合理的地方,因為在布局階段並沒有清晰地將輸入內容和輸出內容區分開來。針對這個問題,Chrome 團隊正在重構布局代碼,下一代布局系統叫 LayoutNG,試圖更清晰地分離輸入和輸出,從而讓新設計的布局算法更加簡單

 

4.分層

為了更方便的實現頁面中的複雜的效果,如3D 變換、頁面滾動等,渲染引擎會為特定的節點生成專用的圖層,並生成一棵對應的圖層樹(LayerTree)。即將多個圖層疊加在一起構成最終的頁面圖像。(打開 Chrome 的「開發者工具」,選擇「Layers」標籤即可看到頁面的分層)。

                       image.png

圖層和布局樹節點之間的關係

關於圖層有如下幾點需要注意⚠️

  • 並不是布局樹的每個節點都包含一個圖層,如果一個節點沒有對應的層,那麼這個節點就從屬於父節點的圖層
  • 擁有層疊上下文屬性(定位屬性、透明屬性、CSS 濾鏡、z-index 等)的元素會被提升為單獨的一層。
  • 需要剪裁(clip)的地方也會被創建為圖層

 

5.圖層繪製

構建完圖層樹之後之後,渲染引擎會對圖層樹中的每個圖層進行繪製。

渲染引擎實會把一個圖層的繪製拆分成很多小的繪製指令,然後再把這些指令按照順序組成一個待繪製列表。

image.png

繪製列表中的指令其實非常簡單,就是讓其執行一個簡單的繪製操作,比如繪製粉色矩形或者黑色的線等。而繪製一個元素通常需要好幾條繪製指令,因為每個元素的背景、前景、邊框都需要單獨的指令去繪製。所以在圖層繪製階段,輸出的內容就是這些待繪製列表。(點擊開發者工具」的「Layers」標籤,選擇「document」層,顯示的則是繪製列表)

 

6.柵格化(raster)操作

繪製列表只是用來記錄繪製順序和繪製指令的列表,而實際上繪製操作是由渲染引擎中的合成線程來完成的。當圖層的繪製列表準備好之後,主線程會把該繪製列表提交(commit)給合成線程。

        image.png

渲染主線程和合成線程之間的關係

1.合成線程會將圖層劃分為圖塊(tile),這些圖塊的大小通常是 256×256 或者 512×512。

視口:通常一個頁面可能很大,但是用戶只能看到其中的一部分,我們把用戶可以看到的這個部分叫做視口

劃分圖塊的原因:在有些情況下,有的圖層可以很大,比如有的頁面你使用滾動條要滾動好久才能滾動到底部,但是通過視口,用戶只能看到頁面的很小一部分,所以在這種情況下,要繪製出所有圖層內容的話,就會產生太大的開銷,而且也沒有必要。

2.合成線程會按照視口附近的圖塊來優先生成位圖,實際生成位圖的操作是由柵格化來執行的。所謂柵格化,是指將圖塊轉換為位圖

渲染進程維護了一個柵格化的線程池,所有的圖塊柵格化都是在線程池內執行的(圖塊是柵格化執行的最小單位)

   image.png

合成線程提交圖塊給柵格化線程池

通常,柵格化過程都會使用 GPU 來加速生成,使用 GPU 生成位圖的過程叫快速柵格化,或者 GPU 柵格化,生成的位圖被保存在 GPU 內存中。注意⚠️:GPU 操作是運行在 GPU 進程中,如果柵格化操作使用了 GPU,那麼最終生成位圖的操作是在 GPU 中完成的,這就涉及到了跨進程操作。即渲染進程把生成圖塊的指令發送給 GPU,然後在 GPU 中執行生成圖塊的位圖,並保存在 GPU 的內存中

                image.png

7.合成和顯示

一旦所有圖塊都被光柵化,合成線程就會生成一個繪製圖塊的命令——「DrawQuad」,然後將該命令提交給瀏覽器進程。瀏覽器進程裏面有一個叫 viz 的組件,用來接收合成線程發過來的 DrawQuad 命令,然後根據 DrawQuad 命令,將其頁面內容繪製到內存中,最後再將內存顯示在屏幕上。

 

完整渲染流程圖image.png

參考自:極客時間-瀏覽器引擎與實踐

Tags: