3.走近DrawCall
1.Hello,Unity Shader
2.渲染流水線
在前面我們就已經多次提到DrawCall,做過圖形或者優化的同學也一定接觸過DrawCall,那麼DrawCall是什麼?
其實,DrawCall很簡單,就是cpu對圖形繪製介面的調用,CPU通過調用圖形庫(directx/opengl)介面,命令GPU進行渲染操作。
3.1 Draw Call與性能
經常,我們會提到DrawCall性能優化。。很多時候我們會誤以為DrawCall造成的性能問題是GPU切換渲染狀態導致,其實這裡的元兇是CPU。要理解這一點,我們就需要明白下面2個問題
- CPU和GPU是如何進行並行工作和交互的?
試想,渲染流程沒用採用流水線的工作方式:CPU發送一個渲染命令之後,GPU立即執行渲染命令繪製圖形,等到渲染任務結束之後,CPU才可以繼續發送下一個渲染命令,這樣顯然影響工作效率。
採用渲染流水線後,CPU與GPU並行工作,獨立而不相互依賴。這是通過命令緩衝區來實現的:命令緩衝區維護一個命令隊列,CPU向其中發送命令,GPU從中取出命令並執行。命令有很多種,DrawCall是一種,其他命令還有改變渲染狀態、設置渲染數據流等。
這種方式就類似於遊戲開發的網路通信:維持一個消息隊列,網路線程接收解析消息並將之添加到消息隊列,遊戲主線程更新時從中取出消息並做派發處理。
- DrawCall是如何影響性能的?
每一次繪製CPU都要調用DrawCall,而在調動DrawCall前,CPU還要進行很多準備工作:檢測渲染狀態、提交渲染所需要的數據、提交渲染所需要的狀態。
而GPU本身具有很強大的計算能力,可以很快就處理完渲染任務。
當DrawCall過多,CPU就會很多額外開銷用於準備工作,CPU本身負載,而這時GPU可能閑置了。
做個試驗:拷貝1000個總大小1M的文件和單個大小為1M的文件,明顯拷貝1000個文件要慢很多,DrawCall調用和這個很類似。
3.2 DrawCall優化:減少DrawCall
既然,我們已經知道DrawCall導致的性能問題在於DrawCall數量過多,那麼我們優化的思路就是減少DrawCall。這裡我們只討論批處理(Batching)。
過多的DrawCall會造成CPU的性能瓶頸:大量時間消耗在DrawCall準備工作上。很顯然的一個優化方向就是:盡量把小的DrawCall合併到一個大的DrawCall中,這就是批處理的思想。
使用批處理我們需要在CPU和RAM中合併網格,而合併網格本身是需要計算消耗,而且創建新網格也會佔用內存。因此批處理的頻次不宜太高,不然造成的消耗可能得不償失。
使用批處理的注意事項:
- 合併的網格會在一次渲染任務中進行繪製,他們的渲染數據,渲染狀態和shader都是一樣的,因此合併的條件至少是:同材質、同貼圖、同shader。最好網格頂點格式也一致。
- 盡量避免使用大量小的網格,當確實需要時,考慮是否要合併。
- 避免使用過多的材質,盡量共享材質。
- 網格合併的頂點數量有上限(Unity中好像是65535)
- 合併本身有消耗,因此盡量在編輯器下進行合併
- 確實需要在運行時合併的,將靜態的物體和動態的物體分開合併:靜態的合併一次就可以,動態的只要有物體發生變換就要重新合併。
這一章為止渲染流水線的概念就結束了。
下一章會開始學習圖形數學的 知識:數學基礎和幾何變換。
能說已經對純理論感到無聊了么,好想擼碼~
推薦閱讀:
※《Splatoon 2》繪製效果的簡單實現
※Unity移動端動態陰影總結
※Instagram濾鏡,影視級調色演算法實現
※2D遊戲開發時有那些驚艷的效果是一定要學會著色器(shader)編程?
※編寫Unity Shader的時候,語義POSITION和SV_POSITION的區別?