您的位置:首页 > 产品设计 > UI/UE

【NGUI源码剖析】深入理解NGUI的drawcall

2017-07-10 18:33 701 查看
引言

上篇【NGUI源码剖析】NGUI的drawcall简单认识了NGUI中的drawcall。借着近来工作更替的间隙,本文在之前的基础上做一点深入的分析:

- UIPanel工作流程图

- 如何生成drawcall

- widgets的生成及作用

- drawcall的合并

UIPanel的工作流程

无论是UIDrawCall还是UIWidget的流程,都离不开UIPanel的工作流。“吾尝终日而思矣,不如须臾之所学也”看了几天NGUI的源代码,才发觉抓住主线才不会跑偏,而这幅图就是打开NGUI秘密的金钥匙。



如何生成drawcall

这里还是要从UIPanel中的数据drawCalls说起

/// <summary>
/// List of draw calls created by this panel. Do not attempt to modify this list yourself.
/// </summary>

[System.NonSerialized]
public List<UIDrawCall> drawCalls = new List<UIDrawCall>();


可以看到drawCalls.Add()的调用全都在FillAllDrawCalls()内部:

/// <summary>
/// Fill the geometry fully, processing all widgets and re-creating all draw calls.
/// </summary>

void FillAllDrawCalls ()
{
for (int i = 0; i < drawCalls.Count; ++i)
UIDrawCall.Destroy(drawCalls[i]);
drawCalls.Clear();

Material mat = null;
Texture tex = null;
Shader sdr = null;
UIDrawCall dc = null;
int count = 0;

if (mSortWidgets) SortWidgets();

for (int i = 0; i < widgets.Count; ++i)
{
UIWidget w = widgets[i];

if (w.isVisible && w.hasVertices)
{
Material mt = w.material;

if (onCreateMaterial != null) mt = onCreateMaterial(w, mt);

Texture tx = w.mainTexture;
Shader sd = w.shader;

// 判断widgets[i-1]与widgets[i]的mateial,texture,shader是否一致
if (mat != mt || tex != tx || sdr != sd)
{
if (dc != null && dc.verts.Count != 0)
{
drawCalls.Add(dc);          //缓存widgets[i-1]的drawcall
dc.UpdateGeometry(count);
dc.onRender = mOnRender;
mOnRender = null;
count = 0;
dc = null;                  //缓存drawcall后,重置drawcall
}
//缓存widgets[i]的material,texture,shader
mat = mt;
tex = tx;
sdr = sd;
}

if (mat != null || sdr != null || tex != null)
{
// 创建新的drawcall,设置drawcall相关的数据
if (dc == null)
{
dc = UIDrawCall.Create(this, mat, tex, sdr);
dc.depthStart = w.depth;
dc.depthEnd = dc.depthStart;
dc.panel = this;
dc.onCreateDrawCall = onCreateDrawCall;
}
//widgets[i-1]与widgets[i]的material,shader,texture相同时,共用一个drawcall
else
{
// 更新drawcall的深度范围
int rd = w.depth;
if (rd < dc.depthStart) dc.depthStart = rd;
if (rd > dc.depthEnd) dc.depthEnd = rd;
}

w.drawCall = dc;

++count;
if (generateNormals) w.WriteToBuffers(dc.verts, dc.uvs, dc.cols, dc.norms, dc.tans, generateUV2 ? dc.uv2 : null);
else w.WriteToBuffers(dc.verts, dc.uvs, dc.cols, null, null, generateUV2 ? dc.uv2 : null);

if (w.mOnRender != null)
{
if (mOnRender == null) mOnRender = w.mOnRender;
else mOnRender += w.mOnRender;
}
}
}
else w.drawCall = null;
}

//缓存widgets[widgets.Count-1]的drawcall
if (dc != null && dc.verts.Count != 0)
{
drawCalls.Add(dc);
dc.UpdateGeometry(count);
dc.onRender = mOnRender;
mOnRender = null;
}
}


从代码中可以看到,新增drawcall主要通过缓存widgets[i-1]的material,texture,shader与widgets[i]进行对比。如果使用相同的material,texture,shader则共用同一个drawcall,否则新创建一个drawcall。

其中都是在遍历widgets进行操作,那么widgets从哪里来?又要到哪里去?

widgets的生成及作用

UIWidget的定义



UIPanel与UIWidget的对应关系是一对多的,而UIWidget本质上是UISprite,UI2DSprite,UITexture,UILabel等几种不同的控件。

widgets的生成

widgets.Add的调用有两处:

1.改变UIWidget的深度时

/// <summary>
/// Depth controls the rendering order -- lowest to highest.
/// </summary>

public int depth
{
get
{
// Experiment with a transform-based depth, uGUI style
//if (mDepth == int.MinValue)
//{
//    int val = cachedTransform.GetSiblingIndex();
//    UIWidget pt = parent as UIWidget;
//    if (pt != null) val += pt.depth;
//    return val;
//}
return mDepth;
}
set
{
if (mDepth != value)
{
if (panel != null) panel.RemoveWidget(this);
mDepth = value;

if (panel != null)
{
panel.AddWidget(this);

...


2.创建UIPanel时

/// <summary>
/// Ensure we have a panel referencing this widget.
/// </summary>

public UIPanel CreatePanel ()
{
if (mStarted && panel == null && enabled && NGUITools.GetActive(gameObject))
{
panel = UIPanel.Find(cachedTransform, true, cachedGameObject.layer);

if (panel != null)
{
mParentFound = false;
panel.AddWidget(this);
CheckLayer();
Invalidate(true);
}
}
return panel;
}


drawcall的合并

从FillAllDrawCalls函数中看drawcall的生成过程,可以肯定的是

widgets.Count >= drawcalls.Count


如果符合【NGUI源码剖析】NGUI如何优化drawcall数量中drawcall的合并条件,这两个List变动的数量就是drawcall合并的数量。

参考文章:

NGUI所见即所得之深入剖析UIPanel,UIWidget,UIDrawCall底层原理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ngui UIDrawCall