2016-02-28 113 views
0

我在這裏沒有想法,SceneKit在內存上堆積如山,我只是開始了。我顯示的是SNCNodes,它們存儲在數組中,所以我可以分離動畫分子的各個組成部分。這些樹模型我最終可能會顯示50個分子,每個「章節」說一個。問題是,當我轉到另一章時,前幾章的分子持續記憶。SceneKit:太多內存持續存在

分子節點是子節點的樹。大約一半的節點是用於定位的空容器。否則,幾何形狀是SCNPrimitives(球體,膠囊和圓柱體)。每個幾何都有一個鏡面和一個由UIColor組成的漫反射material,不使用紋理。

當應用第一次啓動時,這些分子是從代碼構建的,並存檔到一本字典中。然後,在後續的引導中,將歸檔字典讀入本地字典供VC使用。 (我在這個崗位爲簡潔移除的安全功能。)

moleculeDictionary = Molecules.readFile() as! [String: [SCNNode]] 

當一個章節要顯示一個分子,它調用加載從本地詞典給定的分子所需的組件到本地SCNNode特定功能屬性。

// node stores (reuseable) 
var atomsNode_1 = SCNNode() 
var atomsNode_2 = SCNNode() 
     . . . 

func lysozyme() { // called by a chapter to display this molecule 
     . . . 
    components = moleculeDictionary["lysozyme"] 

    atomsNode_1 = components[0]   // protein w/CPK color 
    baseNode.addChildNode(atomsNode_1) 
    atomsNode_2 = components[2]   // NAG 
    baseNode.addChildNode(atomsNode_2) 
     . . . 
} 

要顯示的下一個分子之前,我所說的「清理」功能:

atomsNode_1.removeFromParentNode() 
atomsNode_2.removeFromParentNode() 
     . . . 

當我在儀器研究,最臃腫的內存是32KB的塊由C3DMeshCreateFromProfile稱爲和80kB大小的C3DMeshCreateCopyWithInterleavedSources

我也有泄漏我需要跟蹤哪些是可追溯到檔案的解碼的NSKeyedUnarchiver。所以我也需要處理這些,但它們只是積累每個分子呼叫的記憶使用的一小部分。

如果我回到先前查看的分子,內存使用量不會再增加,它會累積並持續存在。

我試過宣佈atomsNode_1及其親屬作爲可選項,然後在清理時將它們設置爲零。沒有幫助。我試過,在清理功能,

atomsNode_1.enumerateChildNodesUsingBlock({ 
    node, stop in 
    node.removeFromParentNode() 
}) 

好了,記憶又回到下降,但節點似乎現在從加載的字典中永遠消失。該死的參考類型!

因此,也許我需要一種方法來存檔[SCNNode]數組,以解壓和單獨檢索它們。在這種情況下,我會將它們從內存中清除,並在重新訪問該分子時從存檔中重新加載。但我還不知道如何去做這些。在投入更多時間感到沮喪之前,我會很感激這方面的意見。

回答

2

球體,膠囊和圓柱體都有相當密實的網格。你需要所有的細節嗎?嘗試減少不同的段計數屬性(segmentCount,radialSegmentCount等)。作爲一種快速測試,將所有基元類型(即具有最低向量數的基元)替換爲SCNPyramid。如果這是一個因素,你應該看到內存使用量急劇減少(它看起來很醜,但會立即給你提供關於你是否在可用軌道上的反饋)。你可以使用長的SCNBox而不是圓柱體嗎?

另一個優化步驟是使用SCNLevelOfDetail來允許當物體很遠時使用替代的低頂點數幾何。這將比單純地減少細分市場數量更有效,但如果您有時需要更多細節,則會付出代價。

除了自己在數組中管理組件外,還可以使用節點層次結構來完成此操作。以SCNNodes的樹形成每個分子或分子的動畫片。給它一個名字。製作一個flattenedClone。現在將其歸檔。在需要時從檔案中讀取節點樹;不要擔心節點數組。

考慮編寫兩個程序。一個是你的iOS程序,可以操縱/顯示分子。另一種是Mac(或iOS?)程序,它可以生成分子節點樹並將其歸檔。這將爲您提供一堆SCNNode樹檔案,您可以將它們作爲資源嵌入到您的顯示程序中,而不需要即時生成。

scene kit memory management using swift的回答指出需要清除「紋理」(materialsfirstMaterial屬性?)以釋放節點。似乎值得一看,但因爲你只是使用UIColor,我懷疑這是一個因素。

下面是創建複合節點並將其存檔的示例。在真實代碼中,您可以將存檔與創建分開。還要注意使用一個很長的瘦箱子來模擬一條線。嘗試一個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 
} 
} 
+0

哈爾,感謝您的反饋。盒子不會削減它,它對分子顯示器來說看起來非常不正統。在一些模型上,我可能會減少多邊形數量,但是我會有儘可能多的模型,在長期運行中效率不會很高。我真的需要防止模型積累。我認爲每次閱讀它們都是潛在的解決方案,但到目前爲止,我只設法將它們一起保存在一本字典中。有節點在數組中的動畫原因加上:它沒有更多的內存(我測試過)加上比枚舉更快的動畫。 – bpedit

+0

所以,我真的很喜歡你的想法:「一堆嵌入資源的節點樹......」。事實上,我已經在這裏發佈了2個問題,如何做到這一點,但沒有收到任何迴應!建議歡迎!再次感謝,Byrne – bpedit

+0

用一些示例代碼編輯。您可能會搔癢SceneKit內存循環; http://stackoverflow.com/questions/32997711/removing-scnnode-does-not-free-memory-before-creating-new-scnnode?rq=1可能是一回事。作爲一種解決方法,您可以嘗試銷燬/重新創建SCNScene或SCNView。 –

1

我也經歷從SceneKit大量內存膨脹在我的應用程序,並在儀器類似的內存塊,你(C3DGenericSourceCreateDeserializedDataWithAccessorsC3DMeshSourceCreateMutable等)。我發現設置geometry屬性爲nil上的SCNNode對象之前讓Swift去初始化他們解決了它。

在你的情況,你的清理功能,這樣做:

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

的你會如何實施清洗又如:

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 
    } 
} 

如果不工作,你可能通過將其紋理設置爲nil可獲得更好的成功,如scene kit memory management using swift所報告。

+0

謝謝!已經有一段時間了,我不確定所有幫助的改進。但目前最大的因素是將我的模型節點樹存檔爲資源,然後根據需要調用。感謝Hal Mueller在另一個主題中的幫助。 – bpedit