SceneKit: too much memory persisting

本秂侑毒 提交于 2019-12-01 13:23:55
Hal Mueller

Spheres, capsules, and cylinders all have fairly dense meshes. Do you need all that detail? Try reducing the various segment count properties (segmentCount, radialSegmentCount, etc). As a quick test, substitute SCNPyramid for all of your primitive types (that's the primitive with the lowest vector count). You should see a dramatic reduction in memory use if this is a factor (it will look ugly, but will give you immediate feedback on whether you're on a usable track). Can you use a long SCNBox instead of a cylinder?

Another optimization step would be to use SCNLevelOfDetail to allow substitute, low vertex count geometry when an object is far away. That would be more work than simply reducing the segment counts uniformly, but would pay off if you sometimes need greater detail.

Instead of managing the components yourself in arrays, use the node hierarchy to do that. Create each molecule, or animatable piece of a molecule, as a tree of SCNNodes. Give it a name. Make a flattenedClone. Now archive that. Read the node tree from archive when you need it; don't worry about arrays of nodes.

Consider writing two programs. One is your iOS program that manipulates/displays the molecules. The other is a Mac (or iOS?) program that generates your molecule node trees and archives them. That will give you a bunch of SCNNode tree archives that you can embed, as resources, in your display program, with no on-the-fly generation.

An answer to scene kit memory management using swift notes the need to nil out "textures" (materials or firstMaterial properties?) to release the node. Seems worth a look, although since you're just using UIColor I doubt it's a factor.

Here's an example of creating a compound node and archiving it. In real code you'd separate the archiving from the creation. Note also the use of a long skinny box to simulate a line. Try a chamfer radius of 0!

extension SCNNode {

public class func gizmoNode(axisLength: CGFloat) -> SCNNode {
    let offset = CGFloat(axisLength/2.0)
    let axisSide = CGFloat(0.1)
    let chamferRadius = CGFloat(axisSide)

    let xBox = SCNBox(width: axisLength, height: axisSide, length: axisSide, chamferRadius: chamferRadius)
    xBox.firstMaterial?.diffuse.contents = NSColor.redColor()
    let yBox = SCNBox(width: axisSide, height: axisLength, length: axisSide, chamferRadius: chamferRadius)
    yBox.firstMaterial?.diffuse.contents = NSColor.greenColor()
    let zBox = SCNBox(width: axisSide, height: axisSide, length: axisLength, chamferRadius: chamferRadius)
    zBox.firstMaterial?.diffuse.contents = NSColor.blueColor()
    let xNode = SCNNode(geometry: xBox)
    xNode.name = "X axis"
    let yNode = SCNNode(geometry: yBox)
    yNode.name = "Y axis"
    let zNode = SCNNode(geometry: zBox)
    zNode.name = "Z axis"

    let result = SCNNode()
    result.name = "Gizmo"
    result.addChildNode(xNode)
    result.addChildNode(yNode)
    result.addChildNode(zNode)
    xNode.position.x = offset
    yNode.position.y = offset
    zNode.position.z = offset

    let data = NSKeyedArchiver.archivedDataWithRootObject(result)
    let filename = "gizmo"

    // Save data to file
    let DocumentDirURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)

    // made the extension "plist" so you can easily inspect it by opening in Finder. Could just as well be "scn" or "node"
    // ".scn" can be opened in the Xcode Scene Editor
    let fileURL = DocumentDirURL.URLByAppendingPathComponent(filename).URLByAppendingPathExtension("plist")
    print("FilePath:", fileURL.path)

    if (!data.writeToURL(fileURL, atomically: true)) {
        print("oops")
    }
    return result
}
}   

I did also experience a lot of memory bloat from SceneKit in my app, with similar memory chunks as you in Instruments (C3DGenericSourceCreateDeserializedDataWithAccessors, C3DMeshSourceCreateMutable, etc). I found that setting the geometry property to nil on the SCNNode objects before letting Swift deinitialize them solved it.

In your case, in you cleanup function, do something like:

atomsNode_1.removeFromParentNode()
atomsNode_1.geometry = nil
atomsNode_2.removeFromParentNode()
atomsNode_2.geometry = nil

Another example of how you may implement the cleaning:

class ViewController: UIViewController {
    @IBOutlet weak var sceneView: SCNView!
    var scene: SCNScene!

    // ...

    override func viewDidLoad() {
        super.viewDidLoad()
        scene = SCNScene()
        sceneView.scene = scene

        // ...
    }

    deinit {
        scene.rootNode.cleanup()
    }

    // ...
}

extension SCNNode {
    func cleanup() {
        for child in childNodes {
            child.cleanup()
        }
        geometry = nil
    }
}

If that doesn't work, you may have better success by setting its texture to nil, as reported on scene kit memory management using swift.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!