RenderingNG 架構總覽

Chris Harrelson
Chris Harrelson

前一篇文章中,我們概略說明瞭 RenderingNG 架構目標和主要屬性。這篇文章將說明元件部分的設定方式,以及轉譯管道如何流經。

轉譯工作包括從最高層級開始往下細查:

  1. 將內容算繪至螢幕上的像素。
  2. 為內容之間的內容加上動畫效果
  3. 捲動以回應輸入。
  4. 有效率地將輸入內容轉送至適當位置,以便開發人員指令碼和其他子系統能回應要求。

要轉譯的「內容」是每個瀏覽器分頁與瀏覽器 UI 的「頁框」樹狀結構。以及來自觸控螢幕、滑鼠、鍵盤和其他硬體裝置的原始輸入事件串流。

每個影格都包含:

  • DOM 狀態
  • CSS
  • 面板
  • 外部資源,例如圖片、影片、字型和 SVG

頁框是一種 HTML 文件及其網址。瀏覽器分頁中載入的網頁具有頂層頁框、頂層文件中每個 iframe 的子頁框,以及遞迴 iframe 子系。

「視覺效果」是套用至點陣圖的圖形作業,例如捲動、轉換、裁剪、濾鏡、不透明度或混合。

架構元件

在轉譯 NG 中,這些工作會按照邏輯分成幾個階段和程式碼元件。元件分屬這些執行緒中的各種 CPU 程序、執行緒及子元件。每項要素都有助於實現所有網路內容的穩定性可擴充的效能擴充性

轉譯管道結構

算繪管道圖表,如下列文字所述。

轉譯作業會在管道中執行,過程中會建立多個階段和構件。每個階段都代表程式碼,用於在轉譯中執行一項明確定義任務。成果是階段的輸入或輸出內容的資料結構;在圖表的輸入或輸出中,則會以箭頭表示。

這篇網誌文章不會詳細說明構件,下一篇文章將會說明關鍵資料結構及其在 RenderingNG 中的角色

管道階段

在上圖中,各個階段會以顏色標示,指出這些階段執行的執行緒或程序:

  • 綠色:轉譯程序主執行緒
  • 「黃色」:轉譯程序合成器
  • Orange:視訊處理流程

在某些情況下,容器可以在多個位置執行,視實際情況而定,因此有些位置會採用兩種顏色。

階段如下:

  1. 動畫:根據宣告式時間軸變更計算樣式,並隨著時間變更屬性樹狀結構
  2. 樣式:將 CSS 套用至 DOM 並建立運算樣式
  3. 版面配置:決定螢幕上 DOM 元素的大小和位置,並建立不可變動的片段樹狀結構
  4. 預先繪製:運算屬性樹狀結構,並視情況invalidate任何現有的顯示清單和 GPU 紋理圖塊
  5. Scroll:藉由變更屬性樹狀結構,更新文件和可捲動 DOM 元素的捲動偏移。
  6. Paint:計算顯示清單,瞭解如何從 DOM 光柵 GPU 紋理圖塊。
  7. 修訂:將屬性樹狀結構和顯示清單複製到合成器執行緒。
  8. 圖層化:將顯示清單分為複合圖層清單,用於獨立的光柵化和動畫。
  9. 「Raster、decode and paint Worklet」:將顯示清單、編碼圖片和繪製 Worklet 程式碼分別轉換為 GPU 紋理圖塊
  10. 啟用:建立合成影格,代表如何繪製 GPU 圖塊及在螢幕上放置任何視覺效果。
  11. 「Aggregate」(匯總):將所有可見合成器影格的合成器影格結合為單一全域合成影格。
  12. 「Draw」:在 GPU 上執行匯總合成合成影格,在螢幕上顯示像素。

如果轉譯管道階段不需要,可以略過。舉例來說,視覺效果動畫,以及捲動功能,都可能略過版面配置、預先繪製和繪製作業。 因此,圖表中動畫和捲動會以黃色和綠色圓點標示。如果針對視覺效果可以略過版面配置、預先繪製和繪製作業,則可完全在合成器執行緒上執行,並略過主執行緒。

本文並未直接描述瀏覽器 UI 轉譯內容,但可以視為這個管道的簡化版本 (事實上,其實作共用大部分程式碼)。影片 (也不會直接呈現) 通常會透過獨立程式碼轉譯,將影格解碼為 GPU 紋理圖塊,然後再插入合成影格和繪圖步驟。

程序和執行緒結構

CPU 程序

使用多個 CPU 程序可將網站與瀏覽器狀態之間的效能和安全性隔離,以及不受 GPU 硬體的穩定性和安全性隔離。

