源码分析NGUI的DrawCall合并原理
2014-05-29 16:46
465 查看
楼主自学Unity不久,有纰漏的地方请大神指正。正文如下:
NGUI为了减少GPU状态切换的消耗(比如切换material),把相同material的widget合并,减少DrawCall的数量。下文描述了NGUI如何对widget归类,以及减少DrawCall需要注意的地方。
归类widget的代码在UIPanel中的FillAllDrawCalls()里,代码如下
void FillAllDrawCalls ()
{
for (int i = 0; i < drawCalls.size; ++i)
UIDrawCall.Destroy(drawCalls.buffer[i]);
drawCalls.Clear();
Material mat = null;
Texture tex = null;
Shader sdr = null;
UIDrawCall dc = null;
if (mSortWidgets) SortWidgets();
for (int i = 0; i < widgets.size; ++i)
{
UIWidget w = widgets.buffer[i];
if (w.isVisible && w.hasVertices)
{
Material mt = w.material;
Texture tx = w.mainTexture;
Shader sd = w.shader;
if (mat != mt || tex != tx || sdr != sd)
{
if (mVerts.size != 0)
{
SubmitDrawCall(dc);
dc = null;
}
mat = mt;
tex = tx;
sdr = sd;
}
if (mat != null || sdr != null || tex != null)
{
if (dc == null)
{
dc = UIDrawCall.Create(this, mat, tex, sdr);
dc.depthStart = w.depth;
dc.depthEnd = dc.depthStart;
dc.panel = this;
}
else
{
int rd = w.depth;
if (rd < dc.depthStart) dc.depthStart = rd;
if (rd > dc.depthEnd) dc.depthEnd = rd;
}
w.drawCall = dc;
if (generateNormals) w.WriteToBuffers(mVerts, mUvs, mCols, mNorms, mTans);
else w.WriteToBuffers(mVerts, mUvs, mCols, null, null);
}
}
else w.drawCall = null;
}
if (mVerts.size != 0) SubmitDrawCall(dc);
}
复制代码
算法描述如下
先把UIPanel中的Widget按depth从小到大排序,如果depth相同那按照material的ID来排序。然后遍历每个元素,把material相同的Widget归类到同一个drawCall。合并之后的结果如下图
最后生成了3个DrawCall,并按顺序提交GPU绘制。
为何要采用这个算法呢?因为NGUI的Material是透明材质,不会写入深度缓存(但是会进行深度测试,以保证与非透明物体的层次正确),我们可以看NGUI材质所使用的Unlit/Transparent Colored这个Shader,里面有一句ZWrite Off。所以widget的前后关系与z坐标是没有关系的,而是与DrawCall的绘制顺序有关。所以如果要按照上图的depth来显示widget,必然只能分成3个DrawCall,并且按顺序绘制。
NGUI为了减少GPU状态切换的消耗(比如切换material),把相同material的widget合并,减少DrawCall的数量。下文描述了NGUI如何对widget归类,以及减少DrawCall需要注意的地方。
归类widget的代码在UIPanel中的FillAllDrawCalls()里,代码如下
void FillAllDrawCalls ()
{
for (int i = 0; i < drawCalls.size; ++i)
UIDrawCall.Destroy(drawCalls.buffer[i]);
drawCalls.Clear();
Material mat = null;
Texture tex = null;
Shader sdr = null;
UIDrawCall dc = null;
if (mSortWidgets) SortWidgets();
for (int i = 0; i < widgets.size; ++i)
{
UIWidget w = widgets.buffer[i];
if (w.isVisible && w.hasVertices)
{
Material mt = w.material;
Texture tx = w.mainTexture;
Shader sd = w.shader;
if (mat != mt || tex != tx || sdr != sd)
{
if (mVerts.size != 0)
{
SubmitDrawCall(dc);
dc = null;
}
mat = mt;
tex = tx;
sdr = sd;
}
if (mat != null || sdr != null || tex != null)
{
if (dc == null)
{
dc = UIDrawCall.Create(this, mat, tex, sdr);
dc.depthStart = w.depth;
dc.depthEnd = dc.depthStart;
dc.panel = this;
}
else
{
int rd = w.depth;
if (rd < dc.depthStart) dc.depthStart = rd;
if (rd > dc.depthEnd) dc.depthEnd = rd;
}
w.drawCall = dc;
if (generateNormals) w.WriteToBuffers(mVerts, mUvs, mCols, mNorms, mTans);
else w.WriteToBuffers(mVerts, mUvs, mCols, null, null);
}
}
else w.drawCall = null;
}
if (mVerts.size != 0) SubmitDrawCall(dc);
}
复制代码
算法描述如下
先把UIPanel中的Widget按depth从小到大排序,如果depth相同那按照material的ID来排序。然后遍历每个元素,把material相同的Widget归类到同一个drawCall。合并之后的结果如下图
最后生成了3个DrawCall,并按顺序提交GPU绘制。
为何要采用这个算法呢?因为NGUI的Material是透明材质,不会写入深度缓存(但是会进行深度测试,以保证与非透明物体的层次正确),我们可以看NGUI材质所使用的Unlit/Transparent Colored这个Shader,里面有一句ZWrite Off。所以widget的前后关系与z坐标是没有关系的,而是与DrawCall的绘制顺序有关。所以如果要按照上图的depth来显示widget,必然只能分成3个DrawCall,并且按顺序绘制。
相关文章推荐
- 源码分析NGUI的DrawCall合并原理
- NGUI架构和Draw Call合并原理
- 【小松教你手游开发】【unity实用技能】从NGUI的UIScrollview的实现原理延伸到ngui的层次,合并,drawcall生成原理
- NGUI源码分析之----UIDrawCall
- NGUI源码分析(七) Panel,Widget,DrawCall 三者之间的关系
- 【NGUI源码剖析】深入理解NGUI的drawcall
- Unity中的DrawCall与NGUI原理
- 【NGUI源码剖析】NGUI如何优化drawcall数量
- Android系统原理与源码分析(1):利用Java反射技术阻止通过按钮关闭对话框
- SSCLI中GC源码分析(1) - EE与BCL之间的调用接口FCall
- Android移植: wifi设计原理(源码分析) [嵌入式]
- Android系统原理与源码分析(1):利用Java反射技术阻止通过按钮关闭对话框
- VLD检测内存泄露原理及源码分析
- Android wifi设计原理(源码分析)
- 【转】Android系统原理与源码分析:利用Java反射技术阻止通过按钮关闭对话框
- Struts2源码粗略分析四:理解xwork工作原理
- Linux Call Trace原理分析
- PHPCMS2008源码模板原理分析 PHPCMS20008二次开发
- Android系统原理与源码分析(1):利用Java反射技术阻止通过按钮关闭对话框
- u-boot源码配置原理分析