Addressable卸载单个资源的疑问

Addressable卸载单个资源的疑问

1)Addressable卸载单个资源的疑问
​2)如何判断硬件支持GPU Instance
3)StringBuilder反射实现String报错
4)Unity 2018 RequireES3.1+AEP有什么作用
5)Unity打包AssetBundle闪退


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

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

Addressable

Q1:通过Addressable加载的Prefab A,里面引用了Sprite A,现在通过Addressable加载Sprite B,把Sprite A释放,替换为Sprite B。

这时候Sprite A无法通过Addressable.Releases释放,提示Addressables.Release was called on an object that addressables was not previously aware of.Thus nothing is being released。

如果不释放Sprite A,只是引用改为Sprite B,那么在释放Prefab A的时候是否会正常释放Sprite A呢?

A1:Addressable内的Load和Release是配对调用的,第一步Load了Prefab A,对内部的Sprite A是Dependency引用,没走Load流程,自然无法通过Release释放,可以考虑,Prefab A不要直接挂Sprite A,也是通过运行时去Load Sprite A再挂,然后就可以在需要的时候Release掉再替换B了。

这里的细节是:

调用LoadAssetAsync时会调用TrackHandle,给Load的Completed事件添加一个OnHandleCompleted处理,这个处理内会将Load方法返回的Handle给保存到m_resultToHandle,在Release的时候去找这个Handle,并调用这个Handle.Release方法来做实际的Release操作。


找不到,自然就报题主的错误了。

Q2:Prefab A不引用Sprite A,运行时再加载的方式会导致在编辑器非运行时无法看到图片,对于拼UI的同事会不太方便。我考虑的方案是,记录当前资源是否运行时加载的,只有是才调用释放,但是不确定原来的Sprite A失去引用后,是否能在Prefab A释放时正常释放Sprite A。

A:我想一般来说你也是把Prefab A实例化以后使用,那么2种情况:

  1. 使用系统的Object.Instantiate,这时就和Addressable没什么关系了。Sprite A在切换时直接Destory就行了。

  2. 使用Addressables.ReleaseInstance,那么系统会去调用AsyncOperationBase.DecrementReferenceCount,当引用计数为0时,调用ResourceManager内部类的InstanceOperation.Destroy方法,最终调用到InstanceProvider.ReleaseInstance,这时你会发现系统其实也是简单的Object.Destroy而已。

那么我们可以这样理解,对于Prefab A实例化出来的对象,由Addressbles管理,需要调用ReleaseInstance来释放,当没有引用的时候,系统Destroy它,那么对于该对象内部的引用资源Sprite A,其实并不是Addressable管理的那一层级。

最后整理一下:

  • 对于自己Instantiate出来的对象,Sprite A随便什么时候都可以Destroy,和一般Unity对象做法一样。
  • 对于通过Addressable实例化出来的对象,则可以在需要切换Sprite的时候,调用Destroy方法释放Sprite A,然后载入并设置Sprite B。
  • Sprite B如果使用Addressable来实例的,则在ReleaseInstance这个Prefab A实例化出来的对象前先调用ReleaseInstance方法释放Sprite B。
  • 如果Sprite A的图片资源并不在Prefab A所在AssetBundle包内,那么是作为依赖被载入的,理论上也是要在Prefab A实例被Release后,发现没有引用的情况下才Unload,也就是说,提前Destroy Sprite A并不会释放它所用资源和AssetBundle包。
  • 最后还是建议测试一下。

感谢黄程@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5f17e18c87d730028cd4d81f


Rendering

Q:在国产安卓机上发现不少号称支持ES 3.0的,但是实际上却不支持GPU Instance,现在大家都是如何判断硬件支持GPU Instance的?SystemInfo.supportsInstancing 还是SystemInfo.graphiceDeviceType==OpenGLES3?

SystemInfo.supportsInstancing模拟器上调用这个接口,支持Instancing,但实际使用过程中却出现显示问题,或者闪退的Bug。这个有什么好的方式处理这种情况吗?

A1:建议使用SystemInfo.supportsInstancing,链接:
https://docs.unity3d.com/ScriptReference/SystemInfo-supportsInstancing.html

该问答由UWA提供

A2:上周也碰到了类似的问题,希望能对你有所帮助。
当时碰到的问题是:想在小米4上用GPU Instance,但Unity的接口SystemInfo.supportsInstancing竟然是False, 去查了下,Mi4的CPU是骁龙801,GPU支持OpenGL ES 3.0的,理论上应该是支持的,有点奇怪,就去查了下Unity源代码。

结论是:Unity内部对满足“Adreno GPU”和“OpenGL ES 3.0”这两个条件的机器做了特殊处理。

