< Summary

Class:DCL.Pool
Assembly:PoolManager
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/PoolManager/Pool.cs
Covered lines:102
Uncovered lines:36
Coverable lines:138
Total lines:353
Line coverage:73.9% (102 of 138)
Covered branches:0
Total branches:0
Covered methods:24
Total methods:25
Method coverage:96% (24 of 25)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Pool(...)0%220100%
ForcePrewarm(...)0%24.436020%
InstantiateOnPrewarm(...)0%6200%
PrewarmAsync()0%17.85020%
Get()0%5.095084.62%
Get[T]()0%110100%
Extract()0%110100%
Instantiate()0%110100%
InstantiateAsOriginal()0%220100%
SetupPoolableObject(...)0%3.013091.67%
Release(...)0%330100%
ReleaseAll()0%220100%
AddToPool(...)0%5.44055.56%
RemoveFromPool(...)0%3.023087.5%
Cleanup()0%4.134080%
EnablePoolableObject(...)0%3.023087.5%
DisablePoolableObject(...)0%6.566075%
RefreshName()0%220100%
FindPoolInGameObject(...)0%2.262060%
IsValid()0%110100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/PoolManager/Pool.cs

#LineLine coverage
 1using System.Collections.Generic;
 2using System.Threading;
 3using Cysharp.Threading.Tasks;
 4using DCL.Configuration;
 5using UnityEngine;
 6using DCL.Helpers;
 7using UnityEngine.Assertions;
 8
 9namespace DCL
 10{
 11    public interface IPooledObjectInstantiator
 12    {
 13        bool IsValid(GameObject original);
 14
 15        GameObject Instantiate(GameObject gameObject);
 16    }
 17
 18    public class Pool : ICleanable
 19    {
 20        public delegate void OnReleaseAllDlg(Pool pool);
 21
 22        public const int PREWARM_ACTIVE_MULTIPLIER = 2;
 23        public object id;
 24        public GameObject original;
 25        public GameObject container;
 26
 27        public bool persistent = false;
 28
 29        /// <summary>
 30        /// If this is set to true, all Unity components in the poolable gameObject implementing ILifecycleHandler
 31        /// will be registered and called when necessary.
 32        ///
 33        /// The interface call responsibility lies in the PoolableObject class.
 34        /// </summary>
 35        public bool useLifecycleHandlers = false;
 36
 37        public System.Action<Pool> OnCleanup;
 38
 39        public IPooledObjectInstantiator instantiator;
 40
 553741        private readonly LinkedList<PoolableObject> unusedObjects = new LinkedList<PoolableObject>();
 553742        private readonly LinkedList<PoolableObject> usedObjects = new LinkedList<PoolableObject>();
 43
 44        private readonly int maxPrewarmCount;
 45
 46        private bool isInitialized;
 47
 61848        public float lastGetTime { get; private set; }
 49
 512650        public int objectsCount => unusedObjectsCount + usedObjectsCount;
 51
 728552        public int unusedObjectsCount => unusedObjects.Count;
 53
 1302454        public int usedObjectsCount => usedObjects.Count;
 55
 553756        public Pool(string name, int maxPrewarmCount)
 57        {
 553758            if (PoolManager.USE_POOL_CONTAINERS)
 553759                container = new GameObject("Pool - " + name) { transform = { position = EnvironmentSettings.MORDOR } };
 60
 553761            this.maxPrewarmCount = maxPrewarmCount;
 553762        }
 63
 64        public void ForcePrewarm(bool forceActive = true)
 65        {
 512266            if (maxPrewarmCount <= objectsCount)
 512267                return;
 68
 069            Assert.IsTrue(original != null, $"Original should never be null here ({id})");
 070            var parent = PoolManager.USE_POOL_CONTAINERS && container != null ? container.transform : null;
 71
 072            int objectsToInstantiate = Mathf.Max(0, maxPrewarmCount - objectsCount);
 73
 074            for (var i = 0; i < objectsToInstantiate; i++)
 075                InstantiateOnPrewarm(parent, forceActive);
 076        }
 77
 78        /// <summary>
 79        /// Lightweight version of Instantiate() that doesn't activate/deactivate the gameObject and parent it straight 
 80        /// </summary>
 81        private void InstantiateOnPrewarm(Transform parent, bool forceActive = true)
 82        {
 083            GameObject gameObject = Object.Instantiate(original, parent);
 84
 085            PoolableObject poolable = new PoolableObject(this, gameObject);
 086            PoolManager.i.poolables.Add(gameObject, poolable);
 087            PoolManager.i.poolableValues.Add(poolable);
 88
 089            poolable.node = unusedObjects.AddFirst(poolable);
 90
 091            if (forceActive)
 92            {
 093                gameObject.SetActive(true);
 094                gameObject.SetActive(false);
 95            }
 96#if UNITY_EDITOR
 097            RefreshName();
 98#endif
 099        }
 100
 101        public async UniTask PrewarmAsync(int createPerFrame, CancellationToken cancellationToken)
 102        {
 103            int objectsToInstantiate;
 104
 105            // It is dynamic in case we instantiate manually between frames
 4106            while ((objectsToInstantiate = Mathf.Max(0, maxPrewarmCount - objectsCount)) > 0)
 107            {
 0108                objectsToInstantiate = Mathf.Min(createPerFrame, objectsToInstantiate);
 109
 0110                for (var i = 0; i < objectsToInstantiate; i++)
 0111                    Instantiate();
 112
 0113                await UniTask.NextFrame(cancellationToken);
 114            }
 4115        }
 116
 117        /// <summary>
 118        /// This will return an instance of the poolable object
 119        /// </summary>
 120        /// <returns></returns>
 121        public PoolableObject Get()
 122        {
 123            // These extra instantiations during initialization are to populate pools that will be used a lot later
 615124            if (PoolManager.i.initializing && !isInitialized)
 125            {
 403126                isInitialized = true;
 127
 806128                for (int i = unusedObjectsCount; i < Mathf.Min(usedObjectsCount * PREWARM_ACTIVE_MULTIPLIER, maxPrewarmC
 0129                    Instantiate();
 130
 403131                Instantiate();
 132            }
 347133            else if (unusedObjects.Count == 0) { Instantiate(); }
 134
 615135            PoolableObject poolable = Extract();
 136
 615137            EnablePoolableObject(poolable);
 615138            poolable.OnPoolGet();
 139
 615140            return poolable;
 141        }
 142
 143        public T Get<T>() where T: MonoBehaviour =>
 54144            this.Get().gameObject.GetComponent<T>();
 145
 146        private PoolableObject Extract()
 147        {
 615148            PoolableObject po = null;
 615149            po = unusedObjects.First.Value;
 615150            unusedObjects.RemoveFirst();
 615151            po.node = usedObjects.AddFirst(po);
 152
 153#if UNITY_EDITOR
 615154            RefreshName();
 155#endif
 615156            return po;
 157        }
 158
 159        private void Instantiate() =>
 538160            SetupPoolableObject(
 161                InstantiateAsOriginal());
 162
 163        public GameObject InstantiateAsOriginal()
 164        {
 549165            Assert.IsTrue(original != null, $"Original should never be null here ({id})");
 166
 549167            GameObject gameObject = instantiator != null
 168                ? instantiator.Instantiate(original)
 169                : Object.Instantiate(original);
 170
 549171            gameObject.SetActive(true);
 172
 549173            return gameObject;
 174        }
 175
 176        private void SetupPoolableObject(GameObject gameObject, bool active = false)
 177        {
 541178            if (PoolManager.i.poolables.ContainsKey(gameObject))
 0179                return;
 180
 541181            PoolableObject poolable = new PoolableObject(this, gameObject);
 541182            PoolManager.i.poolables.Add(gameObject, poolable);
 541183            PoolManager.i.poolableValues.Add(poolable);
 184
 541185            if (!active)
 186            {
 538187                DisablePoolableObject(poolable);
 538188                poolable.node = unusedObjects.AddFirst(poolable);
 189            }
 190            else
 191            {
 3192                EnablePoolableObject(poolable);
 3193                poolable.node = usedObjects.AddFirst(poolable);
 194            }
 195
 196#if UNITY_EDITOR
 541197            RefreshName();
 198#endif
 541199        }
 200
 201        public void Release(PoolableObject poolable)
 202        {
 784203            if (poolable == null || !PoolManager.i.HasPoolable(poolable))
 186204                return;
 205
 598206            DisablePoolableObject(poolable);
 207
 598208            poolable.node.List.Remove(poolable.node);
 598209            poolable.node = unusedObjects.AddFirst(poolable);
 210
 211#if UNITY_EDITOR
 598212            RefreshName();
 213#endif
 598214        }
 215
 216        public void ReleaseAll()
 217        {
 6445218            while (usedObjects.Count > 0) { usedObjects.First.Value.Release(); }
 5593219        }
 220
 221        /// <summary>
 222        /// This will add a gameObject that is not on any pool to this pool.
 223        /// </summary>
 224        /// <param name="gameObject"></param>
 225        public void AddToPool(GameObject gameObject, bool addActive = true)
 226        {
 3227            if (instantiator != null && !instantiator.IsValid(gameObject))
 228            {
 0229                Debug.LogError($"ERROR: Trying to add invalid gameObject to pool! -- {gameObject.name}", gameObject);
 230
 0231                return;
 232            }
 233
 3234            PoolableObject obj = PoolManager.i.GetPoolable(gameObject);
 235
 3236            if (obj != null)
 237            {
 0238                Debug.LogError($"ERROR: gameObject is already being tracked by a pool! -- {gameObject.name}", gameObject
 239
 0240                return;
 241            }
 242
 3243            SetupPoolableObject(gameObject, addActive);
 3244        }
 245
 246        public void RemoveFromPool(PoolableObject poolable)
 247        {
 1248            if (poolable.node != null)
 249            {
 1250                if (poolable.node.List != null)
 0251                    poolable.node.List.Remove(poolable);
 252
 1253                poolable.node = null;
 254            }
 255
 1256            PoolManager.i.poolables.Remove(poolable.gameObject);
 1257            PoolManager.i.poolableValues.Remove(poolable);
 258#if UNITY_EDITOR
 1259            RefreshName();
 260#endif
 1261        }
 262
 263        public void Cleanup()
 264        {
 5518265            ReleaseAll();
 266
 6029267            while (unusedObjects.Count > 0)
 268            {
 511269                PoolManager.i.poolables.Remove(unusedObjects.First.Value.gameObject);
 511270                PoolManager.i.poolableValues.Remove(unusedObjects.First.Value);
 511271                unusedObjects.RemoveFirst();
 272            }
 273
 5518274            while (usedObjects.Count > 0)
 275            {
 0276                PoolManager.i.poolables.Remove(usedObjects.First.Value.gameObject);
 0277                PoolManager.i.poolableValues.Remove(usedObjects.First.Value);
 0278                usedObjects.RemoveFirst();
 279            }
 280
 5518281            unusedObjects.Clear();
 5518282            usedObjects.Clear();
 283
 5518284            Utils.SafeDestroy(this.original);
 285
 5518286            Utils.SafeDestroy(this.container);;
 287
 5518288            OnCleanup?.Invoke(this);
 105289        }
 290
 291        public void EnablePoolableObject(PoolableObject poolable)
 292        {
 618293            GameObject go = poolable.gameObject;
 294
 618295            if (go == null)
 0296                return;
 297
 618298            if (!go.activeSelf)
 615299                go.SetActive(true);
 300
 618301            go.transform.ResetLocalTRS();
 302
 618303            lastGetTime = Time.unscaledTime;
 618304        }
 305
 306        public void DisablePoolableObject(PoolableObject poolable)
 307        {
 308#if UNITY_STANDALONE || UNITY_EDITOR
 1136309            if (DataStore.i.common.isApplicationQuitting.Get())
 0310                return;
 311#endif
 1136312            GameObject go = poolable.gameObject;
 313
 1136314            if (go == null)
 44315                return;
 316
 1092317            if (go.activeSelf)
 1088318                go.SetActive(false);
 319
 1092320            if (PoolManager.USE_POOL_CONTAINERS)
 321            {
 2184322                if (container != null) { go.transform.SetParent(container.transform); }
 323            }
 0324            else { go.transform.SetParent(null); }
 0325        }
 326
 327#if UNITY_EDITOR
 328        private void RefreshName()
 329        {
 1755330            if (this.container != null)
 1755331                this.container.name = $"in: {unusedObjectsCount} out: {usedObjectsCount} id: {id} persistent: {persisten
 1755332        }
 333#endif
 334        public static bool FindPoolInGameObject(GameObject gameObject, out Pool pool)
 335        {
 3336            pool = null;
 337
 3338            if (PoolManager.i.poolables.TryGetValue(gameObject, out PoolableObject poolable))
 339            {
 0340                pool = poolable.pool;
 341
 0342                return true;
 343            }
 344
 3345            return false;
 346        }
 347
 348        public bool IsValid()
 349        {
 12350            return original != null;
 351        }
 352    }
 353};