【UE】性能優(yōu)化策略集錦

CPU

游戲線程

Animation

  • EventGraph的執(zhí)行需要調(diào)用虛擬機(jī),性能較低,建議盡量避免這類使用,可以考慮多用Fast Path+多線程異步計(jì)算來提升性能[1]
  • LOD不僅可以用于mesh的面片,還可以用于模型的動(dòng)畫,屏占比較低的蒙皮模型可以考慮降低其骨骼動(dòng)畫計(jì)算頻率(中間通過插值來過渡),從而降低計(jì)算消耗,這種技術(shù)叫做URO(Update Rate Optimization)[1],這里除了對(duì)全局更新頻率進(jìn)行控制之外,還可以在AnimGraph中設(shè)置某級(jí)LOD開始,哪些節(jié)點(diǎn)就不需要更新。
  • Character的動(dòng)畫消耗相對(duì)于其他動(dòng)畫消耗要高很多,因?yàn)樾枰M(jìn)行一系列的規(guī)則判斷,比如碰撞、斜坡檢測、跨越高度檢測等,因此通常會(huì)考慮只對(duì)主角進(jìn)行一些復(fù)雜計(jì)算,其他玩家的角色則會(huì)考慮根據(jù)服務(wù)器下發(fā)的數(shù)據(jù)進(jìn)行插值處理,絕大部分情況下表現(xiàn)不會(huì)有太大差異[1]

GamePlay

  • 藍(lán)圖中的Tick調(diào)用是通過Event Graph來實(shí)現(xiàn)的,前面說過,Event Graph的調(diào)用會(huì)增加虛擬機(jī)的調(diào)用開銷,當(dāng)次數(shù)多了就會(huì)影響性能,因此可以考慮盡量避免這種方式,常用的策略有轉(zhuǎn)移到C++,或者采用直接換成其他的不需要Tick的方案(比如風(fēng)車的轉(zhuǎn)動(dòng)可以考慮使用頂點(diǎn)動(dòng)畫?)[1]

Physics

  • 一個(gè)物理場景通常有兩棵樹,一棵負(fù)責(zé)Query,一棵負(fù)責(zé)Simulation,要想得到較好的性能,一個(gè)方面是減少注冊(cè)到場景中的物理物件數(shù)目,另一個(gè)方面是降低物理對(duì)象的空間復(fù)雜度(面數(shù))[1]
  • 可以考慮共享異步物理場景的數(shù)據(jù),這種共享可以通過較小的內(nèi)存消耗來實(shí)現(xiàn),從而在進(jìn)行物理模擬的時(shí)候,同時(shí)允許異步線程進(jìn)行物理查詢[1]
  • 視野范圍比物理模擬范圍通常要大很多,而UE中Mesh只要加載到內(nèi)存中就會(huì)自動(dòng)將物理對(duì)象注冊(cè)到物理場景中,從而導(dǎo)致物理對(duì)象占用的內(nèi)存與計(jì)算消耗都高很多,可以通過將這兩種數(shù)據(jù)進(jìn)行解耦的方式來降低消耗[1]

Draw Commit

  • UE中靜態(tài)物件是在添加到場景中就確定其渲染順序,添加到對(duì)應(yīng)的Draw Policy中,而動(dòng)態(tài)創(chuàng)建的單位(這里指的應(yīng)該是動(dòng)態(tài)的物體,比如蒙皮模型等)則會(huì)在每幀InitViews階段才能獲得對(duì)應(yīng)的數(shù)據(jù),因此其渲染順序是動(dòng)態(tài)計(jì)算的,渲染效率較低,對(duì)于這種情況,其實(shí)是可以做區(qū)分處理的,將一些短期內(nèi)不變的物件添加到一個(gè)StaticRenderPath中來提升渲染性能[1]
  • UE的Texture Streaming每幀會(huì)在CPU中計(jì)算每張貼圖渲染時(shí)候所需要的精度(游戲線程),之后按需提交給GPU,而分析畫面的Wanted Mip的計(jì)算量是不低的,因此,在游戲線程性能吃緊的時(shí)候,可以考慮降低Wanted Mip的計(jì)算頻率。[1]

