前言
CPU準(zhǔn)備數(shù)據(jù)并通知GPU渲染的過程就是一次DrawCall,如果這個過程出現(xiàn)的次數(shù)太多,就會影響CPU的執(zhí)行效率,就會出現(xiàn)卡頓。
當(dāng)然,影響CPU的不僅僅只有DrawCall,還有物理組件、GC(垃圾對象太多,造成GC負(fù)擔(dān),影響CPU的執(zhí)行效率)、代碼質(zhì)量。
Draw Call Batching
在屏幕上繪制一個物體,U3D引擎必須向繪圖API(openGL或者D3D)發(fā)起一次DrawCall。
這些DrawCall往往是昂貴的,當(dāng)繪圖API為每個DrawCall做一些重要的事情,引起GPU的性能消耗較高。
這些消耗大部分是因為DrawCall結(jié)束的狀態(tài)切換引起的(比如,從一個材質(zhì)切換到另一個材質(zhì)),因為這會引發(fā)昂貴顯卡驅(qū)動的驗證和變換步驟。
Unity使用多種技術(shù)來解決這個問題:
靜態(tài)Batching:把靜態(tài)的物體合并成一個大meshes,然后用更快的方法渲染他們。
動態(tài)Batching:對于足夠小的meshes,在CPU上變換他們的頂點,將一些相似的組合到一起,在一次中繪制。
內(nèi)建的Baching相對于手動合并物體到一起有幾個好處(值得注意的是,這些對象仍可以被單獨(dú)銷毀)。
但是他也有它的缺點(靜態(tài)Batching會導(dǎo)致內(nèi)存和存儲的開銷,動態(tài)Batching會導(dǎo)致CPU開銷)。
Material Setup For Batching(合并材質(zhì))
只有共享同一個材質(zhì)的物體才能被Batched在一起。如果,你想達(dá)到一個好的batching,你需要竟可能多的在不同物體中共享材質(zhì)。
如果你有兩個同樣的材質(zhì)但是他們的textures不同,你可以合并這些textures到一個大texture - 這個過程經(jīng)常被叫做texture atlasing。
一旦textures在同一個圖集中,你就可以使用一個材質(zhì)來代替了。
如果你需要在腳本中訪問共享了的材質(zhì),有一個很重的點需要注意:修改Renderer.material屬性會創(chuàng)建一個當(dāng)前材質(zhì)的副本。
所以,作為替換方案,你應(yīng)該使用Rednerer.sharedMaterial以保證材質(zhì)被共享。
當(dāng)渲染陰影投射的時候,即使他們的材質(zhì)是不同的,也經(jīng)常被batched到一起。
只要材質(zhì)中的數(shù)值在shadow pass是相同的,即使陰影投射有不同的材質(zhì),他們也可以使用動態(tài)batching。
舉個例子,許多箱可以使用具有不同的texture的材質(zhì),但對于陰影投射渲染 texture是不相關(guān) - 在這種情況下,他們可以被batched到在一起。
Dynamic Batching(動態(tài)批處理)
Unity可以自動batch移動物體到相同的draw call,如果它們具有相同的材質(zhì)和滿足其它標(biāo)準(zhǔn)。動態(tài)Batching是自動完成的,不需要你做額外的事情。
Batching 動態(tài)的物體每個頂點會有某些開銷,所以batching只適用于頂點數(shù)小于900的meshes。如果你得shader使用了頂點位置,法線和單獨(dú)的UV,那么你可以batch 300 個頂點;如果你得shader使用了頂點位置,法線,UV0,UV1,和正切,只能batch 180個頂點。
這個限制的數(shù)量將來有可能會變化。
其它限制
- 如果物體包含鏡像變換,他們將不會被batched,例如,object A 的scale 為 +1 object B 的scale 為-1,就不能batched到一起。
- 使用不同材質(zhì)的實例,即使他們實質(zhì)上是相同的,也會導(dǎo)致兩個物體不能被batched到一起。陰影投射除外。
- 具有光照貼圖的對象有額外的渲染參數(shù):光照索引和 偏移/放縮 的光照。所以一般動態(tài)lightmapped對象應(yīng)指向完全相同的光照貼圖位置然后再進(jìn)行batch。
- 多通道的shader將不會被batching幾乎所有的Unity shader都支持前置渲染幾個燈光,有效的為他們做更多的通道?!邦~外的逐像素的燈光”的draw call 將不會被batched。
- 傳統(tǒng)的延遲渲(逐通道光照)染通道禁用了動態(tài)batching,因為它必須繪制兩次。
由于它的工作原理是變換所有的物體的頂點到CPU的世界坐標(biāo)中,所以它僅僅在它的工作(變換到cpu的世界坐標(biāo))比做一次“draw call”小的時候才能起到好的作用。
究竟一個DrawCall有多昂貴取決于諸多因素,主要是所用的繪圖API。例如,在控制臺或當(dāng)前流 行的APIs例如Apple Metal DrawCall的開銷一般比較低,所以一般動態(tài)batching不會達(dá)到好的效果。
Static Batching(靜態(tài)批處理)
靜態(tài)batching允許引擎減少draw call適用于任何大小的幾何對象(假設(shè)沒有移動和共享材質(zhì))。大部分情況下他比動態(tài)batching更高效 ,但是它會占用更多的內(nèi)存。
為了使靜態(tài)batching更好的獲益,你需要明確的指定游戲中某些物體是靜態(tài)的而且不會移動,旋轉(zhuǎn)或者縮放。這樣做,你可以在Inspector界面中的“Static”選項(chekbox)標(biāo)記物體為static:
使用靜態(tài)batching將需要額外的內(nèi)存來存儲合并后的幾何信息。如果幾個物體在靜態(tài)batching之前共享同一個幾何圖元,那么這個幾何圖元將會為每個物體復(fù)制一份,無論在Editor中還是runtime中都是如此。這不是一個好方法 - 有時候為了保持更小的內(nèi)存占用量,你必須犧牲渲染性能為了避免一些物體的靜態(tài)batching。例如,在一個稠密的森林中,標(biāo)記樹木為static會產(chǎn)生嚴(yán)重的內(nèi)存影響。
在內(nèi)部,靜態(tài)batching的原理是變換這些靜態(tài)物體到世界控件然后為他們建立一個很大頂點+索引 緩沖區(qū)。然后所有顯示的物體都放到一個batch,一系列“便宜的”draw call就完成了,這期間幾乎沒有狀態(tài)切換(state change)。所以 從技術(shù)上來講 這并沒有節(jié)省“3D API draw call”,但是他節(jié)省了他們之間的狀態(tài)切換(狀態(tài)切換才是罪魁禍?zhǔn)祝?/p>
Other Batching Related Tips
目前,只有網(wǎng)格渲染才被batched。像 skinned meshes,cloth,拖尾渲染器(Trail Renderer)和其他類型的渲染組件是不被batched的。
半透明shader為了做透明度的工作,經(jīng)常需要物體以從后到前的順序進(jìn)行渲染。
Unity首先會對物體進(jìn)行排序,然后試著batch他們 - 因為這個順序是嚴(yán)格限制的,這就意味著 相對于不透明物體來說,會有很少的batching產(chǎn)生。
相對調(diào)用draw call來說,手動合并比較近的物體可能是一個非常不錯的選擇。
例如,許多抽屜的靜態(tài)柜子合并成一個mesh經(jīng)常是有道理的,無論是在3D建模軟件中還是使用Mesh.CombineMeshaes.
