2

Update: For anyone who stumbles upon this, it seems like SceneKit has a threshold for the maximum number of objects it can render. Using [SCNNode flattenedClone] is a great way to help increase the amount of objects it can handle. As @Hal suggested, I'll file a bug report with Apple describing the performance issues discussed below.

I'm somewhat new to iOS and I'm currently working on my first OS X project for a class. I'm essentially creating random geometric graphs (random points in space connected to one another if the distance between them is ≤ a given radius) and I'm using SceneKit to display the results. I already know I'm pushing SceneKit to its limits, but if the number of objects I'm trying to graph is too large, the whole thing just crashes and I don't know how to interpret the results.

My SceneKit scene consists of the default camera, 2 lighting nodes, approximately 5,000 SCNSpheres each within an SCNNode (the nodes on the graph), and then about 50,0000 connections which are of type SCNPrimitiveSCNGeometryPrimitiveTypeLine which are also within SCNNodes. All of these nodes are then added to one large node which is added to my scene.

The code works for smaller numbers of spheres and connections.

When I run my app with these specifications, everything seems to work fine, then 5-10 seconds after executing the following lines:

dispatch_async(dispatch_get_main_queue(), ^{ [self.graphSceneView.scene.rootNode addChildNode:graphNodes]; });

the app crashes with this resulting screen: this resulting screen.

Given that I'm sort of new to Xcode and used to more verbose output upon crashing, I'm a bit over my head. What can I do to get more information on this crash?

Spencer
  • 373
  • 5
  • 20

1 Answers1

1

That's terse output for sure. You can attack it by simplifying until you don't see the crash anymore.

First, do you ever see anything on screen?

Second, your call to

dispatch_async(dispatch_get_main_queue(), ^{
    [self.graphSceneView.scene.rootNode addChildNode:graphNodes];
});

still runs on the main queue, so I would expect it to make no difference in perceived speed or responsiveness. So take addChildNode: out of the GCD block and invoke it directly. Does that make a difference? At the least, you'll see your crash immediately, and might get a better stack trace.

Third, creating your own geometry from primitives like SCNPrimitiveSCNGeometryPrimitiveTypeLine is trickier than using the SCNGeometry subclasses. Memory mismanagement in that step could trigger mysterious crashes. What happens if you remove those connection lines? What happens if you replace them with long, skinny SCNBox instances? You might end up using SCNBox by choice because it's easier to style in SceneKit than a primitive line.

Fourth, take a look at @rickster's answer to this question about optimization: SceneKit on OS X with thousands of objects. It sounds like your project would benefit from node flattening (flattenedClone), and possibly the use of SCNLevelOfDetail. But these suggestions fall into the category of premature optimization, the root of all evil.

It would be interesting to hear what results from toggling between the Metal and OpenGL renderers. That's a setting on the SCNView in IB ("preferred renderer" I think), and an optional entry in Info.plist.

Community
  • 1
  • 1
Hal Mueller
  • 7,019
  • 2
  • 24
  • 42
  • Thanks for the detailed response! Ok, so (1) _First, do you ever see anything on screen?_ Yes! The app works perfectly for a smaller number of nodes and edges. If I do 5,000 with a degree of 10 (50,000 SCNNodes), it works fine. (2) Regarding the main queue, moving that call to `addChildNode` outside the block doesn't seem to affect anything whatsoever; I was just doing that because I've always been taught to place things that affect the UI on the main queue. I'll remove it per your advice. (3) _ What happens if you remove those connection lines?_ I can handle substantially more SCNNodes... – Spencer Dec 14 '15 at 05:27
  • [Cont.] The reason why I didn't use another SCNGeometry subclass is that it seemed to have the same effect but the threshold it could handle was much lower. I used cylinders but SceneKit just couldn't handle that many objects. It was also very difficult to get the cylinder placement and rotation correct, but I saved that code so I can switch back if that seems like a better route. And finally, I'll make sure to check out that link, it sounds like an awesome resource. Thanks again for the help, let me know if my responses help at all! – Spencer Dec 14 '15 at 05:30
  • 1
    That all sounds like you're hitting limits, then, and that your implementation code is correct. I suggested SCNBox/SCNCylinder only to rule out rookie mistakes--so scratch that idea. I think your best bet is `flattenedClone` of your graph before adding it to the scene. Be sure to file a performance report, with your project, at bugreport.apple.com. – Hal Mueller Dec 14 '15 at 05:44
  • So `flattenedClone`sounds like it's exactly what I need. After using it, I actually got a verbose error, so that's something haha Here's what it resulted in: `SceneKit: error, RendererElementStore does not support span of more than 65536` – Spencer Dec 14 '15 at 06:25
  • Maybe split your connection lines off into multiple nodes? I've never seen that error. You should file a bug report against the documentation for sure. – Hal Mueller Dec 14 '15 at 06:39
  • Well it's super slow, but using `flattenedClone` absolutely increased the threshold! Thank you so much for your help!! – Spencer Dec 14 '15 at 06:42
  • rdar://23886236 filed for better documentation of these limits. – Hal Mueller Dec 15 '15 at 03:38