UE4:关于Texture Streaming的一部分总结
前言
Leader告诉我可以看下Texture Streaming相关,这里总结一下下
日常费解.jpg
UE4的Texture Streaming
纹理流送系统或流送器是引擎的一部分,负责增大和减小每个纹理的分辨率。该系统使您可以拥有良好的视觉质量, 同时有效地管理可用内存。(来自UE4官方文档机翻)
看完这句话我的脑子里冒出来的问题大概有:
- 为什么要有流送系统?
- 它流送的是啥?
- 流送系统怎么管理内存了?
- 流送系统如何影响画质了?
大概是这样的:
依然是LOD的思想,在绘制远距离物体的时候,最终在屏幕上该物体占据的像素数量极小的情况下,如果依然使用较大的贴图(比如在屏幕上占了3232像素但是用了一个10241024大小的贴图),采样会产生锯齿或者摩尔纹,显示质量是有问题的,而且显然很浪费显存。再考虑到硬件算理有限,不能当场进行卷积来消除这些画面问题,最好的办法就是在这种情况下直接用小点的接近屏幕上显示大小的贴图。
于是多级渐进纹理Mipmap出现了,这样可以在远处的时候直接用小尺寸贴图,解决掉画面的锯齿之类的问题,但是他消耗的显存就更大了,多出来几份Mips占用了更多的显存。
解决了一个问题使显存问题稍微严重了一点,那么再去想办法解决内存的问题,这种过程算是开发的常见情况了……- -
为了解决内存占用过大的问题,UE4这里做了一个纹理流送系统,通过一些计算获取当前最合适的Mips,将其加载,可以节约很多内存
这样基本就解决了两个问题,只不多最后是多带来了一点点的CPU开销罢了……
其余的相关概述可以看官方文档
比较需要注意的是,小于等于Mip7也就是64*64的Mip是会常驻内存的
Stat Streaming
UE4提供了一个关于Streaming的控制台调试命令,可以分析内存使用状况
按照官方的解释,Pool指的是理想情况预计使用的内存,并不是当前实际占用的内存,Mips则是当前已经占用的内存
逐个解释一下图上的Memory Counters参数:
Safety Pool
这个值是在Engine的配置文件中指定的,比如BaseEngine.ini,属于TextureStreaming模块,是一个预留内存,主要是用在AsyncTextureStreaming计算可分配内存
Temporary Pool
这个值可以通过r.Streaming.MaxTempMemoryAllowed控制,PrepareAsyncTask中会使用这个值,在处理所有纹理时让纹理都处于其所需的Mips
Streaming Pool
流送池大小,在移动端是PoolSize - Safety Pool - Temporary Pool - NonStreaming Mips剩余的空间
在PC端是PoolSize给定的大小
其中存放了Visible Mips,Hidden Mips, Forced Mips, Cached Mips
这个值在移动端会因为NonStreaming Mips的增或减少而进行波动
Required Pool
计算出来的理想状态下需要的池子的大小,详细的后面一起说
Visible Mips
可见纹理当前占用的所需内存,不包括Forced Mips
Hidden Mips
非可见纹理,不包括Forced Mips
Forced Mips
强制流入纹理当前占据的所需内存,不包括被明确指定为不可流送的纹理
Cached Mips
不再需要的纹理所占据的内存,只要有空间,不会立即从内存中释放
Wanted Mips
Visible Mips + Hidden Mips + Forced Mips
上面这些值,假设PoolSize给了一个足够的值,Required Pool会等于Wanted Mips,符合理论,预计需要的和实际需要的相同,因为有足够大的空间
并且在这种情况下,会有一部分暂时不再需要的纹理被缓存在Pool里,实际目前占用的内存是Wanted+Cached
如果我们减小PoolSize,向Required Pool的值逼近,Streaming Pool的大小越来越小的时候,Cached Mips就会逐渐从内存里释放,给其他的腾地方
如果继续减小PoolSize,导致Streaming Pool的大小小于Required Pool的大小,会发现画面中的贴图质量随着Pool的变小慢慢下降,为了实际占用的内存不超过我们给的限制,引擎会降低自己对纹理大小的要求,降低1级、2级、3级……
直到给了一个1M的大小,所有的流送的贴图都变成了最小的,没有可以下降的余地了,这个时候Wanted Mips会明显小于Required Pool,但是会大于Streaming Pool(给的可用太小了)
这里懒的去截图了,写得也已经很清楚了,几个参数的关系就如上所说
相关控制台指令
r.Streaming.PoolSize
运行时可以动态修改流送池大小
0 表示不受任何限制
100 设置为100M大小
r.Streaming.FramesForFullUpdate
重新计算Texture需求的Mip等级的时间间隔,单位是帧
r.Streaming.HLODStrategy
0 流送所有Mip
1 仅流送最后一级Mip,其他等级的始终加载到内存里
r.Streaming.DropMips
会立即清理内存
0 不清理任何Mips
1 只会清理Cached Mip
2 会清理掉Cached Mips和Hidden Mips,使用后可以看到Stat上关于这两个所占内存的变化,程序所占用的RAM也会发生变化
r.Streaming.UseAllMips
1 会使用所有的可用Mips,在FStreamingRenderAsset::UpdateDynamicData函数里,会计算LODBias,如果是1则会跳过计算直接为0,没有偏移
r.MobileMaxLoadedMips 和 r.MobileReduceLoadedMips
简称为Max和Reduce
这两个在同一个函数中被调用,
1 |
|
其中Max会被Clamp到1和GMaxTextureMipCount之间(默认值15)
Reduce最低只能是0
函数参数Num,先减去NumReduceMips,最小取到1,然后和Max之间取较大的一个
该函数只有一个地方调用,就是
1 |
|
平台不支持Streaming
NumMips在Texture2D的CreateResource函数中用来检查MipLevel
目前不会还有不支持的吧!= =不必在考虑这个了吧
r.Streaming.MipBias
调试时非常有用的一个指令,在r.Streaming.UsePerTextureBias设置为0的情况下,MipBias会作为全局的Mips向下偏移的参数,比如等于1的时候,最后使用的Mip会是计算出的理想Mip的向下一级,可以用来调试确认合适的Mip
这个指令不仅是贴图,静态模型也会受影响
总结
学习了一下UE4纹理流送相关的知识,debug用的相关参数的意义,有助于排查游戏中的Texture使用和打包相关的问题
可以通过r.Streaming.PoolSize进行调试,结合Stat Streaming信息来寻找游戏比较合适的一个Pool的大小