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

来源:互联网 发布:淘宝实名认证小号购买 编辑:程序博客网 时间:2024/05/18 20:51

引言

上篇【NGUI源码剖析】NGUI的drawcall简单认识了NGUI中的drawcall。借着近来工作更替的间隙,本文在之前的基础上做一点深入的分析:
- UIPanel工作流程图
- 如何生成drawcall
- widgets的生成及作用
- drawcall的合并


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


如何生成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的定义

image
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底层原理

原创粉丝点击