CPU 程序各個部分的圖表

  • 「轉譯程序」會算繪單一網站和分頁組合的路徑,進而呈現、動畫、捲動和路徑輸入內容。轉譯程序有很多種,
  • 瀏覽器程序會轉譯、呈現瀏覽器 UI 的動畫及路徑輸入動作 (包括網址列、分頁標題和圖示),並將所有剩餘輸入內容轉送至適當的轉譯程序。只有一個瀏覽器處理程序。
  • Viz 程序會匯總多個轉譯程序及其瀏覽器程序的合成資料。系統會使用 GPU 進行光柵與繪圖。Viz 只有一個程序。

不同的網站始終處於不同的轉譯程序。(實際上,一律會使用電腦;如果可以的話,請在行動裝置上使用。我在下方寫「永遠」 但請留意這一點)

除非分頁彼此相關 (其中一個「開啟」另一個),否則相同網站的多個瀏覽器分頁或視窗通常會進入不同的轉譯程序。當電腦版 Chromium 的記憶體壓力過大時,系統可能會將同一網站的多個分頁放在相同的轉譯程序中,即使分頁不相關。

在單一瀏覽器分頁中,不同網站的影格一律位於不同的轉譯程序,但相同網站的頁框一律位於相同的轉譯程序。從轉譯的角度來看,多個轉譯程序的主要優勢在於跨網站 iframe 和分頁彼此間隔離效能。此外,來源可選擇進一步隔離

所有的 Chromium 只有一個 Viz 程序。畢竟,通常只會有一個 GPU 和畫面要繪圖。將 Viz 分隔成自己的程序,就能確保在 GPU 驅動程式或硬體發生錯誤時,能維持穩定性。此外,這也有助於隔離安全性,對於 Vulkan 等 GPU API 至關重要。對於一般的安全性也很重要。

由於瀏覽器可以有多個分頁和視窗,且所有分頁和視窗都具有瀏覽器 UI 像素可繪製,因此您可能會好奇為何只有一個瀏覽器處理程序?這是因為一次只能聚焦一個瀏覽器分頁;事實上,未顯示的瀏覽器分頁主要會停用並捨棄所有 GPU 記憶體。不過,我們也漸漸在算繪程序 (也稱為 WebUI) 中實作複雜的瀏覽器 UI 轉譯功能。這並非為瞭解決效能隔離問題,而是為了充分利用 Chromium 的網頁轉譯引擎。

在舊版 Android 裝置上,當您在 WebView 中使用 WebView 時,系統會共用轉譯和瀏覽器程序 (這不適用於 Android 版 Chromium,僅適用 WebView)。在 WebView 上,瀏覽器程序也會與嵌入應用程式共用,而 WebView 只有一個轉譯程序。

有時候也會有公用程式程序,可以解碼受保護的影片內容。未在上方說明這項程序。

執行緒

在緩慢的工作、管道平行處理和多次緩衝的情況下,執行緒可幫助維持效能隔離和回應速度。

本文所述的轉譯程序圖表。

  • 主要執行緒會執行指令碼、轉譯事件迴圈、文件生命週期、命中測試、指令碼事件分派,以及剖析 HTML、CSS 和其他資料格式。
    • 主執行緒輔助程式執行的工作,例如建立需要編碼或解碼的圖片點陣圖和 blob。
    • 網路工作站:執行指令碼,以及 OffscreenCanvas 的轉譯事件迴圈。
  • 合成執行緒會處理輸入事件、執行網頁內容的捲動和動畫作業、計算網頁內容的最佳分層,以及協調圖片解碼、繪製工作小程式和光柵工作。
    • 合成執行緒輔助程式會協調 Viz 光柵工作,並執行圖片解碼工作、繪製作業小程式和備用光柵。
  • 媒體、 demuxer 或音訊輸出執行緒 - 解碼、處理及同步處理影片和音訊串流。(提醒您,影片會與主要轉譯管道同時執行)。

為使動畫隔離效能以及從主執行緒工作中捲動,請務必區隔主要執行緒和合成器執行緒。

即使一個網站有多個分頁或頁框,最終透過同一程序處理,但每個轉譯程序都只有一個主要執行緒。不過,不同瀏覽器 API 中所執行的工作的效能會區隔開來。例如,在 Canvas API 中產生圖片點陣圖和 blob 會在主執行緒輔助執行緒中執行。

同樣地,每個轉譯程序也只有一個合成器執行緒。這通常並非只有一個問題,因為合成器執行緒上所有非常昂貴的作業會委派給合成工作站執行緒或 Viz 程序,而且這項作業可與輸入轉送、捲動或動畫並行完成。合成工作站執行緒會協調 Viz 程序中執行的工作,但 GPU 加速功能可能會因 Chromium 無法控制以外的原因 (例如驅動程式錯誤) 而失敗。在這種情況下,背景工作執行緒會在 CPU 上的備用模式執行工作。

合成器工作站執行緒的數量取決於裝置的能力。舉例來說,電腦使用的執行緒數量較多,而且比行動裝置更低,因此通常會使用較多執行緒。此範例為擴充和縮減的範例。

