Unity 2017 Game Optimization 讀書筆記 The Benefits of Batching

batching(合批) 和大量的描述一個3D物體的數據有關係,比如meshes,verices,edges,UV coordinates 以及其他不同類型的數據。在Unity中談論batching,指的是用於合批mesh數據的兩個東西:Dynamic Batching(動態合批) 和 Static Batching(靜態合批)。渲染管線特別喜歡處理合在一起提交的mesh數據而不是單獨的一個個的mesh數據。

Draw Calls

在討論Dynamic Batching(動態合批) 和 Static Batching(靜態合批)之前,先理解下Dynamic Batching(動態合批) 和 Static Batching(靜態合批)要解決渲染管線的問題:降低用於繪製當前視圖所有物體的Draw calls。

Draw call是從CPU發送到GPU的一個請求,用於物體的繪製。在Draw call請求之前,需要完成一系列的準備工作。

首先,mesh和textrue數據必須從CPU 的內存(RAM)推送到GPU內存(VRAM),對於已經存在於場景裏的,這通常發生在場景初始化的時候。對於預先沒有在場景內的,只能在它們被instantiate的時候進行該操作。其次,cpu必須爲gpu準備渲染所需要的設置和數據。cpu和gpu之間的通信是通過不同平臺的圖形API來完成的,有可能是DirectX,OpenGL,Vulkan等等。

API通過驅動程序調用,大量繁雜的渲染管線渲染物體所需的設置信息通常被叫做Render State。在Render State改變之前,GPU都將會一直使用這個Render State來渲染物體。對Render State做改變是一個開銷非常大的過程,比如我們設置Render State使用一張藍色texture去渲染mesh,這會將整個mesh渲染成藍色,我們可以再渲染9個完全不同的mesh,它們都會被渲染成藍色,因爲我們沒有改變使用的texture。但是如果我們想渲染10個mesh喲個10張不同的貼圖,就會十分耗時,因爲需要渲染每個mesh時都去準備新的貼圖信息發送draw call。越少更改Render State,圖形api處理我們的請求就會越快。

能觸發Render State同步的操作包括但不限於:添加實時的texture到GPU,修改Shader,修改燈光信息,修改陰影和透明度等等有關渲染的設置。

CPU和GPU之前通過Command Buffer保持通信,這個隊列存儲了CPU創建好的用於渲染的操作指令,GPU在每次完成渲染指令後再從這個隊列中獲取。

一個新的Draw Call並不意味着必須有一個新的Render State。合批能夠提供渲染性能的原因是如果倆個物體使用同樣的RenderState信息來渲染,GPU就可以立刻渲染下一個物體,這消除了Render State的同步時間,合批同樣可以減少渲染的指令數量,減少CPU和GPU的負荷。

Materials and Shaders

Unity中通過Materials來把Render State暴露給我們。Materials是一個Shader的容器,shader本身並不感知實際的Render State,shader所需的diffuse textures,normal maps等實際就是隱式的Render State變量。

每一個shader都需要一個Material,一個Material也必須有一個Shader。因此如果我們想減少Render State的變化,我們可以通過減少場景中Materials的數量。CPU每幀會消耗更少的時間來生成和傳輸渲染指令,GPU也不需要頻繁停下來重新同步Render State的變化。

做個實驗,四個cube,四個sphere,各自不同的material,關掉shadow,關掉dynamic batching 和 static batching:

結果爲9個batches(還有一個是背景,比如天空盒)

像之前介紹的那樣,我們可以減少material數量來提高效率,如果都用同樣的material會是什麼效果:依然還是9個,這是因爲我們沒開啓Dynamic Batching,渲染管線並不能夠意識到需要重複使用Render State。

The Frame Debugger

Unity中調試渲染非常有用的工具,可以查看每一幀的渲染過程,對於跟蹤場景中DrawCall來源,優化遊戲非常有用。這一小節就不詳細介紹了

 Dynamic Batching

三個特性:

1. 運行中實時動態合批

2. 根據主攝像機視野中可見的物體,每一幀合批的內容都可以不同

3. 運動的物體也可以進行合批

上一小節的例子,開啓動態合批後結果爲:6個batches,這是因爲4個Sphere因爲不滿足動態合批條件而沒有進行合批。

動態合批的條件具體參考Unity的文檔,這玩意隨着Unity版本的變化不斷在變化。

https://docs.unity3d.com/Manual/DrawCallBatching.html

