CSS3硬件加速 - GPU加速
首先說明,這里討論的是 WebKit,描述的是 Chrome 的實現細節,而并非是 web 平臺的功能,因此這里介紹的內容不一定適用于其他瀏覽器。
瀏覽器器內核拿到內容(渲染線程接收請求,加載網頁并渲染網頁),渲染大概可以劃分成以下幾個步驟:
- 解析HTML(HTML Parser)
- 構建DOM樹(DOM Tree)
- 解析css構建render樹(將CSS代碼解析成樹形的數據結構,然后結合DOM合并成render樹)
- 布局render樹(Layout/reflow),負責各元素尺寸、位置的計算
- 繪制render樹(paint),繪制頁面像素信息
- 瀏覽器會將各層的信息發送給GPU(GPU進程:最多一個,用于3D繪制等),GPU會將各層合成(composite),顯示在屏幕上。
這里先解釋一下幾個概念:
- DOM Tree:瀏覽器將HTML解析成樹形的數據結構。
- CSS Rule Tree:瀏覽器將CSS解析成樹形的數據結構。
- Render Tree: DOM和CSSOM合并后生成Render Tree。
- layout: 有了Render Tree,瀏覽器已經能知道網頁中有哪些節點、各個節點的CSS定義以及他們的從屬關系,從而去計算出每個節點在屏幕中的位置。
- painting: 按照算出來的規則,通過顯卡,把內容畫到屏幕上。
- reflow(回流):當瀏覽器發現某個部分發生了點變化影響了布局,需要倒回去重新渲染,這個回退的過程叫 reflow。reflow 會從 這個 root frame 開始遞歸往下,依次計算所有的結點幾何尺寸和位置。
repaint(重繪):改變某個元素的背景色、文字顏色、邊框顏色等等不影響它周圍或內部布局的屬性時,屏幕的一部分要重畫,但是元素的幾何尺寸沒有變。
注意:
- display:none 的節點不會被加入Render Tree,而visibility: hidden
則會,所以,如果某個節點最開始是不顯示的,設為display:none是更優的。 - display:none 會觸發 reflow,而 visibility:hidden 只會觸發 repaint,因為沒有發現位置變化。
- 有些情況下,比如修改了元素的樣式,瀏覽器并不會立刻reflow 或 repaint 一次,而是會把這樣的操作積攢一批,然后做一次reflow,這又叫異步 reflow 或增量異步 reflow。但是在有些情況下,比如resize窗口,改變了頁面默認的字體等。對于這些操作,瀏覽器會馬上進行 reflow。
層(layer)
首先要了解CSS的圖層的概念(Chrome瀏覽器)
瀏覽器在渲染一個頁面時,會將頁面分為很多個圖層,圖層有大有小,每個圖層上有一個或多個節點。在渲染DOM的時候,瀏覽器所做的工作實際上是:
- 獲取DOM后分割為多個圖層
- 對每個圖層的節點計算樣式結果(Recalculate style–樣式重計算)
- 為每個節點生成圖形和位置(Layout–回流和重布局)
- 將每個節點繪制填充到圖層位圖中(Paint Setup和Paint–重繪)
- 圖層作為紋理上傳至GPU
- 組合多個圖層到頁面上生成最終屏幕圖像(Composite Layers–圖層重組)
而在 Chrome 中,存在有不同類型的層: RenderLayer(負責 DOM 子樹),GraphicsLayer(負責 RenderLayer 的子樹)。接下來我們所討論的將是 GraphicsLayer 層。
GraphicsLayer 層是作為紋理(texture)上傳給 GPU 的。
chrome中查看圖層的方式如下:F12打開開發者工具,再按ESC,從左上角選擇"渲染"(Rendering),找到"圖層邊框"(Layer borders)。
Composite(渲染層合并)
對頁面中 DOM 元素的繪制是在多個層上進行的。在每個層上完成繪制過程之后,瀏覽器會將所有層按照合理的順序合并成一個圖層,然后顯示在屏幕上。對于有位置重疊的元素的頁面,這個過程尤其重要,因為一旦圖層的合并順序出錯,將會導致元素顯示異常。
在 Chrome 中其實有幾種不同的層類型:
- RenderLayers 渲染層,這是負責對應 DOM 子樹
- GraphicsLayers 圖形層,這是負責對應 RenderLayers子樹。
在 DOM 樹中每個節點都會對應一個 LayoutObject,當他們的 LayoutObject 處于相同的坐標空間時,就會形成一個 RenderLayers ,也就是渲染層。RenderLayers 來保證頁面元素以正確的順序合成,這時候就會出現層合成(composite),從而正確處理透明元素和重疊元素的顯示。
某些特殊的渲染層會被認為是合成層(Compositing Layers),合成層擁有單獨的 GraphicsLayer,而其他不是合成層的渲染層,則和其第一個擁有 GraphicsLayer 父層公用一個。
而每個GraphicsLayer(合成層單獨擁有的圖層) 都有一個 GraphicsContext,GraphicsContext 會負責輸出該層的位圖,位圖是存儲在共享內存中,作為紋理上傳到 GPU 中,最后由 GPU 將多個位圖進行合成,然后顯示到屏幕上。
紋理(texture)
這里的紋理指的是 GPU 的一個術語:可以把它想象成一個從主存儲器(例如 RAM)移動到圖像存儲器(例如 GPU 中的 VRAM)的位圖圖像(bitmap image)。一旦它被移動到 GPU 中,你可以將它匹配成一個網格幾何體(mesh geometry),在 Chrome 中使用紋理來從 GPU 上獲得大塊的頁面內容。通過將紋理應用到一個非常簡單的矩形網格就能很容易匹配不同的位置(position)和變形(transformation),這也就是 3D CSS 的工作原理。
CSS3加速原理
瀏覽器接收到頁面文檔后,會將文檔中的標記語言解析為DOM樹。DOM樹和CSS結合后形成瀏覽器構建頁面的渲染樹。渲染樹中包含了大量的渲染元素,每一個渲染元素會被分到一個圖層中,每個圖層又會被加載到GPU形成渲染紋理,而圖層在GPU中 transform 是不會觸發 repaint 的,這一點非常類似3D繪圖功能,最終這些使用 transform 的圖層都會由獨立的合成器進程進行處理。
合成層創建標準
Chrome中滿足以下任意情況就會創建圖層:
- 3D或透視變換(perspective transform)CSS屬性
- 使用加速視頻解碼的節點
- 擁有3D(WebGL)上下文或加速的2D上下文的節點
- 混合插件(如Flash)
- 對自己的opacity做CSS動畫或使用一個動畫webkit變換的元素
- 擁有加速CSS過濾器的元素
- 元素有一個包含復合層的后代節點(一個元素擁有一個子元素,該子元素在自己的層里)
- 元素有一個z-index較低且包含一個復合層的兄弟元素(換句話說就是該元素在復合層上面渲染)
合成層的優點
一個元素開啟硬件加速后會變成合成層,可以獨立于普通文檔流中,改動后可以避免整個頁面重繪,提升性能。
- 合成層的位圖,會交由 GPU 合成,比 CPU 處理要快
- 當需要 repaint 時,只需要 repaint 本身,不會影響到其他的層
- 對于 transform 和 opacity 效果,不會觸發 layout 和 paint
注意:
- 提升到合成層后合成層的位圖會交GPU處理,但請注意,僅僅只是合成的處理(把繪圖上下文的位圖輸出進行組合)需要用到GPU,生成合成層的位圖處理(繪圖上下文的工作)是需要CPU。
- 當需要repaint的時候可以只repaint本身,不影響其他層,但是paint之前還有style, layout,那就意味著即使合成層只是repaint了自己,但style和layout本身就很占用時間。
- 僅僅是transform和opacity不會引發layout 和paint,那么其他的屬性不確定。
性能優化
- 提升動畫效果的元素 合成層的好處是不會影響到其他元素的繪制,因此,為了減少動畫元素對其他元素的影響,從而減少paint,我們需要把動畫效果中的元素提升為合成層。 提升合成層的最好方式是使用 CSS 的 will-change屬性。will-change 設置為opacity、transform、top、left、bottom、right 可以將元素提升為合成層。
- 使用 transform 或者 opacity 來實現動畫效果, 這樣只需要做合成層的合并就好了。
- 減少繪制區域 對于不需要重新繪制的區域應盡量避免繪制,以減少繪制區域。對于固定不變的區域,我們期望其并不會被重繪,因此可以將其提升為獨立的合成層,減少繪制區域,需要仔細分析頁面,區分繪制區域,減少重繪區域甚至避免重繪。
層的重繪
對于靜態 Web 頁面而言,層在第一次被繪制出來之后將不會被改變,但對于 Web 動畫,頁面的 DOM 元素是在不斷變換的,如果層的內容在變換過程中發生了改變,那么層將會被重繪(repaint)。
需要注意的是,如果圖層中某個元素需要重繪,那么整個圖層都需要重繪。比如一個圖層包含很多節點,其中一個節點發生變化,都會重繪整個圖層的其他節點,然后生成最終的圖層位圖。所以這需要通過特殊的方式來強制這個節點擁用一個自己的圖層(translateZ(0)或者translate3d(0,0,0)),CSS3的動畫也是一樣(好在絕大部分情況瀏覽器自己會為CSS3動畫的節點創建圖層)。
層和CSS動畫
簡化一下上述過程,每一幀動畫瀏覽器可能需要做如下工作:
- 瀏覽器解析 HTML 獲取 DOM 后分割為多個圖層(GraphicsLayer)
- 對每個圖層的節點計算樣式結果(Recalculate style–樣式重計算)
- 為每個節點生成圖形和位置(Layout–回流和重布局)
- 將每個節點繪制填充到圖層位圖中(Paint Setup和Paint–重繪)
- 圖層作為紋理(texture)上傳至 GPU
- 組合多個圖層到頁面上生成最終屏幕圖像(Composite Layers–圖層重組)
如果我們需要使得動畫的性能提高,需要做的就是減少瀏覽器在動畫運行時所需要做的工作。最好的情況是,改變的屬性僅僅印象圖層的組合,變換(transform)和透明度(opacity)就屬于這種情況。
現代瀏覽器如Chrome,Firefox,Safari和Opera都對變換和透明度采用硬件加速。
使用 GPU 渲染元素
- transform
- opacity
- filter
3D 和 2D transform 的區別就在于,瀏覽器在頁面渲染前為3D動畫創建獨立的復合圖層,而在運行期間為2D動畫創建。動畫開始時,生成新的復合圖層并加載為GPU的紋理用于初始化 repaint。然后由GPU的復合器操縱整個動畫的執行。最后當動畫結束時,再次執行 repaint 操作刪除復合圖層。
強制使用GPU渲染
為了避免 2D transform 動畫在開始和結束時發生的 repaint 操作,我們可以硬編碼一些樣式來解決這個問題:
.example1 {transform: translateZ(0); }.example2 {transform: rotateZ(360deg); }這段代碼的作用就是讓瀏覽器執行 3D transform。瀏覽器通過該樣式創建了一個獨立圖層,圖層中的動畫則有GPU進行預處理并且觸發了硬件加速。
如果某一個元素的背后是一個復雜元素,那么該元素的 repaint 操作就會耗費大量的資源,此時也可以使用上面的技巧來減少性能開銷。
使用硬件加速的注意事項
- 內存。如果GPU加載了大量的紋理,那么很容易就會發生內容問題,這一點在移動端瀏覽器上尤為明顯,所以,一定要牢記不要讓頁面的每個元素都使用硬件加速。
- 使用GPU渲染會影響字體的抗鋸齒效果。這是因為GPU和CPU具有不同的渲染機制。即使最終硬件加速停止了,文本還是會在動畫期間顯示得很模糊。
觸發回流的屬性
有些節點,當你改變他時,會需要重新布局(這也意味著需要重新計算其他被影響的節點的位置和大小)。這種情況下,被影響的DOM樹越大(可見節點),重繪所需要的時間就會越長,而渲染一幀動畫的時間也相應變長。所以需要盡力避免這些屬性
一些常用的改變時會觸發重布局的屬性:
盒子模型相關屬性會觸發重布局:
- width
- height
- padding
- margin
- display
- border-width
- border
- min-height
定位屬性及浮動也會觸發重布局:
- top
- bottom
- left
- right
- position
- float
- clear
改變節點內部文字結構也會觸發重布局:
- text-align
- overflow-y
- font-weight
- overflow
- font-family
- line-height
- vertival-align
- white-space
- font-size
這么多常用屬性都會觸發重布局,可以看到,他們的特點就是可能修改整個節點的大小或位置,所以會觸發重。
觸發重繪的屬性
修改時只觸發重繪的屬性有:
- color
- border-style
- border-radius
- visibility
- text-decoration
- background
- background-image
- background-position
- background-repeat
- background-size
- outline-color
- outline
- outline-style
- outline-width
- box-shadow
這樣可以看到,這些屬性都不會修改節點的大小和位置,自然不會觸發重布局,但是節點內部的渲染效果進行了改變,所以只需要重繪就可以了。
總結
以上是生活随笔為你收集整理的CSS3硬件加速 - GPU加速的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 浅谈数学、数学建模与人工智能(机器学习,
- 下一篇: VPX显示计算机学习资料第711篇:飞腾