首頁 > 遊戲
CSS 硬體加速的好與壞
由 文江部落格 發表于 遊戲2023-01-25
簡介之後瀏覽器只需要告訴GPU去轉換指定的紋理來實現DOM元素的動畫效果
硬體超級加速是什麼意思
每個人都痴迷於
60楨每秒
的順滑動畫。為了實現這個順滑體驗現在用的最流行的一個做法就是使用『
CSS 硬體加速
』。在一些極端例子中,強制使用translate3d意味著大大提高應用程式的效能。
現代瀏覽器大都可以利用GPU來加速
頁面渲染
。在GPU的眾多特性之中,它可以儲存一定數量的紋理(一個矩形的畫素點集合)並且高效地操作這些紋理(比如進行特定的移動、縮放和旋轉操作)。這些特性在實現一個
流暢的動畫
時特別有用。瀏覽器不會在動畫的每一幀都繪製一次,而是生成DOM元素的快照,並作為GPU紋理(也被叫做層)儲存起來。之後瀏覽器只需要告訴GPU去轉換指定的紋理來實現DOM元素的動畫效果。這就叫做
GPU合成
,也經常被稱作『硬體加速』。
不幸的是,瀏覽器是一個很
複雜
的軟體(Firefox 有
幾百萬行程式碼
)。因此一句簡單的『使用translate3d來提高效能』並不能囊括所有的情況。如果碰巧有效那不過是瞎貓碰上死耗子而已。所以有必要知道更多的執行機制,才能更好地處理實際情況。
想象使用GPU加速的動畫就像是Vin Diesel(速度與激情的主角)開著Dominic標誌性的汽車 —— Dodge Charger。它的定製900 hp引擎可以讓它在一瞬間從0加速到60碼。但是如果你開著它在擁擠的高速公路上又有什麼用呢?這種情況下你選擇的車輛Charger是正確的。但是問題是你還在一個擁堵的高速公路上。
GPU合成也是同樣的道理。許多動畫還是需要CPU的介入,這畢竟是瀏覽器工作的方式,你無法改變它。而連線CPU和GPU的匯流排的頻寬不是無限的,所以需要關注資料在CPU和GPU之間的傳輸,要儘量避免造成通道的擁擠。換句話說你需要一直注意
畫素的傳輸
。
首先也是最重要的任務就是了解建立的
合成層的數量
。因為每一個層都對應了一個GPU紋理,所以有太多的層會消耗很多記憶體。這可能導致出現預期之外的行為,可能會導致潛在的崩潰。幸運的是你很容易就能透過瀏覽器來檢查頁面上的合成層數量。
對於Firefox,開啟
about:config
然後設定
layers。draw-borders
為true。
如果是Chrome使用者,開啟
chrome://flags/#composited-layer-borders
啟用,然後開啟開發工具勾選
Show composited layer borders
。
對於Safari使用者,先開啟終端執行
defaults write com。apple。Safari IncludeInternalDebugMenu 1
。然後重新啟動下Safari,選單中找到一個開發選單開啟Web檢查器就能在右邊看到一個tab叫『層』了。選中之後你就可以在Web檢查器的
邊欄
中看到每個層的記憶體消耗。
當這些瀏覽器都正確的配置之後,每個DOM元素的合成層都會被標記一個額外的邊框(你可以透過這個
Spinning Cube
Demo來測試下)。用這種方法就可以驗證你的頁面是否有太多的層。
另一個重點就是保持GPU和CPU之間的傳輸量達到最小值。換句話說,層的更新數量最好是一個理想的常量。每次合成層更新,一堆新的畫素就可能需要傳輸給GPU。因此為了高效能,在動畫開始之後避免層的更新也是很重要的(避免動畫進行中時有其他層一直更新導致擁堵)。這可以透過選擇
恰當的CSS屬性
實現動畫來解決:
transformation
(translate, scale, rotate)、
opacity
或者
filters
。
如果你在使用Safari的web檢查器,選擇『層』標籤後就能在側欄看到『繪圖』區域。這裡的數字代表了Safari提交當前層的新紋理次數。在
Colorful Boxes
這個demo上試一試。這個demo中每個box都會不停地修改自己的背景顏色。不幸地是修改box的背景色會強制合成層更新紋理,因此它的『繪圖』數量會不停的變大。如果只有一個盒子,那還沒什麼關係,如果是幾百個盒子那就很容易達到GPU的瓶頸。當然這是一個極端的例子,只是提醒下你在這種情況下
translate3d
也救不了你。
需求是創造之源。合成層的限制也會引導我們創造更多令人驚訝的方法來利用瀏覽器的硬體加速特性。比如我們可以將UI的初始狀態和結束狀態放在同一個合成層中,然後透過
剪下
的方法來顯示一部分並隱藏另一部分。還有一個類似的方法是透過
兩層疊加
造成視覺錯覺來實現一些特別的效果。透過修改兩個層的透明度來實現動畫效果,比如這個
Glowing Effect
demo。
另外一個常用的方法就是維護一個合成層池,這樣也可以減少畫素的傳輸。當有些層不需要的時候,它們不會被銷燬。它們會被移到螢幕之外或者設定為透明的。在一些情況下,UI設計時可以規定一個固定的合成層數量。比如下面這個
Cover Flow
的例子,同時只能顯示9張圖片。即使它需要可以顯示成千上萬的書本封面(在左右滑動時),你也不需要一次性構建這麼多合成層。只需要一個小小的修改,那就是在滑動時將舊圖片的層移出作為新圖片的層使用。使用者根本不會感覺到變化。
同樣不要忘記你必須使用效能檢測工具(profiler)來檢查你的理論是否成立。效能最佳化是非常嚴肅的話題,如果只是依靠自己的直覺那就很容易出錯。Chrome 的使用者應該啟用
chrome://flags/#show-fps-counter
。同樣 Firefox 也要在
about:config
裡面啟用
layers。acceleration。draw-fps
。透過幀率來檢測你的動畫。如果幀率下降到 60fps(或者沒達到你要的效果),那麼就該調查下原因。Chrome 的
Timeline特性
或者Safari的
Timeline面板
都可以讓你瞭解渲染過程中的細節:layout、painting 和合成。
為了做好效能的迴歸測試,自動實現如上操作是很有必要的。這時
Parashuram
的
browser-perf
就會變得非常有用。幾星期前他已經寫過一些
博文
來介紹自動化測試網頁效能。對於本文的情況,
測量層數和層更新次數
是非常有用的。有了這些資料你就可以在數值超過限制的時候告警。
已經有許多文章講述過 CSS 硬體加速這個課題了,希望這篇文章能成為另一個快速幫助手冊,教你如何正確地使用GPU合成來加速你的CSS動畫。遠離麻煩絲般順滑!