另請留意,轉譯程序執行緒架構是三種不同的最佳化模式應用:

  • 輔助執行緒:將長時間執行的子工作傳送至其他執行緒,讓父項執行緒同時回應其他發生的要求。主要執行緒輔助程式和合成輔助執行緒是這項技巧的絕佳範例。
  • 多重緩衝在算繪新內容時顯示先前轉譯的內容,以隱藏轉譯的延遲時間。合成器執行緒使用這項技術。
  • 管道平行處理:同時在多個位置執行轉譯管道。即使主要執行緒轉譯更新發生,捲動和動畫速度仍可能會更快,因為捲動和動畫可以平行執行。

瀏覽器處理程序

瀏覽器程序圖表,顯示轉譯和合成執行緒之間的關係,以及轉譯和合成執行緒輔助程式的關係。

  • 轉譯和合成執行緒會回應瀏覽器 UI 中的輸入內容,將其他輸入內容轉送至正確的轉譯程序;配置及繪製瀏覽器 UI。
  • 轉譯及合成執行緒輔助程式會執行圖片解碼工作及備用光柵或解碼。

瀏覽器程序轉譯和合成執行緒與轉譯程序的程式碼和功能相似,差別在於主執行緒和合成器執行緒會合併為一個執行緒。在此情況下,只需要一個執行緒,因為不需要與長時間的主要執行緒工作區隔效能,因為設計完全不需要。

Viz 程序

顯示 Viz 程序包含 GPU 主執行緒和螢幕合成器執行緒的圖表。

  • GPU 主執行緒光柵會在 GPU 紋理圖塊中顯示清單和影片影格,並在螢幕上繪製合成影格。
  • 顯示合成器執行緒會將每個轉譯程序 (以及瀏覽器程序) 的合成作業匯總及最佳化,並將其彙整到單一合成影格中,以便在螢幕上呈現。

光柵和繪圖通常在同一個執行緒上發生,因為兩者都依賴 GPU 資源,而且使用 GPU 並不可靠 (為了開發新的 Vulkan 標準),多執行緒對 GPU 的存取體驗就會比較高。在 Android WebView 中,由於 WebView 如何嵌入原生應用程式,所以使用個別的 OS 層級轉譯執行緒進行繪圖。其他平台日後也可能有這類執行緒。

螢幕合成器必須隨時回應,且不會在 GPU 主執行緒上發生任何可能速度變慢的原因,因此位於不同的執行緒。GPU 主執行緒執行速度變慢的其中一個原因是,對非 Chromium 程式碼的呼叫 (例如特定供應商的 GPU 驅動程式) 的呼叫速度可能會在難以預測的情況下緩慢。

元件結構

每個轉譯程序主或合成程序執行緒中都是以結構化方式彼此互動的邏輯軟體元件。

轉譯程序主執行緒元件

Blink 轉譯器圖表。

  • Blink 轉譯器:
    • 本機頁框樹狀結構代表本機頁框的樹狀結構和框架內的 DOM。
    • 「DOM 和 Canvas API」元件包含所有 API 的實作。
    • 文件生命週期執行器會執行向上的轉譯管道步驟,納入修訂版本步驟。
    • 輸入事件命中測試和分派元件會執行命中測試,找出事件指定的 DOM 元素,並執行輸入事件分派演算法和預設行為。
  • 算繪事件迴圈排程器和執行器會決定要在事件迴圈上執行哪些內容,以及執行時機。這項功能會安排在符合裝置螢幕的頻率算繪結果。

框架樹狀結構圖表。

本機頁框樹狀結構有點太複雜,需要留意。提醒您,頁框樹狀結構是以遞迴方式為主頁面及其子項 iframe,如果影格在該程序中執行,則其位於轉譯程序的「local」,否則為「remote」

您可以依據轉譯程序構思畫面上色。在上圖中,綠色圓圈代表在一個轉譯程序中的所有影格;橘色的圓圈為一秒,藍色圓圈為第三。

本機頁框樹狀結構片段是影格樹狀結構中相同顏色的相連元件。圖片中有四個本機頁框樹狀結構,分別代表網站 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. 將內容轉譯成螢幕上的像素。
  2. 為內容之間的內容加上動畫效果。
  3. 捲動以回應輸入。
  4. 有效將輸入內容轉送至適當位置,以便開發人員指令碼和其他子系統回應。

