「轉自 TGDC」 UE4製作多人大地型遊戲的優化

8月11日,由騰訊遊戲學院舉辦的第二屆騰訊遊戲開發者大會(TGDC)在深圳舉行。大會技術論壇中,Epic Games 中國資深技術工程師王禰帶來了《UE4製作多人大地型遊戲的優化》的主題演講。演講中,王禰分享了在當前重度手遊的市場下,如何從遊戲線程、渲染線程、GPU、內存等各方面進行優化,從而提升遊戲品質。


「轉自 TGDC」 UE4製作多人大地型遊戲的優化

以下為演講實錄:

大家好!我是王禰,來自Epic Games,現在在中國區負責引擎技術支持以及一些針對中國區的技術功能的開發。我將分享使用UE4製作大地型遊戲的挑戰和優化的手段。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

這是今天會講到的總體內容,比較多一些。首先我們來看一下在移動設備上做大地型多人遊戲的挑戰。大地型肯定是開放的地圖,視野比較寬,視距比較遠,地圖比較大,還會有比較多的風格變換,導致繪製內容的種類比較多,資源的使用、變化比簡單一些的遊戲複雜非常多。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

遊戲線程的優化

對於同樣的移動硬件來看,優化的壓力會大非常多。我們來看看優化分為哪幾部分,主要的優化包括有,大量的角色需要跟場景發生交互,角色的動畫之類的計算以及與場景交互的計算發生在遊戲線程,因此遊戲線程承擔了非常重的優化任務。所以首先我們講遊戲線程的優化。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

引擎裡面有一個東西,我知道這個是比較偏向於遊戲邏輯業務的概念,可能一般大家不太認為會在引擎裡面實現,我們叫做重要度管理系統,大家知道遊戲的常規優化手段叫做LOD,不管是面數、更新頻率,我們都會根據在屏幕上所佔比進行調整,這是很通用的,沿用很久的優化手段。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

我們怎麼樣讓各個遊戲模塊從遊戲邏輯層去修正LOD的計算?這時我們引入Significance Manager,我們會分配針對每個平臺的Bucket,大家可以看到右下示意圖中藍色的小點代表玩家控制的角色,邊上的小點是別的玩家和交互的動態對象。我們根據離主角玩家的距離,在屏幕上的尺寸或者可見性,決定使用什麼Bucket。例如基於可見性的計算,雖然離我很近,但是因為在我的背後,可能很多時候我都感受不到,Bucket就可以分得不一樣,通過Bucket我們會用來控制、修正LOD的各種計算。這裡是一個例子,我們這個系統本身用於我們自己比較火熱的遊戲《堡壘之夜》,手機、掌機、電腦都可以跑,我們兼容所有的平臺可以聯機玩,遊戲在不同平臺上的場景、複雜程度其實是一樣的。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

這種情況下,硬件的計算能力有非常大的差別,所以我們針對移動平臺和主機Bucket也不一樣,除了自身控制的角色給的Bucket比較高,剩下的角色的比較低,主機有四個,手機有一個,這個設置不僅按平臺來,也可以按設備來,移動設備好的和差的硬件計算能力差很多,我們可以在Device profile指定當前這臺設備Bucket的規劃。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

剛才是比較全局的系統,接下來我們看遊戲線程裡開銷最大的部分,就是我們的動畫。動畫系統大部分角色是可以定製的,角色會分為幾個部分,繪製調用的數量、動畫骨骼更新、不同部件的不同動畫計算量非常大,針對《堡壘之夜》這樣的遊戲有一些特殊的遊戲模式,例如50V50,這種情況下,最終在縮圈以後,同屏會出現超過50甚至80個角色,每個角色還分了好幾個部件,揹包、武器都有不同的動畫,這個時候計算量非常大,我們需要對動畫做非常大量的優化。