動態合批需要滿足的條件:

  • 使用相同的材質引用
  • 只有ParticleSystem和MeshRender可以合批,SkinnedMeshRender等其他Component無法合批.Currently, only Mesh RenderersTrail RenderersLine RenderersParticle Systems and Sprite Renderers are batched. This means that skinned Meshes, Cloth, and other types of rendering components are not batched.
  • 每個mesh最多300個頂點索引。
  • 最多900個頂點屬性 。
  • mesh的Scale要麼都統一,要麼都別統一(這句沒看懂,令人費解)
  • 材質的shader不能使用多passes
  • mesh不能接收實時陰影
  • 合批後mesh總共的索引數不能超過各自平臺的上限,一般是32k-64k個。

Vertex attributes

頂點屬性是每個頂點的一系列信息,比如位置,法線,UV等。如果每個頂點3個屬性,則如果想合批,可以最多支持的頂點數是900/3 = 300。如果每個頂點5個屬性,則最多可以持的頂點數是900/5 = 180。要注意的是即使頂點屬性少於3個,合批最多也只能支持300個頂點(另外一條規則)

Mesh scaling

奇數個負數Scale不能合批,比如(1,-1,1),偶數個可以(1,-1,-1)

Dynamic Batching summary

  • 適用於場景中含有大量簡單的物體,比如大片有石頭和樹木的森林等。
  • 如果只是單純的texture不同,可以考慮使用圖集的方式,這樣就可以合批。
  • 如果場景中有很多個比較簡單的物體,但是隻有少數可以合批的話,還不如不開啓合批,因爲遍歷場景判斷合批的操作可能更費。
  • 動態合批的條件非常苛刻,經常會出現打破了合批規則而自己不知道,所以要多注意,要經常不定期的去調試check來保持Draw call的合理值,當然,常常的狀況是我們直到Draw Call真的成爲性能瓶頸影響表現時,並不需要去考慮Draw call。
  • 最後一條建議,多嘗試,看看哪些不能合批哪些能合批來保證Draw call

Static Batching

Unity 提供了第二種合批方式,那就是Static Batching。Static Batching的條件:

  • 物體必須被標記爲Static
  • 需要額外的內存
  • 合批後mesh總共的索引數不能超過各自平臺的上限,一般是32k-64k個。
  • mesh可以不同,但是必須使用相同的材質引用

The Static flag

要小心如果只是把物體標記爲Batching Static,運行時可能會產生奇怪的現象:mesh由於Static Batching沒有移動,但是Rigidbody什麼的可能會動。

Memory requirements

額外的內存開銷是Static Batching的一個缺點,Static Batching copy 需要合批的mesh的數據到一個單獨的大mesh buffer中,並將其傳入渲染管線中通過一個Draw Call進行渲染,完全忽略掉原始的mesh。如果需要合批的mesh都是不同的,那是否使用靜態合批內存的開銷是一樣的。然而如果渲染的是相同的object,則內存開銷會翻倍,平常渲染一個,10個或者100萬個同樣object的clone花費的內存開銷是一樣的,因爲他們引用的是相同的mesh 數據,對於每個Object唯一不同的只是transform。然而Static Batching 需要copy mesh 數據到大的buffer中,這種引用關係就沒有了,每個物體都會將原始的mesh數據copy到buffer中。因此使用Static Batching 渲染1000個tree的內存開銷是不使用Static Batching的1000倍,所以在使用Static Batching時要慎重,要判斷使用的是否合適。

Material references

有時候,Static Batching會需要處理多個Materials,在這種情況下,每個Material會被分組到各自的Static Bathc中,也就是說Static Batching可以用和Materials數量相等的Draw Call渲染所有的靜態meshes。

Static Batching caveat & Edit Mode debugging of Static Batching & Instantiating static meshes at runtime

  • Static Batching 不能在編輯器中立刻查看效果,只能運行起來後看效果,調試起來非常麻煩
  • 運行時將物體動態標記成Static並不會被合批,但是可以通過 StaticBatchUtility.Combine() 強制合批,但是要非常謹慎使用,這個函數開銷極大

Static Batching summary

Static Batching 是一個十分有用但是又十分危險的工具,如果使用的好會給渲染帶來提升,如果使用的不好,內存的開銷以及各種其它弊端都會很蛋疼。

Summary

Dynamic Batching 和 Static Batching summary都是有用但是又有風險的工具,要真正搞明白它們才能正確的使用它們。第六章會更多更深入的介紹Dynamic Graphics。

 

GPU Instancing

此處加一點書中這章沒寫的內容,GPU Instancing也會有效降低DrawCall,它的原理詳見

https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/10%20Instancing/

GPU Instancing 非常高效,它的要求也更高,不僅要求Material引用一樣,mesh也必須一樣,因爲mesh一樣所以纔不會存在Batching合併大mesh帶來的內存開銷。

Unity Batching優先級:靜態合批 > GPU Instancing  > 動態合批

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章