如何針對分頁一轉譯已變更的 DOM:

  1. 開發人員指令碼變更 foo.com 轉譯程序中的 DOM。
  2. Blink 轉譯器會通知合成器需要轉譯。
  3. 合成器會指示 Viz 需要轉譯。
  4. Viz 向合成器發出算繪開始訊號。
  5. 合成器會將起始信號轉送至 Blink 轉譯器。
  6. 主要執行緒事件迴圈執行器會執行文件生命週期。
  7. 主要執行緒會將結果傳送至合成器執行緒。
  8. 合成器事件迴圈執行器會執行組合生命週期。
  9. 所有光柵工作都會傳送到 Viz 做為光柵工作 (通常不只一項工作)。
  10. GPU 上的 Viz 光柵內容。
  11. Viz 確認了光柵工作已完成。注意:Chromium 通常不會等待光柵完成,而是使用稱為同步權杖的內容,而這類權杖必須在步驟 15 執行前透過光柵工作解析。
  12. 將合成器影格傳送至 Viz。
  13. Viz 會匯總 foo.com 轉譯程序、 bar.com iframe 轉譯程序和瀏覽器 UI 的合成器框架。
  14. Viz 安排繪圖活動。
  15. Viz 會在螢幕上繪製匯總合成合成影格。

如何在分頁二上為 CSS 轉換轉換加上動畫效果

  1. bar.com 轉譯程序的合成器執行緒會變動現有的屬性樹狀結構,在合成器事件迴圈中固定動畫。這項操作會重新執行合成器生命週期。(可能會發生光柵工作和解碼作業,但此處並未列出這些工作)。
  2. 將合成器影格傳送至 Viz。
  3. Viz 會匯總 foo.com 轉譯程序、bar.com 轉譯程序和瀏覽器 UI 的合成器框架。
  4. Viz 安排繪圖活動。
  5. Viz 會在螢幕上繪製匯總合成合成影格。

如要捲動分頁三分頁的網頁:

  1. 一連串 input 事件 (滑鼠、觸控或鍵盤) 到瀏覽器程序。
  2. 每個事件都會轉送至 baz.com 的轉譯程序合成器執行緒。
  3. 合成器會決定主執行緒是否需要瞭解事件。
  4. 事件會在必要時傳送至主要執行緒。
  5. 主要執行緒會觸發 input 事件監聽器 (pointerdowntouchstarpointermovetouchmovewheel),確認事件監聽器是否會對事件呼叫 preventDefault
  6. 主執行緒會傳回是否要將 preventDefault 呼叫至合成器。
  7. 如果沒有,輸入事件會傳回瀏覽器程序。
  8. 瀏覽器程序會結合其他近期事件,將其轉換成捲動手勢。
  9. 捲動手勢再次傳送到 baz.com 的轉譯程序合成執行緒執行緒。
  10. 然後套用捲動, bar.com 轉譯程序的合成器執行緒會在其合成事件迴圈中固定動畫。這樣做會變更屬性樹狀結構中的捲動偏移,並重新執行合成器生命週期。此外,也會指示主執行緒觸發 scroll 事件 (此處未呈現)。
  11. 將合成器影格傳送至 Viz。
  12. Viz 會匯總 foo.com 轉譯程序、bar.com 轉譯程序和瀏覽器 UI 的合成器影格。
  13. Viz 安排繪圖活動。
  14. Viz 會在螢幕上繪製匯總合成合成影格。

如何在分頁一的 iframe 中,為 iframe 超連結「click轉送 click 事件:

  1. input 事件 (滑鼠、觸控或鍵盤) 在瀏覽器程序內。這會執行近似命中測試,藉此判斷 bar.com iframe 轉譯程序是否應接收點擊,並將點擊傳送至該處。
  2. bar.com 的合成執行緒執行緒將 click 事件轉送至 bar.com 的主要執行緒,並排定轉譯事件迴圈工作來處理。
  3. 為 bar.com 主要執行緒命中測試的輸入事件處理器,判斷使用者點選了 iframe 中的哪個 DOM 元素,並觸發 click 事件讓指令碼觀察。聽到 preventDefault 時,系統會導向超連結。
  4. 載入超連結的到達網頁時,系統會呈現新狀態,步驟與上述「轉譯已變更的 DOM」範例類似。(這些後續變更不包括在本文中)。

結論

嗯,這已經搞得好多細節。 如你所見,在 Chromium 中算繪相當複雜! 需要花很多時間記住並內化所有部分,因此如果看起來過於龐大,也不用擔心。

最重要的一點,就是有一個概念上簡單明瞭的轉譯管道,經過謹慎的模組化與詳細資訊,已分割成數個獨立元件。接著,這些元件已分割至多個平行程序和執行緒,盡可能提高可擴充的效能擴充性機會。

這些元件在實現所有效能和功能現代化網頁應用程式需要的關鍵角色。我很快就會將深入探討每個項目 以及它們所扮演的重要角色

在此之前,我也會說明本文中提及的主要資料結構 (轉譯管道圖表側邊以藍色表示的關鍵資料結構) 和轉譯 NG 就如同程式碼元件一樣重要。

感謝您的閱讀,敬請期待!

插圖:Una Kravets。