2017年4月17日 星期一

Draw Call未被批次處理?告訴你在Unity 5.6中如何找出原因

作者:Valentin Simonov 原文
潤稿:Kelvin Lo


相信各位都知道Unity內建動態和靜態批次處理,能有效地降低Draw Call的數量。

當你查看Stats介面時,如果看到一個標為"Saved by batching"的值,它就是用來顯示批次處理的次數。可惜它很難用來反推為何批次沒有被處理。儘管Unity手冊裡有說明可能的發生原因,但要理解這些資訊需要開發者已有相關的基礎知識。

好裡加在,Unity5.6 在Frame Debugger中新增了一項功能,能解釋這些批次資訊。

Frame Debugger是Unity 5.x推出的功能,你可以點功能表的Window > Frame Debugger 來打開Frame Debugger。它能顯示遊戲中所有的批次處理資訊,以及這些批次處理的所有細節資訊,包括著色器、貼圖及批次處理所用的大量資訊等。

在詳細介紹Unity何時發起新的批次處理之前,我們先來瞭解批次處理的概念及作用。

Unity 5.6中的Frame Debugger,這裡說明為何Unity要發動批次處理



批次處理 - Batch


Unity為了在螢幕上繪製物件,它需要向圖形API發起一次“繪製”命令,就是一次“Draw Call”。但在發起命令之前,Unity還需要為繪製的物件設定所需的GPU狀態:網格、著色器、貼圖、混合設定以及一些其他的著色器屬性。而狀態改變命令再加上一個或多個繪製命令就稱為一次批次處理(a Batch)



批次處理過程 - Batching


導致批次處理緩慢的原因就是改變GPU狀態的指令,而繪製指令實際上僅佔用很少的資源。所以Unity總是試圖利用同一個GPU狀態同時渲染多個物件。這一過程被稱為批次處理(Batching)。


Unity提供三種類型的批次處理:靜態批次處理(Static batching),動態批次處理(dynamic batching)以及GPU Instancing。

  • 靜態批次處理會在構建時將多個靜態網格物件合併為一個或多個大的網格物件,然後在運行時一次批次處理渲染一個大網格中的多個物件。
  • 動態批次處理在每幀中獲取多個小型網格物件,在CPU中對其進行頂點變換,將相似的頂點組合到一起,然後一次繪製它們。
  • GPU Instancing(Unity 5.3導入)可以利用少量Draw Call繪製多個具有不同的位置、旋轉以及其他著色器屬性的相同物件。


導致批次處理失敗的原因


有時在編輯器中可以清楚地看到,一些本應被批次處理的物件出於某些原因沒有被批次處理。首先,請檢查Player Settings中是否啟用批次處理功能。這個步驟看似多餘,但我們遇到太多的無法處理的原因都是因為忘記開啟。

我們專門為此提供了展示專案來演示Unity在什麼情況下必須發起新的批次處理請求。首先下載專案並複製到Unity專案中。請注意,你需要安裝Unity 5.6才能看到Frame Debugger中關於批次處理狀態的說明。


以下是展示專案(Unity 5.6)中導致無法進行批次處理的原因。每個原因對應一次單獨的批次處理:

  • Additional Vertex Streams — 物件使用MeshRenderer.additionalVertexStreams設定了額外的頂點資訊流。
  • Deferred Objects on Different Lighting Layers — 該物件位於另一不同的光照層中。
  • Deferred Objects Split by Shadow Distance — 兩個物體中有一個在陰影距離範圍內而另一個不是。
  • Different Combined Meshes — 該物件屬於另一個已合併的靜態網格。
  • Different Custom Properties — 該物件設定了不同的MaterialProperyBlock。
  • Different Lights — 該物件受不同的前向光照(Forward Light)影響。
  • Different Materials — 該物件使用不同的材質。
  • Different Reflection Probes — 該物件受不同的反射探頭(Reflection Probe)影響。
  • Different Shadow Caster Hash — 該物件使用其他的陰影投射著色器,或是設定了不同的著色器參數/關鍵字,而這些參數/關鍵字會影響陰影投射Pass的輸出。
  • Different Shadow Receiving Settings — 該物件設定了不同的“Receive Shadows”參數,或是一些物件在陰影距離內,而另一些在距離之外。
  • Different Static Batching Flags — 該物件使用不同的靜態批次處理設定。
  • Dynamic Batching Disabled to Avoid Z-Fighting — Player Settings中關閉了動態批次處理,或在當前環境中為避免深度衝突而被臨時關閉。
  • Instancing Different Geometries — 使用GPU Instancing渲染不同的網格或子網格。
  • Lightmapped Objects — 物件使用了不同的光照貼圖,或在相同的光照貼圖中有不同的光照貼圖UV轉換關係。
  • Lightprobe Affected Objects — 物件受其他光照探頭(Light Probe)影響。
  • Mixed Sided Mode Shadow Casters — 物件的“Cast Shadows”設定不同。
  • Multipass — 物件使用了帶多個Pass的著色器。
  • Multiple Forward Lights — 該物件受多個前向光渲染影響。
  • Non-instanceable Property Set — 為instanced著色器設定來non-instanced屬性。
  • Odd Negative Scaling — 該物件的縮放為很奇怪的負值,例如(1,-1,1)。
  • Shader Disables Batching — 著色器使用“DisableBatching”標籤顯式關閉了批次處理。
  • Too Many Indices in Dynamic Batch — 動態批次處理索引過多(超過32k)。
  • Too Many Indices in Static Batch — 靜態批次處理中的組合網格索引過多。對於OpenGL ES來說是48k,OSX是32k,其他平台是64k。
  • Too Many Vertex Attributes for Dynamic Batching — 欲進行動態批次處理的子網格擁有超過900個頂點屬性。
  • Too Many Vertices for Dynamic Batching — 欲進行動態批次處理的子網格頂點數量超過300個。

結論


現在可以開始使用Frame Debugger新功能來檢查你的專案,看看是否能找到可以優化的線索。隨著引擎不斷更新,將來也會加入更多批次處理的資訊。

這裡是展示專案的Github,如果你有興趣也可以關注一下。

沒有留言:

張貼留言

著作人