剛剛我們已經說到角色可能分為幾個部分,有一些不同的策略,引擎提供各種方式,一種是將不同的部位的Mesh合成為一個,這個模型有一個問題,材質是要合併起來的,你的表情的動畫就沒有了,在這個方案上我們做了一些取捨,最終決定不在《堡壘之夜》用這種方式。另一種,身上不需要動畫的剛體掛件可以方便的掛在角色骨骼的Socket上面,這是比較簡單的方式。還有Master Slave的方式,主體動畫是一套完整的骨骼,身上掛載的動畫是這個骨骼的子支,這個時候我們可以把這些掛載的部件的動畫完全跳過自己的動畫更新計算,完全用Master驅動,這樣的骨骼動畫直接使用Master的骨骼矩陣,沒有辦法擴展,比如Master Skeleton沒有尾巴或是披風的骨骼,尾巴或是披風的獨立動畫或者物理模擬就沒辦法做。針對這種情況,我們還有一個解決方案是Copy Pose,可以把主體的計算完的骨骼矩陣拷貝給附屬的骨骼矩陣,只要保持目標骨骼和原骨骼的層級結構一致,就可以在目標骨骼上增加擴展性的骨骼,可以根據自己的狀態播自己的動畫,也可以模擬物理。這是四種多部件角色setup的方案,無論使用哪一種,都需要對骨骼模型和骨架設置LOD,這是下面提到多種優化的前提。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

第一步比較直觀的是在動畫更新的時候會有大量的邏輯事件的計算,我們稱之為Event Graph,這是UE4提供的圖形化的腳本功能,Event Graph是需要經過圖形化的腳本虛擬機,這個調用在動畫邏輯比較複雜的時候開銷有點高,我們把在虛擬機上計算的Event Grape轉到C++,省掉了大量開銷。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

再有一個是Anim Graph,我們根據當前的狀態選擇不同的骨骼層級,播放哪個動畫,或是經過哪些骨骼控制節點,比如說IK、物理模擬最終的POSE的計算。在這個計算中間有一些步驟會用到數學計算,因為是在Graph,會有一些額外的開銷。我們做了一些優化,我們把所有這些獨立計算的模塊通通納入到一些基礎的骨骼動畫混合節點,包括偏移和縮放,這樣可以減少虛擬機的調用開銷,我們把這些包含簡單計算項的動畫混合節點叫做Fast Path節點,骨骼混合的計算邏輯通通是用Fast Path就可以完全消除在虛擬機上的開銷。

同屏有那麼多的角色要做骨骼動畫計算,大家知道移動設備是多核設備,為了更好地利用多核的定性,我們需要把剛剛這種虛擬機上的調用更好地平攤到不同的線程。基於上面兩個優化方向,我們不要使用Event Graph,把遊戲邏輯更新的部分放在AnimInstanceProxy上,這樣引擎會自動判斷這個Event Graph是不是可以放在別的線程上更新。如果你用了Fast Path,我們就可以把骨骼的update和evaluation都放到working thread上面去,例如有50個角色,在任一角色更新開始,就把計算分到別的線程上面,主線程繼續往下走。

即使我們能利用多線程,計算量還是非常大的,我們要減少動畫更新的數據量,已經有些設置可以幫助動畫在不渲染的時候跳過 Tick pose,也可以通過Singnificance Manager跳過附屬武器、揹包的更新,除了自己的主角,別的角色離你遠一些,信息不更新其實你是注意不到的。

我們的掉落物會模擬物理,是骨骼物體。骨骼計算有一個問題,是走的Dynamic Path。我們引擎的中的靜態對象,會在加到場景中的時候就直接排序分組到自己的Drawing Policy,繪製的時候可以很大程度減少渲染狀態的切換。而動態的單位,是每一幀在渲染開始的InitViews階段動態獲取到數據,它和靜態獲取數據的方式不一樣,不會進入到靜態排序的表裡,繪製的效率比較低。針對這種實際每一幀渲染數據不發生變化的骨骼物體,我們把這些物體額外加到了一個StaticRenderPath,加速了這些物體的渲染。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

URO(Update Rate Optimization),我們其實沒有必要對所有的角色在每一幀都做骨骼計算。比如畫面中一個角色的POSE上半身動作是怎麼樣,下半身動作是怎麼樣,是否需要融合,什麼頻率融合,中間是不是要插值,這些設置可以非常大程度決定骨骼更新的計算量。大家可以看到下面的圖,左一是每一幀都更新;左邊二是每四幀更新一次,中間用插值;第三張圖是每十幀更新一次,中間用插值;最後一張圖是每四幀更新一次,不用插值。大家可以看到當角色佔屏面積比較小,離得比較遠的時候其實是沒有大差別的。

