当新节点出现在屏幕上时,即使应用程序在前后60 fps平稳运行,我的SceneKit应用程序(使用Metal)也会出现问题。
想象一下,游戏中的东西被破坏,有时会出现破坏物体的地方。我很确定口吃与出现的能量有关,因为当事物被摧毁(并因此从场景中移除)时它不会发生。
到目前为止,我尝试修复口吃: 我通过SceneKit视图的预加载方法预加载节点,并仅在其完成处理程序中将它们添加到场景中。 我需要在它们需要显示之前将它们添加到相机上方,并且在我将它们移动到正确位置时。 我已经实现了一个排队机制,以确保每帧只进行一次更改(删除被破坏的项目的节点,在其位置移动加电)。
但是当出现电源时,有时(并非总是)出现口吃。我想知道SceneKit是否仅在节点第一次出现时才做某事(即使它们已被预加载)。无论发生什么事情似乎都足以导致口吃,但XCode性能表太短以至于无法显示。每一帧都有足够的空闲时间,CPU和GPU永远不会接近最大化。
我不认为这个问题与复杂的几何形状或巨大的纹理有关,因为当我使用具有均匀颜色的简单立方体时,它仍然会发生。
知道这里发生了什么,或者我如何追踪它?
答案 0 :(得分:2)
我自己找到了这个理由,如果你遇到同样的问题,我想与你们分享。
SceneKit在幕后所做的神秘任务是重新编译各个节点的着色器。虽然Apple没有证实这一点,但我很确定SceneKit有一个策略,即始终使用最有效的着色器,这些着色器足够复杂,可以按预期呈现相应的节点。这意味着无论何时添加效果,材质属性或光源,它都会编译更复杂的着色器。当您消除了增加照明计算复杂性的原因时,它将使用更简单的着色器再次替换它。
虽然这对于始终获得最高性能而言非常好,但它也有一个缺点,这是我所经历的。重新编译着色器需要一些时间,导致CPU上的负载并迫使GPU等待新版本的着色器。最后,它会导致应用程序断断续续,即使它们在大多数时间内都非常流畅。
解决这个问题的最简单方法是用你自己的代码替换SceneKit着色器(使用SCNProgram),但这也会让你失去SceneKit提供的大部分舒适感。 由于这不是我想要的,我最终采用了以下方法:
我强制SceneKit初始编译所有可能在以后看到的节点的着色器,方法是在开始时用黑色覆盖覆盖场景,添加摄像机前面的所有节点,然后在淡入淡出之前将它们移动到正确的位置覆盖层。 我也避免重新编译,因为从来没有完全关闭我一直不需要的效果(例如通过将亮度改为0.1而不是0来关闭灯光)。这使得SceneKit始终保持相同的着色器,从而避免了口吃。
再说一遍:这还没有得到Apple的证实,但它到目前为止一直有效,所以我认为我的假设是正确的;-)。