Transform

  • SceneComponent是場景中有坐標(biāo)的物件都會(huì)攜帶的一個(gè)組件,這個(gè)組件的Transform更新是在Game線程中進(jìn)行的,當(dāng)場景中物件較多的時(shí)候,這個(gè)更新的消耗會(huì)占用較多的性能,雖然這個(gè)計(jì)算過程可以考慮放在異步線程中完成,還是會(huì)有較多的消耗[1]
  • SceneComponent位置變化的時(shí)候,會(huì)觸發(fā)Overlap檢查,如果每幀Transform變化數(shù)過多,就會(huì)導(dǎo)致這個(gè)計(jì)算消耗變高,因此可以考慮關(guān)閉不必要的overlap[1]

UI

  • UI層級(jí)復(fù)雜,就會(huì)導(dǎo)致位置更新計(jì)算消耗高,可以考慮通過SlatLayoutCaching和Invalidation Box對(duì)數(shù)據(jù)進(jìn)行Cache處理來避免不必要的Transform更新計(jì)算[1]
  • 盡量將Widget進(jìn)行合批處理,引擎有一些自動(dòng)合批的策略,比如自帶的一些控件如Horizental和Vertical Box,Grid等,其下的子控件是屬于同一層的,因此引擎會(huì)考慮作為一個(gè)Draw Call繪制出來,而其他的靈活控件其下的子控件由于引擎無法自動(dòng)識(shí)別順序,會(huì)默認(rèn)對(duì)新增的子控件的Z-Order加一,通過手動(dòng)調(diào)整這個(gè)Order可以實(shí)現(xiàn)合批以降低消耗,當(dāng)然,合批的前提是多個(gè)UIWidget使用的是同一個(gè)材質(zhì)且貼圖是同一張。[1]

音頻

  • 音頻開銷很大,《堡壘之夜》中移動(dòng)端上Sound Source的總數(shù)是16,主機(jī)上則是32[1]

特效

  • 特效的消耗在于OverDraw,UE提供了一種對(duì)貼圖進(jìn)行自動(dòng)裁剪的方案,這種方案可以通過八邊形而非四邊形來最大化貼圖的有效面積率以降低overdraw,這種方案在支持SRV的設(shè)備上可以打開[1]

Level Streaming

  • Level Streaming整個(gè)過程包含三個(gè)步驟,分別是IO,數(shù)據(jù)反序列化以及PostLoad,在Event Driven模式下,IO跟反序列化可以并行進(jìn)行,且打開s.AsyncLoadingThreadEnabled之后還可以將反序列化放在異步線程完成,而PostLoad由于需要注冊(cè)對(duì)象到游戲線程,因此整個(gè)過程是放在主(游戲)線程完成的,為了提升性能,UE這邊將一些不需要游戲線程參與的內(nèi)容挪到了異步線程,從而減輕游戲線程的負(fù)擔(dān)

渲染線程

  • 場景遍歷消耗,場景過于復(fù)雜會(huì)導(dǎo)致場景遍歷需要較高的消耗,從而導(dǎo)致渲染Draw調(diào)用時(shí)間延后影響整體性能,這就是為什么會(huì)有加速遍歷(如四叉樹等加速結(jié)構(gòu)以及UE的多線程并行遍歷)策略的原因
  • 剔除消耗,剔除是為了最快速度的移除不需要繪制的物件,常見的剔除有視錐剔除、遮擋剔除等,想要提升剔除的精確性,就要增加計(jì)算的復(fù)雜度,二者不可兼得,因此這里會(huì)存在一個(gè)平衡。
  • Draw Call消耗,每個(gè)Draw Call在提交的時(shí)候都有不低的消耗,尤其是當(dāng)渲染狀態(tài)發(fā)生變化時(shí),消耗尤其之高,這就是為什么需要減少Draw Call以及對(duì)Draw Call進(jìn)行排序(DrawPolicy)的原因,常見的策略是合批,包括HISM等GPU Instancing方案,以及HLOD等靜態(tài)合批方案。

