5

I traced a major performance bottleneck in my SceneKit app to a nested loop being run a few thousand times. In that loop is a bunch of code that zooms along quite happily, apart from this one line:

var scenePos = presentation.position

It's over 100 times slower than just asking for the position PLUS dozens of other calculations, comparisons, array lookups & method calls combined that I was doing inside the same loop. I'm surprised no-one seems to have commented on this yet that I can find.

Why is this, and is there a workaround for it apart from making a copy of each node's presentation.position yourself each frame so you don't have to keep asking the presentation node for it? Thanks.

Edits: presentation.position is only ever being read, never written to. No boundingBox is ever edited. I'm using a dynamic SCNPhysicsBody for a few of the SCNNodes, but the vast majority are static.

DenverCoder9
  • 3,635
  • 3
  • 31
  • 57
  • 1
    How are you using the `presentation.position`? Are you altering it? Apple's documentation says that if you try and change the `presentation.position` it could result in some undefined behavior. – NFarrell Mar 13 '19 at 22:40
  • I've just checked to confirm - in the entire app presentation.position is only ever being read, never written to. – DenverCoder9 Mar 14 '19 at 12:50
  • 1
    My guess is that multiple calls to presentation node don't return you exactly the same object. Docs say it returns a copy of the node, and it looks as though the geometry changes every time. Just do what you suggest and create a copy outside your loop. – James P Mar 15 '19 at 12:44
  • @JamesP so it's doing an object creation each time you query presentation, in a highly expensive way? That's awful but I wouldn't put it past SceneKit! Please put this in an answer so if it's the best guess I can bounty you up :) – DenverCoder9 Mar 15 '19 at 19:30

2 Answers2

1

Here's the workaround I'm currently using (for this, and also the separate issue that presentation.position can be zero if physics has never been run for that node). It's orders of magnitude faster, as nearly all my nodes are not dynamic.

// When you're not sure if physics has first run yet
func currentScenePos() -> SCNVector3 {
    if physicsBody?.type == .dynamic {
        var scenePos = presentation.position
        if scenePos.isZero() {
            // Looks like the physics hasn't run on this node yet. Use the regular position.
            // If that's zero too, it must really be at the scene origin.
            scenePos = position
        }
        return scenePos
    } else {
        // Non-dynamic physics. Just use position, it's a lot faster.
        return position
    }
}
DenverCoder9
  • 3,635
  • 3
  • 31
  • 57
0

I am currently fighting a bug where a reference to presentation.transform HANGS forever. The documentation says that references to SceneKit data (e.g. SCNNodes) are thread safe. E.g. they wait on a mutex lock if another another process (or the 3D hardware) is using the node. Although I have not found out who is holding the lock in my case (probably a dumb program bug), it struck me reading your post that if the 3D hardware were using the node, you would wait perhaps milliseconds till it is through. This could cause your performance issue.

Allen King
  • 303
  • 2
  • 8