剛才講的這些是針對骨骼動畫更新的優化,其實伴隨著骨骼LOD的設置,我們在AnimGraph中可以設置骨骼控制節點從某一級LOD下不計算,比如說IK、物理模擬。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

說完動畫的優化,接下來遊戲線程還有大量的Scene Component,Scene Component是指世界中有座標位置的對象,它的Transform更新都是在遊戲線程中計算。當你大地圖、大場景動態更新對象非常多,同時每個對象身上會掛很多Scene Component的時候,計算量是非常大的。儘管我們會把Scene Component的計算踢到異步線程,但是計算量依然很大。我們做了一些改進,針對一些掛載在人物身上,不是處於激活狀態的Scene Component做了自動的管理。

打開Auto Manage Attachment,對於音頻和粒子特效,可以自動根據它是否激活的狀態,決定是否掛在父級Scene Component。如果Detach掉,它的Transform就不會再更新。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

當Scene Component發生位置變化的時候會觸發Overlap的檢查,每一幀有大量運動對象時會產生大量Overlap事件,耗費比較大的開銷。優化的原則是儘可能把不需要產生Overlap的事件關掉,注意引擎默認是打開的。我們對層級結構比較複雜的做了子Component是否打開overlap事件的引用計數,會看自己是不是打開了Overlap事件,以及自己的子對象有沒有打開。這個時候我們在做Overlap檢查的時候可以很快地跳過,這個節點往下都沒有,就不需要再檢查自己的子節點,這在場景的對象結構比較複雜的情況下是可觀的優化。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Character Movement,因為角色比較多,角色的移動更新是非常大的遊戲線程的計算,針對這個計算,一部分是角色在移動的時候要檢查新的位置是不是能站立,要做一些掃描,要做一些碰撞,還要找落腳點是不是斜坡,這個斜坡的斜率是不是角色可以站上的,往前走的高度變化是不是可以超過跨過階梯最大的高度,角色一多計算量就非常大。所以除了玩家自己控制的角色,需要比較精確的計算外,其餘角色分到的Significance Manager的Bucket我們最終是用了插值,通過網絡同步過來的位置做簡單的插值來模擬預測計算,在大部分時候都不容易注意到明顯的差異,只有在幀數較低或者網絡帶寬受限比較嚴重的時候,對於落地點會有顯著的偏差,大家可以對比看到這兩個視頻中左邊是預測計算,右邊是插值。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Physics,我們會盡可能地用一些替代的Physics優化物理註冊的對象,有一組對象,比如說邊界,不需要很細緻的碰撞模型,我們可以用簡單的volume來表達物理碰撞對象,減少註冊到物理場景中的對象數量。物理的一個場景會有兩個樹,一個用以做Query,一個用於做Simulation,我們要儘可能保證註冊進去的對象最優化。因此需要儘可能的簡化每個物理對象的複雜度,以及減少整個場景註冊的物理對象數。可以同時以比較小的內存開銷打開異步的物理場景,Physics註冊的對象是一樣的,只不過他會用Shared Shape的方式加到Async Scene裡,這樣在場景做物理模擬的同時,他可以在異步的scene裡做其他的query。

另外我還嘗試過把同樣mesh的不同實例對象用Shared shapes減少註冊的物理對象的內存開銷,在內存敏感的場景下也可以嘗試。還有一個思路是我們可以把物理對象和視覺對象解耦,默認的情況下,引擎的Mesh對象打開碰撞就會註冊物理對象到PhysX Scene,增加了物理場景的複雜度和物理的內存佔用。因此當你的Mesh加載到內存裡,即使不被渲染出來,這些開銷就在了,但是其實很多情況下視覺會看得更遠一些,實際需要物理計算交互的距離在有些遊戲中沒那麼遠,我們可以用一些手段把視覺上對象的物理關掉,把這個物理屬性轉到一些新的Component和Actor上面放到新的Streaming Level裡,用更近的加載卸載距離來管理,這樣實際的物理場景複雜度和內存佔用都會小很多。另外移動端的布料,計算量和網格數量相關,在移動端會不太推薦使用那麼複雜的模擬,引擎也就沒有提供移動端的NvCloth的lib,所以我們一般會用剛體來模擬。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Ticking,也即所有動態邏輯更新的對象,引擎的圖形化腳本可以讓美術策劃和GamePlay程序很方便的在Event Graph做Tick更新,但是需要付出一定虛擬機的調用開銷,當Tick事件觸發的執行隊列非常長,每一幀付出虛擬機的成本就會比較高一些。一個方法是轉到C++,另外一個方法是減低Tick的頻率,更有一些特殊的,例如每一幀只是視覺上在轉動的風車或是旗幟在飄、樹在擺動,其實可以不需要用骨骼動畫、或者在Tick做旋轉,可以用頂點動畫來做。

