问答 Addressable 卸载单个资源的疑问

侑虎科技 · 2020年07月30日 · 1188 次阅读

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问答社区提供了回答


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,这样变体就不会被裁减掉了。

2.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问答社区提供了回答


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问答社区提供了回答


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 提供


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问答社区提供了回答

封面图来源于网络


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

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

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册