AssetBundle异步加载被中断的问题

AssetBundle异步加载被中断的问题

1)AssetBundle异步加载被中断的问题
​2)LuaDLL.lua_pcall()自身产生开销问题
3)法线在手机渲染时出现的错误问题
4)UNITY_MATRIX_I_V 和Camera.main.worldToCameraMatrix.inverse区别


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

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

Resources

Q:在使用异步接口yield return AssetBundle.ASyncLoad的时候,难免会想到:这个异步处理完之前如何Cancel掉这个任务?也就是说一个AssetBundle加载到一半,现在要放弃加载,应该怎么处理?

A1:其实底层调用GetAssetBundleBlocking,会在这个函数内部调用PreloadManager的加载更新逻辑,其实就是把Async操作改成了Sync了,会一直阻塞到AssetBundle加载成功。

但是也有坑,这儿因为执行了PreloadManager.UpdatePreloading,内部还会做GC。遇到过把不应该卸载的资源给卸载了,说是Unused,但是实际正在使用。

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

A2:资源加载管理的第一难题就是异步IO怎么被Cancel掉,或者说从设计上怎么去实现等同的效果。

但是相对于从Flash到Cocos到Unity来说,Unity还是做的很好的。之前的任何引擎要处理好这个问题,非常之困难,需要很多代码的支持,包括多线程更低层次的相关代码。这一块Unity做得非常好,我们不用绞尽脑汁去实现异步IO Cancel的问题,只需要做好上层架构,设计出类似的功能。

Unity的异步加载会返回一个Request对象,该对象有一个Get属性AssetBundle,仔细查看该Get函数API会发现。当调用该属性时,会促使底层异步IO中断(具体怎么实现的,有C++源码的才能知道),并立即同步加载该资源,且回调,回调顺序是和你调用顺序相当的(比如异步加载调用了2次并设置了回调,现在触发中断并立即完成,会优先回调异步设置的回调)。

所以基于这个设计一个好的引用计数器管理资源非常重要。

大部分项目的引用计数器设计都是待资源完成后添加引用计数。这种设计的适用场景非常局限。在大型项目中使用环境非常复杂的资源加载情况下(异步加载中调用同步,异步加载中需要取消,异步加载中取消后立即同步等)会出现非常多的未知问题和开发中的局限性,这种设计只适合微型项目快速开发。

所以一个完备的引用计数设计应该是调用加载方法时,就生成该资源未来被加载完成后的数据壳子,并添加引用计数(因为加载本身其实也是一次引用,包括bundle.AsynLoadAsset,其实任何的异步加载自身都需要添加一次引用,防止上层代码的逻辑,造成引用计数器非法=0触发bundle.unload方法),完成后减少引用计数。那么同步加载时需要查找数据壳子,如果该资源正在异步加载,需要调用其request.assetBundle让其立即中断异步并立即同步完成加载。(否则同步加载时底层会抛出警告。)

基于这个我们可以设计一套非常完备的Cancel系统,上层调用Cancel时,其实就是让其外部引用-1,因为理论上我们能拿到的所有接口无法真正中断一次IO,只有待其加载完成后,发现引用计数等于0时,才会Unload掉资源。

设计思路这里只是抛出一点,具体怎么去实现和设计各抒己见。只是如果一个资源管理器不能适应任何(绝大部分)加载情形,那么这还是一个资源管理器吗?

感谢1 9 7 3-311135@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5af3db530e95a527a7a81d31


Lua

Q:为什么LuaDLL.lua_pcall()自身会产生这么大的开销?

A1:是不是Lua自身逻辑消耗比较高,这个可以看Lua侧的消耗:《Lua Profiler——快速定位Lua性能问题》

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

A2:lua_pcall只是通过压栈的方式调用了Lua,具体消耗还得在Lua那边看看,估计是某函数C#和Lua疯狂调用比较厉害。

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

A3:用DeepProfile才能知道具体是哪个方法调用高,这里只是C# call到Lua,后面的栈信息已经没有了,不能单纯的说LuaDLL.lua_pcall()开销大。

感谢1 9 7 3-311135@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/606184ecafbc233466c02c80


Rendering

Q:法线在手机渲染时出现的错误问题:
用的版本是Unity 2019.4 URP。Editor模式下是正确的,在默认Unity Build的打包正确,将资源打成AssetBundle后渲染出现错误。Shader使用了多种,包括默认的Lit Shader,都会出现该问题。

A:怀疑是渲染管线的Shader问题,变体收集问题,最后一一排除后变成Mesh问题。有好大哥MoMo的奶爸告诉我,Unity部分版本的Optimize Mesh Data选项默认打开,会优化掉部分网格,导致以上的问题。

感谢题主霸气九号@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/616e6d248f8c834241e4f742


Rendering

Q:URP中Shader的UNITY_MATRIX_I_V 和C#中Camera.main.worldToCameraMatrix.inverse获取到的矩阵并不一样,在Shader中用ViewSpace的坐标乘以UNITY_MATRIX_I_V并不能够得到正确的世界坐标,乘以Camera.main.worldToCameraMatrix.inverse则可以,请问他们二者的区别是什么呢?

A:我这边做的测试并没有发现这两个矩阵有什么不同。

fixed4 frag (v2f i) : SV_Target
{
     fixed4 col = UNITY_MATRIX_I_V[0];
     return col;
}

在Shader中修改Frag来调用UNITY_MATRIX_I_V:

void Start()
{
    Debug.Log(Camera.main.cameraToWorldMatrix);
    Debug.Log(m_camera.cameraToWorldMatrix);
    Debug.Log(Camera.main.worldToCameraMatrix.inverse);
}

在C#中打印这个矩阵,从Frame Debugger和控制台输出的结果来看这两个矩阵没有什么不同。

使用URP的Shader Simple Lit,计算逆矩阵之后的结果也是一致的:

感谢宗卉轩@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/616d6b2c8f8c834241e38f1d

20211025
更多精彩问题等你回答~

  1. Unity增量打包AssetBundle没变化的资源也会被重新打包
  2. 在模型有UV2的情况下开启Generate Lightmap UVs
  3. 如何实现AAB包的增量更新

封面图来源于网络


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

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