异步上传管线AUP答疑

异步上传管线AUP答疑

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

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


本期目录:

  • 异步上传管线AUP答疑
  • PSS的优化方法
  • 2017.3 iOS运行内存过高
  • PhysicMaterial中的bounciness问题
  • UGUI相同材质合批后如何确认UI元素的遮挡关系

加载

Q:对AUP产生疑问,主要目的是想知道是否已经适合在现在的项目里使用了?

目前项目里大部分用的是同步加载,所以应该全部改成异步加载吗?博客里的意思是AUP可以在渲染线程做加载,那如何保证渲染线程不会卡主线程呢?我们使用2018.4版本,如果加载一个角色,除了Texture、Mesh之外,其它的资源还是会走正常的同步加载?这种相对复杂的Prefab加载适合用异步吗?

A1:AUP跟同步/异步加载没关系,只是Texture跟Mesh的加载放到了渲染线程。我这里也是同步加载的,从真机上看到同步记载Texture和Mesh也是走渲染线程。同步加载的时候,不会卡主线程的,这点跟UWA的技术人员确认了。在加载界面的时候,我调大了时间切片耗时。判断机子内存多,加大了加载内存。有一点不明白的是,当Loading界面同步加载非常多的情况下,例如:一帧耗时500多毫秒,相对应的时间切片耗时可以跑到100ms耗时。
感谢剑影蒙残@UWA问答社区提供了回答

A2:回答第一个问题:

正如@剑影蒙残 所说,AUP和加载API的同步加载还是异步加载没有关系。当AUP功能和多线程渲染功能开启后,无论是Load还是LoadAsync,Texture和Mesh资源都是在加载线程加载好后,从渲染线程提交到GPU的。而在没有AUP功能时,这两项资源的GPU Upload都是在主线程完成。这样的好处显而易见,就是将主线程的卡住时间转移到渲染线程进行完成,从而让主线程可以处理更多逻辑事物。

需要注意的是,在2018.3及以后版本中,AUP才支持Texture和Mesh,在2018.2版本中,AUP只支持Texture资源。

回答第二个问题:

确实,当有大量Texture或者Mesh资源需要传输时,渲染线程很可能是吃不消的,但需要考虑加载的时间点,如果是在场景切换时加载,且允许可以存在一定的画面卡住需求,那么可以尝试通过Load API来进行加载;反之,在战斗过程中或者需要平滑加载时,则使用LoadAsync API。二者的最主要区别是在于加载的资源量要不要在下一帧结束前完成。如果需要,则使用Load,Texture和Mesh虽然在渲染线程进行Upload,但一定要在下一帧结束前完成,这样就很有可能会造成主线程的等待,但相较于以前Unity版本,其优点是主线程依然可以执行其它耗时计算,比如UnloadUnusedAssets、配置文件的加载等。

而如果想要尽可能平滑,则Unity引擎提供了AsyncUploadTimeSlice和AsyncUploadBuffer两个参数,虽然在之前的Unity版本中也这两个参数,但经过大量测试下来,并无法像2018.2之后容易控制。

这两个参数一个控制每帧中的Upload最大时间,一个控制每帧中允许Upload的最大数据量,两者相辅相成,控制AUP在每帧中的加载耗时。所以,我们在UWA DAY 2019中也对这两个数值的使用进行了详细说明,如下图所示:


当渲染线程压力较大时,可尽可能控制上述两个参数来降低渲染线程由Upload数据而造成卡住主线程的概率。

回答第三个问题:

其实目前加载一个角色Prefab时,已经可以有很多资源都可以通过异步加载来进行,直接通过异步加载接口调用即可,这个跟AUP就是另外的问题了。AUP指的是Texture和Mesh在提交到GPU时的异步提交操作,其本质是在资源加载之后进行的,也就是说和异步加载是两回事。

相对复杂的Prefab确实适合来做异步加载,我们曾经在今年UWA DAY上做了一个非常重的UI界面加载测试,如果按照默认设置进行同步和异步加载,则耗时分别为:293ms和851ms,小米Mix2设备上。


而在经过了调整加载策略后,其异步加载耗时可以将为313ms,和同步加载相差很小,但好处是主线程留下来大量的时间来处理其它逻辑操作,且很平滑。

对于UI Prefab如此,对于角色Prefab亦如此。在优化2018.2以后的异步加载时,需要更多考虑如何控制资源在异步加载之后,在主线程的“后加载”耗时。比如,各种资源的AwakeFromLoad和Shader.Parse等操作。对此,我们在《Unity引擎加载模块和内存管理的量化分析及优化方法》进行了详细的分享,有兴趣的朋友前往观看。

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


PSS

Q:vivo、OPPO机型统计的PSS相比于其它厂商要高,有没有相应的解释文章,或者针对这类机型优化的办法?

A:不同手机厂商或者说相同厂商不同的机型,其PSS统计是会有差异的,主要还是其系统管理策略不同。楼主如果觉得某一机型PSS高,那么可以拿它与其它机型的PSS各个模块进行对比,看看有什么模块较为明显异常。

一般来说,你只要优化你的资源和代码使用,即可降低PSS的占用。比如,在华为mate8上EGL Mtrack和GL Mtrack加起来就是你的Graphics大小,EGL Mtrack一般是OpenGL驱动层的一些Buffer的占用,一般只要你降低分辨率或者说编辑器设置不勾选use32BitDisplayBuffer选项,都能减少EGL Mtrack的占用;GL Mtrack则是纹理网格等一些内容的占用。

