How to add SCNNodes without blocking the main thread?

I create and add a large number of SCNNodes to the SceneKit scene, which is why the application freezes for a second or two.

I thought I could fix this by putting the whole action in a background thread using DispatchQueue.global(qos: .background).async(), but not cubes. He behaves in exactly the same way.

I saw this answer and put the nodes through SCNView.prepare()before adding them, hoping that this would slow down the background thread and prevent blocking. This is not true.

Here is a test function that reproduces the problem:

func spawnNodesInBackground() {
    // put all the action in a background thread
    DispatchQueue.global(qos: .background).async {
        var nodes = [SCNNode]()
        for i in 0...5000 {
            // create a simple SCNNode
            let node = SCNNode()
            node.position = SCNVector3(i, i, i)
            let geometry = SCNSphere(radius: 1)
            geometry.firstMaterial?.diffuse.contents = UIColor.white.cgColor
            node.geometry = geometry
            nodes.append(node)
        }
        // run the nodes through prepare()
        self.mySCNView.prepare(nodes, completionHandler: { (Bool) in
            // nodes are prepared, add them to scene
            for node in nodes {
                self.myRootNode.addChildNode(node)
            }
        })
    }
}

spawnNodesInBackground(), , (, ), , . , .

, ?

+4
3

, DispatchQueue. - SCNNode, , , , SceneKit.

, SceneKit , . , , SCNNodes, , .

, , , SceneKit renderer(_:updateAtTime:), .

+4

( ).

, prepare() , , , , . , prepare() , , . - .

, geometry node ( ), , root node. 10 ( 48).

, . 11 , 7410 , 8,18 . 60 , 3 1,67 (iPhone 6s).

? , , . , , SCNSceneRenderer present(_:with:incomingPointOfView:transition:completionHandler), .

func spawnNodesInBackgroundClone() {
    print(Date(), "starting")
    DispatchQueue.global(qos: .background).async {
        let tempParentNode = SCNNode()
        tempParentNode.name = "spheres"
        let geometry = SCNSphere(radius: 0.4)
        geometry.segmentCount = 10
        geometry.firstMaterial?.diffuse.contents = UIColor.green.cgColor
        for x in -10...10 {
            for y in -10...10 {
                for z in 0...20 {
                    let node = SCNNode()
                    node.position = SCNVector3(x, y, -z)
                    node.geometry = geometry
                    tempParentNode.addChildNode(node)
                }
            }
        }
        print(Date(), "cloning")
        let scnView = self.view as! SCNView
        let cloneNode = tempParentNode.flattenedClone()
        print(Date(), "adding")
        DispatchQueue.main.async {
            print(Date(), "main queue")
            print(Date(), "prepare()")
            scnView.prepare([cloneNode], completionHandler: { (Bool) in
                scnView.scene?.rootNode.addChildNode(cloneNode)
                print(Date(), "added")
            })
            // only do this once, on the simulator
            // let sceneData = NSKeyedArchiver.archivedData(withRootObject: scnView.scene!)
            // try! sceneData.write(to: URL(fileURLWithPath: "/Users/hal/scene.scn"))
            print(Date(), "queued")
        }
    }
}
+1

10000 . node, .

SCNAction node, node.

let action = runBlock { 
    Container in
    // generate nodes
    /// then For each node in generatedNodes
    Container.addChildNode(node)
}

node , .

50 , , . pregen, simd, .

I am considering using a pyramid for LOD, but a 5 x 10 x 15 block works for my purpose. Also, this method can be easily throttled to add only the number of blocks at a time, creating and passing multiple actions to the node. Initially, I passed each node as an action, but this method also works.

Displaying the entire 10000 field still affects the FPS a little by 10 20 FPS, but at this moment a single ring appears in the container nodes that own the LOD.

0
source

Source: https://habr.com/ru/post/1663934/


All Articles