您將瞭解 RenderingNG 元件片段的設定方式,以及轉譯管道如何流經這些元件。
從最高層級開始,轉譯工作如下:
- 將內容算繪至螢幕上的像素。
- 為內容加入視覺效果動畫,讓內容從一個狀態變換到另一個狀態。
- 根據輸入內容捲動。
- 將輸入內容有效地導向正確位置,以便開發人員指令碼和其他子系統做出回應。
要轉譯的內容是每個瀏覽器分頁的框架樹狀結構,以及瀏覽器介面。以及來自觸控螢幕、滑鼠、鍵盤和其他硬體裝置的原始輸入事件串流。
每個影格都包含:
- DOM 狀態
- CSS
- 面板
- 外部資源,例如圖片、影片、字型和 SVG
頁框是 HTML 文件和網址的組合。在瀏覽器分頁中載入的網頁會包含頂層框架、頂層文件中每個 iframe 的子框架,以及這些 iframe 的遞迴子項。
視覺效果是指套用至位圖的圖形運算,例如捲動、轉換、剪輯、濾鏡、不透明度或混合。
架構元件
在 RenderingNG 中,這些工作會在邏輯上分散到多個階段和程式碼元件。這些元件最終會出現在各種 CPU 程序、執行緒,以及這些執行緒中的子元件。每項技術都對達成所有網站內容的可靠性、可擴充的效能和可擴充性扮演重要角色。
算繪管道結構
算繪會在管道中進行,過程中會產生多個階段和構件。每個階段都代表在轉譯期間執行一項明確定義的工作的程式碼。這些成果是資料結構,可做為階段的輸入或輸出內容。
這些階段如下:
- Animate:根據宣告式時間表,變更計算的樣式,並隨時間變更屬性樹狀結構。
- 樣式:將 CSS 套用至 DOM,並建立計算樣式。
- 版面配置:決定 DOM 元素在畫面上的大小和位置,並建立不可變動的片段樹狀結構。
- 預先繪製:計算屬性樹狀結構,並視需要invalidate
- 捲動:透過屬性樹狀結構的變異,更新文件和可捲動的 DOM 元素的捲動偏移量。
- 繪圖:計算顯示清單,說明如何從 DOM 將 GPU 紋理圖塊轉為點陣圖。
- 提交:將屬性樹狀結構和顯示清單複製到合成器執行緒。
- 分層:將顯示清單分割成合成的圖層清單,以便獨立進行轉譯和動畫處理。
- 光柵、解碼和繪圖工作區:將顯示清單、已編碼圖片和繪圖工作區程式碼分別轉換為 GPU 紋理圖塊。
- 啟用:建立合成器影格,代表如何在畫面上繪製及放置 GPU 圖塊,以及任何視覺效果。
- 匯總:將所有可見合成器影格合併為單一全域合成器影格。
- 繪製:在 GPU 上執行匯總的轉譯器影格,以便在畫面上建立像素。
您可以略過不需要的轉譯管道階段。舉例來說,視覺效果和捲動的動畫可以略過版面配置、預先繪製和繪製。因此,動畫和捲動會在圖表中以黃色和綠色圓點標示。如果版面配置、預先繪製和繪製作業可為視覺效果略過,則可完全在轉譯器執行緒上執行,並略過主執行緒。
這裡並未直接說明瀏覽器 UI 算繪作業,但可將其視為這項管道的簡化版本 (實際上,其實作會共用大部分程式碼)。影片 (同樣未直接顯示) 通常會使用獨立程式碼進行算繪,該程式碼會將影格解碼為 GPU 紋理圖塊,然後插入合成器影格和繪圖步驟。
程序和執行緒結構
CPU 程序
使用多個 CPU 程序可在網站之間和瀏覽器狀態中實現效能和安全性隔離,並從 GPU 硬體中實現穩定性和安全性隔離。
- 轉譯程序會為單一網站和分頁組合轉譯、製作動畫、捲動及路由輸入內容。您可以使用多種算繪程序。
- 瀏覽器程序會為瀏覽器 UI (包括網址列、分頁標題和圖示) 算繪、製作動畫,並將輸入內容路由至適當的算繪程序,只有一個瀏覽器程序。
- Viz 程序會匯總多個轉譯程序和瀏覽器程序的合併作業。它會使用 GPU 進行算繪和繪製。只有一個 Viz 程序。
不同的網站最終都會進入不同的轉譯程序。
同一個網站的多個瀏覽器分頁或視窗通常會進入不同的轉譯程序,除非分頁之間有相關連結,例如一個分頁開啟另一個分頁。在電腦上,如果記憶體壓力過大,Chromium 可能會將同一網站的多個分頁放入同一個轉譯程序,即使這些分頁不相關也一樣。
在單一瀏覽器分頁中,不同網站的框架一律會在不同的轉譯程序中,但同一個網站的框架一律會在同一個轉譯程序中。從轉譯的角度來看,多個轉譯程序的重要優點在於跨網站 iframe 和分頁可彼此達成效能隔離。此外,來源也可以選擇啟用更進階的隔離功能。
所有 Chromium 都只有一個 Viz 程序,因為通常只有一個 GPU 和螢幕可繪製。
將 Viz 分離為專屬程序,有助於在 GPU 驅動程式或硬體發生錯誤時維持穩定性。這也適用於安全性隔離,這對 Vulkan 等 GPU API 和一般安全性而言相當重要。
由於瀏覽器可以有許多分頁和視窗,且所有分頁和視窗都需要繪製瀏覽器 UI 像素,因此您可能會想知道:為何只有一個瀏覽器程序?這是因為每次只會聚焦其中一個分頁;事實上,非顯示的瀏覽器分頁大多會停用,並放棄所有 GPU 記憶體。不過,複雜的瀏覽器 UI 算繪功能也越來越常在算繪程序中實作 (稱為 WebUI)。這並非出於效能隔離的原因,而是為了充分利用 Chromium 網路轉譯引擎的便利性。
在舊版 Android 裝置上,在 WebView 中使用時,轉譯和瀏覽器程序會共用 (這通常不適用於 Android 上的 Chromium,僅適用於 WebView)。在 WebView 上,瀏覽器程序也會與嵌入應用程式共用,而 WebView 只有一個轉譯程序。
有時也會有用於解碼受保護影片內容的工具程式。這項程序未在前述圖表中顯示。
執行緒
即使工作速度緩慢、管道並行化和多重緩衝,執行緒仍可達到效能隔離和回應速度。
- 主執行緒會執行指令碼、轉譯事件迴圈、文件生命週期、命中測試、指令碼事件調度,以及剖析 HTML、CSS 和其他資料格式。
- 主執行緒輔助程式會執行需要編碼或解碼的圖片位圖和 Blob 等工作。
- Web Workers 執行指令碼,以及 OffscreenCanvas 的轉譯事件迴圈。
- 合成器執行緒會處理輸入事件、執行網頁內容的捲動和動畫、計算網頁內容的最佳分層方式,以及協調圖片解碼、繪圖工作項和光柵工作。
- 合成器執行緒輔助程式會協調 Viz 光柵工作,並執行圖片解碼工作、繪圖工作項和備用光柵。
- 媒體、解複合器或音訊輸出執行緒會解碼、處理及同步處理影片和音訊串流。(請注意,影片會與主要轉譯管道並行執行)。
將主執行緒和轉譯器執行緒分開,對於從主執行緒工作分離動畫和捲動的效能至關重要。
每個轉譯程序只有一個主執行緒,即使同一個網站的多個分頁或影格可能會在同一個程序中結束。不過,在各種瀏覽器 API 中執行的工作,會受到效能隔離的影響。舉例來說,在 Canvas API 中產生圖片位元圖和 Blob 時,會在主執行緒輔助執行緒中執行。
同樣地,每個轉譯程序只有一個合成器執行緒。一般來說,只有一個工作緒並不會造成問題,因為合成器執行緒上所有非常耗費資源的作業都會委派給合成器 worker 執行緒或 Viz 程序,而這項工作可以與輸入路由、捲動或動畫並行執行。合成器工作站執行緒會協調在 Viz 處理程序中執行的工作,但隨處 GPU 加速可能會因 Chromium 無法控制的原因而失敗,例如驅動程式錯誤。在這種情況下,工作執行緒會在 CPU 的備用模式下執行工作。
合成器工作站執行緒的數量取決於裝置的功能。舉例來說,電腦通常會使用較多執行緒,因為電腦的 CPU 核心較多,且電池限制較少。以下是放大和縮小的範例。
轉譯程序執行緒架構是三種不同的最佳化模式的應用:
- 輔助執行緒:將耗時較長的子工作傳送至其他執行緒,讓父執行緒能回應其他同時提出的要求。主執行緒輔助程式和轉譯器輔助程式是這項技巧的絕佳範例。
- 多重緩衝:在轉譯新內容時顯示先前轉譯的內容,以隱藏轉譯延遲。合成器執行緒會使用這項技巧。
- 管道並行化:同時在多個位置執行轉譯管道。這就是捲動和動畫可以快速執行的原因;即使主執行緒正在進行轉譯更新,捲動和動畫仍可同時執行。
瀏覽器程序
- 轉譯和合成的執行緒會回應瀏覽器 UI 中的輸入內容,並將其他輸入內容導向正確的轉譯程序;並會排版及繪製瀏覽器 UI。
- 轉譯和合成執行緒輔助程式會執行圖片解碼工作,並備援轉譯或解碼。
瀏覽器處理程序的算繪和合成執行緒與算繪程序的程式碼和功能相似,但主執行緒和合成器執行緒會合併為一個執行緒。在這種情況下,只需要一個執行緒,因為設計上沒有長時間主執行緒工作,因此不需要進行效能隔離。
Viz 處理程序
- GPU 主執行緒會將顯示清單和影片影格轉為 GPU 紋理圖塊,並將合成器影格繪製至螢幕。
- 顯示轉譯器執行緒會匯總並最佳化各個轉譯程序 (以及瀏覽器程序) 的轉譯,並將轉譯結果轉換為單一轉譯器影格,以便顯示在螢幕上。
一般來說,影像處理和繪圖作業會在同一個執行緒中執行,因為兩者都需要 GPU 資源,而要可靠地使用多執行緒的 GPU 相當困難 (開發新的 Vulkan 標準,就是為了讓多執行緒存取 GPU 更容易)。由於 WebView 的嵌入方式,Android WebView 會提供獨立的 OS 級轉譯執行緒來繪製畫面。其他平台日後也可能會提供這類執行緒。
顯示合成器位於不同的執行緒,因為它需要隨時回應,且不會阻斷 GPU 主執行緒的任何可能的減速來源。導致 GPU 主執行緒速度變慢的原因之一,是呼叫非 Chromium 程式碼 (例如供應商專屬的 GPU 驅動程式),這些程式碼可能會以難以預測的方式變慢。
元件結構
在每個轉譯程序主執行緒或轉譯器執行緒中,都有邏輯軟體元件,以結構化方式彼此互動。
轉譯處理主執行緒元件
在 Blink 轉譯器中:
- 本機影格樹狀結構片段代表本機影格樹狀結構,以及影格中的 DOM。
- DOM 和 Canvas API 元件包含所有這些 API 的實作項目。
- 文件生命週期執行程式會執行算繪管道步驟,包括提交步驟。
- 「input event hit testing and dispatching」元件會執行命中測試,找出事件指定的 DOM 元素,並執行輸入事件調度演算法和預設行為。
轉譯事件迴圈排程器和執行程會決定在事件迴圈中執行的內容和時間。並安排在與裝置螢幕相符的節奏進行轉譯。
本地影格樹狀結構片段有點複雜。請注意,影格樹狀結構是主頁面及其子項 iframe 的遞迴。如果影格是在算繪程序中算繪,則該影格對該程序而言為本機;否則,該影格為遠端。
您可以根據渲染程序為影格著色。在上圖中,綠色圓圈是單一轉譯程序中的所有影格;橘色圓圈是第二個轉譯程序中的影格,藍色圓圈則是第三個轉譯程序中的影格。
本機影格樹狀結構片段是影格樹狀結構中同色連接元件。圖片中包含四個本機影格樹狀結構:兩個用於網站 A、一個用於網站 B,以及一個用於網站 C。每個本機影格樹狀結構都會取得自己的 Blink 轉譯器元件。本機影格樹狀結構的 Blink 轉譯器可能會與其他本機影格樹狀結構位於相同轉譯程序中,也可能不會。這會根據選取算繪程序的方式決定,如前文所述。
算繪程序合成器執行緒結構
轉譯程序合成器元件包括:
- 資料處理常式,可維護組合圖層清單、顯示清單和屬性樹狀結構。
- 生命週期執行程式,可執行動畫、捲動、組合、光柵、解碼和啟用算繪管道的步驟。(請注意,動畫和捲動動作可能會同時發生在主執行緒和合成器中)。
- 輸入和觸發測試處理常式會在合成圖層的解析度上執行輸入處理和觸發測試,以判斷是否可以在合成器執行緒上執行捲動手勢,以及應以哪個轉譯程序觸發測試為目標。
實際應用架構範例
本範例有三個分頁:
分頁 1:foo.com
<html>
<iframe id=one src="foo.com/other-url"></iframe>
<iframe id=two src="bar.com"></iframe>
</html>
分頁 2:bar.com
<html>
…
</html>
分頁 3:baz.com
html
<html>
…
</html>
這些分頁的程序、執行緒和元件結構如下所示:
讓我們逐一介紹算繪的四項主要工作。提醒:
- 將內容算繪至螢幕上的像素。
- 動畫:在內容從一個狀態變更為另一個狀態時,顯示視覺效果。
- 根據輸入內容捲動。
- 路由輸入內容,以便開發人員指令碼和其他子系統回應。
如要算繪分頁 1 的變更 DOM:
- 開發人員指令碼會變更 foo.com 轉譯程序中的 DOM。
- Blink 轉譯器會告知合成器需要進行轉譯。
- 合成器會告訴 Viz 需要進行轉譯。
- Viz 會向合成器傳回算繪開始的訊號。
- 合成器會將開始訊號轉送至 Blink 轉譯器。
- 主要執行緒事件迴圈執行程式會執行文件生命週期。
- 主執行緒會將結果傳送至轉譯器執行緒。
- 組合器事件迴圈執行程式會執行組合生命週期。
- 所有影像處理工作都會傳送至 Viz (通常會有多個這類工作)。
- 在 GPU 上將內容轉為點陣圖。
- Viz 確認已完成影像處理工作。注意:Chromium 通常不會等待光柵完成,而是使用稱為「同步權杖」的東西,該權杖必須在步驟 15 執行前由光柵工作項解析。
- 將合成器影格傳送至 Viz。
- Viz 會匯總 foo.com 轉譯程序、bar.com iframe 轉譯程序和瀏覽器 UI 的轉譯器影格。
- Viz 排定繪製時間。
- Viz 會將匯總的轉譯器影格繪製至螢幕。
如要在第二個分頁上animate CSS 轉換效果,請按照下列步驟操作:
- bar.com 算繪程序的轉譯器執行緒會透過變更現有屬性樹狀結構,在轉譯器事件迴圈中標記動畫。然後重新執行合成器生命週期。(可能會執行轉譯和解碼工作,但這裡未加以顯示)。
- 將合成器影格傳送至 Viz。
- Viz 會匯總 foo.com 轉譯程序、bar.com 轉譯程序和瀏覽器 UI 的 compositor 影格。
- Viz 排定繪製時間。
- Viz 會將匯總的轉譯器影格繪製至螢幕。
如要在第三個分頁上捲動網頁,請按照下列步驟操作:
- 一系列
input
事件 (滑鼠、觸控或鍵盤) 會傳送至瀏覽器程序。 - 每個事件都會路由至 baz.com 的轉譯程序轉換器執行緒。
- 合成器會判斷主執行緒是否需要瞭解事件。
- 必要時,系統會將事件傳送至主執行緒。
- 主執行緒會觸發
input
事件監聽器 (pointerdown
、touchstar
、pointermove
、touchmove
或wheel
),以查看監聽器是否會在事件上呼叫preventDefault
。 - 主執行緒會傳回
preventDefault
是否已呼叫至轉譯器。 - 如果不是,則輸入事件會傳回至瀏覽器程序。
- 瀏覽器程序會將其與其他近期事件結合,將其轉換為捲動手勢。
- 捲動手勢會再次傳送至 baz.com 的轉譯程序轉譯器執行緒,
- 捲動畫面會套用在該處,而 bar.com 算繪程序的轉換器執行緒會在轉換器事件迴圈中標記動畫。接著,這會變更屬性樹狀結構中的捲動偏移量,並重新執行轉譯器生命週期。它也會告知主執行緒觸發
scroll
事件 (未顯示於此)。 - 將合成器影格傳送至 Viz。
- Viz 會匯總 foo.com 轉譯程序、bar.com 轉譯程序和瀏覽器 UI 的轉譯器影格。
- Viz 排定繪製時間。
- Viz 會將匯總的轉譯器影格繪製至螢幕。
如要轉送第一個分頁中 iframe #two 超連結中的 click
事件:
- 瀏覽器程序收到
input
事件 (滑鼠、觸控或鍵盤)。它會執行近似命中測試,判斷 bar.com iframe 轉譯程序應接收點擊,並將點擊傳送至該程序。 - bar.com 的轉換器執行緒會將
click
事件路由至 bar.com 的主要執行緒,並排定轉譯事件迴圈工作來處理該事件。 - bar.com 主執行緒的輸入事件處理器會執行測試,判斷點選的是 iframe 中的哪個 DOM 元素,並觸發
click
事件,供指令碼觀察。由於沒有preventDefault
,因此系統會前往超連結。 - 載入超連結的目的網頁後,系統會轉譯新狀態,步驟與前述「轉譯變更的 DOM」範例相似。(這裡未顯示後續的變更)。
外帶
您可能需要花費大量時間,才能記住及內化算繪的運作方式。
最重要的收穫是,經過仔細的模組化和細節處理,算繪管道已分割為多個獨立的元件。接著,這些元件會在平行程序和執行緒中分割,以便盡可能提升可擴充的效能和可擴充性機會。
每個元件都扮演著關鍵角色,可提供現代網頁應用程式的效能和功能。
請繼續閱讀關於主要資料結構的相關資訊,這些結構對於 RenderingNG 來說,與程式碼元件同樣重要。
插圖由 Una Kravets 繪製。