uGUI Text富文本的顶点数优化的优化
来源:互联网 发布:java技术 编辑:程序博客网 时间:2024/06/05 08:22
在写 uGUI Text富文本的顶点数优化 的时候正好看到这篇文档,
https://unity3d.com/cn/learn/tutorials/topics/performance-optimization/optimizing-garbage-collection-unity-games
于是想测试下自己写的顶点数优化组件对GC的影响,不测不知道,一测吓一跳
将下面的富文本复制30份填入一个Text中,在Text下挂上UIVertexOptimize组件
<size=30><b><color=#000000ff>1</color></b></size>
打开Profiler工具,运行一看,每次Text更新,OptimizeVert函数都会产生高达12M多的堆分配
展开可以看到最主要的堆分配是在
可以看出在调用Linq下的去重扩展函数Distinct时会进行元素比较,由于比较的元素类型Triangle是值类型,默认的值类型比较器会有装箱的操作,所以导致了大量的堆分配
扩展函数Distinct还有个带比较器参数的重载版本,于是创建一个Triangle类型,的比较器
class TriangleCompare : IEqualityComparer<Triangle>
{
public bool Equals(Triangle x, Triangle y)
{
return UIVertexEquals(x.v1, y.v1) && UIVertexEquals(x.v2, y.v2) && UIVertexEquals(x.v3, y.v3);
}
public int GetHashCode(Triangle obj)
{
return GetUIVertexHashCode(obj.v1)
^ GetUIVertexHashCode(obj.v2)
^ GetUIVertexHashCode(obj.v3);
}
int GetUIVertexHashCode(UIVertex vertex)
{
return vertex.color.a.GetHashCode()
^ vertex.color.b.GetHashCode()
^ vertex.color.g.GetHashCode()
^ vertex.color.r.GetHashCode()
^ vertex.normal.GetHashCode()
^ vertex.position.GetHashCode()
^ vertex.tangent.GetHashCode()
^ vertex.uv0.GetHashCode()
^ vertex.uv1.GetHashCode();
}
bool UIVertexEquals(UIVertex x, UIVertex y)
{
return x.color.a == y.color.a
&& x.color.b == y.color.b
&& x.color.g == y.color.g
&& x.color.r == y.color.r
&& x.normal == y.normal
&& x.position == y.position
&& x.tangent == y.tangent
&& x.uv1 == y.uv1
&& x.uv0 == y.uv0;
}
}
去重函数改为调用带参版本
vertices = tris.Distinct(new TriangleCompare()).SelectMany(tri =>
new[]{
tri.v1,
tri.v2,
tri.v3
}).ToList();
重新运行堆分配降为了1M多
1M多还是非常夸张,继续查看堆分配最多的地方
可以看出往List中添加元素时,List会先判断当前的容量是否足够大,如果不够,会将容量扩大为当前的两倍,这个操作会有相应大小的堆分配
这个堆分配是免不了的,不过原先List<Triangle> tris作为局部变量,分配的堆内存在OptimizeVert函数执行完后就变为垃圾内存了,并在下次执行时又重复创建重复扩容,可以将tris变量提升为成员变量以避免不必要的重复堆分配
最终组件修改为
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
public class UIVertexOptimize : BaseMeshEffect
{
struct Triangle
{
public UIVertex v1;
public UIVertex v2;
public UIVertex v3;
}
class TriangleCompare : IEqualityComparer<Triangle>
{
public bool Equals(Triangle x, Triangle y)
{
return UIVertexEquals(x.v1, y.v1) && UIVertexEquals(x.v2, y.v2) && UIVertexEquals(x.v3, y.v3);
}
public int GetHashCode(Triangle obj)
{
return GetUIVertexHashCode(obj.v1)
^ GetUIVertexHashCode(obj.v2)
^ GetUIVertexHashCode(obj.v3);
}
int GetUIVertexHashCode(UIVertex vertex)
{
return vertex.color.a.GetHashCode()
^ vertex.color.b.GetHashCode()
^ vertex.color.g.GetHashCode()
^ vertex.color.r.GetHashCode()
^ vertex.normal.GetHashCode()
^ vertex.position.GetHashCode()
^ vertex.tangent.GetHashCode()
^ vertex.uv0.GetHashCode()
^ vertex.uv1.GetHashCode();
}
bool UIVertexEquals(UIVertex x, UIVertex y)
{
return x.color.a == y.color.a
&& x.color.b == y.color.b
&& x.color.g == y.color.g
&& x.color.r == y.color.r
&& x.normal == y.normal
&& x.position == y.position
&& x.tangent == y.tangent
&& x.uv1 == y.uv1
&& x.uv0 == y.uv0;
}
}
List<UIVertex> verts = new List<UIVertex>();
List<Triangle> tris = new List<Triangle>();
public override void ModifyMesh(VertexHelper vh)
{
vh.GetUIVertexStream(verts);
Debug.Log(verts.Count);
OptimizeVert(ref verts);
Debug.Log(verts.Count);
vh.Clear();
vh.AddUIVertexTriangleStream(verts);
}
void OptimizeVert(ref List<UIVertex> vertices)
{
if (tris.Capacity < vertices.Count / 3)
{
tris.Capacity = vertices.Count;
}
for (int i = 0; i <= vertices.Count - 3; i += 3)
{
tris.Add(new Triangle() { v1 = vertices[i], v2 = vertices[i + 1], v3 = vertices[i + 2] });
}
vertices.Clear();
vertices.AddRange(tris.Distinct(new TriangleCompare()).SelectMany(t => new[]
{
t.v1,
t.v2,
t.v3
}));
tris.Clear();
}
}
其中List的Clear操作不会影响到List的容量大小
Distinct扩展函数还是会产生可观的堆分配,是因为其使用了临时HashSet容器,跟List一样在容量变化时会有堆分配,所以在一些频繁调用的地方需要实现一个无GC的去重函数
还有两个迭代操作产生的几百B的堆分配,这就是Unity使用的Mono编译器令人诟病已久的foreach问题
总结为三个问题
1.值类型的默认比较会有装箱操作,这个不确定是普遍的问题还是Unity的Mono编译器的问题,在值类型里重写GetHashCode和Equals函数或写个对应的比较器类可解决
2.容器类的扩容问题,特别是临时容器变量,最好实现一个公用的容器对象池避免重复创建容器 ,Linq的扩展函数中可能有大部分会用到临时容器变量,这些临时容器扩容时可能产生巨量的堆分配,并在函数执行完后直接变为垃圾内存,所以在频繁调用的地方最好不用Linq操作
3.foreach迭代操作的问题,Unity5.5版本说明中说已经修复了该问题,未验证
阅读全文
0 0
- uGUI Text富文本的顶点数优化的优化
- uGUI Text富文本的顶点数优化
- 优化UGUI的ScrollRect
- ugui的优化
- 如何优化UGUI的ScrollRect
- 如何优化uGUI的性能
- 如何优化UGUI的ScrollRect
- 查看uGUI UI组件生成的顶点数
- UGUI富文本
- UGUI Text组件实际文本宽高的获取
- unity的UGUI的一些优化方法
- [Unity优化]UGUI图集的使用
- Unity UGUI的DrawCall优化(一)
- UGUI滑动容器的优化思路
- 关于UGUI的个人优化总结
- 轻量级富文本(待优化)
- ugui优化
- Lable的富文本
- 妈妈回忆录1
- Mysql实现全局唯一ID
- fliter过滤器过滤未登录用户
- LeetCode [DP]322. Coin Change
- JAVA命令参数详解:-D
- uGUI Text富文本的顶点数优化的优化
- dicom文件tag详解
- vm如何安装xenserver
- FTP命令字和响应码解释
- 《深入理解Java虚拟机》->Java内存区域与内存溢出异常
- Android Studio中查看模拟器或真机sqlite数据库的方法
- 小知识记录
- 对数据仓库的一些理解
- Codeforces 735 D Taxes (哥德巴赫猜想)