DOTS全称是Data-Oriented Tech Stack,翻译过来就是多线程式数据导向型技术堆栈(DOTS),它由任务系统(Job System)、实体组件系统(ECS)、Burst Compiler编译器三部分组成。
ECS + JobSystem + BurstCompile = 高性能 + 多线程 + 编译层面优化
DOTS保证相同类型组件在内存中都是顺序排列,极大程度增加缓存的命中率,此外配合任务系统(Job System)让开发者无需头疼多线程同时访问数据需要手动加解锁的麻烦,最终加持Burst Compiler让性能飞起来。
ECS、JobSystem、Burst,这三个组件是可以相互独立使用,并不是说使用一个这三个必须同时用,你可以任意选择其中一个来进行使用,用于不同的应用场景。
如果说我们需要使用JobSystem,其实它跟ECS没有太大的关系,你可以在ECS里面用,也可以不在ECS里面用,只要是需要并行计算的地方都可以使用。
Burst也一样,它也不需要配合ECS使用,不需要跟并行计算捆绑使用,它的作用仅仅是对于一些复杂的计算密集的东西去进行编译器优化,来达到性能提升。
只要是计算密集型的东西,都可以使用Burst,同步方法也是可以的。
关于ECS,一个比较大的误解,可能大家会觉得用ECS之后,所有东西都可以用ECS来写,就会想UI的业务逻辑怎么用ECS实现。大可不必,并不是说用ECS,所有东西全部都要用ECS来做,而是大家可以根据项目需求选择其中适合那部分来用ECS去写,剩下的部分还是使用传统的面向对象的方式去写,没有任何问题,只要用代码稍微结合一下就可以了。
在《黑暗之潮》当中利用ECS的例子,我们通过ECS渲染了大量的怪物。我们游戏里面怪物通常有一个特点,一组怪由几名精英配合一两种大量的存在的爪牙组成的,大家可以看到右面的图只有三种怪,如果说用默认的SkinMeshRenderer的话,就有一个非常严重的问题,没有办法合批了,画面上面有多少个怪,有多少个DrawCall而且Animator开销也不小,还有一个问题,GameObject为.Instantiate开销也是比较大的,如果说我要同时刷出来三四十只怪的话,肯定会卡顿,用ECS就能比较好的解决这三个问题。
使用ECS先把整个动画信息去烘焙到这么一张动画贴图上面,在GPU当中进行蒙皮操作,我们再通过JobSystem和Burst实现视锥剔除和动画系统的更新,最后我们再在面向对象那块业务逻辑那块控制ECS Enity就可以了。也就是说ECS的部分,我们只是提供渲染的和动作的结构,其他部分业务逻辑还是完全用面向对象去实现的,相当于各取所长。
用ECS最大的好处就是性能。
首先第一个,因为我们采用了GPU蒙皮,整个DrawCall的数量下降到有几种怪就是几个DrawCall,这个就非常好了。实例化也是非常快,ECS基本上就是无感的,在极端机上消耗,即便同时刷一千只怪也不足1毫秒,借助Burst力量类似于视锥剔除这些计算量比较大的操作,在低端机上也是可以忽略不计的。
大家可以看到下面的截图,演示我们整个动画更新阶段,也是同样在骁龙450 SoC上测的,100只怪左右的情况,动画整个更新过程只用了0.008毫秒,这就是忽略不计,根本不需要考虑的一个量级。通过ECS,我们画面上怪物的渲染完全取决于GPU本身的渲染性能,CPU的开销完全不需要去考虑了,所以也不会出现卡顿。
第二个,我们通过Jobsystem去实现了怪物击飞的效果,大家可以看到这个怪物被打下悬崖,它如果说碰到墙壁必须要被墙壁挡下来,需要进行一些物理运算,如果直接使用Unity的Ragdoll也就是布娃娃系统,它的物理计算非常复杂,对于低端机会造成比较大的性能负担。我们把这个过程稍微简化了一下,所有的这些怪物在被击飞的时候,使用的是预先制作好的动画,我们只需要计算它的运行轨迹就行了。
我们首先用Job去并行计算这些怪物的分析轨迹,再通过Unity提供的多线程Raycast方法去进行射线检测来判断它是否撞到墙或者碰到地面了。最后如果说我们还有一些非ECS了对象,我们可以在计算完毕之后再通过一个单独的Job把这个所有GameObject的位置给同步一下就可以了。
第三个,我们通过Burst实现的就是射线技能,这个东西看上去很简单,实际上需要对整个场景以及所有的怪物和其他对象产生交互。射线打到墙上能够实时产生反映,我们这个东西需要每帧对整个场景进行射线检测,整个计算过程实际上是开销比较大的。通过Burst我们相当于把这个东西做成了一个Job,通过Job.Run的方法去直接进行调用,就是在当前这个线程进行的操作。
使用起来跟一个静态方法没有太大的差别,还有像大家看到的这个技能,会有大量的子弹,对这些子弹我们同样需要进行运行轨迹的计算。通过Burst非常有效的把这两个计算开销降的非常低,Burst开启之后,它的性能提升基本能上百倍,通过刚才也提到Job.Run的方式实现同步调用,我们在整个计算流程当中不需要开额外的线程,直接在当前线程,单个静态方法直接调用就可以了,也是非常方便的。
大家可以看一下开启和不开启Burst效果的差别,左边是开启,右边是不开启,我们在一个计算体系化模型工具中测试,左边只用241毫秒,右边用了20毫,真是一百倍的差别。而不是说它用了多线程所以更快,大家可以看到每个线程都快了100倍,如果算总耗时,这边用了143秒,这边只用了1秒钟,如果把所有线程的时间加起来,就是100倍的差别,效果非常明显。
具有大量相同类型运算的项目,例如RTS、SLG、我的世界类型方块沙盒。
Dots这套解决方案,暂时只能对CPU进行优化,GPU方向期待Unity后续计划。
Burst目前还是Preview,正式项目中使用可能会有很多坑 。
https://aladdin.blog.csdn.net/article/details/105081530
Unity DOTS技术浅析_IT善男信女鬼的博客-CSDN博客
[Unity 活动]-Unity DOTS技术详解 - 宣雨松_哔哩哔哩_bilibili
ILRuntime作者林若峰分享:次世代手游渲染怎么做 - GameRes游资网
[Unity 活动]-游戏专场|Unity 2019 新特性在次时代手游 《黑暗之潮》中的应用经验及技术分享_哔哩哔哩_bilibili
Unity2019 Dots初试_Hello Captain-CSDN博客
Unity DOTS学习教程汇总_学无止境的专栏-CSDN博客
ECS的泛泛之谈 - 知乎
一种类ECS设计范式的介绍 链接: https://pan.baidu.com/s/1hH3YwgPvB9m39_e3HzlAcA 提取码: 5wwm