2D项目大量物品图标Draw Call优化方案

2D项目大量物品图标Draw Call优化方案

1)2D项目大量物品图标Draw Call优化方案
​2)UGUI SpriteAtlas的热更新问题
3)iOS平台突然有一帧UI渲染开销很高
4)iOS上频繁Crash,堆栈很奇怪


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

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

Rendering

Q:我们有个2D项目,大致布局如下图:

1.场景按格子划分,玩家可以在每个格子上面摆放物品,物品种类非常多(700+)。
2.部分物品还有动画效果,动画会改变该物品的材质属性。
3.物品图标的大小不是严格一个格子大小,因此会有部分物品图标间的重叠。
4.场景的视角可拖动,最远视距下可以看到超过100个物品。
5.场景上除了物品,还有云雾、水波、发光粒子等各种特效。

具体实现:
1. 场景底图采用美术分割方式,每张2048*2048大小,由16张组成,直接使用SpriteRenderer摆放渲染。
2. 物品图标每张256以内,同样采用SpriteRenderer摆放渲染。
3. 使用Unity内置渲染管线。

经过一番Profile,发现目前主要性能问题在于Drawcall数量非常大,没摆满的情况下都接近300,主要消耗点在于特效跟物品图标的渲染。

1.由于物品数量巨大(后续还会增加),目前采用SpriteAtlas方式把所有物品图标进行合图,会产生3张2048*2048尺寸的图集,各个物品可能会随意分散在这3张图里,加上物品穿插显示,导致无法合批。
2.物品有状态,不同状态会对材质属性进行变更,改变灰度明亮度等效果,导致无法合批。
3. 场景上的粒子特效是3D的,会随意在物品图标中移动穿插,导致图标无法合批。

已经考虑过,但否决了的方案:
1.把图标缩小,使生成的图集在一张2048以内。
=> 询问过策划,物品种类目前还不是全部的,完整的配置里有超过1000+物品,都合成到一张2048里不现实。
2.图标不合图集,直接散图集成,采用运行时动态合图方式对场上物品进行合图。
=> 游戏主要玩法会导致不停拖动物品图标以及旧物品的删除跟新物品的生成,操作会非常频繁,动态合图方式消耗巨大。

鉴于上面所描述的状况,各位大佬有没有什么推荐的优化方案?

A1:可以从以下方式入手:

  1. 首先图片和图集减少Draw Call没问题,图集也应该做分类,把能组合的类型分配并归类。图集不是追求少就好,应该是动静分离,还有按项目分类。做到图集数最优,这个就要结合自己的项目分析了。
  2. 然后合批的问题要解决。重写一个Shader,挂载到SpriteRenderer上,改变这个材质上的属性可以避免导致无法合批。
  3. 格子布局应该避免元素重叠,这个在设计就要避免的。粒子特效尽量用2D效果来做表现,粒子的发射数量与粒子数量也要做权衡。

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

A2:建议动态图集,静态图集在这种情况下,基本是负优化。

感谢uwa菜鸡奶泡泡.@UWA问答社区提供了回答

A3:这个问题,其实挺好解决。

1.背包的物品组件做一个循环动态列表功能,这样同屏加载不会很多。
2.物品的同一个组件进行拆分,比如bg一个图集, icon一个图集,数字图片一个图集,特效纹理一个图集。

这样下来DrawCall一般在三四个可以搞定。

感谢郑佳宇joe@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/6215b229e7172c2775f6f5c6


Atlas

Q:UGUI图集切换后,图集中的图片名称不变,只是改变该图片为其他图片,但是为什么在加载UIPrefab的AssetBundle之后,UI上对应修改的图片会出现白图?会发现SpriteAtlas的CanBindTo返回false。

A:替换图片时,不修改原GUID则可以避免这个问题。

感谢题主Austin@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/621c3e1be7172c277500573d


UGUI

Q:iOS平台通过Profile定位性能,偶现某一帧UI开销异常高,通过Timeline看到是Render引起的,但是不知道后续应该如何定位。

这里可以看到这一阵Render开销达到了12.35ms,其他帧只有不到2ms。

A:EmitWorldScreenspaceCameraGeometry(图中紫色)这个函数是UGUI的Canvas网格重建生成UI Draw Call的开销,主要工作在Native子线程进行,主线程表现为该函数的等待开销,建议排查该时刻是否有较为复杂的UIPanel创建(结合Profile的UI模式)。

感谢羽飞@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/6215fde7e7172c2775f76fd9


iOS

Q:在线上FireBase收集到不少的iOS Crash。Crash的栈有很多种,但共同点是通过Il2CppInvokerTable.cpp的第4336936768行(一个不合理的行号)指到了某个函数,之后可能会调用自身若干次,再跳转到一个不确定的位置Crash掉。我们尝试过给该函数做过保护修复,但发布后还s会Crash,调用又全部换成了另一个函数。所以怀疑并不是某个函数本身的问题。

大部分是后台触发,但也有前台触发的。大部分系统剩余内存不多(<100MB),但也有剩余超过1GB的。系统版本主要是iOS 15,我们有接入一些iOS原生的SDK。

版本信息:
Unity 2018.4.30
IL2CPP
只有iOS



怀疑有某个函数指针的指向错误导致,但如何继续定位问题呢?

A1:FireBase上面对IL2CPP部分的堆栈还原是有Bug的,基本看不出来是哪个堆栈,可以尝试下通过添加自定义事件和Log查看大概在哪崩溃的,然后再尝试重现和修复。

这个从Unity的堆栈上面来看,是挂在某个线程了。

我年前给FireBase团队提的Bug,对应的issue在这里:https://github.com/firebase/quickstart-unity/issues/1200

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

A2:建议添加一下Unity的符号表。

感谢郑佳宇joe@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/62130bdce7172c2775f35d30

封面图来源于:Simple inventory system for unity
Unity3D的背包系统。


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

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