引擎還有個功能較TextureStreaming,這個系統會在遊戲線程計算用到貼圖的精度,用以決定更新給渲染線程的資源的精度再提交給GPU,對於這個每幀分析畫面貼圖Wanted Mip的計算量每幀還是佔比較多的,遊戲線程吃緊的情況下可以降低Texture Streaming的分析計算的頻率。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

UI,如果遊戲HUD有大量的UI對象,它的位置計算會比較複雜,在遊戲線程的計算量就會比較大,可以多利用我們新出的SlatLayoutCaching和Invalidation Box來Cache Prepass減少widget transform更新的計算,這些Cache可以把計算的位置和大小記錄下來,有一些可以把頂點Buffer Cache下來。另外,我們也需要儘量讓UI的Widget可以Batching起來。引擎的一些佈局空間會自動幫你佈局子控件,例如Horizental和Vertical Box,Grid等,這時候子控件是在同一層上,引擎會優先Batch起來。當使用比較靈活的Canvas Panel時,會導致引擎默認的行為會把每個加入的子空間的Implicit Zorder自動增一,這時候如果你確定這些子Widget不重疊,其實可以手動控制這個ZOrder。當然Batch的前提還是你用了同樣的材質和貼圖。那麼如果做一個揹包界面,裡有很多不同東西的圖標,我們又希望這些圖標有一些特效,我們可以用同一個材質,這隻同一個Texture Altas,針對每個子控件設置不同的Vertex Color,在Vertex Shader裡通過VC的值做為uv來使得這些子控件可以被Batching起來。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

音頻和特效,音頻是比較大的開銷,我們之前的《堡壘之夜》又是從主機到移動端兼容的項目,為了優化音頻在移動端的開銷,我們增加了做了很多設置,使得在移動端不同的設備可以設置不同的SoundCue併發的數量,以及SoundSource的數量。其中SoundSource默認在移動端上總數是16個,主機上可能是32個。簡單說明一下什麼是SoundCue,這就是原始的SoundWave資源拿過來做一些實時處理封裝後的音頻資源,

例如可以在多個SoundWave中做一些隨機、拼接,以及一些聲音效果的實時處理,這些處理效果對計算量要求比較大,我們可以針對不同的硬件設備做一些LOD的設置,比如說在比較差的CPU移動設備上,可以把Reverb,EQ等關掉,或者減少隨機的Wave的數量等。

Particle比較顯著的開銷是Overdraw,我們在PC上有自動把貼圖的Alpha切割出八面體,減少Overdraw的功能,但是這個功能之前在移動端無法使用,最近我發現其實只要支持SRV的設備,是完全可以用這個功能的,移動端上也可以打開。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Level Streaming,為什麼用Level Streaming?其實道理很簡單,因為場景非常大時,我們不可能把所有的場景加載到內存裡面,這時候我們可以把地圖拆得非常碎,每次只加載視距內的一小部分,使得內存的佔用變得比較低。這樣一來場景在內存裡的東西比較小,場景遍歷的開銷也會比較小。同時也可以在設計上增加場景可使用的物件的種類,豐富了場景的複雜度。整個Level Streaming總共分為三個步驟:

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

IO,這一步我們是放在Worker thread做的。第二個步驟是反序列化,在啟用Event Driven Loader後,IO和Deserialization可以並行,其中反序列化也可以由打開s.AsyncLoadingThreadEnabled放到異步的ALT去做。最後一步是Postload,這個有很多時候需要對遊戲線程註冊對象,需要在主線程做,在引擎裡可以用Time Slice的方式分幀異步來做,同時,對於PostLoad中某些不影響遊戲線程的行為,我們也挪到了ALT裡,很大提升了Level Streaming的效益。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

