当新节点变得可见时,SceneKit 应用程序会卡顿

Posted

技术标签:

【中文标题】当新节点变得可见时,SceneKit 应用程序会卡顿【英文标题】:SceneKit app stutters when new nodes become visible 【发布时间】:2017-03-18 16:26:44 【问题描述】:

当新节点出现在屏幕上时,我遇到了一个 SceneKit 应用程序(使用 Metal)卡顿的问题,即使该应用程序在之前和之后以 60 fps 的速度平稳运行。

想象一个游戏,其中东西被破坏,有时在被破坏的地方会出现道具。我很确定口吃与出现的能量提升有关,因为当东西刚刚被破坏(并因此从场景中移除)时不会发生这种情况。

到目前为止,我为尝试解决口吃所做的工作: 我通过 SceneKit 视图的 preload 方法预加载节点,并仅在其完成处理程序中将它们添加到场景中。 我在需要显示它们之前很久就将它们添加到相机上方,到时候我只需将它们移动到正确的位置。 我已经实现了一种排队机制,以确保每帧只进行一次更改(删除被破坏的项目的节点,将电源移到它的位置)。

但是,当电源出现时,有时(并非总是)会出现口吃。我想知道 SceneKit 是否仅在节点第一次出现时才做某事(即使它们已被预加载)。无论发生什么似乎都足以引起口吃,但对于 XCode 性能仪表来说太短了,无法显示出来。每一帧都有大量空闲时间,CPU 和 GPU 永远不会接近极限。

我不认为这个问题与复杂的几何形状或巨大的纹理有关,因为当我使用具有统一颜色的简单立方体时,它仍然会发生。

知道这里发生了什么或者我如何追踪它吗?

【问题讨论】:

【参考方案1】:

我自己找到了原因,我想和大家分享一下,以防你遇到同样的问题。

SceneKit 在幕后所做的神秘任务是重新编译各个节点的着色器。尽管 Apple 没有确认这一点,但我很确定 SceneKit 的策略是始终使用最有效的着色器,这些着色器足够复杂,可以按预期渲染相应的节点。这意味着无论何时添加效果、材质属性或光源,它都会编译一个更复杂的着色器。当您消除照明计算复杂性增加的原因时,它将再次用更简单的着色器替换它。

虽然这在始终获得尽可能高的性能方面很棒,但它也有一个缺点,这是我所经历的。重新编译着色器需要一些时间,导致 CPU 负载并迫使 GPU 等待着色器的新版本。最后,即使应用程序在大多数情况下运行非常流畅,它也会导致应用卡顿。

解决此问题的最简单方法是用您自己的代码(使用 SCNProgram)替换 SceneKit 着色器,但这也会使您失去 SceneKit 提供的大部分舒适度。 由于这不是我想要的,我最终采用了以下方法:

我强制 SceneKit 对所有节点的所有着色器进行初始编译出覆盖层。 我还通过从不完全关闭我一直不需要的效果来避免重新编译(例如,通过将灯的强度更改为 0.1 而不是 0 来关闭灯)。这使得 SceneKit 始终保持相同的着色器,从而避免了卡顿。

再次重申:Apple 尚未证实这一点,但到目前为止它确实有效,所以我认为我的假设是正确的 ;-)。

【讨论】:

我遇到了同样的问题(见这里:***.com/questions/65179315/…),我不能相信苹果没有像预加载处理程序那样提供适当的解决方案,可以为整个场景打开- 即使它需要更多的内存。而这是 4 年后的现在。您对此有什么新的好主意吗? 这在 2021 年仍然适用,但似乎这是防止新节点出现时卡顿的唯一方法。如果节点不可见,则不渲染节点是有意义的,但 SNCNode 上应该有一个简单的标志 shouldPreload = true。我知道 SCNSceneRenderer 有一个 prepare 方法,但我一直无法让它工作。

以上是关于当新节点变得可见时,SceneKit 应用程序会卡顿的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法链接 SceneKit 动画?

SCENekit:未隐藏节点的问题

SceneKit - 如何知道带有 MDLObject 的 SCNNode 是不是已加载并可见?

SceneKit开发教程02 SCNScene和SCNView

SceneKit 相机节点在捏合手势时重置

写入 dae 时,Scenekit 会自动为节点名称中的空格插入下划线