draw call是openGL的描繪次數(directX沒怎么研究,但原理應該差不多)
一個簡單的openGL的繪圖次序是:設置顏色→繪圖方式→頂點座標→繪制→結束。
每幀都會重復以上的步驟。這就是一次draw call
如果有兩個model,那么需要
設置顏色→繪圖方式→頂點座標A→繪制→結束。
設置顏色→繪圖方式→頂點座標B→繪制→結束。
兩次draw calls;
也就是說在openGl繪制前,如果色彩通道(color filter),繪圖方式(shader),頂點座標(model)不同的情況下draw calls就會增加。
對openGl來說繪制參數(狀態值)的變更要比繪制大量的頂點更耗費cpu。
所謂高速繪圖就是,在盡量不改變openGl狀態值的情況下,用一次draw call完成所有繪制。
比如上面的例子:
設置顏色→繪圖方式→頂點座標A+頂點座標B→繪制→結束。
就要更加有效率。
個人估計unity3d的dynamic batch,static batch都是通過一定的方法使不同的object的頂點座標能夠結合成一個整體,達到減少draw calls的效果。
但是有一定的要求限制,比如material要相同,mesh要相同並在300個面以內等等,這些都是為了保證openGl的狀態值不改變。
Unity在 Player Setting 里的兩個功能選項 Static Batching 與 Dynamic Batching。功能描述如下:
- Static Batching 是將標明為 Static 的靜態物件,如果在使用相同材質球的條件下,Unity 會自動幫你把這兩個物件合並成一個 Batch,送往 GPU 來處理。這功能對效能上非常的有幫助,所以是需要付費才有的。
- Dynamic Batching 是在物件小於300面的條件下(不論物件是否為靜態或動態),在使用相同材質球下,Unity就會自動幫你合合並成一個 Batch 送往 GPU 來處理。
Unity3D項目優化--繪制調用批處理unity3D Draw
在屏幕上渲染物體,引擎需要發出一個繪制調用來訪問圖形API(iOS系統中為OpenGL ES)。每個繪制調用需要進行大量的工作來訪問圖形API,從而導致了CPU方面顯著的性能開銷。
Unity在運行時可以將一些物體進行合並,從而用一個繪制調用來渲染他們。這一操作,我們稱之為“批處理”。一般來說,Unity批處理的物體越多,你就會得到越好的渲染性能。
Unity中內建的批處理機制所達到的效果要明顯強於使用幾何建模工具(或使用Standard Assets包中的CombineChildren腳本)的批處理效果。這是因為,Unity引擎的批處理操作是在物體的可視裁剪操作之后進行的。Unity先對每個物體進行裁剪,然后再進行批處理,這樣可以使渲染的幾何總量在批處理前后保持不變。但是,使用幾何建模工具來拼合物體,會妨礙引擎對其進行有效的裁剪操作,從而導致引擎需要渲染更多的幾何面片。
材質
只有擁有相同材質的物體才可以進行批處理。因此,如果你想要得到良好的批處理效果,你需要在程序中盡可能地復用材質和物體。
如果你的兩個材質僅僅是紋理不同,那么你可以通過 紋理拼合 操作來將這兩張紋理拼合成一張大的紋理。一旦紋理拼合在一起,你就可以使用這個單一材質來替代之前的兩個材質了。
如果你需要通過腳本來訪問復用材質屬性,那么值得注意的是改變Renderer.material將會造成一份材質的拷貝。因此,你應該使用Renderer.sharedMaterial來保證材質的共享狀態。
動態批處理
如果動態物體共用着相同的材質,那么Unity會自動對這些物體進行批處理。
動態批處理操作是自動完成的,並不需要你進行額外的操作。
Tips:
提醒:
1、 批處理動態物體需要在每個頂點上進行一定的開銷,所以動態批處理僅支持小於900頂點的網格物體。
2、 如果你的着色器使用頂點位置,法線和UV值三種屬性,那么你只能批處理300頂點以下的物體;如果你的着色器需要使用頂點位置,法線,UV0,UV1和切向量,那你只能批處理180頂點以下的物體。
3、請注意:屬性數量的限制可能會在將來進行改變。
4、 不要使用縮放尺度(scale)。分別擁有縮放尺度(1,1,1)和(2,2,2)的兩個物體將不會進行批處理。
5、 統一縮放尺度的物體不會與非統一縮放尺度的物體進行批處理。
使用縮放尺度(1,1,1)和 (1,2,1)的兩個物體將不會進行批處理,但是使用縮放尺度(1,2,1)和(1,3,1)的兩個物體將可以進行批處理。
6、 使用不同材質的實例化物體(instance)將會導致批處理失敗。
7、擁有lightmap的物體含有額外(隱藏)的材質屬性,比如:lightmap的偏移和縮放系數等。所以,擁有lightmap的物體將不會進行批處理(除非他們指向lightmap的同一部分)。
8、 多通道的shader會妨礙批處理操作。比如,幾乎unity中所有的着色器在前向渲染中都支持多個光源,並為它們有效地開辟多個通道。
9、預設體的實例會自動地使用相同的網格模型和材質。
靜態批處理
相對而言,靜態批處理操作允許引擎對任意大小的幾何物體進行批處理操作來降低繪制調用(只要這些物體不移動,並且擁有相同的材質)。因此,靜態批處理比動態批處理更加有效,你應該盡量低使用它,因為它需要更少的CPU開銷。
為了更好地使用靜態批處理,你需要明確指出哪些物體是靜止的,並且在游戲中永遠不會移動、旋轉和縮放。想完成這一步,你只需要在檢測器(Inspector)中將Static復選框打勾即可,如下圖所示:
使用靜態批處理操作需要額外的內存開銷來儲存合並后的幾何數據。在靜態批處理之前,如果一些物體共用了同樣的幾何數據,那么引擎會在編輯以及運行狀態對每個物體創建一個幾何數據的備份。這並不總是一個好的想法,因為有時候,你將不得不犧牲一點渲染性能來防止一些物體的靜態批處理,從而保持較少的內存開銷。比如,將濃密森里中樹設為Static,會導致嚴重的內存開銷。
靜態批處理目前只支持Unity iOS Advanced。
nity(或者說基本所有圖形引擎)生成一幀畫面的處理過程大致可以這樣簡化描述:引擎首先經過簡單的可見性測試,確定攝像機可以看到的物體,然后把這些物體的頂點(包括本地位置、法線、UV等),索引(頂點如何組成三角形),變換(就是物體的位置、旋轉、縮放、以及攝像機位置等),相關光源,紋理,渲染方式(由材質/Shader決定)等數據准備好,然后通知圖形API——或者就簡單地看作是通知GPU——開始繪制,GPU基於這些數據,經過一系列運算,在屏幕上畫出成千上萬的三角形,最終構成一幅圖像。
在Unity中,每次引擎准備數據並通知GPU的過程稱為一次Draw Call。這一過程是逐個物體進行的,對於每個物體,不只GPU的渲染,引擎重新設置材質/Shader也是一項非常耗時的操作。因此每幀的Draw Call次數是一項非常重要的性能指標,對於iOS來說應盡量控制在20次以內,這個值可以在編輯器的Statistic窗口看到。
Unity內置了Draw Call Batching技術,從名字就可以看出,它的主要目標就是在一次Draw Call中批量處理多個物體。只要物體的變換和材質相同,GPU就可以按完全相同的方式進行處理,即可以把它們放在一個Draw Call中。Draw Call Batching技術的核心就是在可見性測試之后,檢查所有要繪制的物體的材質,把相同材質的分為一組(一個Batch),然后把它們組合成一個物體(統一變換),這樣就可以在一個Draw Call中處理多個物體了(實際上是組合后的一個物體)。
但Draw Call Batching存在一個缺陷,就是它需要把一個Batch中的所有物體組合到一起,相當於創建了一個與這些物體加起來一樣大的物體,與此同時就需要分配相應大小的內存。這不僅會消耗更多內存,還需要消耗CPU時間。特別是對於移動的物體,每一幀都得重新進行組合,這就需要進行一些權衡,否則得不償失。但對於靜止不動的物體來說,只需要進行一次組合,之后就可以一直使用,效率要高得多。
Unity提供了Dynamic Batching和Static Batching兩種方式。Dynamic Batching是完全自動進行的,不需要也無法進行任何干預,對於頂點數在300以內的可移動物體,只要使用相同的材質,就會組成Batch。Static Batching則需要把靜止的物體標記為Static,然后無論大小,都會組成Batch。如前文所說,Static Batching顯然比Dynamic Batching要高效得多,於是,Static Batching功能是收費的……
要有效利用Draw Call Batching,首先是盡量減少場景中使用的材質數量,即盡量共享材質,對於僅紋理不同的材質可以把紋理組合到一張更大的紋理中(稱為Texture Atlasing)。然后是把不會移動的物體標記為Static。此外還可以通過CombineChildren腳本(Standard Assets/Scripts/Unity Scripts/CombineChildren)手動把物體組合在一起,但這個腳本會影響可見性測試,因為組合在一起的物體始終會被看作一個物體,從而會增加GPU要處理的幾何體數量,因此要小心使用。
對於復雜的靜態場景,還可以考慮自行設計遮擋剔除算法,減少可見的物體數量同時也可以減少Draw Call。
總之,理解Draw Call和Draw Call Batching原理,根據場景特點設計相應的方案來盡量減少Draw Call次數才是王道,其它方面亦然。
U3D DrawCall優化手記
http://www.cnblogs.com/ybgame/p/3588795.html
在最近,使用U3D開發的游戲核心部分功能即將完成,中間由於各種歷史原因,導致項目存在比較大的問題,這些問題在最后,恐怕只能通過一次徹底的重構來解決
現在的游戲跑起來會有接近130-170個左右的DrawCall,游戲運行起來明顯感覺到卡,而經過一天的優化,DrawCall成功縮減到30-70個,這個效果是非常顯著的,並且這個優化並沒有通過將現有的資源打包圖集來實現,圖集都是原有的圖集,如果從全局的角度對圖集再進行一次優化,那么DrawCall還可以再減少十幾個
本次優化的重點包括:層級關系和特效
對於U3D,我是一個菜鳥,對於U3D的一些東西是一知半解,例如DrawCall,我得到的是一些並不完全正確的信息,例如將N個紋理打包成一個圖集,這個圖集就只會產生一個DrawCall,如果不打成圖集,那么就會有N個DrawCall,這個觀點在很多人的認識里都是正確的,因為可以通過簡單的操作來驗證,但嚴格來說,這個觀點是錯誤的,因為它還受層級關系影響!
渲染順序
U3D的渲染是有順序的,U3D的渲染順序是由我們控制的,控制好U3D的渲染順序,你才能控制好DrawCall
一個DrawCall,表示U3D使用這個材質/紋理,來進行一次渲染,那么這次渲染假設有3個對象,那么當3個對象都使用這一個材質/紋理的時候,就會產生一次DrawCall,可以理解為一次將紋理輸送到屏幕上的過程,(實際上引擎大多會使用如雙緩沖,緩存這類的手段來優化這個過程,但在這里我們只需要這樣子認識就可以了),假設3個對象使用不同的材質/紋理,那么無疑會產生3個DrawCall
接下來我們的3個對象使用2個材質,A和B使用材質1,C使用材質2,這時候來看,應該是有2個DrawCall,或者3個DrawCall。應該是2個DrawCall啊,為什么會有3個DrawCall???而且是有時候2個,有時候3個。我們按照上面的DrawCall分析流程來分析一下:
1.渲染A,使用材質1
2.渲染B,使用材質1
3.渲染C,使用材質2
在這種情況下是2個DrawCall,在下面這種情況下,則是3個DrawCall
1.渲染A,使用材質1
2.渲染C,使用材質2
3.渲染B,使用材質1
因為我們沒有控制好渲染順序(或者說沒有去特意控制),所以導致了額外的DrawCall,因為A和B不是一次性渲染完的,而是被C打斷了,所以導致材質1被分為兩次渲染
那么是什么在控制這個渲染順序呢?首先在多個相機的情況下,U3D會根據相機的深度順序進行渲染,在每個相機中,它會根據你距離相機的距離,由遠到近進行渲染,在UI相機中,還會根據你UI對象的深度進行渲染
那么我們要做的就是,對要渲染的對象進行一次規划,正確地排列好它們,規則是,按照Z軸或者深度,對空間進行划分,然后確定好每個對象的Z軸和深度,讓使用同一個材質的東西,盡量保持在這個空間內,不要讓其他材質的對象進入這個空間,否則就會打斷這個空間的渲染順序
在這個基礎上,更細的規則有:
- 場景中的東西,我們使用Z軸來進行空間的划分,例如背景層,特效層1,人物層,特效層2
- NGUI中的東西,我們統一使用Depth來進行空間的划分
- 人物模型,當人物模型只是用一個材質,DrawCall只有1,但是用了2個以上的材質,DrawCall就會暴增(或許對材質的RenderQueue進行規划也可以使DrawCall只有2個,但這個要拆分好才行),3D人物處於復雜3D場景中的時候,我們的空間規則難免被破壞,這只能在設計的時候盡量去避免這種情況了
- 使用了多個材質的特效,在動畫的過程中,往往會引起DrawCall的波動,在視覺效果可以接受的范圍內,可以將特效也進行空間划分,假設這個特效是2D顯示,那么可以使用Z軸來划分空間
打包圖集
每個材質/紋理的渲染一定是會產生DrawCall的,這個DrawCall只能通過打包圖集來進行優化
制作圖集一般遵循幾個規則:
- 從功能角度進行划分,例如UI可以划分為公共部分,以及每個具體的界面,功能上,顯示上密切相關的圖片打包到一起
- 不要一股腦把所有東西打包到一個圖集里,特別是那些不可能同時出現的東西,它們就不應該在一個圖集里,這樣的圖集意義不大,減少不了DrawCall,並且一個你不需要顯示的圖片,會一直占用你的內存,這讓我非常不爽
- 注意控制圖集的大小,不要讓圖集太大,一個超級大圖集的DrawCall消耗或許頂的上十幾個小圖集的消耗
字符圖集,在使用BMFont或者其他工具生成圖片字的時候,我們往往是直接導入一大串文字,然后直接生成圖片,但實際上這上面的操作也有優化空間,例如BMFont生成的圖片大小,是可以設置的,有兩個規則,一個規則是導出的圖片盡量小,另一個是導出的圖片盡量少,默認的大小應該是512x512,假設你生成的圖片256x256就可以容納,那么多做一個操作你可以節省這么多空間,另外當你輸入多幾個字,就導致增加一張圖片時,例如1024變成2048,那么你可以考慮使用3張512的圖片,這樣也會節省空間
經過精心划分的圖集在加上精心規划的渲染順序,DrawCall會有一個質的優化
特效清理
U3D提供了非常便捷的方法讓我們很輕易地使用美術給過來的特效,懶惰的U3D程序猿會直接放入U3D,甚至不去看這是個什么特效,我們的特效一般都是一瞬間的事情,例如技能特效,或者其他什么特效,那么特效播放完,這個特效我們就看不到了,但假設這個特效在播放結束的時候,沒有將自身的Active屬性設置為false,那么它就會繼續占用你的DrawCall,消耗你設備的計算能力,所以程序需要保證當一個特效播放完之后,能夠被消耗,或者設置為非激活的狀態,可以使用一些公共方法來完成特效播放完之后的清理工作(自己實現2個靜態函數,一個播放完銷毀,一個播放完設置未激活)
完成DrawCall的優化之后,接下來就是內存的優化了,(內存優化手記 待續)