| using System; | 
| using System.Collections.Generic; | 
| using System.Linq; | 
| using System.Text; | 
| using MemoryProfilerWindow; | 
| using UnityEditor.MemoryProfiler; | 
| using UnityEngine; | 
|   | 
| namespace MemoryProfilerWindow | 
| { | 
|     class ShortestPathToRootFinder | 
|     { | 
|         private readonly CrawledMemorySnapshot _snapshot; | 
|   | 
|         public ShortestPathToRootFinder(CrawledMemorySnapshot snapshot) | 
|         { | 
|             _snapshot = snapshot; | 
|         } | 
|   | 
|         public ThingInMemory[] FindFor(ThingInMemory thing) | 
|         { | 
|             var seen = new HashSet<ThingInMemory>(); | 
|             var queue = new Queue<List<ThingInMemory>>(); | 
|             queue.Enqueue(new List<ThingInMemory> { thing}); | 
|   | 
|             while (queue.Any()) | 
|             { | 
|                 var pop = queue.Dequeue(); | 
|                 var tip = pop.Last(); | 
|   | 
|                 string reason; | 
|                 if (IsRoot(tip, out reason)) | 
|                     return pop.ToArray(); | 
|   | 
|                 foreach (var next in tip.referencedBy) | 
|                 { | 
|                     if (seen.Contains(next)) | 
|                         continue; | 
|                     seen.Add(next); | 
|                     var dupe = new List<ThingInMemory>(pop) {next}; | 
|                     queue.Enqueue(dupe); | 
|                 } | 
|             } | 
|             return null; | 
|         } | 
|   | 
|         public bool IsRoot(ThingInMemory thing, out string reason) | 
|         { | 
|             reason = null; | 
|             if (thing is StaticFields) | 
|             { | 
|                 reason = "Static fields are global variables. Anything they reference will not be unloaded."; | 
|                 return true; | 
|             } | 
|             if (thing is ManagedObject) | 
|                 return false; | 
|             if (thing is GCHandle) | 
|                 return false; | 
|   | 
|             var nativeObject = thing as NativeUnityEngineObject; | 
|             if (nativeObject == null) | 
|                 throw new ArgumentException("Unknown type: " + thing.GetType()); | 
|             if (nativeObject.isManager) | 
|             { | 
|                 reason = "this is an internal unity'manager' style object, which is a global object that will never be unloaded"; | 
|                 return true; | 
|             } | 
|             if (nativeObject.isDontDestroyOnLoad) | 
|             { | 
|                 reason = "DontDestroyOnLoad() was called on this object, so it will never be unloaded"; | 
|                 return true; | 
|             } | 
|   | 
|             if ((nativeObject.hideFlags & HideFlags.DontUnloadUnusedAsset) != 0) | 
|             { | 
|                 reason = "the DontUnloadUnusedAsset hideflag is set on this object. Unity's builtin resources set this flag. Users can also set the flag themselves"; | 
|                 return true; | 
|             } | 
|   | 
|             if (nativeObject.isPersistent) | 
|                 return false; | 
|   | 
|             if (IsComponent(nativeObject.classID)) | 
|             { | 
|                 reason = "this is a component, living on a gameobject, that is either part of the loaded scene, or was generated by script. It will be unloaded on next scene load."; | 
|                 return true; | 
|             } | 
|   | 
|             if (IsGameObject(nativeObject.classID)) | 
|             { | 
|                 reason = "this is a gameobject, that is either part of the loaded scene, or was generated by script. It will be unloaded on next scene load if nobody is referencing it"; | 
|                 return true; | 
|             } | 
|   | 
|             if (IsAssetBundle(nativeObject.classID)) | 
|             { | 
|                 reason = "this object is an assetbundle, which is never unloaded automatically, but only through an explicit .Unload() call."; | 
|                 return true; | 
|             } | 
|   | 
|             reason = "This object is a root, but the memory profiler UI does not yet understand why"; | 
|             return true; | 
|         } | 
|   | 
|         private bool IsGameObject(int classID) | 
|         { | 
|             return _snapshot.nativeTypes[classID].name == "GameObject"; | 
|         } | 
|   | 
|         private bool IsAssetBundle(int classID) | 
|         { | 
|             return _snapshot.nativeTypes[classID].name == "AssetBundle"; | 
|         } | 
|   | 
|         private bool IsComponent(int classID) | 
|         { | 
|             var packedNativeType = _snapshot.nativeTypes[classID]; | 
|   | 
|             if (packedNativeType.name == "Component") | 
|                 return true; | 
|   | 
| #if UNITY_5_6_OR_NEWER | 
|             var baseClassID = packedNativeType.nativeBaseTypeArrayIndex; | 
| #else | 
|             var baseClassID = packedNativeType.baseClassId; | 
| #endif | 
|   | 
|             return baseClassID != -1 && IsComponent(baseClassID); | 
|         } | 
|     } | 
| } |