当然PSS最大的占用可能还是Native的占用,尽量减少你的Object数量,优化你的Mono内存,减少你的Shader占用,这些都能优化PSS。

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


内存

Q:Unity 2017.3版本iOS打出空包时运行内存达到了80MB,项目是2D卡牌类型。

项目在打包Android APK运行时很流畅,但是在iOS上问题比较严重,检查了,图集资源占用比较高。所以对一些图片资源进行了减低分辨率的操作,并且把相机的HDR MSAA一并去除。

QualitySettings也进行了一些配置,项目中没用到阴影,所以把与阴影相关的该关的关、该降低的降低。

但是发现还是没有改变。

新建一个空工程,只有一个场景,一个脚本控制按钮可点击。用Memory Profile测试过后,编辑器中占用内存在50MB左右。

Render Texture



但是打包到iOS中调试,在场景刚开始加载的时候瞬间就达到了80MB,XCode中查看不了具体的方法名字,只能查看到内存地址。

所以想请教一下,这个空包如何减少内存初始分配占用?是什么占用掉的内存?

A:这个问题Unity已经关闭掉了。原因是在iOS 12和Xcode 10上,Apple的内存报告工具已经更新,以便更准确地跟踪现有版本之前的内存占用。换句话说,并不是因为应用程序消耗更多内存,而是之前报告的内存不足。仅当针对iOS12构建应用程序时,才会观察到此行为,并且不适用于针对iOS11构建的现有应用程序。在使用XCode 10升级应用程序后,将只会观察到这些更改。对于使用Metal的游戏尤其如此,因为大多数分配都发生在内核空间中,而以前并未考虑应用程序的内存占用。

附上iOS对此的解释:https://developer.apple.com/documentation/metal/reducing_the_memory_footprint_of_metal_apps

感谢题主刘逸宸@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5d4bbd27c494b506d26738f5


物理

Q:多年前偶然发现,当PhysicMaterial中的弹性bounciness设置为1时,在Unity默认的物理作用下,它在一个同样材质的地面上,会越弹越高,理论上不是应该在同一个高度一直弹下去吗?最近因为工程需要,发现这个问题还没有解决,我想问下各位大神,这是Unity的问题还是我的操作不对?应该怎么解决呢?


物理材质设置


小球设置


地面设置

A:我尝试按照题主的方式,在Unity 5.6.4p4上完美可以复现这种状况,就感觉很有意思。

解决这个问题倒是简单,题主现在的问题就是莫名其妙地获得了动能。只需要我们固定住动能就可以了。或者说,直接不用自带的运动表现,自己去计算这些东西也可以。

比如:

var firstTimeVelocitySave = false;
var savedVelocity = Vector3.zero;
void  OnCollisionEnter(Collision col ) 
{
    if (! firstTimeVelocitySave) 
    {
        savedVelocity = rigidbody.velocity;
        firstTimeVelocitySave = true;
    }
    myRigidbody.velocity.y = savedVelocity.y;
}

或者:

rigidbody.velocity = new Vector3(collision.relativeVelocity.x,collision.relativeVelocity.y);

或者:

rigidbody.velocity = Vector3.Reflect(rigidbody.velocity, normal);

或者直接使用添加力的方式自行计算都可以。

找了一些相关资料,发现这个东西在不同版本的Unity上表现还不一致。

在Unity 4.x上,将系数设置成0.9699999;

在Unity 5.x上将系数设置成0.9805824可以得到一个相对完美的碰撞小球。

这2个系数来源于:https://answers.unity.com/questions/736091/how-to-get-a-perfect-bouncing-ball.html

在我本机上的版本4.3.2f、4.6.1f、5.2.6f、5.6.5p4都可以得到预期结果。

另外下面有答主说设置成Collision Detection可以解决这个问题,查询了文档:

Collision Detection How collisions with other objects are detected. Discrete A collision is registered only if the object’s collider is in contact with another during a physics update. Continuous A collision is registered if the object’s collider appears to have contacted another between updates.

一个不成熟的猜测是,连续碰撞可以在撞击的有效时间解决碰撞,而离散碰撞在物体已经重叠的帧时间解析。然后,离散碰撞计算的时间不准确性可能会给系统增加能量。另外,这个东西是不是与浮点数计算有关还不确定。

感谢洋葱小同学@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5d45357727d7e5590f6cd850


UI

Q:查看UGUI合批的原理时看到相同Depth,相同材质的元素会合批。如下图所示,4个image会合并为一个Mesh,我的问题是,在1DrawCall的前提下,这4个image的遮挡/先后关系是怎么得到保证的?




A:UGUI同一节点层级下,遮挡关系就是节点的先后顺序。如你举例的图片所示,节点顺序分别是0、1、2、3。

UGUI以Canvas为单位重绘,绘制的CanvasElement依次加入到绘制列表中(UGUI自己实现的数据结构IndexedSet)。

列表中元素即为有序的:0、1、2、3。所以发生rebind时候,会依次把0、1、2、3绘制出来,同一层级下后绘制的由于位置关系影响就会被遮挡。

代码可以阅读UGUI源码中关于Canvas绘制相关的内容,主要看注册更新和rebuild的地方就明白了。

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

封面图来源于网络


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

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