天天看點

浏覽器渲染流水線解析(一)

本文基于目前版本的 Chrome 浏覽器寫成(60 左右),理論上部分知識可以應用于其它浏覽器(當然術語會有一定差别)或者 Chrome 後續的版本,但是并不完全保證這一點。

浏覽器渲染流水線解析(一)

上圖顯示了 Chrome 一個高度簡化後的渲染流水線示意圖:

最底層的是 Chrome 最核心的部分 Blink,負責JS的解析執行,HTML/CSS解析,DOM操作,排版,圖層樹的建構和更新等任務;

Layer Compositor(圖層合成器)接收 Blink 的輸入,負責圖層樹的管理,圖層的滾動,旋轉等矩陣變幻,圖層的分塊,光栅化,紋理上傳等任務;

Display Compositor 接收 Layer Compositor 的輸入,負責輸出最終的 OpenGL 繪制指令,将網頁内容通過 GL 貼圖操作繪制到目标視窗上,如果忽略掉作業系統本身的視窗合成器,也可以簡單認為是繪制在顯示屏上;

當我們說 Compositor,在沒有加修飾語的情況下,一般都是指 Layer Compositor。另外術語 Child Compositor(子合成器)也是指 Layer Compositor,相對于作為 Parent 的 Display Compositor 而言。

一個 Chrome 浏覽器一般會有一個 Browser 程序,一個 GPU 程序,和多個 Renderer 程序,通常每個 Renderer 程序對應一個頁面。在特殊架構(Android WebView)或者特定配置下,Browser 程序可以兼作 GPU 程序或者 Renderer 程序(意味着沒有獨立的 GPU 或者 Renderer 程序),但是 Browser 跟 Renderer,Browser 跟 GPU,Renderer 跟 GPU 之間的系統架構和通訊方式基本保持不變,線程架構也是同樣。

Blink 主要運作在 Renderer 程序的 Renderer 線程,我們通常會稱之為核心主線程;

Layer Compositor 主要運作在 Renderer 程序的 Compositor 線程;

Display Compositor 主要運作在 Browser 程序的 UI 線程;

Display Compositor 未來應該會移到 GPU 程序的主 GPU 線程,當然對父子合成器進行排程的部分仍然是在 Browser 程序的 UI 線程。

所有的渲染流水線都會有幀的概念,幀這個概念抽象描述了渲染流水線下級子產品往上級子產品輸出的繪制内容相關資料的封裝。我們可以看到 Blink 輸出 Main Frame 給 Layer Compositor,Layer Compositor 輸出 Compositor Frame 給 Display Compositor,Display Compositor 輸出 GL Frame 給 Window。我們覺得一個動畫是否流暢,最終取決于 GL Frame 的幀率(也就是目标視窗的繪制更新頻率),而覺得一個觸屏操作是否響應即時,取決于從 Blink 處理事件到 Window 更新的整個過程的耗時(理論上應該還要加上事件從 Browser 發送給 Compositor,再發送給 Blink 的這個過程的耗時)。

Main Frame 包含了對網頁内容的描述,主要以繪圖指令的形式,或者可以簡單了解為某個時間點對整個網頁的一個矢量圖快照(可以局部更新)。目前版本的 Chrome,圖層化的決策仍然由 Blink 來負責,Blink 需要決定如何根據網頁的 DOM 樹來生成一顆圖層樹,并以 DisplayList 的形式記錄每個圖層的内容(未來圖層化決策應該會轉移到 Layer Compositor,Blink 隻輸出 DisplayList 樹和 DisplayList 節點的關鍵屬性,同時 DisplayList 不再以圖層作為機關,而是以每個排版對象作為機關)。

圖層化決策一般由以下幾個因素決定:

特殊元素如 Plugin,Video,Canvas(WebGL);

減少圖層樹的結構變更,減少圖層内容的變更(目前 Blink 網頁内容的變更是以圖層為原子機關的,如果以一個元素為根節點生成圖層,該元素的某些 CSS 屬性如 Transform 的變更不會引起所屬圖層内容的變更);

第三點是可以被頁端所直接控制來優化圖層結構及 Main Frame 性能,像傳統的 translate3d hack 和新的 CSS 屬性 will-change。

Layer Compositor 接收 Blink 生成的 Main Frame,并轉換成合成器内部的圖層樹結構(因為圖層化決策仍然由 Blink 負責,是以這裡的轉換基本上可以認為是生成一棵同樣的樹,再對逐個圖層的進行拷貝)。

Layer Compositor 需要為每個圖層進行分塊,為每個分塊配置設定 Resource(Texture 的封裝),然後安排光栅化任務。

當 Layer Compositor 接收到來自 Browser 的繪制請求時,它會為目前可見區域的每個圖層的每個分塊生成一個 Draw Quad 的繪制指令(矩形繪制,指令實際上指定了坐标,大小,變換矩陣等屬性),所有的 Draw Quad 指令和對應的 Resource 的集合就構成了 Compositor Frame。Compositor Frame 被發送往 Browser,并最終到達 Display Compositor(未來也可以直接發給 Display Compositor)。

Display Compositor 将 Compositor Frame 的每個 Draw Quad 繪制指令轉換一個 GL 多邊形繪圖指令,使用對應 Resource 封裝的 Texture 對目标視窗進行貼圖,這些 GL 繪圖指令的集合就構成了一個 GL Frame,最終由 GPU 執行這些 GL 指令完成網頁在視窗上占據的可見區域的繪制。

Chrome 渲染流水線的排程是基于請求和狀态機響應,排程的最上級中樞運作在 Browser UI 線程,它按顯示器的 VSync(垂直同步)周期向 Layer Compositor 發出輸出下一幀的請求,而 Layer Compositor 根據自身狀态機的狀态決定是否需要 Blink 輸出下一幀。

Display Compositor 則比較簡單,它持有一個 Compositor Frame 的隊列不斷的進行取出和繪制,輸出的頻率唯二地取決于 Compositor Frame 的輸入頻率和自身繪制 GL Frame 的耗時。基本上可以認為 Layer Compositor 和 Display Compositor 是生産者和消費者的關系。