[譯] google 大牛剖析 Chrome 調度細節

原文連接git

翻譯自:The Anatomy of a Frame github

常常被開發者問及像素渲染管道流的細節問題,它每一步是怎樣觸發的,什麼時候觸發的、爲何會觸發。 這篇文章就是爲了解答這些疑惑的,你將瞭解到像素是如何渲染到屏幕的。web

注意:如下講解基於 Blink 內核的 Chrome 瀏覽器。例如像layoutstyle calcs 等步驟運做在瀏覽器主線程中,這在大部分瀏覽器廠商中達成共識,但如下總體架構未必被其餘廠商採納。canvas

一圖勝千言

  • 像素到渲染到屏幕的全過程

anatomy-of-a-frame

流程

在這張圖裏涉及到太多的內容,能夠大體分爲兩個大的部分。 (如下文字須要反覆對照這張圖,爲了方便,做者很貼心的給了圖片地址The Anatomy of a Frame。)api

  • 渲染流程 Renderer Process

該流程包含許多線程負責處理不一樣的事物,確保讓你的頁面渲染到屏幕。 主要的線程有以下瀏覽器

  1. Compositor 合成線程
  2. Tile Worker 瓷磚工線程(每一個像素就像瓷磚一塊塊疊加起來)
  3. Main threads 主線程
  • GPU流程 GPU Process

該流程服務於上述的線程和瀏覽器其餘的進程。當渲染流程處理完幀後,將觸發 commit 事件。 GPU流程將瓷磚tiles 和其餘數據(例如:quads and matrices)上傳給 GPUGPU 將像素輸出到屏幕。 實際GPU流程包含一條單線程GPU Thread 負責與 GPU 通訊。性能優化

渲染流程線程

如今分析下渲染流程中的線程。多線程

  • 合成線程 Compositor Thread.

當用戶交互例如輸入框輸入數據,OS觸發 vsync event 首先通知 Compositor Thread。 若是能夠的話,該線程避免進入主線程,經過 GPU Thread 直接與 GPU 通訊,把用戶的交互行爲渲染到屏幕上。 若是不能夠(事件綁定了回調處理或其餘可視化DOM操做),這時才須要主線程來處理。架構

  • 主線程 Main Thread

該線程執行着咱們耳熟能詳的工做,如JavaScript, styles, layoutpaint 等。 因爲大量的工做運行在該線程中,該線程是形成頁面卡頓jank的"真兇"。app

  • Compositor Tile Worker(s)

Compositor Thread 會生成(spawn)一條或多條子線程 Compositor Tile Worker 來處理光柵化任務。

PS:在某種程度上 Compositor Thread 纔是 big boss ,雖然它不處理 JavaScriptLayout, Paint等任務。 但它全面的控制着主線程的初始化和把每幀運輸到屏幕上的工做。

其實 Service WorkersWeb Workers 也存在該過程當中,只是有點複雜,暫且不表。

主線程

  • 主線程全貌(這裏把Parse HTML -> Composite 之間的過程稱爲渲染管道流)

main-thread

接下來分析下圖中完整版主線程中的每一個事件是幹什麼工做的。 有一點須要記住的是:在實際中並非每一步都會執行。 例如沒有新的 HTML 須要解析,Parse HTML 就不會執行。 前面提到的卡頓的優化,就是避免渲染管道中的事件反覆執行或避免某部分的執行。 具體參考渲染性能優化的最佳實踐課程。

一樣值得注意的是:styleslayout 底下的紅色箭頭指向 requestAnimationFramerequestAnimationFrame 能確保在一幀(實際上是指渲染管道流)開始前執行完畢。 但若是你在 requestAnimationFrame 代碼不注意的話,可能會觸發styles,layout的提早執行, 致使強制同步重排/佈局 Forced Synchronous Layout,極大的破壞頁面性能。

