騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用

遊資網發表於2023-03-27
騰訊天美工作室群,在今年全球遊戲開發者大會(GDC) 上共做了4場分享。本演講為天美團隊於 2023 年 GDC 進行的非贊助核心演講(Core Concept)“千人同屏戰鬥:Unity DOTS在《重返帝國》中的應用“分享。

演講者簡介

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
肖健(騰訊天美 T2 工作室客戶端組主程式)

肖健於 2014 年加入騰訊天美工作室群,目前擔任《重返帝國》客戶端組主程式。肖健在遊戲框架、Gameplay 和效能最佳化等方面都有著豐富的研發經驗。

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
侯倉健(騰訊天美 T2 工作室引擎負責人)

侯倉健於 2014 年加入騰訊遊戲,曾參與多款手遊的研究與開發工作。侯倉健於 2019 年加入騰訊天美工作室群《重返帝國》團隊,目前主要負責遊戲引擎和工具鏈的開發工作。

以下是演講正文:

《重返帝國》是一款高品質全 3D SLG 手機遊戲,遊戲場景規模宏大,玩家操作自由多變,畫面上經常會出現超過1000個士兵一起戰鬥的場景。在有限的移動裝置效能上,需要同時兼顧效能與品質,團隊在嘗試過C#、C++以及DOTS等多種技術方案的選型與研究後,最終選擇了Unity DOTS。

Unity DOTS對於團隊可以說是一次敢為人先的選擇,當時市面上並沒有比較知名的使用這項技術的遊戲專案,所以這項技術最後呈現出來的效果其實是沒有太多參考的。其次,當時DOTS是處於一個比較初期的版本,Unity官方還在不停的修改和完善,這意味著團隊享受不到新的features,甚至可能需要處理一些潛在的隱患,這對團隊來說是不小的挑戰。

實戰分享

團隊一方面與Unity官方保持密切的合作與交流,另一方面經過多次的技術迭代與最佳化,最終在《重返帝國》專案上取得了很好的實踐效果,在移動裝置上為玩家呈現了極高品質的視覺效果。同時團隊也積累了一套行之有效的方法論,以下總結了幾點分享給大家。

  • Job資料依賴分析與最佳化,提升整個系統的併發性;
  • 將部分ECS System的Job與非ECS邏輯並行,充分發揮多核;
  • 邏輯資料顯示分離,提升chunk記憶體利用率,減少資源載入帶來的卡頓;
  • 針對System進行邏輯降頻,保證效果同時也提升效能。

當我們完成了整體的框架設計和核心的實現後,在進行效能分析的時候發現Job的併發性並不高,且worker存在大量的idle狀態,導致系統的整體耗時偏高。為此,我們專門開發了靜態分析工具輔助我們找出System之間的讀寫衝突與依賴,透過資料拆分、資料備份來解決衝突,讓耗時較高的Job能夠並行。

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
資料依賴靜態分析工具

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
資料拆分解決寫入衝突

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
資料備份解決讀寫衝突

基於工具的分析,我們不斷的細化和調整,解決了資料的衝突依賴,顯著提高了Job的併發性,最終達到了我們相對滿意的並行效果。

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
資料依賴最佳化後Job的併發執行

之後,我們還將System按照功能進一步的細化拆分,把一部分Job的執行提前到與非ECS程式碼邏輯並行,進一步從整體上提高了我們的遊戲幀率。

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
ECS Job與非ECS邏輯並行

在進行了Job並行性最佳化之後,我們發現在大地圖上拖動時存在由於Entity資源同步載入導致的一些耗時峰刺,這對玩家來說是體驗上的損失。所以我們針對Entity,使用邏輯與顯示分離,一方面讓資源可以非同步載入減少卡頓,另一方面也提升了單個chunk的記憶體利用率減少CPU的cache missing。

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
邏輯顯示分離-資源非同步載入

最後,我們在不影響效果的前提下,針對部分System進行邏輯降頻與錯幀(如移動邏輯計算相關的System降到12幀、耗時較高的MoveJob與AnimatorJob錯幀執行),讓整體的耗時更加平滑,並且有效的降低了遊戲的功耗。

效能最佳化

