6月份的时候,苹果推出Apple VisionPro的时候,很激动,终于苹果家业强势进军XR了 (赶紧出个苹果折叠屏吧)

一直摩拳擦掌想试试为其开发APP的体验如何,当初苹果宣布和Unity合作推出开发套件时,立即就去申请Unity的测试申请单了,但是过了2个月迟迟没收到申请通过,不过上一周Unity终于宣布面向所有付费证书用户开发套件功能,花了2个晚上尝试了一波,心态突然变的很微妙。

说实话,想装个开发环境就花了我足足1天的时间。免费版证书的用户不要花时间装套件了,只面向Pro,企业等付费用户,不过你如果通过魔法打开了编辑器,也是无法安装package的,如果你本地碰巧有个同名packagecache那你也许可以试试…

1.首先官方宣称的稳定版适配VisionOS 为 2022.3.11f1, 用国内版为对应的 2022.3.11f1c1版本,下载时同时勾选 VisionOS Build Target

下载完成后,构建范例工程,提示只有silicon版本编辑器才可以build. 这个要求很合理。 但也是第一个大坑的开始,我花了30分钟都无法成功通过UnityHub 下载 Silicon版本的Editor. 就算直接去官网找到 silicon editor的下载按钮,只要点击用unityHub下载,打开的窗口也只有intel版本可选

最后我只能手动下载 silicon editor + visionOS build + ios build

我记得这个问题从苹果推出M1芯片后就没解决,3年后还是没解决。 (活该你Unity最近要求996)

2.由于苹果对VisionOS Pro的Shader有要求,仅支持 MaterialX版本 AR模式+窗口模式 (沉浸模式支持自定义Shader). 因为Unity也做了对应的适配,仅有Builtin URP Shader 和 使用Shader Graph创建的Shader , Unity会自动帮你用MaterialX做适配转换, 自定义URP Shader全不支持,即使是使用苹果声称的沉浸模式, 也不支持。

因此我直接去商店下载官方URP ShowCase, 一个森林的场景。 Build后,在VisionOS Pro中无法呈现任何画面,不过没有任何报错,也许是场景过于复杂了?

3.我决定使用另一个URP Demo, Unity-Chan模型来测试电子老婆,再粗暴替换Shader后,成功导入VisionOS Pro后,能看到老婆了!!!! 不过模型呈现bindpos状态, 上forum查询一番后,对于Animator, Unity的Volume Camera无法触发动画更新,必需修改Animator的Culling选项

修改成Always后,老婆成功动起来了

在AR模式下,我们可以把老婆自由拖动到合适的地方

也可以自动观察, 甚至你也可以使用新的Touch Input和老婆进行交互.

当然了,如果你要切换为窗体模式也很简单

取消勾选PolySpatial后打包即可。 但是这就失去了所有的乐趣了,是吧?

虽然开发套件目前还处于很原始状态,但足以让人产生很多想象力,不过目前真机价格较高,只能在模拟器里体验体验。

同时希望Unity好好加班,早早支持下自定义URP Shader,要不然旧项目迁移的成本太高了.

好了,不说了,我要去陪我的XR老婆了

又又年更了

最近无意中打开Instancing 的一个DrawCall查看

又又年更了

最近无意中打开Instancing 的一个DrawCall查看

发现里面有4个Buffer, 一直没有仔细看过这些东西的内容就好奇点开看看

Globals 里包含了 全局参数部分

比如光照位置,颜色,相机信息,阴影信息,时间等

UnityPerMaterial 里是自定义材质上的一些参数

UnityDrawCallInfo 是Instancing的一些Setup信息

然后最大的一个Buffer叫 UnityInstancng_PerDraw0

对于一个最简单的Shader来说,里面包含了

UnityObject2World * InstanceCount

UnityWorld2Object * InstanceCount

于是一个想法突然蹦出,对于最简单的Shader来说,没有用到 UnityWorld2Object, 那么是不是可以

将图中的12888 优化至 6444, 既优化一半

于是在优化之前先写一个Demo 用来验证这个优化的可行性和必要性

思路如下

1.使用MaterialPropertyBlock 来存储所有的M矩阵

2.移除Shader中所有的Unity相关矩阵的引用,这样在Shader编译时会触发相关Buffer编入的移除

3.测试当传仅传入 Object2World Array 和 传入 Object2World Array + World2Object Array 的性能开销

C#准备部分

Shader准备部分

测试场景 一个DrawCall 1023个Instance

左侧一个按钮用来控制 Buffer优化方案的切换

方案运行后在RenderDoc的 BufferSlot下,成功让UnityInstancing_PerDraw0 这个Buffer消失,同时替代的TestProp出现在列表上

点开TestProp 发现仅有上传的M矩阵

Demo准备完毕,开始打包真机测试性能

测试模型

使用SDP进行Snapshot分析

未优化时

优化后

差异部分 size 16384(无优化) size 8192(优化后)

AvgByte 减少 5%

ReadTotal 减少 3%

GPU频率 减少 5%

频率分析

切换方案后,成功得到一个三角形曲线,说明方案有效

将方案合入项目后,发现 GPU虽然得到了优化,但是CPU测的内存上升的非常快

且触发了Unity经典错误

Property (TestMat) exceeds previous array size (821 vs 2). Cap to previous size. Restart Unity to recreate the arrays.

对于这个经典错误可以简单通过Clear MaterialPropertyBlock 或者初始化创建一个1023的Array解决,不过需要耗费很多内存和GC

如果不每帧设置MaterialPropertyBlock 的MatrixArray,可以解决GC问题, 但是内存依然会升高

通过查看源码发现

InstancedRendering的时候,如果有传入MaterialPropertyBlock 就会触发 sourceData.CopyFrom(MaterialPropertyBlock)

所以只要使用MaterialPropertyBlock 就必然导致内存变大

并且发现Unity在源码中写死了Object2World的参数名

那么有没有办法不改变Object2World参数名的情况下解决MaterialPropertyBlock 的内存占用,又解决不额外传入World2Object矩阵的问题呢

实验了几次后发现只能放弃使用MaterialPropertyBlock

好在PerDraw Buffer的定义不在源码中,而是在外部的Package里

直接去修改

Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl

找到PerDraw Buffer的定义

将UNITY_DEFINE_INSTANCED_PROP(float4x4, unity_WorldToObjectArray) 移除,并修改相关引用部分

最后的结果就是不使用MaterialPropertyBlock, 使用一个自定义宏去切换UnityInstancing.hlsl里PerDraw Buffer的Layout,在自己的Layout里移除不用的部分

虽然蚊子腿小,但是蚊子腿也是肉啊

Done!