圖層模型
簡介
對大部分的網頁開發人員而言,網頁的基本模型是 DOM。轉譯是指將網頁呈現為螢幕上的圖片,經常難以理解的程序。近年來,新世代瀏覽器改變了算繪的運作方式,活用顯示卡功能,這通常是指「硬體加速」。介紹一般網頁 (例如 Canvas2D 或 WebGL),這意味著什麼意思?本文說明 Chrome 中硬體加速轉譯網頁內容的基本模型。
大脂肪警告
我們在這裡會介紹 WebKit,更具體來說,是討論 Chromium 的 WebKit 連接埠。本文將詳細說明 Chrome 的實作詳細資訊,而非網頁平台功能。網路平台和標準並未編寫此層級的實作細節,因此本文內容無法保證適用於其他瀏覽器,但對內部知識的知識可能仍適合用於進階偵錯和效能調整作業。
此外,本文將介紹 Chrome 的轉譯架構核心,這是將大幅變化的轉譯架構核心。本文僅探討不太可能變動的細節,但無法保證這項規定在未來六個月都適用。
請務必瞭解,Chrome 當時有兩段不同的轉譯路徑:硬體加速路徑和舊版的軟體路徑。在此時,所有網頁都會在 Windows、ChromeOS 和 Android 版 Google Chrome 的硬體加速路徑上進行下去。在 Mac 和 Linux 上,只有需要組合部分內容的網頁才會加快加速程序 (請參閱下方內容,進一步瞭解需要合成的項目),但很快也會在所有網頁採用加速路徑。
最後,我們會搶先一窺轉譯引擎背後的運作原理,並探討會對效能帶來重大影響的部分功能。當您嘗試改善自家網站的效能時,瞭解圖層模型會有所幫助,但親自拍攝也很容易:圖層是很實用的結構,但建立大量圖層會使整個圖像堆疊的負載增加。你自己發動了吧!
從 DOM 到畫面
圖層簡介
網頁載入並剖析後,在瀏覽器中就會以許多網頁開發人員熟悉的結構表示:DOM。不過,算繪網頁時,瀏覽器有一系列的中繼表示法,不會直接提供給開發人員。這類結構最重要的部分是圖層。
Chrome 實際上有幾種不同的圖層類型:RenderLayers 負責 DOM 的子樹狀結構,以及負責 RenderLayers 子樹狀結構的 GraphicsLayers。我們最感興趣的是 GraphicsLayers,是上傳至 GPU 做為紋理的內容。我先從這裡說「圖層」,代表 GraphicsLayer。
GPU 術語簡介:什麼是紋理?可以視為點陣圖圖片從主記憶體 (即 RAM) 移至視訊記憶體 (例如 GPU 上的 VRAM)。經過 GPU 處理後,你就可以將其對應至網格幾何圖形。在電玩遊戲或 CAD 程式中,這項技術就是讓骨架 3D 模型「面色」。Chrome 使用紋理將許多網頁內容寫入 GPU。只要將紋理套用至非常簡單的矩形網格,即可以成本低廉的方式對應至不同位置和轉換。3D CSS 的運作原理,也非常適合快速捲動,後續操作則更多。
讓我們來看看幾個例子,說明圖層概念。
在 Chrome 中研究圖層時,在開發人員工具設定中的「算繪」標題下方,使用「顯示複合圖層框線」旗標 (也就是小圖示) 是相當實用的工具。只會醒目顯示畫面上的圖層。讓我們啟用吧!本文所擷取的螢幕截圖和範例,都是從本文撰寫時最新的 Chrome Canary 27 版擷取。
圖 1:單一圖層頁面
<!doctype html>
<html>
<body>
<div>I am a strange root.</div>
</body>
</html>
這個頁面只有一個圖層。藍色格線代表圖塊,您可以視為 Chrome 用來將大型圖層的一部分上傳至 GPU 的子單位。但在這裡並不重要。
圖 2:其專屬圖層中的元素
<!doctype html>
<html>
<body>
<div style="transform: rotateY(30deg) rotateX(-30deg); width: 200px;">
I am a strange root.
</div>
</body>
</html>
只要將 3D CSS 屬性放到 <div>
上旋轉該屬性,我們就可以查看元素獲得專屬圖層時的外觀:請注意,在此檢視畫面中描繪圖層的橘色框線。
圖層建立條件
還有哪些元件會取得自己的圖層?Chrome 的功能與時俱進,並持續改進,但是目前建立下列觸發圖層的動作如下:
- 3D 或透視轉換 CSS 屬性
<video>
元素使用加速影片解碼功能- 包含 3D (WebGL) 情境或加速 2D 結構定義的
<canvas>
元素 - 複合外掛程式 (即 Flash)
- 含有 CSS 動畫的元素不透明度或使用動畫轉換的元素
- 含有加速 CSS 篩選器的元素
- 元素具有包含組成圖層的子系 (也就是說,如果元素有自己的圖層中所含的子元素)
- 元素具有同等的 Z-index 值較低,而該元素具有合成圖層 (也就是顯示在合成圖層上方)
實用影響:動畫
我們也可以移動圖層,讓這些圖層在製作動畫時非常有用。
圖 3:動畫圖層
<!doctype html>
<html>
<head>
<style>
div {
animation-duration: 5s;
animation-name: slide;
animation-iteration-count: infinite;
animation-direction: alternate;
width: 200px;
height: 200px;
margin: 100px;
background-color: gray;
}
@keyframes slide {
from {
transform: rotate(0deg);
}
to {
transform: rotate(120deg);
}
}
</style>
</head>
<body>
<div>I am a strange root.</div>
</body>
</html>
如前所述,圖層在移動靜態網頁內容時相當實用。在基本情況下,Chrome 會先將圖層的內容繪製成軟體點陣圖,再以紋理形式上傳至 GPU。如果內容日後不會變更,則無須重新繪製。這是很棒的事情:重新繪製需要耗費時間在其他工作上,例如執行 JavaScript,或者,如果繪製時間過長,就會造成動畫失準或延遲。
舉例來說,在這個 開發人員工具時間軸的檢視畫面中,這個圖層在來迴旋轉時不會發生繪製作業。
無效!重新粉刷
但是如果圖層的內容有所變更,就必須重新繪製。
圖 4:重新繪製圖層
<!doctype html>
<html>
<head>
<style>
div {
animation-duration: 5s;
animation-name: slide;
animation-iteration-count: infinite;
animation-direction: alternate;
width: 200px;
height: 200px;
margin: 100px;
background-color: gray;
}
@keyframes slide {
from {
transform: rotate(0deg);
}
to {
transform: rotate(120deg);
}
}
</style>
</head>
<body>
<div id="foo">I am a strange root.</div>
<input id="paint" type="button" value="repaint">
<script>
var w = 200;
document.getElementById('paint').onclick = function() {
document.getElementById('foo').style.width = (w++) + 'px';
}
</script>
</body>
</html>
每當點按輸入元素,旋轉元素時都會增加 1 像素。這會導致整個元素重新配置及重新繪製,在本範例中為整個圖層。
如要查看繪製效果,使用開發人員工具中的「顯示繪製矩形」工具,還有開發人員工具設定「算繪」標題底下的工具。開啟這項功能後,請留意使用者點擊按鈕時,動畫元素和按鈕都會閃爍紅光。
繪製事件也會顯示在開發人員工具的時間軸中。眼看清晰的讀者可能會發現其中有兩個繪製事件:一個用於圖層,另一個用於按鈕本身,當其變更為失壓狀態時,就會重新繪製。
請注意,Chrome 不一定需要重新繪製整個圖層,因此會聰明地只重新繪製無效 DOM 的部分。在本例中,我們修改的 DOM 元素是整個圖層的大小。但在許多情況下,圖層會有許多 DOM 元素。
另一個顯而易見的問題,就是導致失效和強制重新繪製的原因。這很難逐一回答,因為有許多極端案例會強制撤銷。最常見的原因是操控 CSS 樣式或造成重新版面配置造成 DOM 毀損。Tony Gentilcore 發表了一篇文章,說明重新調整版面的原因。Stoyan Stefanov 發表的文章會詳細介紹繪畫 (但最後一篇為繪畫,而非這是什麼精美的合成材料)。
如果想瞭解您正在做的事情是否會影響到正在處理的事務,最好的方法就是使用開發人員工具的時間軸和顯示矩形工具,看看自己是否在造景不甚理想後重繪,然後在重新版面配置/重新繪製之前,試著找出在何處解開 DOM。如果畫作不可避免,但時間過長,請參閱 Eberhard Gräther 文章瞭解開發工具的連續繪製模式。
搭配使用:DOM 到 Screen
那麼 Chrome 如何將 DOM 轉換成螢幕圖片?概念上來說,
- 擷取 DOM 並將其分割成多個圖層
- 將每個圖層獨立繪製成軟體點陣圖
- 以紋理的形式上傳到 GPU
- 將多個圖層合併成最終畫面圖片。
以上所有程序都必須在 Chrome 首次產生網頁頁框時進行。但在之後的影格中,還是可以使用一些快速鍵:
- 如果某些 CSS 屬性有所變更,就不需要重新繪製任何內容。Chrome 可以將原本就在 GPU 上的現有圖層,以其他組合屬性 (例如不同位置、不透明度等) 重新合成。
- 如果部分的圖層失效,系統會重新繪製並重新上傳。如果內容保持不變,但複合屬性有所變更 (例如翻譯或不透明度變更),Chrome 可保留 GPU 並予以重組,製作新影格。
現在應該很清楚,圖層型合成模型會對算繪效能產生深刻影響。在不需要繪製任何項目的情況下,合成成本相當低廉,因此在嘗試對轉譯效能進行偵錯時,避免重新繪製圖層是不錯的整體目標。熟練的開發人員將查看上方的合併觸發條件清單,以輕鬆強制建立圖層。不過請注意,由於這些程式碼會佔用系統 RAM 和 GPU 上的記憶體 (特別是行動裝置上的記憶體限制),因此請謹慎思考是否建立這些容器。如果這類容器佔用大量資源,邏輯中可能會增加其他負擔,持續追蹤哪些資源可見。如果層數較大,而且與先前相比的高度重疊,許多層反而會增加光柵化的時間,這會導致有時稱為「過度繪製」。因此請善用您的知識!
以上是今天的節目內容請密切關注後續幾篇文章,瞭解如何運用圖層模型的實際影響。