系统相关
首页 > 系统相关> > 使用Streaming Mipmap后纹理内存没有下降的疑问

使用Streaming Mipmap后纹理内存没有下降的疑问

作者:互联网

1)使用Streaming Mipmap后纹理内存没有下降的疑问
​2)TCP网络传输大端/小端疑问
3)Texture Compression,Default和Override有什么关系
4)如何快速清除Log


 

这是第299篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

Texture

Q:为什么我在项目中使用了Streaming Mipmap但是在GOT报告中看纹理内存没有下降?是没有正确生效还是统计有问题?

A:之前做过相关测试,发现GOT Online是可以统计到被Streaming Mipmap影响的纹理的正确内存的,所以我推测你遇到的情况大概率还是没有正确生效导致的。以下是对如何让一张纹理应用Streaming Mipmap的简单流程总结,其中会注明需要特别注意的一些条件(有些被官方文档收录,有些则文档中没有,但实验证明为必要):

  1. 在Project Settings-Quality中开启Texture Streaming选项。然而实验发现Editor中开启该选项而真机上却会失效的现象,导致所有纹理的Streaming Mipmap设置全部失效。所以为了确保生效,首先应该在代码中调用QualitySettings.streamingMipmapsActive API全局地开启这个选项,才能确保Streaming Mipmap可用。

  2. 调整1中设置的参数。比较重要的参数是Memory Budget参数和Max Level Reduction参数。Memory Budget表示纹理资源的预算,默认值是512MB,但根据UWA的大量项目数据来看,中低端机上一般为200MB左右。它的数值代表的是所有纹理资源的预算——即,它既包括了非流式的纹理、又包括了我们想要采用流式的纹理——但这个“预算”并不代表纹理资源可占用的上限,只是Unity判断对于一个开启Streaming Mipmap的纹理到底采用它的哪些Mipmap通道的参考值,非流式纹理能够轻易突破这个预算。

    Max Level Reduction则是代表着Unity通过流式存储最高能取到哪一级的Mipmap通道,这个参数的优先级比Memory Budget要高,也就会造成实际内存超过预算的情况。(比如该参数为2时,则最多剔除Mipmap0和1通道,即便丢弃以后还远超出预算值也不会进一步剔除。)

    也就是说,如果Memory Budget值设置的远高于项目中纹理实际占用的内存,则Texture Streaming可能完全不起效,所有开启Streaming Mipmap的纹理仍将保留它们的全部Mipmap通道。

  3. 设置开启Streaming Mipmap的纹理。1、2中提到的设置只对开启了Streaming Mipmap的纹理起效,而这只是官方文档的说法,从实际操作来看,Texture Streaming只对同时满足以下三个条件的纹理生效:

    1)开启了Streaming Mipmap且开启了Generate Mipmap的纹理(这一点官方文档中没有提及,事实上开启Generate Mipmap才会生成Mipmap通道供Streaming Mipmap剔除);

    2)被即时加载的纹理(如一开始就已经在场景中被依赖的纹理,即便开启了Streaming Mipmap其内存也不会发生变化,通过AssetBundle加载和Res.Load()加载的纹理则可以),也即官方文档中这句话的实际含义:

    如果是进行Android开发,还需要打开Build Setting,并将Compression Method设置为LZ4或LZ4HC。Unity需要使用其中一种压缩方法进行异步纹理加载,这是纹理串流系统所必需的操作。

    3)Gfx部分内存(这里指的是纹理资源开启Read/Write选项时,复制到CPU端的那一部分内存是不受Streaming Mipmap影响的);

  4. Streaming Mipmap剔除Mipmap通道的规律。它的机制其实和Texture Quality是类似的。我们知道,开启Mipmap的纹理之所以会变成原来的4/3倍,实际上是它各个通道所占用的内存之和。举例而言,一个具有11个Mipmap通道的原大小为1MB的纹理(10241024分辨率、ASTC44格式),其内存占用为1+1/4+1/16+…的11项等比数列之和,即约4/3。等比数列的各项就对应了Mipmap0、Mipmap1、Mipmap2…等各个Mipmap通道。那么,当Max Level Reduction参数设置为2时,其实际意义就是保留Mipmap2和后续所有更小的通道,而剔除Mipmap0和Mipmap1通道,此时的内存大小为4/3MB-1MB-1/4MB=85.33KB。这和我在Profiler或GOT Online中看到的数据基本一致。

     

  5. 关于采用Streaming Mipmap方案的建议。根据上述实验和分析不难看出,Streaming Mipmap是确实具有一些优点的,对于对内存比较敏感,尤其是纹理内存占用很大的项目,采用Streaming Mipmap方案是非常合理和推荐的选项。与此同时,它的实际使用要求对项目中纹理资源的内存占用有相当的了解和规划——相关设置在Quality中,理所当然地应该考虑到不同设备Lod分级时不同的设置。在中低端机中,设置尽量低的Memory Budget和尽量高的Max Level Reduction;在高端机上则恰恰相反,在内存可接受范围内尽量开启最好的画面表现。除此之外,对于哪些纹理要开启Streaming Mipmap,一般是场景中3D物体的纹理,而UI模块采用的纹理则尽量关闭。因为Mipmap的意义主要在于适应纹理在距离镜头远近时的表现需要、避免失真等,而UI完全不需要这些,开启只会白白浪费内存和计算时间。

