【NGUI源码剖析】NGUI的drawcall

前言
在使用unity3d开发实际的项目中,性能优化一直是一项不得不考虑的重点,而其中UI的优化又是绕不过去的坎,很多看似简单的UI为何会占用大量的cpu开销?本文以NGUI这套UI的解决方案为例,从源码出发,分析影响性能的原因,看清问题的本质,对症下药。

NGUI的drawcall
在Unity中,每次引擎准备数据并通知GPU的过程称为一次Draw Call。这一过程是逐个物体进行的,对于每个物体,不只GPU的渲染,引擎重新设置Material/Shader也是一项非常耗时的操作。

说起NGUI的drawcall,UIPanel是一座绕不过的大山,看源码:

    /// <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>();

可以将UIDrawCall近似的理解为NGUI层对于drawcall需要用到的数据进行的一层封装,后续会写文章来分析UIDrawCall。
这里看到UIPanel会维护一个UIDrawCall的列表,注释说明了这个列表保存的是当前panel创建的draw calls。跟踪这个列表,查询向列表添加元素的地方会发现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();  // 对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;
                        // 调用UpdateGeometry对Mesh,MeshRenderer,MeshFilter进行设置
                        // 之后重置dc
                    }
        ...
        }

上述代码出现了另一个列表widgets:

    /// <summary>
    /// List of widgets managed by this panel. Do not attempt to modify this list yourself.
    /// </summary>

    [System.NonSerialized]
    public List<UIWidget> widgets = new List<UIWidget>();

可以看到一个UIPanel可能对应多个UIWidget,而一个UIWidget不一定会对应一个UIDrawCall(可能对应一个UIDrawCall,也可能与其他UIWidget共用一个UIDrawCall)三者间的关系可以用下图表示:
这里写图片描述

顺藤摸瓜,可以得出函数调用栈:
这里写图片描述

下一次,我将会着重分析SortWidgets()对于UIWidget与UIDrawCall之间关系的影响。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值