卡頓優(yōu)化

  • level streaming在啟用異步加載的時(shí)候,網(wǎng)絡(luò)同步Spawn Actor需要依賴加載數(shù)據(jù)的時(shí)候,可能會(huì)因?yàn)閒lush導(dǎo)致卡頓
  • ShaderCache/ShaderPipelineCache,提供了離線狀態(tài)搜集需要Compile的shader信息,將之寫入文件,在首次啟動(dòng)的時(shí)候分多次預(yù)先進(jìn)行Compile以減少運(yùn)行時(shí)的Compile消耗
  • SpawnActor導(dǎo)致的卡頓,解決方式有,減少每個(gè)Actor掛接的Components的數(shù)量,盡量使用C++ Components而非藍(lán)圖Components,Components注冊(cè)到游戲線程可以分時(shí)完成,對(duì)于同類Actor,如果有大量Spawn需求,建議使用Actor Pool等
  • GC卡頓,GC主要分成兩個(gè)階段,分別是掃描與標(biāo)記階段,以及清理階段,后者可以分幀完成,對(duì)于一些渲染線程依然訪問的資源,會(huì)通過render fence標(biāo)記出來,當(dāng)渲染線程釋放后,在下一幀的主線程再進(jìn)行清理,前面的掃描標(biāo)記階段(也稱為引用分析階段)則需要在一幀之內(nèi)完成,這是GC卡頓的主要原因,UE的優(yōu)化方法有:多線程并行處理、跳過常駐對(duì)象、對(duì)象Clustering處理(一榮俱榮的處理方式來增大處理粒度)等

GPU

  • PS Bound
  • 改分辨率
  • Quality Switch實(shí)現(xiàn)不同設(shè)備的計(jì)算LOD
  • 帶寬Bound
    • 調(diào)整SceneColor的格式
  • 地形Bound
    • LOD策略從基于距離更改為基于屏占比
    • 材質(zhì)建議不超過三層,三層材質(zhì)情況中,兩張weight map可以跟normal共用一張貼圖,比較有優(yōu)勢
  • Discard優(yōu)化,像素discard會(huì)導(dǎo)致early-z失效,從而導(dǎo)致overdraw增加,性能下降較為明顯,解決方案可以考慮增加一個(gè)depth的prepass,這個(gè)pass會(huì)將depth寫入到RT,之后在base pass通過Depth Equal來模擬early z。此外,對(duì)于LOD切換而言,通常不是直接切換(太生硬了),而是將兩個(gè)LOD都繪制一遍,通過一個(gè)mask控制兩級(jí)LOD的平滑過渡(比如某個(gè)像素的mask為1,那么就第一級(jí)LOD繪制,第二級(jí)LOD不畫,反之亦然,這種方式稱為dither),而通過alpha test開啟的mask會(huì)導(dǎo)致discard,為了避免discard,可以考慮用stencil test替代alpha test

內(nèi)存優(yōu)化

  • Shared Shader Code有助于降低Shader代碼導(dǎo)致的內(nèi)存消耗
  • Shader Library,將Shader代碼存入Library,MaterialInstance只存ID,以避免Shader代碼的重復(fù)
  • 由于CDO的存在導(dǎo)致用普通指針引用的UTexture指針無法被GC掉,這種問題可以通過weakptr的方式來解決
  • UI Slate Atlas是為了進(jìn)行UI繪制的合批,提升性能,不過Atlas Size過大也會(huì)導(dǎo)致一些空間占用的浪費(fèi),而Atlas Size太小又會(huì)導(dǎo)致合批效果變差,這里需要做一個(gè)平衡
  • GPU Particle功能開啟的時(shí)候,會(huì)使用兩張分辨率為1024而每個(gè)像素占用128位的貼圖來存儲(chǔ)particle的位置以及速度數(shù)據(jù),因此在不用開啟GPU Particle的時(shí)候可以把fx.AllowGPUParticles關(guān)掉
  • FSlateRHIResoureceManage,F(xiàn)renderTargetPool等Pool中建議根據(jù)需要主動(dòng)調(diào)用資源釋放接口,從而釋放一些短期內(nèi)不需要用到的,但是在之前的使用過程中膨脹大的Pool空間占用
    除此之外,UE針對(duì)內(nèi)存消耗還有一些其他的優(yōu)化點(diǎn):

參考

[1]. Epic Games 王禰:UE4制作多人大地型游戲的優(yōu)化

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容