至于Unity为什么要在这类机器上关闭Instance,注释里有解释说是在这类GPU上,Uniform的效率慢,但这我就没有做进一步验证了,官方既然这么处理了,相信应该是有他的道理。

所以逻辑上判断是否支持Instance,还是建议使用SystemInfo.supportsInstancing这个接口。

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

A3:目前已遇到的Instance的使用中有如下几个情况:

  1. 非AssetBundle的时候Instance没问题,但是用了AssetBundle后,Instance失效。导致DrawCall大幅度变多。

原因是:打AssetBundle的时候,Instance的变体被裁剪掉了,我的方法是对每个Shader创建一个空的,打开Instance的材质球,将这些Shader和材质球打入一个AssetBundle,这样变体就不会被裁减掉了。

  1. PowerVR的GPU,当使用Instance的时候,直接闪退了。

原因是:这是Unity 2018.4.18之前的一个Bug,在Unity 2018.4.18中修复了。
https://unity.cn/releases/full/2018/2018.4.18
Graphics: Fixed an issue with GPU instancing on PowerVR devices. (1156362)

所以听上来,楼上遇到的问题和PowerVR的问题是一样的,机器是支持Instance的,结果Unity 创建环境创建错了。

所以,要么就是模拟器设置问题,没打开OpenGL ES3.0的支持,要么就是Unity又犯了和PowerVR GPU一样的错误。所以先检查下模拟器的设置,如果没问题可以升级Unity试试。

感谢王烁@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5ae714b00956834900752685


Script

Q1:想通过反射的方式将StringBuilder转换为String来避免多余的装箱,但发现“_str”不存在,请问.net3.5之后应该用什么替换“_str”,可以实现Stringbuilder通过反射达到转换成String的方式?

A:貌似已经改为"m_ChunkChars"了。

Q2:没有再出现空异常,但强转却失败了,是在使用反射的时候又出现什么问题了吗?
我是将值类型装到StringBuilder并通过这种反射来转成String类型的。

A:char[]类型不能强转成String吧。可以通过New String(char[])转成String,但终归是达不到你的需求的。

StringBuilder本身是可以通过指定Capacity设置初始大小来避免中途扩容的。我想你是想避免ToString过程中的内存分配。


反正你是通过反射来获取到内部的ChunkChars来直接操作。干脆在外部重写一个ToString方法,ToString内部的这个str你可以准备一个全局的,尺寸设置稍微大点。这样是不是可以达到你的目标。(我没试过,提供一种思路参考。)

感谢黄程@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5f19304da03b4d13c422e339


Build

Q1:大概知道Auto Graphics API是判断设备是否支持OpenGL3的,如果不支持尝试OpenGL2,但是线性空间下,只能选OpenGLES3,会多出来Require ES3.1和Require ES3.1+AEP。

想问下这两个选项作用是什么,会不会更加兼容更多设备?

A1:这两个选项貌似是勾选后在Manifest里面增加对ES3.1和ES3.1+AEP的Requirement,这样如果是不兼容的设备在访问Google Play Store时,该App就不会出现了。

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

A2:如果项目中用到了依赖ES3.1的特性,而且不支持Fallback,那么可以增加这个选项。比如用到了Compute Shader,但这样会减少兼容的设备。

该问答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5f12ac2bf0561208afaf3b68


AssetBundle

Q1:我们项目使用Unity 2018,在打包AssetBundle时会导致Unity闪退。尝试过很多其它2018版本的Unity,依然没有解决问题。下面是Unity闪退时的软件报告:

https://uwa-public.oss-cn-beijing.aliyuncs.com/answer/attachment/public/101067/1594881341141.unityCrash

A:去掉如下图的设置就正常了:

感谢题主廖武兴@UWA问答社区提供了回答

A2:我没看你的Crash文件,我先说Unity 2018打包会闪退的最常见的情况吧。

Unity 2018新增的Prefab嵌套引用机制,如果嵌套引用的对象丢失,会闪退,比如你把模型文件xx.FBX存成关联引用的prefab,xx.prefab,然后删除xx.FBX,打包会闪退。断掉关联就不会了。

另外楼上回答的Bundle打包时候存在嵌套循环引用,并不会引起打包失败,只是加载的时候会出问题。项目刚好出现了。还是拿模型举例:A.ab 打包了模型贴图, B.ab打包了模型的动作文件,a.prefab(打包在A.ab)跟b.prefab(打包在B.ab)都引用了这些贴图跟动作文件。

感谢简单就好@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5f0ff589ce656404de28c88b

封面图来源于网络


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

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