服務器,其實剛才針對客戶端的優化,都會惠及服務器的優化。在新版本中,我們加入了Replication Graph,在集中的類裡做了ServerReplicateActors的計算,總體思路就是減少PerConnection,PerActor的relevancy以及priority的計算量,通過把Net Actor註冊到以空間位置劃分的grid中,每次針對當前Connection只檢查所在Grid內對象的信息來大大降低整個Replication的計算量。另外,對於不同Connection見的部分對象,我們也會Cache下來需要replicate的數據結果針對別的connection複用。這個改動優化使得在我們的項目中我們服務器的整個CPU用以做replication的開銷降到原先的1/4。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

另外一些服務器優化手段有,這是降低所有對象Net relevancy distance的距離;把以移動的RPC包做優化,如果連續的幾個移動方向和速度是一致的,可以把幾個移動RPC包合併起來只發一個,減少網絡帶寬的佔用和包的序列化等計算量。

服務器我們也可以關掉大量動畫的計算,只在播一些特殊動畫的蒙太奇的時候才會打開動畫的更新。在Server上也可以把一些只關注渲染視覺和實際遊戲邏輯計算沒有關係的Component在Server上去掉。

渲染線程的優化

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

好了,看完大量遊戲線程的優化手段,接下來我們來看看渲染線程,渲染線程的第一個開銷取決於場景的複雜度,即使實際繪製出來的內容很少,但是場景遍歷的開銷卻是正比於場景在內存裡的Primitive數量的。如果我這個遍歷時間很長,那麼實際繪製調用發出的時間就會比較晚。這個時候,我們就要利用好Streaming Level來最小化Scene Tranversal的開銷。另外,動態的對象每一幀重新獲取要繪製的渲染數據,也會有不小的開銷,同時也會降低靜態對象的渲染狀態排序的優勢。這也是上面提到過的加入了特殊的Static Render Path的優化手段的原因。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

場景遍歷後的大頭是是Culling,包括預計算的Precomputed visibility Volume,場景針對每個場景的可見性,不是特別大的地圖比較適用,在runtime幾乎沒有開銷,tradeoff是離線計算的時間和一部分內存。然後是並行的視錐體裁剪和基於距離的裁剪,都是很常規的Culling手段。移動端的occlusion是比較頭痛的問題,我們在支持ES3.1的設備上,使用了Hardware occlusion query,在3.1以下的設備我們提供了一個Software occlusion的解決方案。當然要注意這並不是萬能的,有些情況下還多了繪製的三角形面數及大量bounds transform的CPU開銷,卻沒有實際occlude掉什麼對象。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

剔除完就到了最終頭的開銷來源:Draw Calls,減少DC的手段多種多樣,譬如引擎提供了刷foliage的工具,對於石頭、樹之類大量複用的對象,用這種方式刷出的HISCM,會做gpu instancing大大減少DC數。然後一個有用的方案是HLOD,可以把一組Mesh甚至是一個關卡合併成一個Proxy Mesh,在最低級LOD後,可以切換到這個合併的Mesh,大大的減少遠處物件的Draw Call並依然保持很遠的視距。HLOD依然可以做多級的LOD幫助進一步減少DrawCall和減少面數,這些工具都是引擎內建,可以很方便部署自動化。

Dynamic Instancing,我們有一些特殊的方案,針對騰訊的Studio也做了一些整合,接下來的引擎版本會有非常大的渲染pipeline的重構,會對這個有更天然支持,甚至支持帶光照烘焙的Dynamic Instancing,在光照圖計算的時候就把可以instancing到一起的對象優先併到一張光照圖上。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