當時我們使用的是 Unity2019版本,Hybrid Render V1 版本,為了能更順利的將DOTS適配到我們的專案中,我們也在原始框架的基礎上,在資產與渲染方面也進行了大量的按需開發。

我們在接入 DOTS 技術棧時,主要面臨了以下3個問題:

1、資源相容性:因為在接入時已經處於專案中期,很多遊戲資產及對應的生產流水線已經成型,所以如何將已有遊戲資產轉變成可在 DOTS 技術棧中執行的資產,是我們需要解決的問題。

2、邏輯階段的基礎開銷過大:可能會導致千人同屏場景出現時出現卡頓。

3、渲染階段無法修改自定義的材質屬性:因為我們對於戰鬥場景的還原重度依賴 GPU Instancing 技術,所以需要很多自定義的材質屬性可以在執行時被複寫。

於是,我們針對以上問題逐個研究核心痛點,找到了適合我們專案的解決方案。

在資源相容性方面,在綜合評估了各種方案之後,我們決定實現一套自己的序列化和反序列化流程。我們的方案分為離線和執行時兩個階段:離線時,我們將遊戲中各類資產對應的prefab拆分成二進位制檔案和引用到的資原始檔;執行時,我們建立了一個“deserialize world”,用來把離線時生成的二進位制檔案和資原始檔反序列化,生成entity。當entity生成好後,我們再把它們移入default world進行執行。這樣我們既可以在資產製作階段使用我們熟悉的prefab,也可以減少執行時的轉換時間。

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
資產Entity例項化

對於HybridRenderV1在邏輯階段的開銷過大,我們定位到了核心的瓶頸是主執行緒阻塞。比如整個生成合批資訊的過程都是放在主執行緒中進行的,這個過程有很大的最佳化空間。

我們的最佳化方向就是多執行緒化,充分利用移動端的多核優勢。其實在生成合批資訊時,不同的RenderMesh一定對應不同的batch,任務本身具有可多執行緒化的特性。所以如下圖所示,我們分配了一個較大的快取陣列,陣列的大小與執行緒數量和RenderMesh數量相關。多個執行緒並行完成對含有RenderMesh的Chunk進行篩選,並填入快取陣列的指定位置。因為在快取陣列中,每個執行緒都有自己的寫入空間,所以多執行緒並行時,不會產生資料寫入衝突。

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
多執行緒RenderMesh Batch

我們還對遊戲中LOD的結構進行了最佳化。我們遊戲中的模型一般有4層LOD,在轉換成entity後,將會有6個相關的entities生成。

過多的entity不僅浪費記憶體,同時也會導致很多冗餘計算(比如同步位置資訊),而根據LOD的特點,我們可以只記錄單個LOD的資訊,在渲染時按需替換成應當顯示的LOD Mesh即可,這樣我們就可以把原本的4個LOD網格當做一個單獨的網格來對待。同時,我們也將LOD Group節點和Root節點進行了合併,Entity的數量也從原來的6個下降到2個,效能也有了提升。

這種方式帶來的一個額外好處是當我們更高層級的LOD還未載入完成或渲染壓力過大時,我們可以只載入低層級的LOD模型來顯示。

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
LOD結構最佳化

為了在C#中更改材質的Instance屬性,我們定義一個和Instance屬性完全匹配的IComponentData Struct,在資料對齊方面,我們遵循std140記憶體資料對齊原則。如下圖所示

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
Instance屬性對齊

在渲染執行時,我們根據entities的數量預先分配一塊大的快取,之後利用多執行緒把各個可見的entity的InstanceParam資料複製到Buffer中的指定位置。最後將整個快取直接提交至GPU,我們就可以按照傳統的GPU Instance方式來使用快取中的資料了。

在有了RenderMesh上的材質資訊和mesh資料之後,我們的InstanceBuffer也組織好了,這樣透過呼叫Unity的DrawMeshInstanced介面就可以進行渲染了。

騰訊天美GDC分享:千人同屏戰鬥,Unity DOTS在《重返帝國》中的應用
Instance Data Buffer

以上都是團隊在實踐中不斷迭代總結出來的寶貴經驗,希望能對那些同樣想使用Unity DOTS技術的團隊能有所啟發。

相關文章