mvaganov

Unity3D memory pool

May 10th, 2015
272
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // MIT license - TL;DR - Do whatever you want with it, I won't fix it for you!
  2. #define FAIL_FAST
  3. using UnityEngine;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6.  
  7. /// <summary>
  8. /// <para>Used for memory recycling of reference types, like GameObjects</para>
  9. /// <para>Example usage:</para>
  10. /// <para>MemoryPool&lt;GameObject&gt; memPool = new MemoryPool&lt;GameObject&gt;(); // construction</para>
  11. /// <para>memPool.Setup(
  12. ///     ()    => { return Instantiate(prefab); },
  13. ///     (obj) => { obj.SetActive(true);  },
  14. ///     (obj) => { obj.SetActive(false); },
  15. ///     (obj) => { Object.Destroy(obj);  }
  16. /// );</para>
  17. /// <para>GameObject gobj = memPool.Alloc();  // allocate object in pool</para>
  18. /// <para>memPool.Free(gobj); // deallocate object in pool</para>
  19. /// <para>MemoryPoolItem.Destroy(gobj); // deallocate object in pool OR Object.Destroy non-MemoryPool object (for GameObjects only)</para>
  20. /// </summary>
  21. public class MemoryPool<T> where T : class {
  22.     private List<T> allObjects = null;
  23.     private int freeObjectCount = 0;
  24.  
  25.     public delegate    T DelegateBeginLife();
  26.     public delegate void DelegateCommission(T obj);
  27.     public delegate void DelegateDecommission(T obj);
  28.     public delegate void DelegateEndLife(T obj);
  29.  
  30.     public DelegateBeginLife birth;
  31.     public DelegateEndLife death;
  32.     public DelegateCommission commission;
  33.     public DelegateDecommission decommission;
  34.  
  35.     /// <summary></summary>
  36.     /// <returns>True if Setup was called with all non-null methods</returns>
  37.     public bool IsFullySetup() { return birth != null && commission != null && decommission != null && death != null; }
  38.  
  39.     /// <summary>
  40.     /// Example usage:
  41.     /// <para>memPool.Setup(
  42.     ///     ()    => Instantiate(prefab),
  43.     ///     (obj) => obj.SetActive(true),
  44.     ///     (obj) => obj.SetActive(false),
  45.     ///     (obj) => Object.Destroy(obj)
  46.     /// );</para>
  47.     /// </summary>
  48.     /// <param name="create">callback function or delegate used to create a new object of type T</param>
  49.     /// <param name="activate">callback function or delegate used to activate an object of type T</param>
  50.     /// <param name="deactivate">callback function or delegate used to de-activate an object of type T</param>
  51.     /// <param name="destroy">callback function or delegate used to destroy an object of type T</param>
  52.     public void Setup(DelegateBeginLife create, DelegateCommission activate, DelegateDecommission deactivate, DelegateEndLife destroy) {
  53.         birth = create; commission = activate; decommission = deactivate; death = destroy;
  54.     }
  55.  
  56.     /// <summary>Constructs and calls <see cref="Setup"/></summary>
  57.     public MemoryPool(DelegateBeginLife create, DelegateCommission activate, DelegateDecommission deactivate, DelegateEndLife destroy) {
  58.         Setup(create, activate, deactivate, destroy);
  59.     }
  60.  
  61.     /// <summary> Be sure to call <see cref="Setup"/>!</summary>
  62.     public MemoryPool() {}
  63.  
  64.     /// <summary>Returns an object from the memory pool, which may have just been created</summary>
  65.     public T Alloc() {
  66.         T freeObject = null;
  67.         if(freeObjectCount == 0) {
  68. #if FAIL_FAST
  69.             if(birth == null) { throw new System.Exception("Call .Setup(), and provide a create method!"); }
  70. #endif
  71.             if(allObjects == null) { allObjects = new List<T>(); }
  72.             freeObject = birth();
  73.             allObjects.Add(freeObject);
  74.             if(typeof(T) == typeof(GameObject)) {
  75.                 GameObject go = freeObject as GameObject;
  76.                 go.AddComponent<MemoryPoolItem>().SetPool(this as MemoryPool<GameObject>);
  77.             }
  78.         } else {
  79.             freeObject = allObjects[allObjects.Count - freeObjectCount];
  80.             freeObjectCount--;
  81.         }
  82.         if(commission != null) { commission(freeObject); }
  83.         return freeObject;
  84.     }
  85.  
  86.     /// <summary>Which object to mark as free in the memory pool</summary>
  87.     public void Free(T obj) {
  88.         int indexOfObject = allObjects.IndexOf(obj);
  89. #if FAIL_FAST
  90.         if(indexOfObject < 0) { throw new System.Exception("woah, this isn't one of mine..."); }
  91.         if(indexOfObject >= (allObjects.Count - freeObjectCount)) { throw new System.Exception("hey, you're freeing this twice..."); }
  92. #endif
  93.         freeObjectCount++;
  94.         int beginningOfFreeList = allObjects.Count - freeObjectCount;
  95.         allObjects[indexOfObject] = allObjects[beginningOfFreeList];
  96.         allObjects[beginningOfFreeList] = obj;
  97.         if(decommission != null) { decommission(obj); }
  98.     }
  99.  
  100.     /// <summary>performs the given delegate on each object in the memory pool</summary>
  101.     public void ForEach(DelegateCommission action) {
  102.         for(int i = 0; i < allObjects.Count; ++i) { action(allObjects[i]); }
  103.     }
  104.  
  105.     /// <summary>Destroys all objects in the pool, after deactivating each one.</summary>
  106.     public void DeallocateAll() {
  107.         ForEach((item) => decommission(item));
  108.         if(typeof(T) == typeof(GameObject)) {
  109.             ForEach((item) => {
  110.                 GameObject go = item as GameObject;
  111.                 Object.DestroyImmediate(go.GetComponent<MemoryPoolItem>());
  112.             });
  113.         }
  114.         if(death != null) { ForEach((item) => death(item)); }
  115.         allObjects.Clear();
  116.     }
  117. }
  118.  
  119. public class MemoryPool : MonoBehaviour {
  120.     MemoryPool<GameObject> pool = new MemoryPool<GameObject>();
  121.     [Tooltip("An object to create and destroy a lot of"), ContextMenuItem("Create Another", "CreateAnother")]
  122.     public GameObject prefab;
  123.  
  124.     public void Init() {
  125.         if(!pool.IsFullySetup()) {
  126.             pool.Setup(() => Instantiate(prefab), (obj) => obj.SetActive(true), (obj) => obj.SetActive(false), (obj) => Destroy(obj));
  127.         }
  128.     }
  129.  
  130.     public void Awake() { Init(); }
  131.  
  132.     public GameObject CreateAnother() {
  133. #if UNITY_EDITOR
  134.         Init();
  135. #endif
  136.         AssertInit(); return pool.Alloc();
  137.     }
  138.  
  139.     public void AssertInit() {
  140. #if FAIL_FAST
  141.         if(!pool.IsFullySetup()) throw new System.Exception("Call Init() on this memory pool before using it");
  142. #endif
  143.     }
  144.  
  145.     public void Setup(MemoryPool<GameObject>.DelegateBeginLife create, MemoryPool<GameObject>.DelegateCommission activate,
  146.         MemoryPool<GameObject>.DelegateDecommission deactivate, MemoryPool<GameObject>.DelegateEndLife destroy) {
  147.         pool.Setup(create, activate, deactivate, destroy);
  148.     }
  149.  
  150.     /// <summary> Be sure to call <see cref="Setup"/>!</summary>
  151.     public MemoryPool() { }
  152.  
  153.     /// <summary>Returns an object from the memory pool, which may have just been created</summary>
  154.     public GameObject Alloc() { AssertInit(); return pool.Alloc(); }
  155.  
  156.     /// <summary>Which object to mark as free in the memory pool</summary>
  157.     public void Free(GameObject obj) { AssertInit(); pool.Free(obj); }
  158.  
  159.     /// <summary>performs the given delegate on each object in the memory pool</summary>
  160.     public void ForEach(MemoryPool<GameObject>.DelegateCommission action) { AssertInit(); pool.ForEach(action); }
  161.  
  162.     /// <summary>Destroys all objects in the pool, after deactivating each one.</summary>
  163.     public void DeallocateAll() { AssertInit(); pool.DeallocateAll(); }
  164.  
  165.     /// <summary>If the given GameObject belongs to a memory pool, mark it as free in that pool. Otherwise, Object.Destroy()</summary>
  166.     static public void Destroy(GameObject go) { MemoryPoolItem.Destroy(go); }
  167. }
  168.  
  169. [System.Serializable]
  170. public class MemoryPoolItem : MonoBehaviour {
  171.     private MemoryPool<GameObject> gameObjectPool;
  172.     public MemoryPoolItem SetPool(MemoryPool<GameObject> pool) { gameObjectPool = pool; return this; }
  173.     static private bool shuttingDown = false;
  174.     static public void SetShutdown(bool sceneIsEnding) { shuttingDown = sceneIsEnding; }
  175. #if FAIL_FAST
  176.     void OnApplicationQuit() { SetShutdown(true); }
  177.     void Start() { SetShutdown(false); }
  178.     void OnDestroy() {
  179.         if(!shuttingDown) throw new System.Exception("Instead of Object.Destroy("+gameObject+"), call MemoryPoolItem.Destroy("+gameObject+")\n"
  180.             +"When changing levels, call MemoryPoolItem.SetShutdown(true) first");
  181.     }
  182. #endif
  183.     public void FreeSelf() { gameObjectPool.Free(gameObject); }
  184.     /// <summary>If the given GameObject belongs to a memory pool, mark it as free in that pool. Otherwise, Object.Destroy()</summary>
  185.     static public void Destroy(GameObject go) {
  186.         MemoryPoolItem i = go.GetComponent<MemoryPoolItem>();
  187.         if(i != null) { i.FreeSelf(); }
  188.         else { Object.Destroy(go); }
  189.     }
  190. }
RAW Paste Data