123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- #if UNITY_5_3_OR_NEWER
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Threading;
- namespace UniRx.Toolkit
- {
- /// <summary>
- /// Bass class of ObjectPool.
- /// </summary>
- public abstract class ObjectPool<T> : IDisposable
- where T : UnityEngine.Component
- {
- bool isDisposed = false;
- Queue<T> q;
- /// <summary>
- /// Limit of instace count.
- /// </summary>
- protected int MaxPoolCount
- {
- get
- {
- return int.MaxValue;
- }
- }
- /// <summary>
- /// Create instance when needed.
- /// </summary>
- protected abstract T CreateInstance();
- /// <summary>
- /// Called before return to pool, useful for set active object(it is default behavior).
- /// </summary>
- protected virtual void OnBeforeRent(T instance)
- {
- instance.gameObject.SetActive(true);
- }
- /// <summary>
- /// Called before return to pool, useful for set inactive object(it is default behavior).
- /// </summary>
- protected virtual void OnBeforeReturn(T instance)
- {
- instance.gameObject.SetActive(false);
- }
- /// <summary>
- /// Called when clear or disposed, useful for destroy instance or other finalize method.
- /// </summary>
- protected virtual void OnClear(T instance)
- {
- if (instance == null) return;
- var go = instance.gameObject;
- if (go == null) return;
- UnityEngine.Object.Destroy(go);
- }
- /// <summary>
- /// Current pooled object count.
- /// </summary>
- public int Count
- {
- get
- {
- if (q == null) return 0;
- return q.Count;
- }
- }
- /// <summary>
- /// Get instance from pool.
- /// </summary>
- public T Rent()
- {
- if (isDisposed) throw new ObjectDisposedException("ObjectPool was already disposed.");
- if (q == null) q = new Queue<T>();
- var instance = (q.Count > 0)
- ? q.Dequeue()
- : CreateInstance();
- OnBeforeRent(instance);
- return instance;
- }
- /// <summary>
- /// Return instance to pool.
- /// </summary>
- public void Return(T instance)
- {
- if (isDisposed) throw new ObjectDisposedException("ObjectPool was already disposed.");
- if (instance == null) throw new ArgumentNullException("instance");
- if (q == null) q = new Queue<T>();
- if ((q.Count + 1) == MaxPoolCount)
- {
- throw new InvalidOperationException("Reached Max PoolSize");
- }
- OnBeforeReturn(instance);
- q.Enqueue(instance);
- }
- /// <summary>
- /// Clear pool.
- /// </summary>
- public void Clear(bool callOnBeforeRent = false)
- {
- if (q == null) return;
- while (q.Count != 0)
- {
- var instance = q.Dequeue();
- if (callOnBeforeRent)
- {
- OnBeforeRent(instance);
- }
- OnClear(instance);
- }
- }
- /// <summary>
- /// Trim pool instances.
- /// </summary>
- /// <param name="instanceCountRatio">0.0f = clear all ~ 1.0f = live all.</param>
- /// <param name="minSize">Min pool count.</param>
- /// <param name="callOnBeforeRent">If true, call OnBeforeRent before OnClear.</param>
- public void Shrink(float instanceCountRatio, int minSize, bool callOnBeforeRent = false)
- {
- if (q == null) return;
- if (instanceCountRatio <= 0) instanceCountRatio = 0;
- if (instanceCountRatio >= 1.0f) instanceCountRatio = 1.0f;
- var size = (int)(q.Count * instanceCountRatio);
- size = Math.Max(minSize, size);
- while (q.Count > size)
- {
- var instance = q.Dequeue();
- if (callOnBeforeRent)
- {
- OnBeforeRent(instance);
- }
- OnClear(instance);
- }
- }
- /// <summary>
- /// If needs shrink pool frequently, start check timer.
- /// </summary>
- /// <param name="checkInterval">Interval of call Shrink.</param>
- /// <param name="instanceCountRatio">0.0f = clearAll ~ 1.0f = live all.</param>
- /// <param name="minSize">Min pool count.</param>
- /// <param name="callOnBeforeRent">If true, call OnBeforeRent before OnClear.</param>
- public IDisposable StartShrinkTimer(TimeSpan checkInterval, float instanceCountRatio, int minSize, bool callOnBeforeRent = false)
- {
- return Observable.Interval(checkInterval)
- .TakeWhile(_ => !isDisposed)
- .Subscribe(_ =>
- {
- Shrink(instanceCountRatio, minSize, callOnBeforeRent);
- });
- }
- /// <summary>
- /// Fill pool before rent operation.
- /// </summary>
- /// <param name="preloadCount">Pool instance count.</param>
- /// <param name="threshold">Create count per frame.</param>
- public IObservable<Unit> PreloadAsync(int preloadCount, int threshold)
- {
- if (q == null) q = new Queue<T>(preloadCount);
- return Observable.FromMicroCoroutine<Unit>((observer, cancel) => PreloadCore(preloadCount, threshold, observer, cancel));
- }
- IEnumerator PreloadCore(int preloadCount, int threshold, IObserver<Unit> observer, CancellationToken cancellationToken)
- {
- while (Count < preloadCount && !cancellationToken.IsCancellationRequested)
- {
- var requireCount = preloadCount - Count;
- if (requireCount <= 0) break;
- var createCount = Math.Min(requireCount, threshold);
- for (int i = 0; i < createCount; i++)
- {
- try
- {
- var instance = CreateInstance();
- Return(instance);
- }
- catch (Exception ex)
- {
- observer.OnError(ex);
- yield break;
- }
- }
- yield return null; // next frame.
- }
- observer.OnNext(Unit.Default);
- observer.OnCompleted();
- }
- #region IDisposable Support
- protected virtual void Dispose(bool disposing)
- {
- if (!isDisposed)
- {
- if (disposing)
- {
- Clear(false);
- }
- isDisposed = true;
- }
- }
- public void Dispose()
- {
- Dispose(true);
- }
- #endregion
- }
- /// <summary>
- /// Bass class of ObjectPool. If needs asynchronous initialization, use this instead of standard ObjectPool.
- /// </summary>
- public abstract class AsyncObjectPool<T> : IDisposable
- where T : UnityEngine.Component
- {
- bool isDisposed = false;
- Queue<T> q;
- /// <summary>
- /// Limit of instace count.
- /// </summary>
- protected int MaxPoolCount
- {
- get
- {
- return int.MaxValue;
- }
- }
- /// <summary>
- /// Create instance when needed.
- /// </summary>
- protected abstract IObservable<T> CreateInstanceAsync();
- /// <summary>
- /// Called before return to pool, useful for set active object(it is default behavior).
- /// </summary>
- protected virtual void OnBeforeRent(T instance)
- {
- instance.gameObject.SetActive(true);
- }
- /// <summary>
- /// Called before return to pool, useful for set inactive object(it is default behavior).
- /// </summary>
- protected virtual void OnBeforeReturn(T instance)
- {
- instance.gameObject.SetActive(false);
- }
- /// <summary>
- /// Called when clear or disposed, useful for destroy instance or other finalize method.
- /// </summary>
- protected virtual void OnClear(T instance)
- {
- if (instance == null) return;
- var go = instance.gameObject;
- if (go == null) return;
- UnityEngine.Object.Destroy(go);
- }
- /// <summary>
- /// Current pooled object count.
- /// </summary>
- public int Count
- {
- get
- {
- if (q == null) return 0;
- return q.Count;
- }
- }
- /// <summary>
- /// Get instance from pool.
- /// </summary>
- public IObservable<T> RentAsync()
- {
- if (isDisposed) throw new ObjectDisposedException("ObjectPool was already disposed.");
- if (q == null) q = new Queue<T>();
- if (q.Count > 0)
- {
- var instance = q.Dequeue();
- OnBeforeRent(instance);
- return Observable.Return(instance);
- }
- else
- {
- var instance = CreateInstanceAsync();
- return instance.Do(x => OnBeforeRent(x));
- }
- }
- /// <summary>
- /// Return instance to pool.
- /// </summary>
- public void Return(T instance)
- {
- if (isDisposed) throw new ObjectDisposedException("ObjectPool was already disposed.");
- if (instance == null) throw new ArgumentNullException("instance");
- if (q == null) q = new Queue<T>();
- if ((q.Count + 1) == MaxPoolCount)
- {
- throw new InvalidOperationException("Reached Max PoolSize");
- }
- OnBeforeReturn(instance);
- q.Enqueue(instance);
- }
- /// <summary>
- /// Trim pool instances.
- /// </summary>
- /// <param name="instanceCountRatio">0.0f = clear all ~ 1.0f = live all.</param>
- /// <param name="minSize">Min pool count.</param>
- /// <param name="callOnBeforeRent">If true, call OnBeforeRent before OnClear.</param>
- public void Shrink(float instanceCountRatio, int minSize, bool callOnBeforeRent = false)
- {
- if (q == null) return;
- if (instanceCountRatio <= 0) instanceCountRatio = 0;
- if (instanceCountRatio >= 1.0f) instanceCountRatio = 1.0f;
- var size = (int)(q.Count * instanceCountRatio);
- size = Math.Max(minSize, size);
- while (q.Count > size)
- {
- var instance = q.Dequeue();
- if (callOnBeforeRent)
- {
- OnBeforeRent(instance);
- }
- OnClear(instance);
- }
- }
- /// <summary>
- /// If needs shrink pool frequently, start check timer.
- /// </summary>
- /// <param name="checkInterval">Interval of call Shrink.</param>
- /// <param name="instanceCountRatio">0.0f = clearAll ~ 1.0f = live all.</param>
- /// <param name="minSize">Min pool count.</param>
- /// <param name="callOnBeforeRent">If true, call OnBeforeRent before OnClear.</param>
- public IDisposable StartShrinkTimer(TimeSpan checkInterval, float instanceCountRatio, int minSize, bool callOnBeforeRent = false)
- {
- return Observable.Interval(checkInterval)
- .TakeWhile(_ => !isDisposed)
- .Subscribe(_ =>
- {
- Shrink(instanceCountRatio, minSize, callOnBeforeRent);
- });
- }
- /// <summary>
- /// Clear pool.
- /// </summary>
- public void Clear(bool callOnBeforeRent = false)
- {
- if (q == null) return;
- while (q.Count != 0)
- {
- var instance = q.Dequeue();
- if (callOnBeforeRent)
- {
- OnBeforeRent(instance);
- }
- OnClear(instance);
- }
- }
- /// <summary>
- /// Fill pool before rent operation.
- /// </summary>
- /// <param name="preloadCount">Pool instance count.</param>
- /// <param name="threshold">Create count per frame.</param>
- public IObservable<Unit> PreloadAsync(int preloadCount, int threshold)
- {
- if (q == null) q = new Queue<T>(preloadCount);
- return Observable.FromMicroCoroutine<Unit>((observer, cancel) => PreloadCore(preloadCount, threshold, observer, cancel));
- }
- IEnumerator PreloadCore(int preloadCount, int threshold, IObserver<Unit> observer, CancellationToken cancellationToken)
- {
- while (Count < preloadCount && !cancellationToken.IsCancellationRequested)
- {
- var requireCount = preloadCount - Count;
- if (requireCount <= 0) break;
- var createCount = Math.Min(requireCount, threshold);
- var loaders = new IObservable<Unit>[createCount];
- for (int i = 0; i < createCount; i++)
- {
- var instanceFuture = CreateInstanceAsync();
- loaders[i] = instanceFuture.ForEachAsync(x => Return(x));
- }
- var awaiter = Observable.WhenAll(loaders).ToYieldInstruction(false, cancellationToken);
- while (!(awaiter.HasResult || awaiter.IsCanceled || awaiter.HasError))
- {
- yield return null;
- }
- if (awaiter.HasError)
- {
- observer.OnError(awaiter.Error);
- yield break;
- }
- else if (awaiter.IsCanceled)
- {
- yield break; // end.
- }
- }
- observer.OnNext(Unit.Default);
- observer.OnCompleted();
- }
- #region IDisposable Support
- protected virtual void Dispose(bool disposing)
- {
- if (!isDisposed)
- {
- if (disposing)
- {
- Clear(false);
- }
- isDisposed = true;
- }
- }
- public void Dispose()
- {
- Dispose(true);
- }
- #endregion
- }
- }
- #endif
|