另外一個和DrawCall開銷息息相關的是渲染狀態切換的數量,引擎裡有個接近的概念叫Drawing Policies,剛才說靜態的對象我們會按Drawing Policies分組排序,現在的版本中,我們針對這個分組排序的規則做了一些改進,可以更好的減少渲染線程的渲染繪製調用的狀態切換,同時也一定程度兼顧gpu的overdraw。剛才說到的新的mesh draw command pipeline要到今年年底,明年年初才上線,在目前的測試場景中,對於渲染線程的優化,可能有近十倍的改善,當然最終在移動端上表現如何還不能下定論。整個新管線的思路是儘可能使得渲染線程在cpu端沒有什麼開銷的,場景資源管理等的開銷都在GPU上。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

RHI Thread,在OpenGL ES上,GraphicAPI的調用必須和glcontext在一個線程,於是,我們把所有的gl command都enqueue到了一個叫RHI Thread的線程,這樣一來,實際渲染驅動的開銷和引擎渲染線程的工作就可以有一部分並行化,減少整個渲染的frame time,以及變向降低渲染線程所在核的主頻,這樣可能在部分設備上還能減少一些功耗開銷。


卡頓優化

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

講完渲染線程,我們來看看Hitches,卡頓主要分為四塊。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Loading,加載,當量啟用streaming level異步加載以後,如果遊戲邏輯發生了阻塞加載,由於引擎並不知道加載數據的依賴性,所以會導致引擎Flush異步線程,造成卡頓。其中普通遊戲邏輯觸發的加載我們可以比較容易的察覺並改正,但是另一個情況是在網絡同步的時候,當服務器第一次同步回來一個新的Actor時,客戶端會創建Actor Channel,並需要實際Spawn Actor,可能會依賴阻塞加載的數據,進而導致flush造成卡頓,我們可以通過打開net.AllowAsyncLoadingEnabled,使得觸發的加載變成一個異步加載,並且這個Actor Channel的創建過程,也會加入一個pending的隊列,等到加載資源都到了以後的那幀才可以實際的創建。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Compile Shader,由於ogl es沒有固定的shadercache標準,引擎提供了ShaderCache,在新版本中改進成了ShaderPipelineCache的功能,該系統可以在離線環境下先跑一遍遊戲,在這個過程中用到的Shader,繪製的狀態記錄都會在Log文件中。Runtime的時候,我們會先讀log,分一些批次預先Compile完以減少runtime發生compile的情況。另外,一旦compile,可以配合另一個ProgramBinaryCache的功能,引擎會把link完的program保存下來,以後再需要加載Shader的時候,如果發現這個link program存在,會直接加載program。這樣不但能省去compile和link的過程,還跳過了shader code的加載過程和節省了內存。除了compile,這個cache系統還會做warmup,也就是預先繪製,以減少第一次使用的額外開銷。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Spawning,降低spawn的開銷一個是減少每個components的數量,再者,儘可能用C++的Component。如果你是BP components,引擎項目設置中有一個選項,可以在cook的時候把components的序列化,初始化的結果存下來,spawn的時候直接拿這個數據做實例化就行了。然後Component註冊到遊戲線程可以做分時。當然最常規的減少spawn卡頓的方法還是做pooling,如果有大量同類型Actor的Spawn,建議這樣做。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

GC,主要分為兩步,先是引用分析,然後分析完標記可以destruct的對象會在這時開始發出BeginDestroy,而實際的Destroy會分幀去做,因為有些對象渲染線程的資源還在訪問,不能當場刪掉,所以只是發出一個render fence,渲染線程回收掉,我們才在下一幀主線程purge的階段把對象刪掉。在整個GC過程中最費的,是引用分析,因為這個必須在當前這幀做完,新版本中我們把標記和引用分析都做了多線程並行,利用所有的核計算,可以比較好的提高引用分析的效率。還有一種手段是可以跳過大量的常駐內存的對象,我這裡列了一個參數,MaxObjectNotConsideredByGC,設置這個參數範圍內的對象是不會在引用分析的時候做檢測的。再有一點是Clustering,一組對象永遠是共生的,可以規劃在Clustering裡面,這樣的場景下GC效率可能提升十幾倍。最後新版本中,我們把BeginDestroy也放到的發生GC的後一幀去做。

GPU優化

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

解析下來是GPU。

