Unity里加速纹理压缩

使用Unity开发项目的人都知道,Build Settings里切换平台时,会根据每个纹理指定的对应平台的压缩格式去压缩纹理。压缩纹理非常慢,会占用总时间的一多半。那能不能把这个时间降下来呢?2018年秋就接到了公司的这个需求。

网上看到有人之前尝试着解决这个问题,说白了就是把压的工作从烂CPU(作者指的主要是Mac)转移到强大的CPU上(主频高、核多),见https://blog.codingnow.com/2016/12/unity3d_remote_pvrtextool.html。这个方案至少打开了一个有启发性的思路。

但是,上面的方案理论上来说最多快上几倍,压缩纹理还是会占用很长时间,因为普通民用CPU的性能不会差别太大。

那怎么才能大幅提升速度呢?想起了开发引擎时经常会使用联合编译,从这得到灵感(UE4里的Lightmass烘焙Lightmap很慢,所以实现了个Swarm,可以分布式烘焙,也是这个做法):为什么不将纹理拆分开,用分布式的方式来压缩呢,压缩完毕后拼合成一张完整的纹理。我们都知道公司工位上的机器大部分时间CPU都是闲置状态,还有公司内部也有很多淘汰下来的服务器。如果这么做,理论上来说能提高几个数量级。很兴奋,搞嘛。

幸好公司购买了Unity源码,很快就搞清楚了纹理压缩的流程:Unity根据压缩格式的不同会启动不同的命令行工具的进程进行处理,例如PVRTexTool.exe、etccompress.exe;ASTC比较特殊一些,将一个ASTC压缩的开源库直接编译进了引擎里,先不管这个,技术Demo没有处理这种情况。压缩工具进程完成后,Unity再继续运行。自己写代码编译出Proxy工具,这些工具命名成PVRTexTool.exe和etccompress.exe,将Unity里的替换成掉。这些Proxy工具只是个中转站,将命令参数和未压缩的纹理数据分别上传到中心服务器和数据服务器。服务器分拆纹理,并将小块的纹理交给不同的任务服务器去压缩。压缩完毕后合并成完整的纹理,通过网络返回给Unity编辑器。对于ASTC这种特殊方式,可以用Hook的技术转发命令参数和纹理数据,不过没有时间去尝试这个(其实强烈建议Unity官方将这个开源库编译成接收命令行参数的可执行程序,这样就处理起来统一了)。

分布式压缩的图:

其实也没什么技术难题,也就分拆PVR格式的纹理时卡了下,因为PVR里的纹素是Z字形排列的。主要是工程化的比重比较多,所以细碎的活很多,需要支持Windows、MacOS和Linux,需要用到多种语言和IDE。但由于人手严重不足,只能完成了各技术Demo。技术Demo到产品化还有很长的距离要走的,需要解决各种细枝末节的问题,所以需要一个团队。

经实测,确实有明显的性能提升:测试了在三台电脑上分布式压缩,压缩性能基本上跟服务器数量的增加成正比。

当然,这种方式也有缺点:拆分纹理很麻烦,例如纹理有很多种格式,还分2D、3D、Cubemap,还有Mipmap。性能也有不小的损失,因为拆分、网络传输、合并都需要时间。

前两天看到一篇文章,Unity官方终于出手解决这个问题了:《Unity 2021.1 beta 中的纹理优化,提速最高达 3.1 倍》,见https://mp.weixin.qq.com/s/GEC6zzNVXKySNcU5gHQAcg。采用的方式是应用 SIMD 或线程优化,以及采用最新压缩库或优化压缩库本身来进行纹理压缩。

不过,经常做性能优化的人都有一个常识:针对语言层面的优化一般最多只能优化出几倍的速度(别抬杠,写的太烂的代码优化出来几百倍的速度也正常);而如果选用不同的架构或者算法,性能的提升有可能达到几个数量级以上。

那有没有更好的方法呢?我觉得理论上来说,“分布式异步压缩”是个可以考虑的方案。也就是压缩纹理时不要同步执行,将需要压缩的纹理直接扔出去让联网的工作机器去执行,Unity继续执行其它的切换平台的工作。当然,选择异步的方式是需要一个前提的:没有任何工作的执行需要依赖于纹理的压缩完成,除了打包这种。这样就把2/3的时间都节省了。

是不是感觉有点意思?有的话可以联系我,一起试着玩玩。

猜你喜欢

转载自blog.csdn.net/CrazyEngine/article/details/114238760
今日推荐