PS:之前的一些庫動畫實現例如 jQuery 依賴 setTimeoutsetInterval,能夠的話儘可能用 CSS3requestAnimationFrame 替代。

  1. Frame StartVsync 觸發, 一幀開始。
  2. Input event handlers :合成線程 compositor threadinput 數據傳給主線程, 處理事件回調。OS調度程序將盡最大努力合理調度事件回調(touchmove, scroll, click等),以及時響應用戶交互。 即使如此,在用戶交互和主線程處理事件得到響應之間多少會有些延遲。
  3. requestAnimationFrame 由於它離vsync很近,能夠就近獲取input data。因此這是操做dom理想的地方,例如修改 100 個元素的 class, 並不會致使100 次樣式計算style calculations,而是會在以後的管道流中批量處理。 須要注意的是:你不能在查詢任何已計算的樣式或佈局屬性(例如el.style.backgroundImage, el.style.offsetWidth)。 若是你這麼作了,就像圖中紅色箭頭標明的同樣,recalc styleslayout 或二者會提早執行,將致使強制佈局,更糟會引發頁面抖動。 Avoid Large, Complex Layouts and Layout Thrashing
  4. Parse HTML:任何新增的 HTML 都會被處理,構建新的DOM元素。 你能夠在頁面加載或 appendChild 等操做中看到這一過程。
  5. Recalc Styles:對於解析樣式文件或classstyle 等樣式操做都會引起樣式計算。可能會從新渲染整棵樣式樹。 具體取決於哪一個元素的樣式改變,例如 body 就影響比較大。值得注意的是,瀏覽器已經很聰明能自動限制波及的範圍。
  6. Layout:計算可見元素幾何(盒模型)信息(位置、尺寸)。一般會對整個文檔操做一遍。 產生的開銷和 DOM 個數成正比例關係。
  7. Update Layer Tree:建立層疊上下文和元素層級順序。
  8. Paint:其實這是繪畫的第一步,這一步記錄須要調用繪製的方法 draw calls (fill a rectangle here, write text there)。 第二步是光柵化 Rasterization (下面會提到),draw calls 會被執行。 第一步顯然速度要快於第二步,但常常把這兩步都成爲 painting
  9. Composite:層 layer 和瓷磚 tile 信息被計算後回傳給 compositor thread 處理。 Composite 負責處理有 will-changeoverlapping elements,或任何硬件加速的 canvas
  10. Raster Scheduled and Rasterize:光柵調度和光柵化,這裏將執行在 Paint 任務中提到的draw calls。 將在 Compositor Tile Workers 中處理,該線程的多少取決於系統和硬件設備的能力。 例如 Android 一般起一個Compositor Tile Workers線程,PC 可能有4個。 根據層 layers 信息來光柵化,layers 是由不少瓷磚 tiles 組成。
  11. Frame End:全部 layers 中被光柵化的 tilesinput data (可能被事件回調處理了)將被提交給 GPU Thread
  12. Frame Ships:最後,全部的瓷磚 tiles 都將被 GPU Thread 上傳給硬件 GPU 處理。 GPU 將使用quads and matrices 來把 tiles 打印在屏幕上。
  • 意外收穫:requestIdleCallback

在主線程處理完一幀後還剩餘一些處理空間的話 requestIdleCallback 會被觸發。 這是一個很是好的機會來處理非必要的工做,如用戶行爲信息等採集。 若是你是一個新手,這裏有份參考:Using requestIdleCallback

值得一提,最新的 React 利用此api來優化性能。

LAYERS AND LAYERS

上面流程說起 layer 有兩中概念,以示區分。

  • Update Layer Tree 中的 Layer

層疊上下文the Stacking Contexts。例如兩個絕對定位的div,根據元素先後出現和z-index會產生層疊關係。 這一過程在 Update Layer Tree 中處理,確保正確的層疊順序。

  • Compositor Layers 中的 Layers

這在以後的流程中處理,更適用於已繪製的元素的概念。 提高元素 Compositor Layer 層級的方法

  1. will-change: transform /* transform 不支持的 hack */
  2. transform: translateZ(0) /* 3D */ 對於層級少 animation 元素能減小性能開銷(避免主線程管道流中的某部分執行),俗稱 GPU加速。 但瀏覽器可能不得不建立額外的 Compositor Layers 來保存層疊順序(z-index指定), 這就是產生了 overlapping elements 元素。

發散主題 RIFFING ON A THEME

上述主線程描述的過程差很少都發生在CPU中。只有最後一步,傳輸完後 tilesGPU中處理。

然而在 Android 中有點不一樣,Compositor Tile Workers 光柵化 Rasterization 的操做,也在 GPU 中完成。 draw calls 做爲 GL 命令在 GPU 着色器中執行。

這被稱爲 GPU Rasterization,這是一種減小 paint 成本的一種方式。 若是頁面使用了GPU光柵化,能在Chrome DevTools 中的 FPS Meter 查看。

  • FPS Meter

FPS Meter

參考

若是你想學習如何避免卡頓 jank,想對性能優化方面有更高級的認知, 下面這些都能幫助你。

  1. Compositing in Blink & WebKit.A little old now, but still worth a watch.
  2. Browser Rendering Performance - Google Developers.
  3. Browser Rendering Performance - Udacity Course (totally free!).
  4. Houdini - The future, where you get to add more script to more parts of the flow.
相關文章
相關標籤/搜索