渲染分辨率,我們可以逐設備地通過MobileContentScaleFactor設置BackBuffer的分辨率。我們也可以通過r.ScreenPercentage把單獨的3D的分辨率改小。改分辨率是顯而易見提升GPU的手段,因為大部分時候我們都是pixel shader bound。當然,帶寬也是很大的因素,引擎還可以靈活的設置SceneColor的格式,默認HDR下我們使用FP16的RGBA,在有些項目裡我們可以用r.Mobile.SceneColorFormat來調整成R11G11B10或者RGBE的方式減少帶寬的佔用。當然要注意,移動端有些特性一來DepthBuffer,而支持DepthStencil fetch擴展的設備並不算太多,所以引擎默認會把Depth存到SceneColor的A通道,所以採用R11G11B10這樣的格式,可能就會使得某些依賴讀回深度的feature發生問題。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

材質,也就是shader複雜度,我們可以設置Quality Switch使用不同複雜度的材質針對設備做優化。也可以直接使用fully rough,non metal之類的材質優化選項。當然濫用的話會使得最終生成的shader permutation的分裂數量很多,需要注意一下。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Shadow,主要分為兩種。Modulate shadow我們已經不太適用,不過因為是單對象一個shadow volume,所以可以設置的shadow map利用率和精度比較高一些,在某些角色展示場景中可能比較有用;CSM是全場景的動態shadow,非全動態光照時,移動端默認只對動態對象投射。可以通過Device Profile控制,例如可以在低端設備上沒有shadow,中等的設備上可以不做PCF filtering,好的設備上才開filtering做多次採樣。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Landscape,我們在近期版本中也做了一些改進,不同層LOD的計算以前是根據距離,現在改成根據屏幕佔比,頂點shader的計算量會小很多。另外現在新的版本中移動端的材質不再受三層的限制,當然三層的時候,兩個weightmap和normal共享一張貼圖,依然是比較優化的情況。地形本來佔屏範圍就廣,採樣多的話pixel shader開銷很高,所以還是儘量推薦使用三層以內的混合。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Base Pass pixel shader,效果上我們做了一些改進,sky light和refleciton的計算都做了修正,Specular換成了GGX,以前GGX在半精度的情況下,NoH接近1時會有比較大誤差,我們做了一些改進。另外,在MobileBasePassPixelShader中的各個模塊,項目組也可以根據需要去除不需要的,例如IBL或者lightmap或者shadowmap的部分。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

後處理,可以根據不同的設備做不同功能的開關。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Mask,在移動硬件上比較費的原因是因為如果寫depth時,某個像素髮生clip/discard,硬件的earlyz就會失效,導致overdraw。一個方案是開啟prepass畫mask,basepass做z equel;還有一個是引擎的LOD transition,在發生LOD時,不是直接換模型,會把兩個LOD模型都畫一下,通過一個dither的mask慢慢的漸變過去,這個時候可以採用類似於mask的行為,我們可以把LOD的結果dither的結果畫到Stencil,在BasePass時做stenciltest減少不必要的discard。

內存優化

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

接下來我們講講內存。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

內存我們針對不同的設備,獨立於其他的優化選項,單獨有一組Bucket設置,可以針對不同設備的可用內存決定自己使用的Memory Bucket設置。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

除了Streaming Level,引擎還有一個內建的很強大的功能是Texture Streaming,剛才已經介紹過一些,IOS上的實現利用了Apple的GL擴展,安卓有些設備沒有擴展,我們可以做完整的貼圖資源拋棄和重新的創建。在cpu上根據物件bounds的屏幕尺寸×材質中用到的對應貼圖的uv scale係數×一個可以由美術tweak的scalar值來決定實際貼圖提交的mip數,可以用r.Streaming.PoolSize在不同設備上很方便設置全局的貼圖資源的內存Budget。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Shader code,我們會利用Shared Shader code的功能,將大量靜態的分裂導致產生的Shader有重複的去除,將實際的Shader code存入ShaderLibrary,在每個MaterialInstance對象上只存ShaderCode的GUID,大大減小了實際的ShaderCode大小。在有些項目裡可以減掉80%。另外,不使用的rendering功能一定要在項目設置中關掉,可以大大減少shader分裂的組合數量。


「轉自 TGDC」 UE4製作多人大地型遊戲的優化

