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的優化以後,接下來就是內存的優化了,(內存優化手記 待續)