感谢Faust@UWA问答社区提供了回答


Network

Q:网络协议规定统一使用大端数据传输,但是测试用小端数据发送,然后服务器收到直接返回给客户端,客户端可以正确解析。所以就有个疑问:数据传输不需要大小端转换?还是底层帮忙转了?

测试例子如下:消息头部固定4个字节,值为body的长度。服务器没做任何处理收到消息直接返回。客户端收到消息先解析头部长度4个字节,拿到body长度再获取body具体数据。没有大小端转换但是解析都正确:

{
        // 测试发送整数
        var bodyMsg = "测试发送数据";
        var bodyBytes = Encoding.UTF8.GetBytes(bodyMsg);

        // 发送头部长度,固定4个字节
        int headLen = bodyBytes.Length;
        var headBytes = BitConverter.GetBytes(headLen);

        // 发送头部长度消息
        _sendSocketAsyncEventArgs.SetBuffer(headBytes, 0, 4);
        _socket.SendAsync(_sendSocketAsyncEventArgs);

        // 发送body消息
        _sendSocketAsyncEventArgs.SetBuffer(bodyBytes, 0, bodyBytes.Length);
        _socket.SendAsync(_sendSocketAsyncEventArgs);
}

  

A:测试发送的数据只有在多字节数据处理时才需要考虑字节序,如果是Protobuf,是不用考虑字节序的。

底层不会自动进行大小端转换,需要写代码,所以一般都是项目初期就约定好。

另外,如果把服务器放到不同物理机则测试结果就是错掉的,正确做法是得把发送数据转为大端,比如上面消息头要加上 if (BitConverter.IsLittleEndian) { Array.Reverse(headBytes); } ,然后在收到数据后再转为小端。

感谢萧小俊@UWA问答社区提供了回答


Texture

Q:请问纹理的格式设置项,BuildSettings里面Texture Compression,Default和Override,这几个是什么关系?

 

 

 

A:纹理设置中Override For Android这一项的优先级最高;不勾选Override的情况下是Default中的格式。如果既没有勾选Override,也没有改变Default标签下的格式设置,并且修改了Texture Compression选项,才会采用Texture Compression的格式设置,并且也只对符合上述条件的纹理生效。

感谢宗卉轩@UWA问答社区提供了回答


Script

Q:我们项目开发过程中使用Log还是比较多的,测试的时候发现对性能影响比较大、所以想在下版本中排除掉Log再看看性能数据。有没有比手动注释Log更好的方法?另外Release版本还会有Log吗?

A1:可以自己封装一个Log函数,Log函数内部再代用Debug.Log。可以在Void上加上[Conditional(“DEVELOPMENT_BUILD”),Conditional(“UNITY_EDITOR”)],这样只有Debug版本和Editor才会调用。

感谢萧小俊@UWA问答社区提供了回答

A2:补充一下,较新版的Unity中提供了接口Debug.unityLogger.logEnabled = false,放在打Log的逻辑之前就能避免所有Debug类触发的所有Log了。另外Release版本是无法清除Debug.Log的,还是要尝试用楼上或者API的方式去清除。

感谢Faust@UWA问答社区提供了回答

 

封面图来源于网络


 

今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859(原群已满员)

标签:Log,开启,Mipmap,纹理,Streaming,内存
来源: https://www.cnblogs.com/uwatech/p/16333389.html