【NGUI源码剖析】NGUI如何优化drawcall数量

引言
接上一篇文章对UIPanel的分析。定量的分析哪些因素会影响NGUI drawcall的增减。
SortWidgets
FillAllDrawCalls的部分代码

        if (mSortWidgets) SortWidgets();  // 对widgets进行排序

        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;
                // 判断材质,贴图,shader是否与当前的widget相同,若不同则增加一个drawcall
                if (mat != mt || tex != tx || sdr != sd)
                {
                    if (dc != null && dc.verts.Count != 0)
                    {
                        drawCalls.Add(dc);
                        dc.UpdateGeometry(count);
                        dc.onRender = mOnRender;
                        mOnRender = null;
                        count = 0;
                        dc = null;
                    }
                ...

可以看到FillAllDrawCalls遍历widgets,并对比当前材质,贴图,shader是否与当前的widget相同,如果其中任意一项不同,则会新增一个drawcall。而在遍历之前调用了SortWidgets()接口:

    /// <summary>
    /// Immediately sort all child widgets.
    /// </summary>

    public void SortWidgets ()
    {
        mSortWidgets = false;
        widgets.Sort(UIWidget.PanelCompareFunc);
    }

    // 排序因子
    /// <summary>
    /// Static widget comparison function used for inter-panel depth sorting.
    /// </summary>

    [System.Diagnostics.DebuggerHidden]
    [System.Diagnostics.DebuggerStepThrough]
    static public int PanelCompareFunc (UIWidget left, UIWidget right)
    {
        if (left.mDepth < right.mDepth) return -1;
        if (left.mDepth > right.mDepth) return 1;

        Material leftMat = left.material;
        Material rightMat = right.material;

        if (leftMat == rightMat) return 0;
        if (leftMat == null) return 1;
        if (rightMat == null) return -1;

        return (leftMat.GetInstanceID() < rightMat.GetInstanceID()) ? -1 : 1;
    }

可以看到PanelCompareFunc 中排序的依据为深度,对于材质仅做了最简单的判空。并没有对影响drawcall产生的因素材质,贴图和shader进行判断。
那么可以进行一个大胆的假设,widget的深度及材质,贴图,shader会影响drawcall的数量
创建测试环境,可以看到空场景中包含2个drawcall:
这里写图片描述
添加3个拥有相同深度,材质,贴图的UITexture:
这里写图片描述
此时的drawcall为3个:
这里写图片描述

深度的影响
分别将tex1,tex2,tex3的深度设置为0,1,2,可以看到drawcall的数量没有变化。

材质的影响
三个texture的材质依次为:
这里写图片描述

此时的drawcall为5个:
这里写图片描述

调整任意两个相邻texture的材质,当材质相同时,drawcall的数量为4:
图示为调整tex2的材质为Backdrop的情况:
这里写图片描述
这里写图片描述

调整不相邻texture的材质,当材质相同时,drawcall的数量不会变化
这里写图片描述
可以得到结论:NGUI会对widgets列表中相邻元素根据材质是否相同进行合并处理,对于widgets列表中不连续的元素则不会进行合并。
此处可以通过修改PanelCompareFunc 来优化:

    static public int PanelCompareFunc (UIWidget left, UIWidget right)
    {
        //原排序规则
        //if (left.mDepth < right.mDepth) return -1;
        //if (left.mDepth > right.mDepth) return 1;

        //Material leftMat = left.material;
        //Material rightMat = right.material;

        //if (leftMat == rightMat) return 0;
        //if (leftMat == null) return 1;
        //if (rightMat == null) return -1;

        //return (leftMat.GetInstanceID() < rightMat.GetInstanceID()) ? -1 : 1;

        //依据材质的排序规则
        Material leftMat = left.material;
        Material rightMat = right.material;

        if (leftMat == rightMat)
        {
            if (left.mDepth < right.mDepth) return -1;
            if (left.mDepth > right.mDepth) return 1;
        }

        //if (leftMat == rightMat) return 0;
        if (leftMat == null) return 1;
        if (rightMat == null) return -1;

        return (leftMat.GetInstanceID() < rightMat.GetInstanceID()) ? -1 : 1;
    }

重新测试上述示例
这里写图片描述
这里写图片描述
可以看到widgets列表中tex元素的排列顺序发生了改变。
这里写图片描述
可以看到减少了1个drawcall,修改后的排序规则,理论上可以达到drawcall数量等于material数量的“理想情况”。

贴图的影响
上面的结论为什么说是“理想情况”呢?
别忘了影响drawcall的因素除了深度,材质之外还有贴图和shader。在上面例子的基础上,修改3个texture的Texture为不同。
这里写图片描述
drawcall又变回了5个。
所以“理想情况”隐含了贴图与shader都一致的条件。
PanelCompareFunc 更完备的写法:

    static public int PanelCompareFunc (UIWidget left, UIWidget right)
    {
        //原排序规则
        //if (left.mDepth < right.mDepth) return -1;
        //if (left.mDepth > right.mDepth) return 1;

        //Material leftMat = left.material;
        //Material rightMat = right.material;

        //if (leftMat == rightMat) return 0;
        //if (leftMat == null) return 1;
        //if (rightMat == null) return -1;

        //return (leftMat.GetInstanceID() < rightMat.GetInstanceID()) ? -1 : 1;

        //依据材质的排序规则
        Material leftMat = left.material;
        Material rightMat = right.material;
        //增加了对texture与shader的判断
        if (leftMat == rightMat && left.mainTexture == right.mainTexture && leftMat.shader == rightMat.shader)
        {
            if (left.mDepth < right.mDepth) return -1;
            if (left.mDepth > right.mDepth) return 1;
        }

        //if (leftMat == rightMat) return 0;
        if (leftMat == null) return 1;
        if (rightMat == null) return -1;

        return (leftMat.GetInstanceID() < rightMat.GetInstanceID()) ? -1 : 1;
    }

总结
对于NGUI drawcall数量的优化:

  1. 重写PanelCompareFunc 排序规则,加入对于材质,贴图,shader的判断;
  2. 在规划UI界面时,需要统筹规划各个UIPanel的深度。

参考文章:
NGUI所见即所得之UIPanel

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值