天天看點

NGUI Drawcall 優化

NGUI 方面的Draw Call 優化:

(1)     打包圖集

一、每個材質/紋理的渲染一定是會産生DrawCall的,這個DrawCall隻能通過打包圖集來進行優化。

二、從功能角度進行劃分,例如UI可以劃分為公共部分,以及每個具體的界面,功能上,顯示上密切相關的圖檔打包到一起 

(2)渲染順序

一、  U3D的渲染是有順序的,NGUI的渲染順序是由我們控制的,控制好NGUI的渲染順序,你才能控制好DrawCall。

二、  一個DrawCall,表示U3D使用這個材質/紋理,來進行一次渲染,那麼這次渲染假設有3個對象,那麼當3個對象都使用這一個材質/紋理的時候,就會産生一次DrawCall。假設3個對象使用不同的材質/紋理,那麼無疑會産生3個DrawCall。

例子:

A,B使用材質1,C使用材質2,這時候會有幾個Draw Call?

有兩種情況,3個Draw Call:

A:材質1

B:材質2

C:材質1

第二種情況,2個DrawCall

A:材質1

C:材質1

B:材質2

渲染順序:Unity預設會按照控件的Depth來渲染。從後往前渲染,當使用相同材質的控件會合并為一個Draw Call。如果和前一個材質不相同則會重新産生一個Draw Call。是以會有上面兩種不同的結果,如果同一個界面有更多的空間存在時,這個問題會更明顯。在UI制作的時候就需要特别注意這一點。

NGUI為了減少GPU狀态切換的消耗(比如切換material),把相同material的widget合并,減少DrawCall的數量。下文描述了NGUI如何對widget歸類,以及減少DrawCall需要注意的地方。

歸類widget的代碼在UIPanel中的FillAllDrawCalls()裡,代碼如下

  1. void FillAllDrawCalls ()
  2.         {
  3.                 for (int i = 0; i < drawCalls.size; ++i)
  4.                         UIDrawCall.Destroy(drawCalls.buffer[i]);
  5.                 drawCalls.Clear();
  6.                 Material mat = null;
  7.                 Texture tex = null;
  8.                 Shader sdr = null;
  9.                 UIDrawCall dc = null;
  10.                 if (mSortWidgets) SortWidgets();
  11.                 for (int i = 0; i < widgets.size; ++i)
  12.                 {
  13.                         UIWidget w = widgets.buffer[i];
  14.                         if (w.isVisible && w.hasVertices)
  15.                         {
  16.                                 Material mt = w.material;
  17.                                 Texture tx = w.mainTexture;
  18.                                 Shader sd = w.shader;
  19.                                 if (mat != mt || tex != tx || sdr != sd)
  20.                                 {
  21.                                         if (mVerts.size != 0)
  22.                                         {
  23.                                                 SubmitDrawCall(dc);
  24.                                                 dc = null;
  25.                                         }
  26.                                         mat = mt;
  27.                                         tex = tx;
  28.                                         sdr = sd;
  29.                                 }
  30.                                 if (mat != null || sdr != null || tex != null)
  31.                                 {
  32.                                         if (dc == null)
  33.                                         {
  34.                                                 dc = UIDrawCall.Create(this, mat, tex, sdr);
  35.                                                 dc.depthStart = w.depth;
  36.                                                 dc.depthEnd = dc.depthStart;
  37.                                                 dc.panel = this;
  38.                                         }
  39.                                         else
  40.                                         {
  41.                                                 int rd = w.depth;
  42.                                                 if (rd < dc.depthStart) dc.depthStart = rd;
  43.                                                 if (rd > dc.depthEnd) dc.depthEnd = rd;
  44.                                         }
  45.                                         w.drawCall = dc;
  46.                                         if (generateNormals) w.WriteToBuffers(mVerts, mUvs, mCols, mNorms, mTans);
  47.                                         else w.WriteToBuffers(mVerts, mUvs, mCols, null, null);
  48.                                 }
  49.                         }
  50.                         else w.drawCall = null;
  51.                 }
  52.                 if (mVerts.size != 0) SubmitDrawCall(dc);
  53.         }

複制代碼

算法描述如下

先把UIPanel中的Widget按depth從小到大排序,如果depth相同那按照material的ID來排序。然後周遊每個元素,把material相同的Widget歸類到同一個drawCall。合并之後的結果如下圖

NGUI Drawcall 優化

最後生成了3個DrawCall,并按順序送出GPU繪制。

為何要采用這個算法呢?因為NGUI的Material是透明材質,不會寫入深度緩存(但是會進行深度測試,以保證與非透明物體的層次正确),我們可以看NGUI材質所使用的Unlit/Transparent Colored這個Shader,裡面有一句ZWrite Off。是以widget的前後關系與z坐标是沒有關系的,而是與DrawCall的繪制順序有關。是以如果要按照上圖的depth來顯示widget,必然隻能分成3個DrawCall,并且按順序繪制。

原文位址:

http://www.cnblogs.com/Oceanou/p/4001650.html

http://bbs.9ria.com/thread-282804-1-1.html

繼續閱讀