UWA问答 | 博客 | 游戏及VR应用性能优化记录分享 | 侑虎科技 - texture
开发微信小程序游戏，有没有类似Debug真机图形的方法
1）开发微信小程序游戏，有没有类似Debug真机图形的方法 2）Unity中如何实现动态实时的车削效果 3）动态创建的Texture，有什么办法可以让他保持ASTC么 4）Unity转微信小游戏的日志问题 这是第416篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：793972859 Rendering Q：开发微信小程序游戏有什么科学的Debug真机图形的方法吗？比如RenderDoc？ 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/677250003d306f3e9d594de3 Rendering Q：请问这种车削的效果怎么实现？ A1：动态生成顶点和索引，比较省事的做法是事先生成很密的圆柱型网格，动态修改下顶点位置。 感谢jump晃悠@UWA问答社区提供了回答 A2：轴向半径尺寸图类似高度图，记录一维度上的R值即可，算很小的量。 感谢偶尔不帅@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/67724d2b3d306f3e9d594de0 Texture Q：动态创建的Texture，有什么办法可以让他保持ASTC么？ 创建是传的纹理格式不起作用么？ _gifTexture = new Texture2D(1, 1, TextureFormat.ASTC_6x6, false); 传了格式，然后加载出来就给转成ARGB32了。 按理说他这API里有格式参数，应该能用才对，不对的话那就是Bug。而且真机测试也是支持ASTC的。 public Texture2D(int width, int height, TextureFormat textureFormat, bool mipChain) :this(width, height, textureFormat, mipChain ?Texture.GenerateAllMips:1,false,IntPtr.Zero,false,false,(string)null) { } A1：自己压缩，CPU或者GPU。你再看看你的设备是否支持你创建的这个格式： SystemInfo.SupportsTextureFormat。 感谢小波@UWA问答社区提供了回答 A2：压缩ASTC极慢，Unity不可能给你动态压缩，要自己转换格式，自己保存文件。可以找找载入Raw纹理的方法，动态载入DDS格式就是用Raw。不是RawImage，是Texture2D.LoadRawTextureData。 感谢jump晃悠@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/67724eb63d306f3e9d594de1 WebGL Q：Unity转微信小游戏有没有类似Bugly之类的日志上报功能？如果要自己做日志上报，是不是要开Full With Stacktrace选项才可以？ 针对以上问题，有经验的朋友欢迎转至社区交流： https://answer.uwa4d.com/question/67724f6b3d306f3e9d594de2 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859
1）Screen.SetResolution和URP的RenderScale有什么区别 2）相同压缩格式相同面积不同数量资源加载速度 3）新增Tag再生成AssetBundle并热更新之后，代码获取到的是undefined 这是第367篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：465082844 Rendering Q：请问Screen.SetResolution和UniversalRenderPipelineAsset.renderScale有什么区别？ 之前低端机都是通过Screen.SetResolution降低渲染分辨率提高性能，发现UniversalRenderPipelineAsset有一个renderScale也可以降低渲染效果。想请问下，两者本质区别是什么？在低端机两者一起降会更好？ A1：参考URP文档： https://docs.unity3d.com/cn/Packages/com.unity.render-pipelines.universal@12.1/manual/universalrp-asset.html RenderScale的调整影响的是ColorAttachment的分辨率，SetResolution影响的是BackBuffer的分辨率，两个一起用会变成相乘的效果。文档里说不影响UI渲染分辨率特指的是Overlay UI，用相机渲染的UI还是会受到RenderScale影响的。 感谢宗卉轩@UWA问答社区提供了回答 A2：RenderScale是针对相机的，这也是URP要做场景分辨率分离的原因。 感谢ZJS@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/657bbfff40a8d93b624afc25 Texture Q：比如1张ASTC的1024*1024和4张512*512，哪种方式加载速度更快，性能最优？ 先说下我的理解：更小的内存意味着内存寻址更快，Miss的更少，而且内存也是分页的，页数更少，同时在GPU的读取过程中，带宽的加载速度以及缓存都会更优一些。 但是我这仍然需要一定的更深的理论或者更权威的证明。 以及是否不同压缩格式，结论可能相反呢？ 针对以上问题，有经验的朋友欢迎转至社区交流分享： https://answer.uwa4d.com/question/6576d6b740a8d93b624afc24 AssetBundle Q：新增Tag名称是：test1， 生成AssetBundle之后进行热更新。 在代码里获取对节点设置的Tag得到的是undefined，我看我新增的test1的Tag是存储在Prefab里面的，为什么生成AssetBundle热更新之后就获取不到了，是不是Unity的Tag是跟随底包一起打进去的。 疑问：是不是读取节点的Tag时会判断这个Tag是不是在底包里Unity的相关设置里是否存在，存在就正确获取到，否则返回undefined，那能不能在不更新APK的情况下，新增一个Tag并热更新下去正常使用获取呢？ A：Tag不能热更，延伸出去就是ProjectSettings下的东西基本都不能热更。所以建议Tag可以在整包更新的时候预留多一些，为后面热更做准备。 感谢junbin.liu@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/658102e340a8d93b624afc64 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：465082844
从Gamma空间改为Linear空间会导致性能下降吗
1）从Gamma空间改为Linear空间会导致性能下降吗 2）如何处理没有使用Unity Ads却收到了GooglePlay平台的警告 3）C#端如何处理xLua在执行DoString时候死循环 4）Texture2DArray相关 这是第350篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：465082844 Rendering Q：Linear性能比Gamma差吗？我们项目从之前使用的是Unity 2020.2.7f1版本，选择的是Gamma。目前升级到2021.3.21f1，切换为Linear空间，Shader和图片都进行过修改，性能也优化过。 但是总体测试下来性能不如之前的Gamma，帧率没有以前高，帧率掉了很多，经过优化，还是不太令人满意，总感觉会卡顿，不知道是不是从Gamma升级到Linear的原因？ A1：应该和换颜色空间没关系。如果没有特殊功能，Gamma和Linear的主要区别就是最后输出到显示器时要不要做颜色的修正，根据你的描述应该和修改Shader有关。 感谢李伟@UWA问答社区提供了回答 A2：在硬件支持的设备上，不应当有明显的性能差异。 在硬件不支持的情况下（比如OpenGL ES 2.0），如果是基于Shader中的模拟转换来实现Linear Space的效果，会有一定的GPU消耗。但是如果感受上是卡顿，而非持续的低帧率，那也不像是这种情况，建议从具体的性能Profile数据入手来进行排查。 这里有一个相关的讨论，搜索performance关键词可以看到一些人做的实验： https://forum.unity.com/threads/gamma-vs-linear-space.613294/ 部分结论引用如下： “I have just done a stress test of Gamma vs Linear color spaces with transparencies and Gamma is indeed a liiiitle faster than Linear, but the difference is so minimal it doesn't even matter. (2 to 3 frames per second more on a stress test that was overdrawing 8 transparent planes one over the other for the full screen space.) On any GPU that handles GLES3 there is no performance reason to use Gamma space over Linear. Gamma space is legacy now and is only useful for legacy GLES2 GPUs (because linear requires GLES3)” 不同的游戏测试结果可能略有差异，但原理上应该是没有可观测的性能差异的，题主可以参考一下。 感谢贾伟昊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/64d2fe8066b5c657c78be152 Script Q：我没有使用Unity Ads，但仍然收到了GooglePlay平台的警告： 您的“xxxxxxx”应用（版本代码：8）包含“com.unity3d.ads:unity-ads”SDK 或您的某个代码库所依赖的SDK，该SDK会收集个人数据或敏感数据，这些数据包括但可能不仅限于Advertising ID，Android ID标识符。根据用户数据政策的相关规定，应用不得将永久性设备标识符与其他个人数据、敏感的用户数据或可重置的设备标识符相关联。 自世界协调时间（UTC）2023年1月11日午夜起，如果新应用版本包含SDK版本且不符合用户数据政策，则可能无法发布。您可以考虑将该SDK升级至不含违规代码的合规版SDK（若有），或者从您的应用内移除该SDK。 根据您的SDK提供方的情况，您可以考虑升级到4.0.1，并/或联系您的SDK提供方，看看是否有合适的更高版本可用。Google无法推荐任何第三方软件，也无法为这类软件提供任何背书。 A1：建议检查下SystemInfo的引用。 感谢Knight-132872@UWA问答社区提供了回答 A2：提供几个思路： 1、确认Package下有没有com.unity.ads包 2、如果使用的是Unity中国版，可以切换为以下版本或同版本国际版 感谢bo@UWA问答社区提供了回答 A3：亲测，用对应的国际版本就行了，解包发现，中国版不论是否导入Ads，打的包里面都会有Ads内容，国际版本不会有这个问题，今天提交谷歌商店验证过了。 解包后在smali\com\unity3d目录下，splash文件夹，首先要在工程里移除Ads，再打包测试，我用2020.3.38f1和2018.4.36f1测试都没问题。 感谢TimWang@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/63a409583bd6691a9dcf2dd8 Script Q：我使用xLua作为游戏的内嵌脚本语言，允许玩家自定义脚本。 现在有个玩家在脚本里面写了一个死循环，当我用DoString去加载这个脚本时候会直接卡死，于是我在外面加入了一个Task： var isSucceed = false; var task = Task.Run(() => { isSucceed = sandbox.DoString(xxxx)//这里简化一下.核心就是加载这个脚本执行DoString }); bool isCompletedSuccessfully = task.Wait(TimeSpan.FromMilliseconds(1500)); //1.5s timeout if (!isCompletedSuccessfully) { CELog.LogError("加载脚本超时"); isSucceed = false; luaEnv.Dispose(); //Kill掉当前的Evn return; //不再加载后续脚本 } 现在是只要调用‘luaEnv.Dispose()’编辑器就直接闪退，无论是这里Timeout了调用，还是等几秒以后。 我感觉是因为Task的Timeout只是在超时后回调，但是此时Lua还在死锁中，所以我无法Dispose。 官方的FAQ提了一句： 调用LuaEnv.Dispose崩溃，很可能是这个Dispose操作是由lua那驱动执行，相当于在lua执行的过程中把lua虚拟机给释放了，改为只由C#执行即可。 我感觉和我这个情况是一样的，应该如何处理？ A：Update一下目前研究的进展： 这是找xLua求教的结果，目前满足我自己的需求： https://github.com/Tencent/xLua/issues/1077 如果以上方法不满足，还搞不定底层，可以尝试以下几个方向： MoonSharp https://www.moonsharp.org/ MoonSharp是纯用C#写的Lua，不过已经不更新了。Discord还有活跃，问的问题都会解答。 https://gist.github.com/xanathar/2c777a79937398834ad4 用Hook的方式可以实现以上的需求。 优点就是纯C#的，相对来说好操作。不过就是性能弱，但是如果和我一样Lua只是用于玩家的Mod。其实还好，因为Lua端的逻辑会很弱。 nLua https://github.com/NLua/NLua/issues/56 nLua的DebugHook是直接被集成好的，xLua的Hook大佬没给暴露出来，要自己搞。 uLua https://assetstore.unity.com/packages/tools/utilities/lua-modding-framework-226043#releases Assetstore上面的资源，近期还在更新。 感谢题主Eran@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/64ce265830070f63751b64f2 Texture Q：从Texture arrays文档（https://docs.unity3d.com/Manual/SL-TextureArrays.html）上来看，GLES3 Metal 已经可以支持了。 最容易想到的就是地形的Splat层，如果是四层Splat，那么就可以用一个Texture2DArray来代替，好处是减少了Bind消耗。其中根据龚大（https://www.zhihu.com/question/56015505）的意思来看，似乎还可以减少Splat采样次数，但根据自己的测试和理解来看并不行，因为Texture2DArray的Slice之间并不会Blending，不知是不是我理解问题。 我使用一个Texture2DArray来代替4层的Splat地形，从xCode上看开销，并没有任何的减小，那目前来看这么做地形的意义就不是很大了。AssetStore上有一个插件叫MegaSplat就是使用Texture2DArray来达到很多层混合。 目前想到的Texture2DArray还有一个可以利用的地方是场景贴图。比如场景用到了多张 1024的贴图，或者多张Lightmap，原来由于贴图不同导致StaticBatching无法合并，现在就可以使用Texture2DArray来做了。 还有比如UI上的Icon之类的，可以合并到一个Texture2DArray来达到DrawCall的合并。 目前Texture2DArray最不好的地方是，只能通过代码来创建，没有编辑器的支持。如果要离线制作Texture2DArray，就需要为不同的平台准备多份资源了。 请问大家有没有Texture2DArray的经验可以分享呢，已经使用或者有计划使用的? A1：研究了一下Texture2DArray，看起来它是将多个2D的Texture组合起来变成一个对象，所以在使用的时候只需要绑定一次，就可以采样多个2D Texture。的确如题主所说，采样的时候还是一次只能采样一个指定的Slice，Blend也需要另外的Shader代码来完成。 题主说了： 其中根据龚大的意思来看，似乎还可以减少splat采样次数，但根据自己的测试和理解来看并不行。 我感觉他的意思可能是：并不是每次采样必须把所有的Slice都采了，而是可以只采其中一部分。估计原本想表达的是一次采样只采一个Slice。 我使用一个Texture2DArray来代替4层的Splat地形，从xcode上看开销，并没有任何的减小，那目前来看这么做地形的意义就不是很大了。 如果单从性能上看，Texture2DArray比Texture2D的确只是减少纹理绑定的开销，然后在游戏引擎中可能会对合批产生影响。其他的做法跟普通Texture2D是一样的。Texture2DArray比Texture3D在LOD处理上不同，Texture3D会减少Slice，这并不是渲染Terrain时想要的。然后Texture2DArray在Filter的时候只会在u，v上做，而Texture3D还会在d上做，所以这部分Texture2DArray也比Texture3D性能更好。综合这些因素可能就是推荐渲染Terrain使用Texture2DArray的原因。题主通过实验说明Texture2DArray渲染地形没有减少开销，也有可能是因为一般一个场景就一个地形，从绑4张纹理变成绑1张就少了几毫秒，而且也不是每帧都绑，所以从整体效果上看表现的不明显。 --- 更新 2018.2.22 做了个简单实验验证了一下，下图分别是用Texture2D和Texture2DArray渲染地形结果和GLES API调用。实验设备为三星s6。 Texture2D： Texture2DArray： 其中，GLES API调用图中红色框表示纹理绑定的API调用，绿色框表示渲染API调用。WT表示了该API调用的耗时，单位为纳秒。从Texture2D图中可知一共有5次纹理绑定，分别对应于材质中的5张纹理绑定，而Texture2DArray图中只有两次调用，分别是Splat纹理和四层混合纹理的绑定。从图中可知一次glBindTexture的耗时大约为1000~10000ns，即最多0.01ms。因此，三次glBindTexture加上三次glActiveTexture也最多0.06ms。所以看不出来。 感谢ZFK@UWA问答社区提供了回答 A2：假如地形需要16张图片，制作上确保同一位置最多受4张图影响，是否可行？若可行是否可以理解为使用Texture2DArray比不使用节省了16-(4+1)=11次Sample，与相应的Bind？（这个4也可能2或者10看需求，+1是因为要分别存索引跟权重，具体要不要+1视实际情况决定。）不使用Array时减少Sample次数也能用if判定但对GPU并行影响更大。 感谢makebalance@foxmail.com@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/5a7e9f79847802258a065084 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：465082844
非2的幂次的ASTC纹理格式尺寸对带宽的影响
1）非2的幂次的ASTC纹理格式尺寸对带宽的影响 2）​C#端如何处理xLua在执行DoString时候死循环 3）如何制定美术规范或者各个模块的指标 4）如何处理Lua的io.open出现中文路径 这是第348篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：465082844 Texture Q：有一张UI纹理尺寸1200x554，纹理格式ASTC 4x4，显示占用内存0.6MB，将MaxSize改为1024，占用内存476KB。 除了内存的降低外，想请问下对带宽或其他性能的影响大么？ 纹理类型是Default，Advanced -> Non-Power of 2 -> ToNearest/... 选项，但是UI没有，UI是否也有必要自己实现ToNearest？ A：题主可以参考一下知乎的这个回答： https://www.zhihu.com/question/376921536 也可以参考： https://gamedev.stackexchange.com/questions/26187/why-are-textures-always-square-powers-of-two-what-if-they-arent 简单来说有如下几个信息： GPU的寻址是按块进行的，ASTC纹理的压缩也是按照块进行压缩的，因此贴图尺寸最好符合这个设定； 不满足的贴图通过补充额外空间来满足对应的需求，这里会有部分的空间浪费。 具体到题主的问题： 将MaxSize设置为1024，其实是减少了贴图尺寸，内存/显存占用变少是肯定的； 贴图尺寸降低，肯定是会减少带宽消耗，也有可能提升显存的采样命中率，包括加载方面因为文件大小变小都会有提升，但这些“好处”未必那么好量化，比较明显的应该是带宽降低。 UI的贴图通常会做合图的操作，因此很多UI自身是不需要做尺寸上的要求的，但是如果不合图的话，整体上还是建议按照power of 2的尺寸在做设计，这种规范比较简洁清晰，容易执行。 感谢贾伟昊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/64c9bb260b33622d467d7c85 Script Q：我使用xLua作为游戏的内嵌脚本语言，允许玩家自定义脚本。 现在有个玩家在脚本里面写了一个死循环，当我用DoString去加载这个脚本时候会直接卡死，于是我在外面加入了一个Task： var isSucceed = false; var task = Task.Run(() => { isSucceed = sandbox.DoString(xxxx)//这里简化一下.核心就是加载这个脚本执行DoString }); bool isCompletedSuccessfully = task.Wait(TimeSpan.FromMilliseconds(1500)); //1.5s timeout if (!isCompletedSuccessfully) { CELog.LogError("加载脚本超时"); isSucceed = false; luaEnv.Dispose(); //Kill掉当前的Evn return; //不再加载后续脚本 } 现在是只要调用‘luaEnv.Dispose()’编辑器就直接闪退，无论是这里Timeout了调用，还是等几秒以后。 我感觉是因为Task的Timeout只是在超时后回调，但是此时Lua还在死锁中，所以我无法Dispose。 官方的FAQ提了一句： 调用LuaEnv.Dispose崩溃。 很可能是这个Dispose操作是由Lua那驱动执行，相当于在Lua执行的过程中把Lua虚拟机给释放了，改为只由C#执行即可。 感觉和我这个情况是一样的，这种情况我应该怎么处理? A：在外部结束Lua虚拟机肯定是有很大崩溃风险的，我觉得有两个思路供参考： 建立一个完整的沙盒环境，类似Dock，这个环境内运行逻辑，外部可以把这个环境销毁掉，不会对外部造成任何影响，这个在Unity下可能难度比较大，要做逻辑和表现的分离，然后构建一个纯逻辑层的运行环境。 在Lua虚拟机内部监测死循环的情况，然后自主中断，抛出Error。这个是我们现在在用的方式，改造Lua虚拟机，监测一定时间内一直在执行某些字节码超过一定阈值就认为自己是在死循环把自己结束掉。当然还有一些别的条件，比如栈深度检测等等。这种自主中断并且抛错的方式其实更加完美。具体实现可以参考这个文章的思路： https://ld246.com/article/1522147783147 个人是比较推荐第二种方案的，也是很多项目验证过的，对于UGC类型的游戏，检测并能提醒用户编辑出来了死循环非常重要。 感谢贾伟昊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/64ce265830070f63751b64f2 Performance Q：在UWA问答上看到了《项目初期如何确定美术规范》这个问题的回答，感觉还是不太理解，好像这个问题是不是只能凭经验，或者通过工具不断地去趋近那个指标，并没有一个指定规范，像是从面数什么之类的来计算？比如，我同屏10人，角色面数5000，渲染模块耗时不超过12ms之类的，有没有一定的公式去量化到美术的一个指标？感觉大部分好像不是根据经验来去做这个样的一个规范，或者是参考竞品之类，UWA给的项目测量好像也没有具体的计算方式，我感觉应该是定一个基准机型，然后再去做项目的各个模块的参考，然后才得出美术或者其他的指标。问下有没有更好的办法，最近看到有根据能耗来计算然后得到各个模块的指标，有大佬有了解过这方面的知识吗？ A：具体的信息在题主引用的问答里面已经有很多的讨论了，从我个人的经验来看，还没有什么更好的办法，主要原因在于性能结果受到的各个方面的影响因素特别多。 比如，同样机型上跑40w面的画面，Shader的复杂度差异就可能让一个GPU满负载而另外一个还有很大的空余量；再举一个例子，同样的一个画面，如果你的CPU很闲，也许你就可以用一些OC算法大大降低最终画面渲染的消耗，当然，GPU有富裕也可以用GPU-Driven这样的思路来做更加高效的剔除。 因此，不同的游戏类型，不同的人来做，都可能达到完全不同的性能结果。我们定义一个机型，给定一个经验值或者参考竞品的美术资产规范，往往是保证在使用相同引擎相似技术的情况下能够保证性能的底线。这个基于经验或者参考设定的规范和指标最大的意义在于告诉你可行性，如果最终游戏的性能没有达成到预期帧率，你可以参考其他游戏或者常规的优化方式来尽量接近可行结果，当然，你也可以通过修改引擎或者渲染管线，抑或其他Trick的方式来超越这个规范，这就取决于团队能力了。除此之外，规范也是要不断根据项目的实际情况进行调整的，而非完全的一成不变。 因此想根据一个公式，可以直接量化出一个规范，这个想法可能有些过于理想，这个公式里要考虑的因素太多，每个因素之间又有互相的影响，最终我们做性能优化的时候，也常常是在这些因素之前进行平衡——比如GPU Bound了就看看是否可以拿CPU换一些计算量出来，CPU Bound了是否可以用内存来换？ 至于基于能耗来计算指标，我们往往是反向的，就是当一个优化做好之后，或者上了一个新Feature之后，我们会通过控制其他变量，单独开关这个Feature来查看它带来的能耗变化（我们的统计方式是基于电流表的，理论上比接口采集的数据更加符合实际情况）。而且在这个过程中能耗是波动的，不是稳定的，所以基本无法测量出增加一个面有多少能耗的增加，增加一个DrawCall有多少能耗的增加，更何况影响能耗最多的往往是带宽。 所以，整体上基于已有的经验来看，非常准确地量化消耗或者能耗，是一件很难的事情，不同设备可能会有不同，不同项目也可能会有不同，我个人是持悲观的观点。在实际工作中，主要基于经验和参考来制定一个大致的规范，然后在正式的资源与功能不断完成之后去迭代和完善这个规范。 如果题主探索出了可量化的路径，也欢迎分享~~ 感谢贾伟昊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/64b11f5d9e99796cd233a4db Script Q：关于Lua的io.open出现中文路径，有办法处理吗？ 如以下的代码，不同版本Lua有不一样的结果，我的Lua 5.1会出现Invalid Argument，用5.4创建了一个乱码文件，因为有时候避免不了中文目录，像Windows平台下有人用了中文用户名，就会出现“C:/User/中文用户/AppData”之类的路径。 local path = "C:\\新建文件夹\\test.txt" local file, error_msg = io.open(path, "w") if file then file:write("这是一个示例文本文件。\n") file:close() print("文件创建成功。") else print("无法创建文件。", error_msg) end A：网上搜到一篇文章，仅供参考： https://juejin.cn/s/lua%20io.open%20%E4%B8%AD%E6%96%87%E8%B7%AF%E5%BE%84 另外就是如果文件内容并不是一定要在Lua中处理的，也可以让C#去做对应的文件操作。 感谢贾伟昊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/646dda9d279f4105e1cb5432 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：465082844
1）SRP Batcher在真机上失效 ​2）Shader里面对同一张纹理多次采样会影响效率吗 3）为什么纹理开启了mipmap后，纹理内存反而下降了 4）TMP为什么有多次Delegate.Combine()的GC 这是第336篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：community.uwa4d.com UWA QQ群：465082844 Rendering Q：Shader是Compatible的，在编辑器里面DrawCall也是SRP Batcher合批的，但是在真机上却没有合批成功，是什么原因造成的？ A：需要排查对应的Shader是否存在在Constant Buffer中却不在Properties中的Uniform变量，这种状况会导致在OpenGLES的真机上SRP Batcher失效，但是这种状况在编辑器里面是SRP Batcher合批且Shader显示是Compatible的。 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6461dca1742de67bf974f2d9 Shader Q：Shader里面对同一张纹理做多次采样会影响效率吗？ float2 flowVector = tex2D(FlowMap, IN.uvMainTex).rg * 2 - 1; float noise = tex2D(FlowMap, IN.uvMainTex).a; 如上代码，有资料简单提了一句说Shader代码编译的时候会自动优化到一次采样里面去，不过并没提是SurfaceShader才有的编译优化还是所有的Shader的编译优化都会这样？ A：测试结果来看，只要是uv相同的贴图采样，surface和unlit shader编译之后都会优化掉多余的采样次数。 感谢范世青@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/645b523cb068ba13373fb56e Texture Q：为什么纹理开启了mipmap后，纹理内存反而下降了？ A：很可能是Quality Settings里面的Texture Quality选择的不是Full Res导致的，比如选择的是Half Res，这样对于开启了mipmap的纹理来说，第0层的mipmap层级就不会加载进内存，所以纹理内存反而表小了。 经过测试，1024x1024 ETC2_8Bits格式的纹理，没开启mipmap是1MB，开启了变成333KB，512x512 ETC2_8Bits格式的纹理，开启mipmap前后分别是256KB和128KB。 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6461dace742de67bf974f2d8 Lua Q：只有一个界面对一个TMP的OnPreRenderText进行了一次+=的操作，为什么在其他界面也发现有Delegate.Combine()的GC，且每次大小不同？ 这是唯一操作过的界面： 这是其他界面，有2M的，还有些几十K的。 A：Unity TextMeshPro (TMP) 组件的OnPreRenderText事件是一个委托类型，可以通过 += 运算符来订阅事件，但每次订阅事件时都会创建一个新的委托实例。如果需要多次订阅事件，需要使用Delegate.Combine() 方法将多个委托实例合并为一个。但是，每次使用Delegate.Combine() 方法都会创建一个新的委托实例，这可能会导致频繁的垃圾回收。 根据你的描述，只有一个界面对一个TMP的OnPreRenderText 进行了一次 += 的操作，但在其他界面也发现了Delegate.Combine() 的垃圾回收。这可能是由于 TMP 组件在内部订阅了OnPreRenderText事件，并且每次订阅事件时都会使用Delegate.Combine() 方法。此外，可能还存在其他脚本或组件订阅了该事件，导致创建了额外的委托实例。 每次Delegate.Combine() 方法创建的委托实例的大小可能不同，这取决于合并的委托实例的数量和每个委托实例的大小。因此，每次Delegate.Combine() 方法调用时，垃圾回收器可能会回收不同大小的垃圾。为了减少垃圾回收的频率和影响，你可以尽可能减少使用 Delegate.Combine() 方法的次数，并确保及时取消订阅不再需要的事件。 感谢轩辕小羽@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6423ff146778221ae30955ca 封面图来源于网络 今天的分享就到这里。生有涯而知无涯，在漫漫的开发周期中，我们遇到的问题只是冰山一角，UWA社区愿伴你同行，一起探索分享。欢迎更多的开发者加入UWA社区。 UWA官网：www.uwa4d.com UWA社区：community.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：465082844
1）纹理开启Mipmap导致压缩失败的问题 ​2）使用TextMeshPro能否避免使用Resources文件夹 3）用透视摄像机去分层管理UI的优势和好处 4）Houdini中切线空间向量怎么在Unity Shader还原出原向量 这是第325篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：answer.uwa4d.com UWA QQ群：465082844 Texture Q：看到一些优化建议中说，ASTC压缩格式在开启Mipmap之后需要保证纹理尺寸为2的幂次方。 可是Mipmap的生成不受纹理尺寸的影响（非2的幂次方会有性能开销，但也可以生成Mipmap），ASTC压缩也是不受尺寸大小限制的。 但为什么非2的幂次方纹理开启Mipmap采用ASTC压缩格式会压缩失败？中间是有什么冲突吗？ 针对以上问题，有经验的朋友欢迎转至社区交流分享： https://answer.uwa4d.com/question/63ef3af206385405990c2e7b TextMeshPro Q：使用TextMeshPro时，自带有Resources文件夹里面会放字体资源和TMP_setting，最后打测试包发现占据的内存还是比较大，而且我只用了一个字体。 有无办法不使用Resources？或者减少Resource内存大小呢？ 参考：https://blog.csdn.net/akof1314/article/details/87985603 A：如果你对修改源码没有意见，建议修改源码。 把所有Resource.Load调用，代理成自定义方法（大概3处左右）。 感谢1 9 7 3-311135@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/63f0798606385405990d43e6 UI Q：用透视摄像机去分层管理UI，是有什么优势和好处吗？能联想到的就是透视摄像机可以调节大小、位置、旋转、方便做3D效果，还是说还有一些别的优势？ 针对以上问题，有经验的朋友欢迎转至社区交流分享： https://answer.uwa4d.com/question/63ecba39063854059909f1e8 Rendering Q：目前有一个需求， 在Houdini中，将法线从模型空间转换到切线空间后，保存到顶点颜色中，在Unity Shader通过读取顶点颜色将法线从切线空间转换到模型空间。 Houdini中：生成切线选择MikkT算法，和Unity保持一致： vector T = normalize(v@tangentu); vector B = normalize(v@tangentv) ; vector N = normalize(@N); matrix m = set(@T, @B, @N); @Cd = m * @Cd; @Cd = @Cd * {-0.5，0.5，0.5} + 0.5; //Houdini是右手坐标系，加负号转左手坐标系 在Unity Shader中： float3 bitangent = cross(normalize(v.normal) , normalize(v.tangent.xyz)) * v.tangent.w; float3x3 TtoO = float3x3(v.tangent.x, bitangent.x, v.normal.x, v.tangent.y, bitangent.y, v.normal.y, v.tangent.z, bitangent.z, v.normal.z); float3 normalOS = v.color.xyz; normalOS = normalOS * 2 -1; normalOS = mul(TtoO, normalOS) ; 但是这样转换出的法线是不对的。 然后做了如下测试：在Houdini中，直接将法线写入顶点颜色通道： @Cd = @N; @Cd = @Cd * 0.5 + 0.5; @Cd = normalize(@Cd); @N = @Cd; Unity中，通过RenderDoc截帧： 向法线和顶点写入同样的数值，Unity中得到的向量顺序不一样，数值可能小数也有偏差。 哪位大佬有Unity源码的代码能不能看下Unity在导入FBX时做了哪些计算，怎么能构建出Houdini中的TBN逆矩阵来还原出houdini写入的切线空间向量？ 针对以上问题，有经验的朋友欢迎转至社区交流分享： https://answer.uwa4d.com/question/63ed923206385405990a9bcb 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：465082844
1）在Runtime下，IL2CPP与Mono打包对应的PSS内存占用问题 ​2）获得AssetBundle内部依赖关系的方法 3）Unity 2019 Streaming Mipmap在某些情况下采样等级错误 4）根据RenderDoc的数据，计算渲染量 这是第322篇UWA技术知识分享的推送，精选了UWA社区的热门话题，涵盖了UWA问答、社区帖子等技术知识点，助力大家更全面地掌握和学习。 UWA社区主页：answer.uwa4d.com UWA QQ群：465082844 Memory Q：IL2CPP打包比Mono打包在Runtime下跑同样的案例PSS内存占用更高一些，请问是合理的吗？ A：推测可能是IL2CPP包是64位的，而Mono包是32位的，天然内存占用就会高一些，另外IL2CPP在打包的时候，会把各种泛型的代码给展开，所以对应的代码so加载进内存，内存占用也会高一些。 下面是同样的测试案例，两种打包方式的PSS占用截图。 Mono包： IL2CPP包： 从上图中可以看到，IL2CPP包比Mono包内存占用更多的体现在Code、Native Heap和Private Other三个类别上。 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/63d774d60638540599f6bdb8 AssetBundle Q：获得AssetBundle内部依赖关系的方法。注：Manifest的依赖关系不准确。 之前发现从Manifest里面获得的依赖关系在很多情况下都不准确。 比较明显的是，当Prefab嵌套时，Manifest数据会显示Prefab依赖被嵌套的Prefab；而SpriteAtlas和Sprite依赖关系是反的。其他还有很多情况会额外依赖不需要的资源。用WebExtract解开AssetBundle本身，里面的数据是正确的依赖数据。 因此，现在想找到一个方法，脱离Manifest，自行建立依赖关系表。 现在的问题在于，如果对每个输出的AssetBundle执行解包和正则分析，会花费大量时间，并且本身文本匹配也有风险。 有没有什么干净的流程，可以获得AssetBundle内部记录的依赖关系？ A：用UnityEditor.Build.Content.ContentBuildInterface下面的方法获取到的依赖信息是准确的。如下面两个函数： ContentBuildInterface.GetPlayerDependenciesForObjects ContentBuildInterface.CalculatePlayerDependenciesForScene 如果用SBP，Manifest可以在编译完成后加个Task读m_DependencyData，自己建立一个。或者直接用ContentBuildInterface预构造的也可以满足了。 感谢题主欧月松@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/632a6dfae73cb53fa4d725bc Texture Q：游戏使用了Mipmap技术，同时为了节省内存开启了流式加载Streaming Mipmap： 但是在做热更模型的时候，发现某些贴图的Mipmap会采样一个很低的值（很糊），然后即便调整摄像机远近也没有变化（不能动态采样了）。 正常打包出来的资源没有问题（贴图在一个独立Bundle里），但热更打出了的资源就有问题（模型、材质、贴图等都在一个Bundle里），而且贴图占用内存未达到Budget上限。 不知道问题出在哪了，该如何解决？ A：根据描述分析，有可能是加载了贴图之后把Bundle卸载了。把贴图放在单独的Bundle里面的时候，可能这个Bundle没有被卸载；但在热更的时候，贴图和模型、材质等放在同一个Bundle，可能用代码加载这个Bundle之后又卸载了，就会出现这个问题。 Streaming系统刚开始加载的时候会加载分辨率较低的一层Mipmap，之后根据距离动态调整，如果此时已经把贴图所在的Bundle卸载了，就无法根据摄像机距离调整采样的Mipmap层级。 感谢龙粲@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/63c78cba0638540599e9a17a Rendering Q：最近在分析几款游戏的特效。已经用RenderDoc抓了几帧。也找到了渲染粒子特效的Pass。 现在问题，能否从顶点Buffer中分析出粒子的数量？或者有无别的方法估算出粒子的数量？ A：如下图，如果是四边形的粒子，比如billboard，66/3=22个三角形，22/2=11个粒子。 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/63d4d6ba0638540599f45b60 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：465082844
1）Unity性能优化分析思路 ​2）资源打包关系依赖树 3）SpriteAtlas 中Include in Build的作用 4）使用Streaming Mipmap后纹理内存没有下降的疑问 5）URP Renderer Feature实现二次元描边，Cutout的处理问题 UWA每周推送的知识型栏目《厚积薄发 | 技术分享》已经伴随大家走过了330个工作周。精选了2022年上半年的精彩问答分享给大家，期待UWA问答继续有您的陪伴。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Unity Q：Unity有多少优化点？比如合批：静态合批、SRP合批、GPU实例化、UGUI Reruild、光照烘焙、反射探针、光照探针、Shader.Parse、Shader.CreateGPUProgram、场景加载优化和GC优化，还有哪些优化点？ A1：大方向上可以从CPU、内存、GPU这三个方向切入。 细分一下可以从CPU、内存、渲染、资源优化、耗电优化、网络优化、卡顿优化、优化工具的选择掌握这几个点入手。 一、CPU优化 缓存计算结果 预处理 限帧法 主次法 多线程 引擎模块（动画、物理、粒子、导航） 逻辑优化 二、内存优化 缓存法 内存池 资源管理器 控制GC 逻辑优化 Shader变体数量优化 三、渲染优化 SetPassCall渲染状态切换频次控制 DrawCall数量控制 带宽负载 显存占用 GPU计算量 四、卡顿优化 降帧法 摊帧法 限制数量法 逻辑优化 IO优化 使用进度条 五、资源优化 纹理优化 UI优化 字体优化 模型优化 场景优化 粒子优化 材质优化 指定标准美术规范 Shader变体数量优化 六、耗电优化 上面说到的优化点，或多或少都会影响到手机的耗电，也是优化耗电的措施，除此之外还有： 动态调整限帧 动态调整画质 七、网络优化 减少无用字段 降低字段精度 避免重复发送 网络异步化 压缩无效字节 压缩协议包 以上说的这些要点，大部分摘抄归纳自《移动游戏性能优化通用技法》。强烈建议多花些时间认真阅读一下这篇文章，然后以这篇文章作为指南，再去仔细研究里面提到的优化细节该如何展开。 感谢马三小伙儿@UWA问答社区提供了回答 A2：优化点肯定是无穷无尽的，这里搬运UWA的客户端性能优化思路，针对常见的引擎模块的相关问题都做了分析，讲的是比较全比较透的，常见的优化难题都罗列了。 《Unity性能优化 — 物理模块》 《Unity性能优化 — 动画模块》 《Unity性能优化系列 — 资源内存泄漏》 《Unity性能优化系列—Lua代码优化》 《粒子系统优化——如何优化你的技能特效》 《Unity性能优化系列—加载与资源管理》 《Unity性能优化系列—渲染模块》 《Unity性能优化 — UI模块》 《支持资源加载分析、场景分割》 《UWA报告使用小技巧，你get了吗？》 《UWA本地资源检测更新，助你严守项目性能的每个角落！》 感谢芭妮妮@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61a0a56ed8413e18eb297ac3 AssetBundle Q：想做包体资源分析，大家有什么好的树显示工具或者思路推荐吗？有比较好的开源方案也可以。最简单就像N叉树一样，比如root一个文件名，然后展开整个树结构。 A1：我自己做了一个，供参考。都是用Unity自己的IMGUI最基本的接口去实现。 EditorWindows GUI.Box GUI.BeginGroup GUI.Label Handles.DrawBezier Handles.DrawWireDisc TreeView 基本上，组织好各个AssetBundle的依赖关系其实是很好呈现的。 感谢黄程@UWA问答社区提供了回答 A2：推荐一款比较好用的插件，不止有依赖树，还有其他打包的资源数据可供分析： https://assetstore.unity.com/packages/tools/utilities/build-report-tool-8162 感谢郑骁@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/623c8322a43e6117d040ae9b AssetBundle Q：SpriteAtlas中Include in Build的作用是什么？ A：专门做了一些测试，具体如下： 以下表达中Sprite对应的是Sprite类型的对象，Texture2D对应的是Texture2D的对象，这和Sprite对象是完全不同的东西，sactx表示生成的图集纹理。 测试情况包括2个变量： SpriteAtlas对象是否主动打包AssetBundle SpriteAtlas对象上是否勾选Include in Build 第一种情况，SpriteAtlas打包AssetBundle： 那这里要考虑的是SpriteAtlas引用的Sprite是否会单独打包，如果这些小Sprite不主动打包，是会被动进这个SpriteAtlas的AssetBundle里面的，如果其他的UI Prefab中，比如有个Image使用了一个小Sprite，那么这个小Sprite就冗余了。 这里勾不勾选Include in Build的区别在于：加载Image的时候，这个Image会不会自动显示，勾选了Include in Build，会自动显示图片，不勾选，则需要脚本添加回调来主动加载SpriteAtlas，并callback（spriteatlas）。 第二种情况，SpriteAtlas不加入AssetBundle打包： 不勾选Include in Build 假设小的Sprite打包AssetBundle，在这个AssetBundle里面不会有sactx，这个sactx的Texture2D的纹理变成“消失”的状态，没有任何东西可以引用到这个sactx纹理，而且由于在工程里面有SpriteAtlas的存在，所以在小的Sprite的AssetBundle里面也不能让其本身对应的小的Texture2D纹理进AssetBundle包，所以图像就永远显示不出来了。 勾选Include in Build 所有的小的Sprite所在的AssetBundle里面都会被动包含sactx的图，且会包含所有没有主动打包的小的Sprite。 如Sprite1和Sprite2是SpriteAtlas里面的两个小的Sprite。Sprite1主动打包，Sprite2不主动打包，那么Sprite1的AssetBundle里面是会有Sprite1和Sprite2以及sactx纹理。 总结： 如果有Sprite加入了某个SpriteAtlas，那么任何真正使用到这个Sprite的资源都不会有对Sprite对应的小的Texture2D纹理的引用，而是对sactx图集纹理的引用。 如果SpriteAtlas不打包，必须勾选Include in Build，否则sactx纹理就“消失”了，在勾选Include in Build的前提下，而且SpriteAtlas中的所有小的sprite必须打包到同一个AssetBundle里面，否则sactx会冗余。 如果SpriteAtlas打包了AssetBundle，sactx永远不会冗余了（这里的冗余是指打包AssetBundle造成的冗余）。SpriteAtlas里面的小的Sprite也最好打包AssetBundle，不然这些小的Sprite就会冗余。勾选或者不勾选Include in Build都不影响各种依赖关系，唯一的区别是是否会主动显示图片，勾选了就会主动显示图片，不勾选就需要脚本控制来显示图片。 感谢Xuan@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/61dbee5cd8413e18eb7bcc3e Texture Q：为什么我在项目中使用了Streaming Mipmap但是在GOT报告中看纹理内存没有下降？是没有正确生效还是统计有问题？ A：之前做过相关测试，发现GOT Online是可以统计到被Streaming Mipmap影响的纹理的正确内存的，所以我推测你遇到的情况大概率还是没有正确生效导致的。以下是对如何让一张纹理应用Streaming Mipmap的简单流程总结，其中会注明需要特别注意的一些条件（有些被官方文档收录，有些则文档中没有，但实验证明为必要）： 在Project Settings-Quality中开启Texture Streaming选项。然而实验发现Editor中开启该选项而真机上却会失效的现象，导致所有纹理的Streaming Mipmap设置全部失效。所以为了确保生效，首先应该在代码中调用QualitySettings.streamingMipmapsActive API全局地开启这个选项，才能确保Streaming Mipmap可用。 调整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通道。 设置开启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影响的）； 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中看到的数据基本一致。 关于采用Streaming Mipmap方案的建议。根据上述实验和分析不难看出，Streaming Mipmap是确实具有一些优点的，对于对内存比较敏感，尤其是纹理内存占用很大的项目，采用Streaming Mipmap方案是非常合理和推荐的选项。与此同时，它的实际使用要求对项目中纹理资源的内存占用有相当的了解和规划——相关设置在Quality中，理所当然地应该考虑到不同设备Lod分级时不同的设置。在中低端机中，设置尽量低的Memory Budget和尽量高的Max Level Reduction；在高端机上则恰恰相反，在内存可接受范围内尽量开启最好的画面表现。除此之外，对于哪些纹理要开启Streaming Mipmap，一般是场景中3D物体的纹理，而UI模块采用的纹理则尽量关闭。因为Mipmap的意义主要在于适应纹理在距离镜头远近时的表现需要、避免失真等，而UI完全不需要这些，开启只会白白浪费内存和计算时间。 感谢Faust@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62943f7fb87a45735168fe5f Rendering Q：之前我们的卡通渲染是在Shader里写多个Pass来绘制的描边，最近尝试用SRP Batcher优化时发现，SRP Batcher不支持多个Pass的Shader。于是我尝试用URP Renderer Feature来渲染所有角色的描边。 实现起来很简单，但有一个问题解决不了，就是Cutout的问题。 裙子的下边缘是用贴图的Alpha控制的，并不是真正的顶点。以前的Pass写在角色渲染的Shader里，可以用贴图来控制，但用Renderer Feature来处理后，所有角色模型的描边是用的同一个材质，不能再用模型各自的贴图的通道来处理了。显示效果就是Cutout的描边部分无法处理： 边缘比较复杂，但是控制的顶点就只有几个，感觉不太好实现。 现在模型顶点的颜色信息我已经用过了，RGB是描边颜色，A是描边粗细。我能想到的办法是用A的一些特殊值来特殊处理一下某些顶点（相当于Clip掉一些顶点，但肯定没有相关API），但又感觉似乎不太可行。不知道大家有没有遇到过，或者有没有什么好办法呢？ 自己尝试了用顶点信息标记点，但有瑕疵，点关联的边会受影响，描边没了： 让美术加了一些点，基本也能解决（其实外边缘还是不会显示全，但已经看不太出来了）： 但这方法还是不好。最好在Renderer Feature里可以获取到正在渲染的模型的材质信息。 是否可以把一个多Pass的Shader里的某个Pass弄到单独的Renderer Feature里画，而不是重新画一遍？比如画第一遍的时候禁用这个Pass，Renderer Feature画的时候再启用它？ A1：自定义一个LightMode，Render Feature里设置这个LightMode，这样材质球上的贴图数据什么就能有。 补充个截图大概是这样的： 对于自定义的LightMode，Unity默认忽略，只有Render Feature里手动指定了要画这个LightMode，Unity才会去绘制。 感谢jim@UWA问答社区提供了回答 A2：如下，这样SRP Batcher终于可以合批多Pass的Shader了： 不用禁用正常的渲染。 两个Pass，一个正常的Pass，一个自定义LightMode的Pass，lightMode的Pass渲染描边用ScriptableRendererFeature手动指定渲染自定义的LightMode。Unity不会去渲染你自定义的LightMode。 感谢题主仇磊@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62036e2834a9f84e39f8bd51 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
1）Android平台压缩纹理ETC2 VS ASTC ​2）Unity使用Profiler和UWA内存差异巨大 3）Unity 2020 IL2CPP打包异常 4）TouchScreenKeyboardWrap在PC模式下打包报错 这是第301篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题，建议阅读时间10分钟，认真读完必有收获。 UWA 问答社区：answer.uwa4d.com UWA QQ群2：793972859（原群已满员） Texture Q：1.2022年在Android平台的ASTC支持度怎么样了，预计2023年上的游戏能不能上ASTC？ 1.1 跟iOS统一方便处理 1.2 更多选择控制压缩比例 44->1212 1.3 效果比ETC2更好 1.4 ASTC有没有什么坑，看到有说华为部分机器不支持，会黑屏 网上只找到下面这个数据，2020年9月，ASTC 77%。 https://developer.android.google.cn/guide/app-bundle/asset-delivery/texture-compression?hl=zh-cn 2.我理解的压缩纹理是CPU不用解压，直接传输给GPU，直接GPU硬件采样。相对于采样ARGB32格式，采样压缩纹理的消耗是不是更高点，还是可以忽略不计？ 3.移动平台纹理是否是2份？CPU一份，GPU显存一份，还是共享同一份？ 4.ASTC/ETC2的压缩纹理尺寸是否需要2的幂？网上有些说法是非2的幂纹理加载到显存的时候会进行转换成2的幂，转换过程耗时，会导致卡顿。 https://blog.csdn.net/linxinfa/article/details/108827197 我做过以下测试： 1.AssetBundle内的纹理尺寸是原始尺寸。 2.Mumu模拟器+GPA纹理尺寸是原始尺寸，格式转换成了ARGB32。 3.Adreno+高通真机，目前还在测试，找的机器连不上Adreno Profiler。 A1：我2017年立项的时候跟你一样处理过这个问题，我当时的策略是主包使用ASTC，启动更新的时候判断玩家是否支持ETC2，不支持的话下载ETC2版本的AssetBundle资源。资源在线上是完全独立的2份资源（打包的时候每次打包2份独立资源）。当年是为了海外做的这个兼容。经过这几年各个大厂的洗礼，直接ASTC就行了。如果你们的游戏类型一定要下探到那么老的机器，就像我那种双保险的做法。 1.黑屏的问题没遇到。ETC2反而因为压缩算法不好，部分颜色失真，导致你在做部分PBR效果的时候，会有问题。 2.需要硬件支持，就是因为不能用CPU去处理这种软解压跟软压缩，可以忽略。 3.移动平台GPU跟CPU是共享一个内存，但是GPU会独立开辟一份内存，所以会有2份。开启了Mipmap的情况下不是单纯的2份相同内存，所以还有Texture Streaming这种方式来减少贴图内存占用。 4.ASTC不需要。 至于你的测试： Mumu模拟器在《楚留香》出来的时候就支持ASTC，后面吃鸡游戏风靡的时候更是所有模拟器都支持了，不支持的都要被淘汰。这块可以不用考虑。如果不是单独为了发行模拟器渠道，大部分情况下，发行公司都是要求禁用模拟器登录的。 感谢简单就好@UWA问答社区提供了回答 A2：1.关于ASTC的支持度，题主查的数据本身可以作为参考，但还需要结合考虑项目发行的市场。比如实际上对于中国市场而言ASTC的覆盖率已经非常高，而对于东南亚南美等海外市场则还要酌情； 2.常见的压缩纹理是直接降低纹理资源质量，以质量换性能的行为，它不会在GPU端重新解压缩，相反在内存、带宽等方面都有所优化； 3.只有当纹理资源需要运行时修改、开启了Read/Write Enable选项时，才会向CPU复制一份，否则就只有GPU的一份； 4.ASTC对分辨率没有需求；ETC2要求分辨率为4的倍数，而非2的次幂；特别地，不论是ASTC还是ETC2，如果开启了Mipmap，则必须是2的次幂，否则同样会压缩失败。 感谢Faust@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62a15f62b87a45735173440e Memory Q：Unity使用Profiler和UWA内存差异为何差别这么大？ A：Profiler显示的是引擎真实的分配内存，但这个内存在OS中并不仅仅是这些，它会分配更多的内存Page，而且不同的OS版本，不同的厂商分配的都不一样。 同时，OS层还会启动很多自身的Lib，比如渲染相关的库、文件加载的加速库等等，这些内存的分配，都是Unity统计不到的。 除此之外，还有虚拟机的第三方库，比如Lua等，这些的内存是会被统计到PSS中，但不会被统计到Profiler中，游戏启动时的各种渠道库，也是如此。 所以，PSS比Reserved大是正常的。 但如果你发现PSS比Reserved大了快2倍了，那一定是大家某些库的内存分配过大，比如Lua、Wwise或其他大家使用的第三方库。 感谢芭妮妮@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62a064d0b87a457351727eee Android Q：Unity版本2020.2.3，在IL2CPP打包Android的编译过程报错，因为项目中用的Puerts框架，需要生成很多Wrap类。如果打包时勾选Split Application Binary，也用OBB分包情况下，使用Unity可以直接导出到APK和OBB。但如果不勾选，编译会报错。 同样不勾选Split Application Binary，在Build时，Export Project导出到AndroidStudio工程再生成APK也不会有问题。请问有遇到过这样的问题吗？ Exception: Unity.IL2CPP.Building.BuilderFailedException: D:\DevEnvironment\android-ndk-r19\toolchains\llvm\prebuilt\windows-x86_64\bin\clang++ @“C:\Users\Administrator\AppData\Local\Temp\tmp30FE.tmp” -o “D:\workroot\Crusher\Crusher\Library\il2cpp_android_arm64-v8a\il2cpp_cache\linkresult_B322E1396ADEEC5519CBECC8D02E669C\libil2cpp.so” -shared -Wl,-soname,libil2cpp.so -Wl,–no-undefined -Wl,-z,noexecstack -Wl,–gc-sections -Wl,–build-id -stdlib=libc++ -static-libstdc++ -target aarch64-linux-android21 -Wl,–wrap,sigaction “D:\Softwares\Unity2020.2.3f1\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Development\StaticLibs\arm64-v8a\baselib.a” -llog -rdynamic -fuse-ld=bfd.exe 报错详情可戳原问答查看。 A1：尝试增加JVM的内存，或者指定一个NDK版本。 感谢廖武兴@UWA问答社区提供了回答 A2：推测可能是静态数组太大了，编译器的内存模型不够。可以自己添加IL2CPP的编译选项，注意下这个： additional relocation overflows omitted from the output 感谢thrt520@UWA问答社区提供了回答 A3：看上去是NDK那边失败了，可以尝试一下不要勾选Development Build，可能是生成的二进制文件太大导致的奔溃。 感谢萧小俊@UWA问答社区提供了回答 A4：亲测Maximun JVM heap size，MBytes增加JVM内存有效： 感谢题主Bomber@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/60892a236bb31032f9791493 Script Q：在Android下可以，切换到PC打包的时候就报错了。请问这是什么意思？如何解决呢？ A：有文档介绍说：Only native iPhone, Android, and Windows Store Apps are supported. 所以加个宏处理一下就可以了。 https://docs.unity3d.com/ScriptReference/TouchScreenKeyboard.html 感谢张迪@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62903ab6b87a45735165da5f 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）
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的简单流程总结，其中会注明需要特别注意的一些条件（有些被官方文档收录，有些则文档中没有，但实验证明为必要）： 在Project Settings-Quality中开启Texture Streaming选项。然而实验发现Editor中开启该选项而真机上却会失效的现象，导致所有纹理的Streaming Mipmap设置全部失效。所以为了确保生效，首先应该在代码中调用QualitySettings.streamingMipmapsActive API全局地开启这个选项，才能确保Streaming Mipmap可用。 调整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通道。 设置开启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影响的）； 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中看到的数据基本一致。 关于采用Streaming Mipmap方案的建议。根据上述实验和分析不难看出，Streaming Mipmap是确实具有一些优点的，对于对内存比较敏感，尤其是纹理内存占用很大的项目，采用Streaming Mipmap方案是非常合理和推荐的选项。与此同时，它的实际使用要求对项目中纹理资源的内存占用有相当的了解和规划——相关设置在Quality中，理所当然地应该考虑到不同设备Lod分级时不同的设置。在中低端机中，设置尽量低的Memory Budget和尽量高的Max Level Reduction；在高端机上则恰恰相反，在内存可接受范围内尽量开启最好的画面表现。除此之外，对于哪些纹理要开启Streaming Mipmap，一般是场景中3D物体的纹理，而UI模块采用的纹理则尽量关闭。因为Mipmap的意义主要在于适应纹理在距离镜头远近时的表现需要、避免失真等，而UI完全不需要这些，开启只会白白浪费内存和计算时间。 感谢Faust@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62943f7fb87a45735168fe5f 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问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/627e709ab87a457351578018 Texture Q：请问纹理的格式设置项，BuildSettings里面Texture Compression，Default和Override，这几个是什么关系？ A：纹理设置中Override For Android这一项的优先级最高；不勾选Override的情况下是Default中的格式。如果既没有勾选Override，也没有改变Default标签下的格式设置，并且修改了Texture Compression选项，才会采用Texture Compression的格式设置，并且也只对符合上述条件的纹理生效。 感谢宗卉轩@UWA问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/62943fe1b87a45735168fed4 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问答社区提供了回答，欢迎大家转至社区交流： https://answer.uwa4d.com/question/6294421eb87a45735169011f 封面图来源于网络 今天的分享就到这里。当然，生有涯而知无涯。在漫漫的开发周期中，您看到的这些问题也许都只是冰山一角，我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入，也许你的方法恰能解别人的燃眉之急；而他山之“石”，也能攻你之“玉”。 官网：www.uwa4d.com 官方技术博客：blog.uwa4d.com 官方问答社区：answer.uwa4d.com UWA学堂：edu.uwa4d.com 官方技术QQ群：793972859（原群已满员）