【NGUI源码剖析】NGUI的drawcall

来源:互联网 发布:知乎每日精选 rss 编辑:程序博客网 时间:2024/05/18 20:06

前言
在使用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之间关系的影响。

原创粉丝点击