1. 程式人生 > >Egret 效能優化

Egret 效能優化

引言

之前完成了專案的邏輯內容開發之後,開始著手解決效能方面的問題,比較嚴重的就是發熱和耗電。而且出現玩的時間越長越卡的問題,想必是有記憶體洩露了。接下來就是優化的主要思路:

  • 首先,降低常駐場景的 drawcall ,即遊戲主場景及主介面 UI ;

  • 其次,排查記憶體洩露;

  • 最後,假如資源回收機制。

減低 Drawcall

設定 index.html 中 egret 的啟動引數:data-show-fps="true" 即可在螢幕左上角實時看到當前的繪製批次和 drawcall 資訊。

1. 優化圖集

我們專案使用 egret + fairygui 來開發的,有相關優化經驗的應該知道,通過打圖集的方式可以達到同一圖集圖片可以通過同一個批次繪製出來,然而 Egret 並不能像 Unity 那樣通過判斷 mesh 是否相同來實現卡節點合批,例如:兩個圖片在同一個圖集中,但在兩個圖片中間插入了一個文字元件,這樣合批就被打斷。

2. 文字合批

其次,基本文字使用的字型相同,但是在 egret 中,每個文字都需要單獨佔用一個繪製批次。要實現文字合批,只能通過自定義字型,使用圖片字型的方式代替原生的字型,但這樣也存在侷限性,即只能針對內容變化較小的情況,例如一些標題和數值的展示情況下。

3. 動靜分離

動態內容與靜態內容分離,這也是我們專案優化中效果最為明顯的一個部分,因為我們主場景是一個由多個格子組成的棋盤,而格子會有多個樣式,可分為三層。

  • 假如把三層放在同一節點下,則每個格子需要3次 drawcall ,那麼當格子越多, drawcall 是成倍上升的,而棋盤格子至少也有 20 個,則原本的節點規劃方案需要 20 x 3 = 60 次 drawcall ;

  • 而修改後,將每個格子的三層,分別是底圖層、圖示層和行走高亮層。

記憶體洩露

主要藉助 Google Chrome 瀏覽器除錯面板中的 Heap Profiling 工具來進行排查,實現方式是通過記錄當前的堆記憶體(heap)快照,並生成物件的描述檔案,給出當時 JS 執行所用到的所有物件、物件佔用的記憶體大小和引用層級關係等。

在除錯面板的 Memory 頁籤便是 Heap Profiling 工具,下面是具體的使用操作:

  • 如何打快照:

    選中左側選單中的 Profiles 選項,在 Select profiling type 中選擇第一項 Take heap snapshot

    ,點選 Take snapshot 點選即可開始打快照。

    需要注意的是,假如在控制檯中列印物件的話,也會把物件引用住,所以打快照前最好也清空一下控制檯中的列印日誌。此外,每次打快照之前都需要點選一下 Collect garbage 按鈕,保證能被回收的資源都被回收了,這樣後面對比快照查詢洩露才準確。

  • 對比快照:

    當打出首個快照之後,我們會進行一些遊戲內的操作,例如:開啟一個介面或切換一下場景,然後再關閉介面或切回原來的場景。一番操作之後,點選 Collect garbage 按鈕然後打出第二個快照。現在在 HEAP SNAPSHOT 下便有兩個快照:

    接下來是對比快照的步驟:

    • 選中快照列表中需要進行對比的一個快照;

    • 右側操作型別選擇 Comparison (對比);

    • 再選擇要進行對比的另一個快照。

  • 定位洩露原因

    在對比結果展示表中有幾項資料:

    • #New

    • #Deleted

    • #Delat

    • #Alloc. Size

    • #Freed Size

    • #Size Delta

    對比技巧:

    • 可以直接在對比資料上方的 Class filter 中輸入開啟過的 UI 的類名;

    • 假如存在,則表示 UI 關閉後仍被引用住沒有被回收,展開資料可以看到引用鏈;

    引起洩露的原因通常是物件被全域性的變數引用住導致 gc 時沒辦法自動釋放掉,排查引用鏈時其實可以定位到具體引用住此UI 的物件和引用的程式碼所在的位置,下面是分析引用鏈的操作:

    • 先選中物件並展開物件的引用鏈,在 Retainers/Object 中第一個物件通常就是引用此 UI 的物件:

      不難看出此 UI 是被 gEventCenter 這個物件引用住了。

    • 接下來排查引用程式碼的位置,可以將滑鼠直接懸停在引用物件上,然後展開物件的 fn 屬性:

      直接點選 [[FunctionLocation]] 後面的指令碼資訊,即可直接開啟引用物件的程式碼所在的位置。

  • 小結:

    在 JavaScript 中提及的記憶體洩露,其實並非正真意義上的洩露,通常只是物件被引用住無法無法被回收而導致記憶體累積。這裡需要注意的就是全域性引用住的物件,除非是要重複使用,否則會導致物件無法被記憶體自動回收,從而導致佔用記憶體一直累加。

資源回收

參考了論壇中的一篇分享,大致思路是通過資源的 hashcode 值弱引用(不至於導致資源無法自動 gc )需要統計回收的資源,使用資源路徑作為索引的 key ,並且給每個資源設定一個回收時間,定時檢查資源回收時間,達到回收時間限制的直接使用 RES.destroyRes(key) 釋放資源。

當然,主動呼叫資源回收能降低記憶體佔用,但回收太過頻繁反而會導致發燙更嚴重。

其他

除了以上幾點,開發 H5 小遊戲儘量避免使用 spine 動畫,雖然可以壓縮資源空間,但是效能比較差,之前在接入 QQ 玩一玩就發現 spine 動畫播放時會導致嚴重掉幀,後來換成了 dragon bones 才改善了一點(DragonBones Pro 可以直接將 spine 動畫導成龍骨動畫,而且支援二進位制資料格式)。儘量使用幀動畫這樣實時計算量小的動畫形式。

參考