RHI,UI的貼圖比較大,由於默認情況下貼圖資源被CDO(Class Default Object)引用住無法GC掉,可以用弱引用技術的方式來緩解這個問題。另外,Slate altas Size可以小一點,可以減少冗餘的空掉的貼圖內存。GPU Particle不用的時候可以把fx.AllowGPUParticles關掉,我們會用到兩張128位1024的RT存gpu particle的position和velocity,有將近60兆的大小。另外,FSlateRHIResoureceManage,FrenderTargetPool裡polling起來的資源,可以適時主動調釋放的接口,以減少之前用過,之後短期內不會用到的資源。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

另外,近期我們還發現在使用UniformBuffer的時候,在一些gles的驅動裡會有非常可觀的內存開銷,因此我們現在改成了在ES3也會用pack過的UniformArray的形式。

還有很多比較散內存優化點,礙於時間關係,這裡就不展開細說了,例如在clang下TCHAR是4字節的,我們改成了二字節,也把相關的字符串函數做了一些自己的實現。

引擎關於適配和迭代的設置手段

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

這是引擎大量依賴的scalability系統,引擎所有可以控制的屬性,都可以放到Scalability Group,引擎內建了一些分組,我列在這裡了,項目組也可以定義任意的分組,每個分組裡面可以有我們不同的參數控制,配合有繼承關係的Device profile系統,可以很方便的針對不同的設備使用不同的scalability設置,單獨可使用的設置項非常多,可能有上千個。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

下面的這個Device Profile的例子是iPhoneX,大家可以看到iPhoneX的設置是繼承自IOS高配的並做了一些override,而ios高配又繼承自IOS,而IOS繼承自移動設備的Profile,一個項目可以適配任意多的硬件和平臺。不同的Device Profile的選擇依靠不同平臺的Selector,安卓上可以根據正則表達式或者嚴格匹配等方案去匹配SoC,GPU Family,Device Module或者GL Version等。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

再來我們看下項目Iterating的步驟,數據轉換過程我們叫做Cook,cook分為兩種方式,一種是你設備跑起來的時候,設備上是沒有資源的,設備的資源訪問不是訪問本地,而是訪問網絡磁盤,編輯器的一個commandlet會作為server端持續提供你要訪問的數據,這個數據如果沒有經過轉換會先阻塞的cook完再發過去,迭代的時候非常有用,叫cook on the fly。還有一個是把資源全部轉化完發到手機上,在不-iterate時,即使資源不改,也會先都load出來再save回去做檢查。項目大了會用很久,如果資源變化了,在DDC(Derived Data Cache)中找不到,需要發生資源轉換的過程,則會更慢。當用了-iterate後就會跳過這個步驟,但是有時候依然會load+save,是因為ini文件發生了變動,引擎不知道這個變動會不會影響cook結果,只能重新load/save,這時候引擎有一些優化選項,可以讓你配置一些特殊的字段告訴引擎,當這些字段發生變化時cook也會不做檢查,例如項目版本號之類的字段。當迭代測試的時候只要改變啟動命令行參數的時候,可以push一個UE4Commandline.txt文件到設備上,就可以免除重新打包的時間。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Debug沒什麼好說的,新版本中,為了加速迭代,我們開始使用Android Studio做debug,可以同時debug native和java代碼。當native代碼改動後,可以在vs裡編譯,UBT會自動更新build.gradle,使得Android Studio會自動識別並更新,改完後直接去android studio中啟動就能debug了,不需要再打包了。

「轉自 TGDC」 UE4製作多人大地型遊戲的優化

Profiling方面,gpu上細節的profiling主要靠移動gpu廠商工具;另外引擎有大量的內建的工具,例如常用的stat系列的命令以及showflag系列命令可以快速幫忙定位問題,cpu的profiling,引擎有自帶的工具,近期還加入了第三方工具framepro的支持,可以以很小的overhead做基於namedevent的profiling。我們也正在和騰訊合作,在做一些新的Profiling工具供大家使用。關於內存的profiling,引擎也有一些Memreport和llm的命令和對應的Memory Profiler工具輔助檢查內存的使用狀況,以及查找內存洩露和優化的方案。

今天要講的就是這些,謝謝大家。

from: www.u3dchina.com/forum.php?mod=viewthread&tid=8676


分享到:


相關文章: