diff --git a/Context/ContextNotifier.cs b/Context/ContextNotifier.cs index dfe4aa9..88d56f6 100644 --- a/Context/ContextNotifier.cs +++ b/Context/ContextNotifier.cs @@ -1,6 +1,6 @@ -using Svelto.DataStructures; using System; using System.Collections.Generic; +using Svelto.DataStructures; namespace Svelto.Context { @@ -33,19 +33,17 @@ namespace Svelto.Context /// public void NotifyFrameworkDeinitialized() { - for (int i = _toDeinitialize.Count - 1; i >= 0; --i) - { + for (var i = _toDeinitialize.Count - 1; i >= 0; --i) try { var obj = _toDeinitialize[i]; - if (obj.IsAlive == true) - (obj.Target as IWaitForFrameworkDestruction).OnFrameworkDestroyed(); + if (obj.IsAlive) + obj.Target.OnFrameworkDestroyed(); } catch (Exception e) { Utility.Console.LogException(e); } - } _toDeinitialize = null; } @@ -55,24 +53,22 @@ namespace Svelto.Context /// public void NotifyFrameworkInitialized() { - for (int i = _toInitialize.Count - 1; i >= 0; --i) - { + for (var i = _toInitialize.Count - 1; i >= 0; --i) try { var obj = _toInitialize[i]; - if (obj.IsAlive == true) - (obj.Target as IWaitForFrameworkInitialization).OnFrameworkInitialized(); + if (obj.IsAlive) + obj.Target.OnFrameworkInitialized(); } catch (Exception e) { Utility.Console.LogException(e); } - } _toInitialize = null; } - List> _toDeinitialize; + List> _toDeinitialize; List> _toInitialize; } } diff --git a/Context/Factories/GameObjectFactory.cs b/Context/Factories/GameObjectFactory.cs index 3f9c806..9519e6d 100644 --- a/Context/Factories/GameObjectFactory.cs +++ b/Context/Factories/GameObjectFactory.cs @@ -16,7 +16,7 @@ namespace Svelto.Context public GameObject Build(string prefabName) { - DesignByContract.Check.Require(_prefabs.ContainsKey(prefabName), "Svelto.Factories.IGameObjectFactory - Invalid Prefab Type"); + DesignByContract.Check.Require(_prefabs.ContainsKey(prefabName), "Svelto.Factories.IGameObjectFactory -prefab was not found:" + prefabName); var go = Build(_prefabs[prefabName][0]); @@ -48,7 +48,7 @@ namespace Svelto.Context /// original prefab public GameObject Build(GameObject prefab) { - Profiler.BeginSample("GameObject Factory Build"); + UnityEngine.Profiling.Profiler.BeginSample("GameObject Factory Build"); var copy = Object.Instantiate(prefab) as GameObject; diff --git a/Context/Factories/Legacy.meta b/Context/Factories/Legacy.meta new file mode 100644 index 0000000..1f220ba --- /dev/null +++ b/Context/Factories/Legacy.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 8d34411ef4f6005489ca3bb91987c84a +folderAsset: yes +timeCreated: 1466179612 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Context/Factories/Legacy/GameObjectFactory.cs b/Context/Factories/Legacy/GameObjectFactory.cs new file mode 100644 index 0000000..49c1008 --- /dev/null +++ b/Context/Factories/Legacy/GameObjectFactory.cs @@ -0,0 +1,98 @@ +#region + +using System.Collections.Generic; +using Svelto.Context.Legacy; +using Svelto.DataStructures; +using UnityEngine; + +#endregion + +namespace Svelto.Context.Legacy +{ + public class GameObjectFactory : Factories.IGameObjectFactory + { + public GameObjectFactory(IUnityContextHierarchyChangedListener root) + { + _unityContext = new WeakReference(root); + + _prefabs = new Dictionary(); + } + + public GameObject Build(string prefabName) + { + DesignByContract.Check.Require(_prefabs.ContainsKey(prefabName), "Svelto.Factories.IGameObjectFactory - Invalid Prefab Type"); + + var go = Build(_prefabs[prefabName][0]); + + GameObject parent = _prefabs[prefabName][1]; + + if (parent != null) + { + Transform transform = go.transform; + + var scale = transform.localScale; + var rotation = transform.localRotation; + var position = transform.localPosition; + + parent.SetActive(true); + + transform.parent = parent.transform; + + transform.localPosition = position; + transform.localRotation = rotation; + transform.localScale = scale; + } + + return go; + } + + /// + /// Register a prefab to be built later using a string ID. + /// + /// original prefab + public GameObject Build(GameObject prefab) + { + DesignByContract.Check.Require(_unityContext.IsAlive == true, "Context is used, but not alive"); + + UnityEngine.Profiling.Profiler.BeginSample("GameObject Factory Build"); + + var copy = Object.Instantiate(prefab) as GameObject; + var components = copy.GetComponentsInChildren(true); + + for (var i = 0; i < components.Length; ++i) + { + var monoBehaviour = components[i]; + + if (monoBehaviour != null) + { + var currentGo = monoBehaviour.gameObject; + + _unityContext.Target.OnMonobehaviourAdded(monoBehaviour); + + if (currentGo.GetComponent() == null) + currentGo.AddComponent().unityContext = _unityContext; + } + else + { + //Utility.Console.Log("delete me"); + } + } + + UnityEngine.Profiling.Profiler.EndSample(); + + return copy; + } + + public void RegisterPrefab(GameObject prefab, string prefabName, GameObject parent = null) + { + var objects = new GameObject[2]; + + objects[0] = prefab; objects[1] = parent; + + _prefabs.Add(prefabName, objects); + } + + Dictionary _prefabs; + WeakReference _unityContext; + } +} \ No newline at end of file diff --git a/Ticker/ITickable.cs.meta b/Context/Factories/Legacy/GameObjectFactory.cs.meta similarity index 69% rename from Ticker/ITickable.cs.meta rename to Context/Factories/Legacy/GameObjectFactory.cs.meta index 0f9fefa..b07702c 100644 --- a/Ticker/ITickable.cs.meta +++ b/Context/Factories/Legacy/GameObjectFactory.cs.meta @@ -1,5 +1,7 @@ fileFormatVersion: 2 -guid: 8830537b810b3fa489b14e66327f0af2 +guid: 110a36d7de251bc41a444815f58a61c5 +timeCreated: 1466179612 +licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/Context/Factories/Legacy/MonoBehaviourFactory.cs b/Context/Factories/Legacy/MonoBehaviourFactory.cs new file mode 100644 index 0000000..5669802 --- /dev/null +++ b/Context/Factories/Legacy/MonoBehaviourFactory.cs @@ -0,0 +1,36 @@ +#region + +using System; +using Svelto.DataStructures; +using UnityEngine; + +#endregion + +namespace Svelto.Context.Legacy +{ + public class MonoBehaviourFactory : Factories.IMonoBehaviourFactory + { + public MonoBehaviourFactory(IUnityContextHierarchyChangedListener unityContext) + { + _unityContext = new WeakReference(unityContext); + } + + public M Build(Func constructor) where M : MonoBehaviour + { + DesignByContract.Check.Require(_unityContext.IsAlive == true, "Context is used, but not alive"); + + var mb = constructor(); + + _unityContext.Target.OnMonobehaviourAdded(mb); + + GameObject go = mb.gameObject; + + if (go.GetComponent() == null) + go.AddComponent().unityContext = _unityContext; + + return mb; + } + + WeakReference _unityContext; + } +} diff --git a/Ticker/ITicker.cs.meta b/Context/Factories/Legacy/MonoBehaviourFactory.cs.meta similarity index 69% rename from Ticker/ITicker.cs.meta rename to Context/Factories/Legacy/MonoBehaviourFactory.cs.meta index c872bc0..8922e2e 100644 --- a/Ticker/ITicker.cs.meta +++ b/Context/Factories/Legacy/MonoBehaviourFactory.cs.meta @@ -1,5 +1,7 @@ fileFormatVersion: 2 -guid: 0bf92a08bb722064ab845dea9463933e +guid: 86181a20ee37df64c9332a1c32369d72 +timeCreated: 1466179768 +licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/Context/IContextNotifer.cs b/Context/IContextNotifer.cs index 13f744f..6ac2c14 100644 --- a/Context/IContextNotifer.cs +++ b/Context/IContextNotifer.cs @@ -5,7 +5,7 @@ namespace Svelto.Context void NotifyFrameworkInitialized(); void NotifyFrameworkDeinitialized(); - void AddFrameworkInitializationListener(IWaitForFrameworkInitialization obj); + void AddFrameworkInitializationListener(IWaitForFrameworkInitialization obj); void AddFrameworkDestructionListener(IWaitForFrameworkDestruction obj); } } diff --git a/Context/IUnityContextHierarchyChangedListener.cs b/Context/IUnityContextHierarchyChangedListener.cs new file mode 100644 index 0000000..5289957 --- /dev/null +++ b/Context/IUnityContextHierarchyChangedListener.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +namespace Svelto.Context.Legacy +{ + public interface IUnityContextHierarchyChangedListener + { + void OnMonobehaviourAdded(MonoBehaviour component); + + void OnMonobehaviourRemoved(MonoBehaviour component); + } +} diff --git a/Context/IUnityContextHierarchyChangedListener.cs.meta b/Context/IUnityContextHierarchyChangedListener.cs.meta new file mode 100644 index 0000000..f26268b --- /dev/null +++ b/Context/IUnityContextHierarchyChangedListener.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 905bfd795bede6d448694b23dd88e0be +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Context/Unity.meta b/Context/Unity.meta new file mode 100644 index 0000000..75e174f --- /dev/null +++ b/Context/Unity.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 14144568b327bbe40876c12a777e5f05 +folderAsset: yes +timeCreated: 1434752394 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Context/Unity/NotifyComponentsRemoved.cs b/Context/Unity/NotifyComponentsRemoved.cs new file mode 100644 index 0000000..429c162 --- /dev/null +++ b/Context/Unity/NotifyComponentsRemoved.cs @@ -0,0 +1,30 @@ +using Svelto.DataStructures; +using UnityEngine; + +namespace Svelto.Context.Legacy +{ + public class NotifyComponentsRemoved : MonoBehaviour + { + public WeakReference unityContext { private get; set; } + + void Start() + { + if (unityContext == null) + { + Destroy(this); + } + } + + void OnDestroy() + { + if (unityContext == null || unityContext.IsAlive == false) + return; + + MonoBehaviour[] components = gameObject.GetComponents(); + + for (int i = 0; i < components.Length; ++i) + if (components[i] != null) + unityContext.Target.OnMonobehaviourRemoved(components[i]); + } + } +} diff --git a/Context/Unity/NotifyComponentsRemoved.cs.meta b/Context/Unity/NotifyComponentsRemoved.cs.meta new file mode 100644 index 0000000..4418602 --- /dev/null +++ b/Context/Unity/NotifyComponentsRemoved.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 13278f0cd187d8d43acc6a02479e459b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/DataStructures/CircularBufferIndexer.cs b/DataStructures/CircularBufferIndexer.cs new file mode 100644 index 0000000..cc9d397 --- /dev/null +++ b/DataStructures/CircularBufferIndexer.cs @@ -0,0 +1,183 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Svelto.DataStructures +{ + // Serves as simple circular buffer dictionary, first in, first out + // Main drawback: it is the oldest in the list that is removed and the fact that we might re access a key + // isn't taken into account (we would have to do a shift in both arrays) + // Could be added as an option? + + class CircularBufferIndexer : IDictionary + { + public ICollection Keys + { + get { return _keys; } + } + + public ICollection Values + { + get { return _values; } + } + + public int Count + { + get { throw new NotImplementedException(); } + } + + public bool IsReadOnly + { + get { throw new NotImplementedException(); } + } + + public CircularBufferIndexer(int size) + { + _keys = new TKey[size]; + _values = new TVal[size]; + _length = _startIndex = _nextIndex = 0; + } + + public TVal this[TKey key] + { + get + { + int index = _startIndex; + for (int i = 0; i < _length; ++i) + { + if (_keys[index].Equals(key)) + { + return _values[index]; + } + + index = NextPosition(index); + } + throw new KeyNotFoundException(); + } + set + { + int index = _startIndex; + for (int i = 0; i < _length; ++i) + { + if (_keys[index].Equals(key)) + { + _values[index] = value; + return; + } + + index = NextPosition(index); + } + throw new KeyNotFoundException(); + } + } + + public void Add(TKey key, TVal value) + { + if (ContainsKey(key)) + { + this[key] = value; + return; + } + + _keys[_nextIndex] = key; + _values[_nextIndex] = value; + _nextIndex = NextPosition(_nextIndex); + if (IsFull()) + { + _startIndex = NextPosition(_startIndex); + } + else + { + ++_length; + } + } + + public bool ContainsKey(TKey key) + { + int index = _startIndex; + for (int i = 0; i < _length; ++i) + { + if (_keys[index].Equals(key)) + { + return true; + } + + index = NextPosition(index); + } + return false; + } + + public bool Remove(TKey key) + { + throw new NotImplementedException(); + } + + public bool TryGetValue(TKey key, out TVal value) + { + value = default(TVal); + int index = _startIndex; + for (int i = 0; i < _length; ++i) + { + if (_keys[index].Equals(key)) + { + value = _values[index]; + return true; + } + + index = NextPosition(index); + } + return false; + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + public void Add(KeyValuePair item) + { + Add(item.Key, item.Value); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(KeyValuePair item) + { + throw new NotImplementedException(); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public bool Remove(KeyValuePair item) + { + throw new NotImplementedException(); + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + throw new NotImplementedException(); + } + + int NextPosition(int position) + { + return (position + 1) % _keys.Length; + } + + bool IsFull() + { + return _length == _values.Length; + } + + TKey[] _keys; + TVal[] _values; + int _startIndex; + int _nextIndex; + int _length; + } +} diff --git a/DataStructures/CircularBufferIndexer.cs.meta b/DataStructures/CircularBufferIndexer.cs.meta new file mode 100644 index 0000000..a7e75ad --- /dev/null +++ b/DataStructures/CircularBufferIndexer.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0d87fb4479e8365499c162fe0fc94acc +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/DataStructures/FasterList.cs b/DataStructures/FasterList.cs index 15dc5b1..78cbaa2 100644 --- a/DataStructures/FasterList.cs +++ b/DataStructures/FasterList.cs @@ -1,7 +1,3 @@ -/** - * Custom modified version of Michael Lyashenko's BetterList - **/ - using System; using System.Collections; using System.Collections.Generic; @@ -16,16 +12,6 @@ namespace Svelto.DataStructures get { return _current; } } - object IEnumerator.Current - { - get { return _current; } - } - - T IEnumerator.Current - { - get { return _current; } - } - public FasterListEnumerator(T[] buffer, int size) { _size = size; @@ -34,19 +20,19 @@ namespace Svelto.DataStructures _current = default(T); } - public void Dispose() + object IEnumerator.Current { - _buffer = null; + get { return _current; } } - bool IEnumerator.MoveNext() + T IEnumerator.Current { - return MoveNext(); + get { return _current; } } - void IEnumerator.Reset() + public void Dispose() { - Reset(); + _buffer = null; } public bool MoveNext() @@ -68,6 +54,16 @@ namespace Svelto.DataStructures _counter = 0; } + bool IEnumerator.MoveNext() + { + return MoveNext(); + } + + void IEnumerator.Reset() + { + Reset(); + } + T[] _buffer; int _counter; int _size; @@ -81,42 +77,42 @@ namespace Svelto.DataStructures get { return (T)_buffer.Current; } } - object IEnumerator.Current + public FasterListEnumeratorCast(FasterListEnumerator buffer) { - get { return (T)_buffer.Current; } + _buffer = buffer; } - T IEnumerator.Current + object IEnumerator.Current { get { return (T)_buffer.Current; } } - public FasterListEnumeratorCast(FasterListEnumerator buffer) + T IEnumerator.Current { - _buffer = buffer; + get { return (T)_buffer.Current; } } public void Dispose() {} - bool IEnumerator.MoveNext() + public bool MoveNext() { - return MoveNext(); + return _buffer.MoveNext(); } - void IEnumerator.Reset() + public void Reset() { - Reset(); + _buffer.Reset(); } - public bool MoveNext() + bool IEnumerator.MoveNext() { - return _buffer.MoveNext(); + return MoveNext(); } - public void Reset() + void IEnumerator.Reset() { - _buffer.Reset(); + Reset(); } FasterListEnumerator _buffer; @@ -124,20 +120,15 @@ namespace Svelto.DataStructures public struct FasterReadOnlyList : IList { + public int Count { get { return _list.Count; } } + public bool IsReadOnly { get { return true; } } + public FasterReadOnlyList(FasterList list) { _list = list; } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + public T this[int index] { get { return _list[index]; } set { throw new NotImplementedException(); } } public FasterListEnumerator GetEnumerator() { @@ -169,9 +160,6 @@ namespace Svelto.DataStructures throw new NotImplementedException(); } - public int Count { get { return _list.Count; } } - public bool IsReadOnly { get { return true; } } - public int IndexOf(T item) { return _list.IndexOf(item); @@ -187,27 +175,32 @@ namespace Svelto.DataStructures throw new NotImplementedException(); } - public T this[int index] { get { return _list[index]; } set { throw new NotImplementedException(); } } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } readonly FasterList _list; } public struct FasterReadOnlyListCast : IList where U:T { + public static FasterList DefaultList = new FasterList(); + + public int Count { get { return _list.Count; } } + public bool IsReadOnly { get { return true; } } + public FasterReadOnlyListCast(FasterList list) { _list = list; } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + public U this[int index] { get { return (U)_list[index]; } set { throw new NotImplementedException(); } } public FasterListEnumeratorCast GetEnumerator() { @@ -239,9 +232,6 @@ namespace Svelto.DataStructures throw new NotImplementedException(); } - public int Count { get { return _list.Count; } } - public bool IsReadOnly { get { return true; } } - public int IndexOf(U item) { return _list.IndexOf(item); @@ -257,14 +247,23 @@ namespace Svelto.DataStructures throw new NotImplementedException(); } - public U this[int index] { get { return (U)_list[index]; } set { throw new NotImplementedException(); } } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } readonly FasterList _list; - static public FasterList DefaultList = new FasterList(); } public class FasterList : IList { + const int MIN_SIZE = 4; + public int Count { get { return _count; } @@ -307,6 +306,12 @@ namespace Svelto.DataStructures _count = listCopy.Count; } + public T this[int i] + { + get { DesignByContract.Check.Require(i < _count, "out of bound index"); return _buffer[i]; } + set { DesignByContract.Check.Require(i < _count, "out of bound index"); _buffer[i] = value; } + } + public void Add(T item) { if (_count == _buffer.Length) @@ -315,44 +320,46 @@ namespace Svelto.DataStructures _buffer[_count++] = item; } + public static FasterList PreFill(int initialSize) where U:T, new() + { + var list = new FasterList(initialSize); + + for (int i = 0; i < initialSize; i++) + list.Add(new U()); + + list.Clear(); + + return list; + } + public void AddRange(IEnumerable items, int count) + { + AddRange(items.GetEnumerator(), count); + } + + public void AddRange(IEnumerator items, int count) { if (_count + count >= _buffer.Length) AllocateMore(_count + count); - for (var i = items.GetEnumerator(); i.MoveNext();) - { - var item = i.Current; - _buffer[_count++] = item; - } + while (items.MoveNext()) + _buffer[_count++] = items.Current; } public void AddRange(ICollection items) { - var count = items.Count; - if (_count + count >= _buffer.Length) - AllocateMore(_count + count); - - for (var i = items.GetEnumerator(); i.MoveNext();) - { - var item = i.Current; - _buffer[_count++] = item; - } + AddRange(items.GetEnumerator(), items.Count); } public void AddRange(FasterList items) { - var count = items.Count; - if (_count + count >= _buffer.Length) - AllocateMore(_count + count); - - Array.Copy(items._buffer, 0, _buffer, _count, count); - _count += count; + AddRange(items.ToArrayFast(), items.Count); } - public void AddRange(T[] items) + public void AddRange(T[] items, int count) { - var count = items.Length; + if (count == 0) return; + if (_count + count >= _buffer.Length) AllocateMore(_count + count); @@ -365,11 +372,22 @@ namespace Svelto.DataStructures return new FasterReadOnlyList(this); } + /// + /// Careful, you could keep on holding references you don't want to hold to anymore + /// Use DeepClear in case. + /// public void Clear() { _count = 0; } + public void DeepClear() + { + Array.Clear(_buffer, 0, _buffer.Length); + + _count = 0; + } + public bool Contains(T item) { var index = IndexOf(item); @@ -387,16 +405,6 @@ namespace Svelto.DataStructures return new FasterListEnumerator(_buffer, Count); } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - public int IndexOf(T item) { var comp = EqualityComparer.Default; @@ -442,16 +450,19 @@ namespace Svelto.DataStructures { DesignByContract.Check.Require(index < _count, "out of bound index"); - --_count; - - if (index == _count) + if (index == --_count) return; Array.Copy(_buffer, index + 1, _buffer, index, _count - index); + + _buffer[_count] = default(T); } public void Resize(int newSize) { + if (newSize < MIN_SIZE) + newSize = MIN_SIZE; + Array.Resize(ref _buffer, newSize); _count = newSize; @@ -468,10 +479,9 @@ namespace Svelto.DataStructures this[index] = value; } - public void Sort(Comparison comparer) + public void Sort(IComparer comparer) { - Trim(); - Array.Sort(_buffer, comparer); + Array.Sort(_buffer, 0, _count, comparer); } public T[] ToArray() @@ -505,46 +515,70 @@ namespace Svelto.DataStructures return true; } - public void UnorderredRemoveAt(int index) + public T UnorderredRemoveAt(int index) { - DesignByContract.Check.Require(index < _count, "out of bound index"); + DesignByContract.Check.Require(index < _count && _count > 0, "out of bound index"); - _buffer[index] = _buffer[--_count]; + T item = _buffer[index]; + + if (index == --_count) + return item; + + T swap = _buffer[index]; + _buffer[index] = _buffer[_count]; + _buffer[_count] = swap; + + return item; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); } - private void AllocateMore() + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + void AllocateMore() { var newList = new T[Mathf.Max(_buffer.Length << 1, MIN_SIZE)]; if (_count > 0) _buffer.CopyTo(newList, 0); _buffer = newList; } - private void AllocateMore(int newSize) + void AllocateMore(int newSize) { - var oldLength = _buffer.Length; + var oldLength = Mathf.Max(_buffer.Length, MIN_SIZE); while (oldLength < newSize) oldLength <<= 1; var newList = new T[oldLength]; - if (_count > 0) _buffer.CopyTo(newList, 0); + if (_count > 0) Array.Copy(_buffer, newList, _count); _buffer = newList; } - private void Trim() + public void Trim() { if (_count < _buffer.Length) Resize(_count); } - public T this[int i] + public bool Reuse(int index, out T result) { - get { DesignByContract.Check.Require(i < _count, "out of bound index"); return _buffer[i]; } - set { DesignByContract.Check.Require(i < _count, "out of bound index"); _buffer[i] = value; } + result = default(T); + + if (index >= _buffer.Length) + return false; + + result = _buffer[index]; + + return result != null; } - private const int MIN_SIZE = 32; - private T[] _buffer; - private int _count; + T[] _buffer; + int _count; } } diff --git a/DataStructures/FasterList.cs.meta b/DataStructures/FasterList.cs.meta index 6c1704e..b678658 100644 --- a/DataStructures/FasterList.cs.meta +++ b/DataStructures/FasterList.cs.meta @@ -1,5 +1,7 @@ fileFormatVersion: 2 -guid: 0859454e6e6d968498cea54757578eda +guid: d19e59cbec974dd4d821a7dd21f87a88 +timeCreated: 1472488070 +licenseType: Free MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/DataStructures/HashableWeakReference.cs b/DataStructures/HashableWeakReference.cs new file mode 100644 index 0000000..62edfa6 --- /dev/null +++ b/DataStructures/HashableWeakReference.cs @@ -0,0 +1,56 @@ +using System; + +namespace Svelto.DataStructures +{ + class HashableWeakRef : IEquatable> where T : class + { + public bool isAlive { get { return _weakRef.IsAlive; } } + public T Target { get { return (T)_weakRef.Target; } } + + public HashableWeakRef(T target) + { + _weakRef = new WeakReference(target); + _hash = target.GetHashCode(); + } + + public static bool operator !=(HashableWeakRef a, HashableWeakRef b) + { + return !(a == b); + } + + public static bool operator ==(HashableWeakRef a, HashableWeakRef b) + { + if (a._hash != b._hash) + return false; + + var tmpTargetA = (T) a._weakRef.Target; + var tmpTargetB = (T) b._weakRef.Target; + + if (tmpTargetA == null || tmpTargetB == null) + return false; + + return tmpTargetA == tmpTargetB; + } + + public override bool Equals(object other) + { + if (other is HashableWeakRef) + return this.Equals((HashableWeakRef)other); + + return false; + } + + public bool Equals(HashableWeakRef other) + { + return (this == other); + } + + public override int GetHashCode() + { + return _hash; + } + + int _hash; + WeakReference _weakRef; + } +} diff --git a/Ticker/TickBehaviour.cs.meta b/DataStructures/HashableWeakReference.cs.meta similarity index 69% rename from Ticker/TickBehaviour.cs.meta rename to DataStructures/HashableWeakReference.cs.meta index f042c9a..6d88c48 100644 --- a/Ticker/TickBehaviour.cs.meta +++ b/DataStructures/HashableWeakReference.cs.meta @@ -1,5 +1,7 @@ fileFormatVersion: 2 -guid: 82d08d9d100803c47b036667fdc671cf +guid: 0cfa789b3147c2e4e80d067693a58103 +timeCreated: 1455809369 +licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/Observer.meta b/DataStructures/Priority Queue.meta similarity index 63% rename from Observer.meta rename to DataStructures/Priority Queue.meta index 1e0c65f..8f88283 100644 --- a/Observer.meta +++ b/DataStructures/Priority Queue.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 48e872bc336b396409c9316cdbfc045a +guid: 8fffa27a4e8919a4db6fe1369e22e1b5 folderAsset: yes DefaultImporter: userData: diff --git a/DataStructures/Priority Queue/HeapPriorityQueue.cs b/DataStructures/Priority Queue/HeapPriorityQueue.cs new file mode 100644 index 0000000..c30e06d --- /dev/null +++ b/DataStructures/Priority Queue/HeapPriorityQueue.cs @@ -0,0 +1,327 @@ +using System.Collections; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Svelto.DataStructures; + +namespace Svelto.DataStructures +{ + /// + /// An implementation of a min-Priority Queue using a heap. Has O(1) .Contains()! + /// See https://bitbucket.org/BlueRaja/high-speed-priority-queue-for-c/wiki/Getting%20Started for more information + /// + /// The values in the queue. Must implement the PriorityQueueNode interface + public sealed class HeapPriorityQueue : IPriorityQueue + where T : PriorityQueueNode + { + private int _numNodes; + private readonly FasterList _nodes; + private long _numNodesEverEnqueued; + + /// + /// Instantiate a new Priority Queue + /// + /// The max nodes ever allowed to be enqueued (going over this will cause an exception) + public HeapPriorityQueue() + { + _numNodes = 0; + _nodes = new FasterList(); + _numNodesEverEnqueued = 0; + } + + public HeapPriorityQueue(int initialSize) + { + _numNodes = 0; + _nodes = new FasterList(initialSize); + _numNodesEverEnqueued = 0; + } + + /// + /// Returns the number of nodes in the queue. O(1) + /// + public int Count + { + get + { + return _numNodes; + } + } + + /// + /// Returns the maximum number of items that can be enqueued at once in this queue. Once you hit this number (ie. once Count == MaxSize), + /// attempting to enqueue another item will throw an exception. O(1) + /// + public int MaxSize + { + get + { + return _nodes.Count - 1; + } + } + + /// + /// Removes every node from the queue. O(n) (So, don't do this often!) + /// + #if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + #endif + public void Clear() + { + _nodes.Clear(); + + _numNodes = 0; + } + + /// + /// Returns (in O(1)!) whether the given node is in the queue. O(1) + /// + #if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + #endif + public bool Contains(T node) + { + return (_nodes[node.QueueIndex] == node); + } + + /// + /// Enqueue a node - .Priority must be set beforehand! O(log n) + /// + #if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + #endif + public void Enqueue(T node, double priority) + { + node.Priority = priority; + _numNodes++; + if (_nodes.Count < _numNodes) + _nodes.Resize(_numNodes + 1); + + _nodes[_numNodes] = node; + node.QueueIndex = _numNodes; + node.InsertionIndex = _numNodesEverEnqueued++; + CascadeUp(_nodes[_numNodes]); + } + + #if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + #endif + private void Swap(T node1, T node2) + { + //Swap the nodes + _nodes[node1.QueueIndex] = node2; + _nodes[node2.QueueIndex] = node1; + + //Swap their indicies + int temp = node1.QueueIndex; + node1.QueueIndex = node2.QueueIndex; + node2.QueueIndex = temp; + } + + //Performance appears to be slightly better when this is NOT inlined o_O + private void CascadeUp(T node) + { + //aka Heapify-up + int parent = node.QueueIndex / 2; + while(parent >= 1) + { + T parentNode = _nodes[parent]; + if(HasHigherPriority(parentNode, node)) + break; + + //Node has lower priority value, so move it up the heap + Swap(node, parentNode); //For some reason, this is faster with Swap() rather than (less..?) individual operations, like in CascadeDown() + + parent = node.QueueIndex / 2; + } + } + + #if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + #endif + private void CascadeDown(T node) + { + //aka Heapify-down + T newParent; + int finalQueueIndex = node.QueueIndex; + while(true) + { + newParent = node; + int childLeftIndex = 2 * finalQueueIndex; + + //Check if the left-child is higher-priority than the current node + if(childLeftIndex > _numNodes) + { + //This could be placed outside the loop, but then we'd have to check newParent != node twice + node.QueueIndex = finalQueueIndex; + _nodes[finalQueueIndex] = node; + break; + } + + T childLeft = _nodes[childLeftIndex]; + if(HasHigherPriority(childLeft, newParent)) + { + newParent = childLeft; + } + + //Check if the right-child is higher-priority than either the current node or the left child + int childRightIndex = childLeftIndex + 1; + if(childRightIndex <= _numNodes) + { + T childRight = _nodes[childRightIndex]; + if(HasHigherPriority(childRight, newParent)) + { + newParent = childRight; + } + } + + //If either of the children has higher (smaller) priority, swap and continue cascading + if(newParent != node) + { + //Move new parent to its new index. node will be moved once, at the end + //Doing it this way is one less assignment operation than calling Swap() + _nodes[finalQueueIndex] = newParent; + + int temp = newParent.QueueIndex; + newParent.QueueIndex = finalQueueIndex; + finalQueueIndex = temp; + } + else + { + //See note above + node.QueueIndex = finalQueueIndex; + _nodes[finalQueueIndex] = node; + break; + } + } + } + + /// + /// Returns true if 'higher' has higher priority than 'lower', false otherwise. + /// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false + /// + #if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + #endif + private bool HasHigherPriority(T higher, T lower) + { + return (higher.Priority < lower.Priority || + (higher.Priority == lower.Priority && higher.InsertionIndex < lower.InsertionIndex)); + } + + /// + /// Removes the head of the queue (node with highest priority; ties are broken by order of insertion), and returns it. O(log n) + /// + public T Dequeue() + { + T returnMe = _nodes[1]; + Remove(returnMe); + return returnMe; + } + + /// + /// Returns the head of the queue, without removing it (use Dequeue() for that). O(1) + /// + public T First + { + get + { + return _nodes[1]; + } + } + + /// + /// This method must be called on a node every time its priority changes while it is in the queue. + /// Forgetting to call this method will result in a corrupted queue! + /// O(log n) + /// + #if NET_VERSION_4_5 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + #endif + public void UpdatePriority(T node, double priority) + { + node.Priority = priority; + OnNodeUpdated(node); + } + + private void OnNodeUpdated(T node) + { + //Bubble the updated node up or down as appropriate + int parentIndex = node.QueueIndex / 2; + T parentNode = _nodes[parentIndex]; + + if(parentIndex > 0 && HasHigherPriority(node, parentNode)) + { + CascadeUp(node); + } + else + { + //Note that CascadeDown will be called if parentNode == node (that is, node is the root) + CascadeDown(node); + } + } + + /// + /// Removes a node from the queue. Note that the node does not need to be the head of the queue. O(log n) + /// + public void Remove(T node) + { + if(_numNodes <= 1) + { + _nodes[1] = null; + _numNodes = 0; + return; + } + + //Make sure the node is the last node in the queue + bool wasSwapped = false; + T formerLastNode = _nodes[_numNodes]; + if(node.QueueIndex != _numNodes) + { + //Swap the node with the last node + Swap(node, formerLastNode); + wasSwapped = true; + } + + _numNodes--; + _nodes[node.QueueIndex] = null; + + if(wasSwapped) + { + //Now bubble formerLastNode (which is no longer the last node) up or down as appropriate + OnNodeUpdated(formerLastNode); + } + } + + public IEnumerator GetEnumerator() + { + for(int i = 1; i <= _numNodes; i++) + yield return _nodes[i]; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Should not be called in production code. + /// Checks to make sure the queue is still in a valid state. Used for testing/debugging the queue. + /// + public bool IsValidQueue() + { + for(int i = 1; i < _nodes.Count; i++) + { + if(_nodes[i] != null) + { + int childLeftIndex = 2 * i; + if(childLeftIndex < _nodes.Count && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i])) + return false; + + int childRightIndex = childLeftIndex + 1; + if(childRightIndex < _nodes.Count && _nodes[childRightIndex] != null && HasHigherPriority(_nodes[childRightIndex], _nodes[i])) + return false; + } + } + return true; + } + } +} \ No newline at end of file diff --git a/DataStructures/Priority Queue/HeapPriorityQueue.cs.meta b/DataStructures/Priority Queue/HeapPriorityQueue.cs.meta new file mode 100644 index 0000000..b994121 --- /dev/null +++ b/DataStructures/Priority Queue/HeapPriorityQueue.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ac754b29409379046935c0890bab6dc5 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/DataStructures/Priority Queue/IPriorityQueue.cs b/DataStructures/Priority Queue/IPriorityQueue.cs new file mode 100644 index 0000000..632820e --- /dev/null +++ b/DataStructures/Priority Queue/IPriorityQueue.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Svelto.DataStructures +{ + /// + /// The IPriorityQueue interface. This is mainly here for purists, and in case I decide to add more implementations later. + /// For speed purposes, it is actually recommended that you *don't* access the priority queue through this interface, since the JIT can + /// (theoretically?) optimize method calls from concrete-types slightly better. + /// + public interface IPriorityQueue : IEnumerable + where T : PriorityQueueNode + { + void Remove(T node); + void UpdatePriority(T node, double priority); + void Enqueue(T node, double priority); + T Dequeue(); + T First { get; } + int Count { get; } + int MaxSize { get; } + void Clear(); + bool Contains(T node); + } +} diff --git a/DataStructures/Priority Queue/IPriorityQueue.cs.meta b/DataStructures/Priority Queue/IPriorityQueue.cs.meta new file mode 100644 index 0000000..21920be --- /dev/null +++ b/DataStructures/Priority Queue/IPriorityQueue.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5d399e4c2c1fe1f47833d6b70bf16184 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/DataStructures/Priority Queue/PriorityQueueNode.cs b/DataStructures/Priority Queue/PriorityQueueNode.cs new file mode 100644 index 0000000..b09f3a4 --- /dev/null +++ b/DataStructures/Priority Queue/PriorityQueueNode.cs @@ -0,0 +1,24 @@ +namespace Svelto.DataStructures +{ + public class PriorityQueueNode + { + /// + /// The Priority to insert this node at. Must be set BEFORE adding a node to the queue + /// + public double Priority { get; + set; + } + + /// + /// Used by the priority queue - do not edit this value. + /// Represents the order the node was inserted in + /// + public long InsertionIndex { get; set; } + + /// + /// Used by the priority queue - do not edit this value. + /// Represents the current position in the queue + /// + public int QueueIndex { get; set; } + } +} diff --git a/DataStructures/Priority Queue/PriorityQueueNode.cs.meta b/DataStructures/Priority Queue/PriorityQueueNode.cs.meta new file mode 100644 index 0000000..1002889 --- /dev/null +++ b/DataStructures/Priority Queue/PriorityQueueNode.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2a6cc02a61a6ff549b1dcac74c71681f +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/DataStructures/ReadOnlyDictionary.cs b/DataStructures/ReadOnlyDictionary.cs index 58b5840..8b09f3a 100644 --- a/DataStructures/ReadOnlyDictionary.cs +++ b/DataStructures/ReadOnlyDictionary.cs @@ -1,28 +1,11 @@ -//note: ripped from openstacknetsdk - using System; using System.Collections; using System.Collections.Generic; namespace Svelto.DataStructures { - public struct ReadOnlyDictionary : IDictionary, IDictionary + public struct ReadOnlyDictionary : IDictionary { - private readonly IDictionary _dictionary; - - /// - /// Initializes a new instance of the class - /// that is a wrapper around the specified dictionary. - /// - /// The dictionary to wrap. - public ReadOnlyDictionary(IDictionary dictionary) - { - if (dictionary == null) - throw new ArgumentNullException("dictionary"); - - _dictionary = dictionary; - } - public bool isInitialized { get { return _dictionary != null; } } /// @@ -40,45 +23,6 @@ namespace Svelto.DataStructures } } - /// - /// - /// Gets the element that has the specified key. - /// - /// If the property is set. - TValue IDictionary.this[TKey key] - { - get - { - return this[key]; - } - - set - { - throw new NotSupportedException(); - } - } - - /// - /// - /// Gets the element that has the specified key. - /// - /// If the property is set. - object IDictionary.this[object key] - { - get - { - if (!(key is TKey)) - return null; - - return this[(TKey)key]; - } - - set - { - throw new NotSupportedException(); - } - } - /// /// Gets the number of items in the dictionary. /// @@ -88,8 +32,8 @@ namespace Svelto.DataStructures public int Count { get - { - return _dictionary.Count; + { + return _dictionary.Count; } } @@ -107,24 +51,6 @@ namespace Svelto.DataStructures } } - /// - ICollection IDictionary.Keys - { - get - { - return Keys; - } - } - - /// - ICollection IDictionary.Keys - { - get - { - return Keys; - } - } - /// /// Gets a collection that contains the values in the dictionary. /// @@ -139,155 +65,82 @@ namespace Svelto.DataStructures } } - /// - ICollection IDictionary.Values + /// + /// Initializes a new instance of the class + /// that is a wrapper around the specified dictionary. + /// + /// The dictionary to wrap. + public ReadOnlyDictionary(Dictionary dictionary) { - get - { - return Values; - } - } + if (dictionary == null) + throw new ArgumentNullException("dictionary"); - /// - ICollection IDictionary.Values - { - get - { - return Values; - } + _dictionary = dictionary; } /// - bool ICollection>.IsReadOnly + /// + /// Gets the element that has the specified key. + /// + /// If the property is set. + TValue IDictionary.this[TKey key] { get { - return true; + return this[key]; } - } - /// - bool IDictionary.IsFixedSize - { - get + set { - return true; + throw new NotSupportedException(); } } /// - bool IDictionary.IsReadOnly + ICollection IDictionary.Keys { get { - return true; + return Keys; } } /// - bool ICollection.IsSynchronized + ICollection IDictionary.Values { get { - return false; + return Values; } } /// - object ICollection.SyncRoot + bool ICollection>.IsReadOnly { get { - ICollection collection = this as ICollection; - if (collection == null) - return collection.SyncRoot; - - throw new NotSupportedException("The current object does not support the SyncRoot property."); + return true; } } - /// - /// Determines whether the dictionary contains an element that has the specified key. - /// - /// The key to locate in the dictionary. - /// if the dictionary contains an element that has the specified key; otherwise, . - public bool ContainsKey(TKey key) - { - return _dictionary.ContainsKey(key); - } - - /// - bool IDictionary.Contains(object key) - { - if (key == null) - throw new ArgumentNullException("key"); - - if (key is TKey) - return ContainsKey((TKey)key); - - return false; - } - - /// - /// Returns an enumerator that iterates through the . - /// - /// An enumerator that can be used to iterate through the collection. - public IEnumerator> GetEnumerator() - { - return _dictionary.GetEnumerator(); - } - /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - /// - IDictionaryEnumerator IDictionary.GetEnumerator() - { - IDictionary dictionary = _dictionary as IDictionary; - if (dictionary != null) - return dictionary.GetEnumerator(); - - return new DictionaryEnumerator(_dictionary); - } - - /// - /// Retrieves the value that is associated with the specified key. - /// - /// The key whose value will be retrieved. - /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. - /// if the object that implements contains an element with the specified key; otherwise, . - public bool TryGetValue(TKey key, out TValue value) - { - return _dictionary.TryGetValue(key, out value); - } - /// void IDictionary.Add(TKey key, TValue value) { throw new NotSupportedException(); } - /// - void IDictionary.Add(object key, object value) - { - throw new NotSupportedException(); - } - /// bool IDictionary.Remove(TKey key) { throw new NotSupportedException(); } - /// - void IDictionary.Remove(object key) - { - throw new NotSupportedException(); - } - /// void ICollection>.Add(KeyValuePair item) { @@ -318,29 +171,60 @@ namespace Svelto.DataStructures throw new NotSupportedException(); } - /// - void IDictionary.Clear() + bool IDictionary.ContainsKey(TKey key) { - throw new NotSupportedException(); + return _dictionary.ContainsKey(key); } - /// - void ICollection.CopyTo(Array array, int index) + bool IDictionary.TryGetValue(TKey key, out TValue value) { - throw new NotImplementedException(); + return _dictionary.TryGetValue(key, out value); } -/// + int ICollection>.Count + { + get { return _dictionary.Count; } + } + + IEnumerator> IEnumerable>.GetEnumerator() + { + return _dictionary.GetEnumerator(); + } + + /// + /// Determines whether the dictionary contains an element that has the specified key. + /// + /// The key to locate in the dictionary. + /// if the dictionary contains an element that has the specified key; otherwise, . + public bool ContainsKey(TKey key) + { + return _dictionary.ContainsKey(key); + } + + public DictionaryEnumerator GetEnumerator() + { + return new DictionaryEnumerator(_dictionary); + } + + /// + /// Retrieves the value that is associated with the specified key. + /// + /// The key whose value will be retrieved. + /// When this method returns, the value associated with the specified key, if the key is found; otherwise, the default value for the type of the parameter. This parameter is passed uninitialized. + /// if the object that implements contains an element with the specified key; otherwise, . + public bool TryGetValue(TKey key, out TValue value) + { + return _dictionary.TryGetValue(key, out value); + } + + readonly Dictionary _dictionary; + + /// /// Represents a read-only collection of the keys of a object. /// public struct KeyCollection : ICollection, ICollection { - /// - /// The wrapped collection of keys. - /// - private readonly ICollection _keys; - - /// + /// /// Initializes a new instance of the class /// as a wrapper around the specified collection of keys. /// @@ -354,48 +238,54 @@ namespace Svelto.DataStructures _keys = keys; } - /// - /// Gets the number of elements in the collection. - /// - /// - /// The number of elements in the collection. - /// - public int Count + /// + bool ICollection.IsSynchronized { get { - return _keys.Count; + return false; } } - /// - bool ICollection.IsReadOnly + /// + object ICollection.SyncRoot { get { - return true; + throw new NotImplementedException(); } } - /// - bool ICollection.IsSynchronized + /// + void ICollection.CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + /// + /// Gets the number of elements in the collection. + /// + /// + /// The number of elements in the collection. + /// + public int Count { get { - return false; + return _keys.Count; } } - /// - object ICollection.SyncRoot + /// + bool ICollection.IsReadOnly { get { - throw new NotImplementedException(); + return true; } } - /// + /// /// Copies the elements of the collection to an array, starting at a specific array index. /// /// The one-dimensional array that is the destination of the elements copied from the collection. The array must have zero-based indexing. @@ -414,13 +304,7 @@ namespace Svelto.DataStructures _keys.CopyTo(array, arrayIndex); } - /// - void ICollection.CopyTo(Array array, int index) - { - throw new NotImplementedException(); - } - - /// + /// /// Returns an enumerator that iterates through the collection. /// /// An enumerator that can be used to iterate through the collection. @@ -429,35 +313,40 @@ namespace Svelto.DataStructures return _keys.GetEnumerator(); } - /// + /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } - /// + /// bool ICollection.Contains(TKey item) { return _keys.Contains(item); } - /// + /// void ICollection.Add(TKey item) { throw new NotSupportedException(); } - /// + /// bool ICollection.Remove(TKey item) { throw new NotSupportedException(); } - /// + /// void ICollection.Clear() { throw new NotSupportedException(); } + + /// + /// The wrapped collection of keys. + /// + readonly ICollection _keys; } /// @@ -465,11 +354,6 @@ namespace Svelto.DataStructures /// public struct ValueCollection : ICollection, ICollection { - /// - /// The wrapped collection of values. - /// - private readonly ICollection _values; - /// /// Initializes a new instance of the class /// as a wrapper around the specified collection of values. @@ -484,44 +368,50 @@ namespace Svelto.DataStructures _values = values; } - /// - /// Gets the number of elements in the collection. - /// - /// - /// The number of elements in the collection. - /// - public int Count + /// + bool ICollection.IsSynchronized { get { - return _values.Count; + return false; } } /// - bool ICollection.IsReadOnly + object ICollection.SyncRoot { get { - return true; + throw new NotImplementedException(); } } /// - bool ICollection.IsSynchronized + void ICollection.CopyTo(Array array, int index) + { + throw new NotImplementedException(); + } + + /// + /// Gets the number of elements in the collection. + /// + /// + /// The number of elements in the collection. + /// + public int Count { get { - return false; + return _values.Count; } } /// - object ICollection.SyncRoot + bool ICollection.IsReadOnly { get { - throw new NotImplementedException(); + return true; } } @@ -544,12 +434,6 @@ namespace Svelto.DataStructures _values.CopyTo(array, arrayIndex); } - /// - void ICollection.CopyTo(Array array, int index) - { - throw new NotImplementedException(); - } - /// /// Returns an enumerator that iterates through the collection. /// @@ -588,54 +472,47 @@ namespace Svelto.DataStructures { throw new NotSupportedException(); } + + /// + /// The wrapped collection of values. + /// + readonly ICollection _values; } - struct DictionaryEnumerator : IDictionaryEnumerator + public struct DictionaryEnumerator:IEnumerator> { - private readonly IEnumerator> _enumerator; - - public DictionaryEnumerator(IDictionary dictionary) - { - if (dictionary == null) - throw new ArgumentNullException("dictionary"); - - _enumerator = dictionary.GetEnumerator(); - } - /// - public DictionaryEntry Entry + public TKey Key { get { - KeyValuePair current = _enumerator.Current; - return new DictionaryEntry(current.Key, current.Value); + return _enumerator.Current.Key; } } /// - public object Key + public TValue Value { get { - return _enumerator.Current.Key; + return _enumerator.Current.Value; } } - /// - public object Value + public DictionaryEnumerator(IDictionary dictionary) { - get - { - return _enumerator.Current.Value; - } + if (dictionary == null) + throw new ArgumentNullException("dictionary"); + + _enumerator = dictionary.GetEnumerator(); } /// - public object Current + public KeyValuePair Current { get { - return Entry; + return _enumerator.Current; } } @@ -650,6 +527,18 @@ namespace Svelto.DataStructures { _enumerator.Reset(); } + + object IEnumerator.Current + { + get { return _enumerator.Current; } + } + + public void Dispose() + { + _enumerator.Dispose(); + } + + readonly IEnumerator> _enumerator; } } } diff --git a/DataStructures/SerializableDictionary.cs b/DataStructures/SerializableDictionary.cs new file mode 100644 index 0000000..93aae33 --- /dev/null +++ b/DataStructures/SerializableDictionary.cs @@ -0,0 +1,183 @@ +#define XML_ENABLED + +using System; +using System.Runtime.Serialization; +#if XML_ENABLED +using System.Xml; +using System.Xml.Serialization; +#endif +using System.Collections.Generic; + +[Serializable()] +public struct KeyValueSerialization +{ + public TKey Key; + public TVal Value; +} + +[Serializable()] +public class SerializableDictionary : Dictionary, +#if XML_ENABLED +IXmlSerializable, +#endif +ISerializable +{ + #region Constants + private const string DictionaryNodeName = "Dictionary"; + private const string ItemNodeName = "Item"; + private const string KeyNodeName = "Key"; + private const string ValueNodeName = "Value"; + #endregion + #region Constructors + public SerializableDictionary() + { + } + + public SerializableDictionary(IDictionary dictionary) + : base(dictionary) + { + } + + public SerializableDictionary(IEqualityComparer comparer) + : base(comparer) + { + } + + public SerializableDictionary(int capacity) + : base(capacity) + { + } + + public SerializableDictionary(IDictionary dictionary, IEqualityComparer comparer) + : base(dictionary, comparer) + { + } + + public SerializableDictionary(int capacity, IEqualityComparer comparer) + : base(capacity, comparer) + { + } + + #endregion + #region ISerializable Members + + public SerializableDictionary(SerializationInfo info, StreamingContext context) + { + int itemCount = info.GetInt32("count"); + + for (int i = 0; i < itemCount; i++) + { + KeyValueSerialization kvp = (KeyValueSerialization)info.GetValue(String.Format("Im{0}", i), typeof(KeyValueSerialization)); + + this.Add(kvp.Key, kvp.Value); + } + } + + void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("count", this.Count); + int itemIdx = 0; + foreach (KeyValuePair kvp in this) + { + KeyValueSerialization kvs = new KeyValueSerialization(); + kvs.Key = kvp.Key; + kvs.Value = kvp.Value; + + info.AddValue(String.Format("Im{0}", itemIdx), kvs, typeof(KeyValueSerialization)); + itemIdx++; + } + } + + #endregion + #if XML_ENABLED + #region IXmlSerializable Members + + void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer) + { + //writer.WriteStartElement(DictionaryNodeName); + foreach (KeyValuePair kvp in this) + { + writer.WriteStartElement(ItemNodeName); + writer.WriteStartElement(KeyNodeName); + KeySerializer.Serialize(writer, kvp.Key); + writer.WriteEndElement(); + writer.WriteStartElement(ValueNodeName); + ValueSerializer.Serialize(writer, kvp.Value); + writer.WriteEndElement(); + writer.WriteEndElement(); + } + //writer.WriteEndElement(); + } + + void IXmlSerializable.ReadXml(System.Xml.XmlReader reader) + { + if (reader.IsEmptyElement) + { + return; + } + + // Move past container + if (!reader.Read()) + { + throw new XmlException("Error in Deserialization of Dictionary"); + } + + //reader.ReadStartElement(DictionaryNodeName); + while (reader.NodeType != XmlNodeType.EndElement) + { + reader.ReadStartElement(ItemNodeName); + reader.ReadStartElement(KeyNodeName); + TKey key = (TKey)KeySerializer.Deserialize(reader); + reader.ReadEndElement(); + reader.ReadStartElement(ValueNodeName); + TVal value = (TVal)ValueSerializer.Deserialize(reader); + reader.ReadEndElement(); + reader.ReadEndElement(); + this.Add(key, value); + reader.MoveToContent(); + } + //reader.ReadEndElement(); + + reader.ReadEndElement(); // Read End Element to close Read of containing node + } + + System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() + { + return null; + } + + #endregion + #region Private Properties + + protected XmlSerializer ValueSerializer + { + get + { + if (valueSerializer == null) + { + valueSerializer = new XmlSerializer(typeof(TVal)); + } + return valueSerializer; + } + } + + private XmlSerializer KeySerializer + { + get + { + if (keySerializer == null) + { + keySerializer = new XmlSerializer(typeof(TKey)); + } + return keySerializer; + } + } + + #endregion + #region Private Members + private XmlSerializer keySerializer = null; + private XmlSerializer valueSerializer = null; + #endregion + #endif +} + diff --git a/DataStructures/SerializableDictionary.cs.meta b/DataStructures/SerializableDictionary.cs.meta new file mode 100644 index 0000000..8ec8a55 --- /dev/null +++ b/DataStructures/SerializableDictionary.cs.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b4a9cb2903cd07946a8650aeefb8d853 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} diff --git a/DataStructures/ThreadSafeDictionary.cs b/DataStructures/ThreadSafeDictionary.cs new file mode 100644 index 0000000..94a3646 --- /dev/null +++ b/DataStructures/ThreadSafeDictionary.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Svelto.DataStructures +{ + /// + /// original code: http://devplanet.com/blogs/brianr/archive/2008/09/29/thread-safe-dictionary-update.aspx + /// simplified (not an IDictionary) and apdated (uses FasterList) + /// + /// + /// + [Serializable] + public class ThreadSafeDictionary + { + // setup the lock; + public virtual int Count + { + get + { + using (new ReadOnlyLock(dictionaryLock)) + { + return dict.Count; + } + } + } + + public virtual bool IsReadOnly + { + get + { + using (new ReadOnlyLock(dictionaryLock)) + { + return dict.IsReadOnly; + } + } + } + + public virtual FasterList Keys + { + get + { + using (new ReadOnlyLock(dictionaryLock)) + { + return new FasterList(dict.Keys); + } + } + } + + public virtual FasterList Values + { + get + { + using (new ReadOnlyLock(dictionaryLock)) + { + return new FasterList(dict.Values); + } + } + } + + public virtual TValue this[TKey key] + { + get + { + using (new ReadOnlyLock(dictionaryLock)) + { + return dict[key]; + } + } + + set + { + using (new WriteLock(dictionaryLock)) + { + dict[key] = value; + } + } + } + + public virtual void Add(KeyValuePair item) + { + using (new WriteLock(dictionaryLock)) + { + dict.Add(item); + } + } + + public virtual void Clear() + { + using (new WriteLock(dictionaryLock)) + { + dict.Clear(); + } + } + + public virtual bool Contains(KeyValuePair item) + { + using (new ReadOnlyLock(dictionaryLock)) + { + return dict.Contains(item); + } + } + + public virtual void CopyTo(KeyValuePair[] array, int arrayIndex) + { + using (new ReadOnlyLock(dictionaryLock)) + { + dict.CopyTo(array, arrayIndex); + } + } + + public virtual bool Remove(KeyValuePair item) + { + using (new WriteLock(dictionaryLock)) + { + return dict.Remove(item); + } + } + + public virtual void Add(TKey key, TValue value) + { + using (new WriteLock(dictionaryLock)) + { + dict.Add(key, value); + } + } + + public virtual bool ContainsKey(TKey key) + { + using (new ReadOnlyLock(dictionaryLock)) + { + return dict.ContainsKey(key); + } + } + + public virtual bool Remove(TKey key) + { + using (new WriteLock(dictionaryLock)) + { + return dict.Remove(key); + } + } + + public virtual bool TryGetValue(TKey key, out TValue value) + { + using (new ReadOnlyLock(dictionaryLock)) + { + return dict.TryGetValue(key, out value); + } + } + + /// + /// Merge does a blind remove, and then add. Basically a blind Upsert. + /// + /// Key to lookup + /// New Value + public void MergeSafe(TKey key, TValue newValue) + { + using (new WriteLock(dictionaryLock)) + { + // take a writelock immediately since we will always be writing + if (dict.ContainsKey(key)) + dict.Remove(key); + + dict.Add(key, newValue); + } + } + + /// + /// This is a blind remove. Prevents the need to check for existence first. + /// + /// Key to remove + public void RemoveSafe(TKey key) + { + using (new ReadLock(dictionaryLock)) + { + if (dict.ContainsKey(key)) + using (new WriteLock(dictionaryLock)) + { + dict.Remove(key); + } + } + } + + // This is the internal dictionary that we are wrapping + readonly IDictionary dict = new Dictionary(); + + [NonSerialized] readonly ReaderWriterLockSlim dictionaryLock = Locks.GetLockInstance(LockRecursionPolicy.NoRecursion); + } + + public static class Locks + { + public static ReaderWriterLockSlim GetLockInstance() + { + return GetLockInstance(LockRecursionPolicy.SupportsRecursion); + } + + public static ReaderWriterLockSlim GetLockInstance(LockRecursionPolicy recursionPolicy) + { + return new ReaderWriterLockSlim(recursionPolicy); + } + + public static void GetReadLock(ReaderWriterLockSlim locks) + { + var lockAcquired = false; + while (!lockAcquired) + lockAcquired = locks.TryEnterUpgradeableReadLock(1); + } + + public static void GetReadOnlyLock(ReaderWriterLockSlim locks) + { + var lockAcquired = false; + while (!lockAcquired) + lockAcquired = locks.TryEnterReadLock(1); + } + + public static void GetWriteLock(ReaderWriterLockSlim locks) + { + var lockAcquired = false; + while (!lockAcquired) + lockAcquired = locks.TryEnterWriteLock(1); + } + + public static void ReleaseLock(ReaderWriterLockSlim locks) + { + ReleaseWriteLock(locks); + ReleaseReadLock(locks); + ReleaseReadOnlyLock(locks); + } + + public static void ReleaseReadLock(ReaderWriterLockSlim locks) + { + if (locks.IsUpgradeableReadLockHeld) + locks.ExitUpgradeableReadLock(); + } + + public static void ReleaseReadOnlyLock(ReaderWriterLockSlim locks) + { + if (locks.IsReadLockHeld) + locks.ExitReadLock(); + } + + public static void ReleaseWriteLock(ReaderWriterLockSlim locks) + { + if (locks.IsWriteLockHeld) + locks.ExitWriteLock(); + } + } + + public abstract class BaseLock : IDisposable + { + protected ReaderWriterLockSlim _Locks; + + public BaseLock(ReaderWriterLockSlim locks) + { + _Locks = locks; + } + + public abstract void Dispose(); + } + + public class ReadLock : BaseLock + { + public ReadLock(ReaderWriterLockSlim locks) + : base(locks) + { + Locks.GetReadLock(_Locks); + } + + public override void Dispose() + { + Locks.ReleaseReadLock(_Locks); + } + } + + public class ReadOnlyLock : BaseLock + { + public ReadOnlyLock(ReaderWriterLockSlim locks) + : base(locks) + { + Locks.GetReadOnlyLock(_Locks); + } + + public override void Dispose() + { + Locks.ReleaseReadOnlyLock(_Locks); + } + } + + public class WriteLock : BaseLock + { + public WriteLock(ReaderWriterLockSlim locks) + : base(locks) + { + Locks.GetWriteLock(_Locks); + } + + public override void Dispose() + { + Locks.ReleaseWriteLock(_Locks); + } + } +} diff --git a/Ticker/UnityTicker.cs.meta b/DataStructures/ThreadSafeDictionary.cs.meta similarity index 69% rename from Ticker/UnityTicker.cs.meta rename to DataStructures/ThreadSafeDictionary.cs.meta index 26f9208..07fdea7 100644 --- a/Ticker/UnityTicker.cs.meta +++ b/DataStructures/ThreadSafeDictionary.cs.meta @@ -1,5 +1,7 @@ fileFormatVersion: 2 -guid: b62a08472017f914d9fc399eeb95108e +guid: db7c720ce4e437f48b1380223ba08192 +timeCreated: 1470829214 +licenseType: Pro MonoImporter: serializedVersion: 2 defaultReferences: [] diff --git a/DataStructures/ThreadSafeQueue.cs b/DataStructures/ThreadSafeQueue.cs index b9b8f7d..4505dd8 100644 --- a/DataStructures/ThreadSafeQueue.cs +++ b/DataStructures/ThreadSafeQueue.cs @@ -1,4 +1,3 @@ -using System; using System.Collections; using System.Collections.Generic; using System.Threading; @@ -88,12 +87,12 @@ namespace Svelto.DataStructures } } - public List DequeueAll() + public FasterList DequeueAll() { LockQ.EnterWriteLock(); try { - List returnList = new List(); + FasterList returnList = new FasterList(); while (m_Queue.Count > 0) returnList.Add(m_Queue.Dequeue()); @@ -107,6 +106,40 @@ namespace Svelto.DataStructures } } + public void DequeueAllInto(FasterList list) + { + LockQ.EnterWriteLock(); + try + { + while (m_Queue.Count > 0) + list.Add(m_Queue.Dequeue()); + } + + finally + { + LockQ.ExitWriteLock(); + } + } + + public FasterList DequeueAllAs() where U:class + { + LockQ.EnterWriteLock(); + try + { + FasterList returnList = new FasterList(); + + while (m_Queue.Count > 0) + returnList.Add(m_Queue.Dequeue() as U); + + return returnList; + } + + finally + { + LockQ.ExitWriteLock(); + } + } + public T Peek() { LockQ.EnterWriteLock(); @@ -126,6 +159,20 @@ namespace Svelto.DataStructures } } + public void Clear() + { + LockQ.EnterWriteLock(); + try + { + m_Queue.Clear(); + } + + finally + { + LockQ.ExitWriteLock(); + } + } + public bool TryDequeue(out T item) { LockQ.EnterWriteLock(); diff --git a/DataStructures/WeakReference.cs b/DataStructures/WeakReference.cs index cd69d9c..4a8d018 100644 --- a/DataStructures/WeakReference.cs +++ b/DataStructures/WeakReference.cs @@ -55,4 +55,9 @@ namespace Svelto.DataStructures : base(info, context) { } } + + public static class WeakReferenceUtility + { + public static bool IsValid(this WeakReference obj) { return obj != null && obj.IsAlive == true && obj.Target != null; } + } } diff --git a/ECS.meta b/ECS.meta index 78d0f09..e1b48eb 100644 --- a/ECS.meta +++ b/ECS.meta @@ -3,3 +3,5 @@ guid: 5015ea56c030d6542bd4daa46f59e549 folderAsset: yes DefaultImporter: userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Dispatcher/Dispatcher.cs b/ECS/Dispatcher/Dispatcher.cs deleted file mode 100644 index 8682a53..0000000 --- a/ECS/Dispatcher/Dispatcher.cs +++ /dev/null @@ -1,39 +0,0 @@ -public class Dispatcher -{ - public event System.Action subscribers; - - private Dispatcher() { } - - public Dispatcher(S sender) - { - _sender = sender; - } - - public void Dispatch(ref T value) - { - if (subscribers != null) - subscribers(_sender, value); - } - - readonly S _sender; -} - -public class Dispatcher -{ - public event System.Action subscribers; - - private Dispatcher() { } - - public Dispatcher(S sender) - { - _sender = sender; - } - - public void Dispatch() - { - if (subscribers != null) - subscribers(_sender); - } - - readonly S _sender; -} diff --git a/ECS/Dispatcher/Dispatcher.cs.meta b/ECS/Dispatcher/Dispatcher.cs.meta deleted file mode 100644 index 8e801c6..0000000 --- a/ECS/Dispatcher/Dispatcher.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: d8886d5d5f5929c479998a1316264c23 -timeCreated: 1436121137 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/ECS/Dispatcher/DispatcherOnChange.cs b/ECS/Dispatcher/DispatcherOnChange.cs index fea28a0..3ab3199 100644 --- a/ECS/Dispatcher/DispatcherOnChange.cs +++ b/ECS/Dispatcher/DispatcherOnChange.cs @@ -1,34 +1,24 @@ using System.Collections.Generic; -public class DispatcherOnChange: Dispatcher +namespace Svelto.ECS { - public DispatcherOnChange(S sender) : base(sender) { } - - public T value + public class DispatchOnChange : DispatchOnSet { - set + public DispatchOnChange(int senderID) : base(senderID) + { } + + public new T value { - if (EqualityComparer.Default.Equals(value, _value) == false) + set { - _value = value; + if (EqualityComparer.Default.Equals(value, _value) == false) + base.value = value; + } - Dispatch(ref value); + get + { + return _value; } } } - - T _value; } - -public class DispatcherOnSet: Dispatcher -{ - public DispatcherOnSet(S sender) : base(sender) { } - - public T value - { - set - { - Dispatch(ref value); - } - } -} \ No newline at end of file diff --git a/ECS/Dispatcher/DispatcherOnSet.cs b/ECS/Dispatcher/DispatcherOnSet.cs new file mode 100644 index 0000000..2c9f069 --- /dev/null +++ b/ECS/Dispatcher/DispatcherOnSet.cs @@ -0,0 +1,43 @@ +using BetterWeakEvents; + +namespace Svelto.ECS +{ + public class DispatchOnSet + { + public DispatchOnSet(int senderID) + { + _senderID = senderID; + _subscribers = new WeakEvent(); + } + + public T value + { + set + { + _value = value; + + _subscribers.Invoke(_senderID, value); + } + + get + { + return _value; + } + } + + public void NotifyOnValueSet(System.Action action) + { + _subscribers += action; + } + + public void StopNotify(System.Action action) + { + _subscribers -= action; + } + + protected T _value; + protected int _senderID; + + protected WeakEvent _subscribers; + } +} diff --git a/ECS/Dispatcher/DispatcherOnSet.cs.meta b/ECS/Dispatcher/DispatcherOnSet.cs.meta new file mode 100644 index 0000000..d868b59 --- /dev/null +++ b/ECS/Dispatcher/DispatcherOnSet.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: cbb1d54b291794b4686526222ee2bbb6 +timeCreated: 1471250507 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/EngineNodeDB.cs b/ECS/EngineNodeDB.cs index 3eb6f61..1ed8944 100644 --- a/ECS/EngineNodeDB.cs +++ b/ECS/EngineNodeDB.cs @@ -2,14 +2,17 @@ using System; using System.Collections.Generic; using Svelto.DataStructures; -namespace Svelto.ES +namespace Svelto.ECS { class EngineNodeDB : IEngineNodeDB { - internal EngineNodeDB(Dictionary> nodesDB, Dictionary> nodesDBdic) + internal EngineNodeDB( Dictionary> nodesDB, + Dictionary> nodesDBdic, + Dictionary> nodesDBgroups) { - this._nodesDB = nodesDB; - this._nodesDBdic = nodesDBdic; + _nodesDB = nodesDB; + _nodesDBdic = nodesDBdic; + _nodesDBgroups = nodesDBgroups; } public FasterReadOnlyListCast QueryNodes() where T:INode @@ -17,7 +20,7 @@ namespace Svelto.ES var type = typeof(T); if (_nodesDB.ContainsKey(type) == false) - return new FasterReadOnlyListCast(FasterReadOnlyListCast.DefaultList); + return RetrieveEmptyNodeList(); return new FasterReadOnlyListCast(_nodesDB[type]); } @@ -32,6 +35,26 @@ namespace Svelto.ES return new ReadOnlyDictionary(_nodesDBdic[type]); } + public T QueryNodeFromGroup(int groupID) where T : INode + { + return QueryNode(groupID); + } + + public bool QueryNodeFromGroup(int groupID, out T node) where T : INode + { + return QueryNode(groupID, out node); + } + + public FasterReadOnlyListCast QueryNodesFromGroups() where T : INode + { + var type = typeof(T); + + if (_nodesDBgroups.ContainsKey(type) == false) + return RetrieveEmptyNodeList(); + + return new FasterReadOnlyListCast(_nodesDBgroups[type]); + } + public bool QueryNode(int ID, out T node) where T:INode { var type = typeof(T); @@ -62,9 +85,15 @@ namespace Svelto.ES throw new Exception("Node Not Found"); } + static FasterReadOnlyListCast RetrieveEmptyNodeList() where T : INode + { + return new FasterReadOnlyListCast(FasterReadOnlyListCast.DefaultList); + } + Dictionary> _nodesDB; - Dictionary> _nodesDBdic; + Dictionary> _nodesDBdic; + Dictionary> _nodesDBgroups; - ReadOnlyDictionary _defaultEmptyNodeDict = new ReadOnlyDictionary(new Dictionary()); + ReadOnlyDictionary _defaultEmptyNodeDict = new ReadOnlyDictionary(new Dictionary()); } } diff --git a/ECS/EnginesRoot.cs b/ECS/EnginesRoot.cs index 4cdc0fe..6d43243 100644 --- a/ECS/EnginesRoot.cs +++ b/ECS/EnginesRoot.cs @@ -1,37 +1,117 @@ using System; +using System.Collections; using System.Collections.Generic; using Svelto.DataStructures; -using Svelto.Ticker; +using UnityEngine; -namespace Svelto.ES +#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR +using Svelto.ECS.Profiler; +#endif + +namespace Svelto.ECS { - public sealed class EnginesRoot: IEnginesRoot, IEndOfFrameTickable, IEntityFactory + class Scheduler : MonoBehaviour { - public EnginesRoot(ITicker ticker) + IEnumerator Start() { - ticker.Add(this); + while (true) + { + yield return new WaitForEndOfFrame(); + + OnTick(); + } + } + + internal Action OnTick; + } + public sealed class EnginesRoot : IEnginesRoot, IEntityFactory + { + public EnginesRoot() + { _nodeEngines = new Dictionary>>(); _engineRootWeakReference = new WeakReference(this); _otherEnginesReferences = new FasterList(); - + _nodesDB = new Dictionary>(); _nodesDBdic = new Dictionary>(); - _nodesToAdd = new Queue(); - _nodesToRemove = new Queue(); + _nodesToAdd = new FasterList(); + _groupNodesToAdd = new FasterList(); + + _nodesDBgroups = new Dictionary>(); + + GameObject go = new GameObject("ECSScheduler"); + + go.AddComponent().OnTick += SubmitNodes; + +#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR + GameObject debugEngineObject = new GameObject("Engine Debugger"); + debugEngineObject.gameObject.AddComponent(); +#endif } - public void EndOfFrameTick(float deltaSec) + void SubmitNodes() { - while (_nodesToAdd.Count > 0) InternalAdd(_nodesToAdd.Dequeue()); - while (_nodesToRemove.Count > 0) InternalRemove(_nodesToRemove.Dequeue()); + int groupNodesCount; + int nodesCount; + bool newNodesHaveBeenAddedWhileIterating; + int startNodes = 0; + int startGroupNodes = 0; + int numberOfReenteringLoops = 0; + + do + { + groupNodesCount = _groupNodesToAdd.Count; + nodesCount = _nodesToAdd.Count; + + for (int i = startNodes; i < nodesCount; i++) + { + var node = _nodesToAdd[i]; + AddNodeToTheDB(node, node.GetType()); + } + + for (int i = startGroupNodes; i < groupNodesCount; i++) + { + var node = _groupNodesToAdd[i]; + AddNodeToGroupDB(node, node.GetType()); + } + + for (int i = startNodes; i < nodesCount; i++) + { + var node = _nodesToAdd[i]; + AddNodeToTheSuitableEngines(node, node.GetType()); + } + + for (int i = startGroupNodes; i < groupNodesCount; i++) + { + var node = _groupNodesToAdd[i]; + AddNodeToTheSuitableEngines(node, node.GetType()); + } + + newNodesHaveBeenAddedWhileIterating = _groupNodesToAdd.Count > groupNodesCount || _nodesToAdd.Count > nodesCount; + + startNodes = nodesCount; + startGroupNodes = groupNodesCount; + + if (numberOfReenteringLoops > 5) + throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method"); + + numberOfReenteringLoops++; + + } while (newNodesHaveBeenAddedWhileIterating); + + _nodesToAdd.Clear(); + _groupNodesToAdd.Clear(); } public void AddEngine(IEngine engine) { +#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR + EngineProfiler.AddEngine(engine); +#endif if (engine is IQueryableNodeEngine) - (engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic); + (engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _nodesDBgroups); if (engine is INodesEngine) { @@ -46,7 +126,7 @@ namespace Svelto.ES if (baseType.IsGenericType) { var genericType = baseType.GetGenericTypeDefinition(); - + if (genericType == typeof(SingleNodeEngine<>)) { AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines); @@ -55,7 +135,7 @@ namespace Svelto.ES } } - _otherEnginesReferences.Add(engine); + _otherEnginesReferences.Add(engine); } public void BuildEntity(int ID, EntityDescriptor ed) @@ -63,14 +143,24 @@ namespace Svelto.ES var entityNodes = ed.BuildNodes(ID, (node) => { if (_engineRootWeakReference.IsValid == true) - _engineRootWeakReference.Target._nodesToRemove.Enqueue(node); + InternalRemove(node); }); - for (int i = 0; i < entityNodes.Count; i++) - _nodesToAdd.Enqueue(entityNodes[i]); + _nodesToAdd.AddRange(entityNodes); } - static void AddEngine(T engine, Type[] types, Dictionary>> engines) where T:INodeEngine + public void BuildEntityGroup(int groupID, EntityDescriptor ed) + { + var entityNodes = ed.BuildNodes(groupID, (node) => + { + if (_engineRootWeakReference.IsValid == true) + InternalGroupRemove(node); + }); + + _groupNodesToAdd.AddRange(entityNodes); + } + + static void AddEngine(T engine, Type[] types, Dictionary>> engines) where T : INodeEngine { for (int i = 0; i < types.Length; i++) { @@ -89,20 +179,15 @@ namespace Svelto.ES } } - void InternalAdd(T node) where T:INode + void AddNodeToGroupDB(INode node, Type nodeType) { - Type nodeType = node.GetType(); - - AddNodeToTheSuitableEngines(node, nodeType); - AddNodeToTheDB(node, nodeType); - } + FasterList nodes; + if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == false) + nodes = _nodesDBgroups[nodeType] = new FasterList(); - void InternalRemove(T node) where T:INode - { - Type nodeType = node.GetType(); + nodes.Add(node); - RemoveNodeFromEngines(node, nodeType); - RemoveNodeFromTheDB(node, nodeType); + AddNodeToNodesDictionary(node, nodeType); } void AddNodeToTheDB(T node, Type nodeType) where T : INode @@ -113,12 +198,17 @@ namespace Svelto.ES nodes.Add(node); + AddNodeToNodesDictionary(node, nodeType); + } + + void AddNodeToNodesDictionary(T node, Type nodeType) where T : INode + { if (node is NodeWithID) { Dictionary nodesDic; if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false) nodesDic = _nodesDBdic[nodeType] = new Dictionary(); - + nodesDic[(node as NodeWithID).ID] = node; } } @@ -128,16 +218,29 @@ namespace Svelto.ES FasterList> enginesForNode; if (_nodeEngines.TryGetValue(nodeType, out enginesForNode)) + { for (int j = 0; j < enginesForNode.Count; j++) + { +#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR + EngineProfiler.MonitorAddDuration(AddNodeToEngine, enginesForNode[j], node); +#else enginesForNode[j].Add(node); +#endif + } + } } void RemoveNodeFromTheDB(T node, Type nodeType) where T : INode { FasterList nodes; if (_nodesDB.TryGetValue(nodeType, out nodes) == true) - nodes.Remove(node); //should I remove it from the dictionary if length is zero? + nodes.UnorderredRemove(node); //should I remove it from the dictionary if length is zero? + + RemoveNodeFromNodesDictionary(node, nodeType); + } + void RemoveNodeFromNodesDictionary(T node, Type nodeType) where T : INode + { if (node is NodeWithID) { Dictionary nodesDic; @@ -147,13 +250,57 @@ namespace Svelto.ES } } + void RemoveNodeFromGroupDB(T node, Type nodeType) where T : INode + { + FasterList nodes; + if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == true) + nodes.UnorderredRemove(node); //should I remove it from the dictionary if length is zero? + + RemoveNodeFromNodesDictionary(node, nodeType); + } + void RemoveNodeFromEngines(T node, Type nodeType) where T : INode { FasterList> enginesForNode; if (_nodeEngines.TryGetValue(nodeType, out enginesForNode)) + { for (int j = 0; j < enginesForNode.Count; j++) + { +#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR + EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, enginesForNode[j], node); +#else enginesForNode[j].Remove(node); +#endif + } + } + } +#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR + void AddNodeToEngine(INodeEngine engine, INode node) + { + engine.Add(node); + } + + void RemoveNodeFromEngine(INodeEngine engine, INode node) + { + engine.Remove(node); + } +#endif + + void InternalRemove(T node) where T : INode + { + Type nodeType = node.GetType(); + + RemoveNodeFromEngines(node, nodeType); + RemoveNodeFromTheDB(node, node.GetType()); + } + + void InternalGroupRemove(T node) where T : INode + { + Type nodeType = node.GetType(); + + RemoveNodeFromEngines(node, nodeType); + RemoveNodeFromGroupDB(node, node.GetType()); } Dictionary>> _nodeEngines; @@ -162,11 +309,13 @@ namespace Svelto.ES Dictionary> _nodesDB; Dictionary> _nodesDBdic; - Queue _nodesToAdd; - Queue _nodesToRemove; + Dictionary> _nodesDBgroups; + + FasterList _nodesToAdd; + FasterList _groupNodesToAdd; WeakReference _engineRootWeakReference; - + //integrated pooling system //add debug panel like Entitas has //GCHandle should be used to reduce the number of strong references diff --git a/ECS/EntityDescriptor.cs b/ECS/EntityDescriptor.cs index 384d2db..6183fd5 100644 --- a/ECS/EntityDescriptor.cs +++ b/ECS/EntityDescriptor.cs @@ -2,37 +2,37 @@ using System.Reflection; using Svelto.DataStructures; -namespace Svelto.ES +namespace Svelto.ECS { public class EntityDescriptor { - EntityDescriptor() - {} - - protected EntityDescriptor(INodeBuilder[] nodesToBuild, params object[] componentsImplementor) + protected EntityDescriptor(INodeBuilder[] nodesToBuild, params object[] componentsImplementor) { - _implementors = componentsImplementor; _nodesToBuild = nodesToBuild; + _implementors = componentsImplementor; + _nodesToBuild = nodesToBuild; } - virtual public FasterList BuildNodes(int ID, Action removeAction) - { - var nodes = new FasterList(); + public virtual FasterList BuildNodes(int ID, Action removeAction) + { + var nodes = new FasterList(); + + for (int index = 0; index < _nodesToBuild.Length; index++) + { + var nodeBuilder = _nodesToBuild[index]; + var node = FillNode(nodeBuilder.Build(ID), () => + { + for (int i = 0; i < nodes.Count; i++) + removeAction(nodes[i]); - for (int index = 0; index < _nodesToBuild.Length; index++) - { - var nodeBuilder = _nodesToBuild[index]; - var node = FillNode(nodeBuilder.Build(ID), () => - { - for (int i = 0; i < nodes.Count; i++) - removeAction(nodes[i]); - } - ); + nodes.Clear(); + } + ); - nodes.Add (node); - } + nodes.Add (node); + } - return nodes; - } + return nodes; + } TNode FillNode(TNode node, Action removeAction) where TNode: INode { @@ -40,15 +40,15 @@ namespace Svelto.ES for (int i = fields.Length - 1; i >=0 ; --i) { - var field = fields[i]; - Type fieldType = field.FieldType; - object component = null; + var field = fields[i]; + Type fieldType = field.FieldType; + object component = null; for (int j = 0; j < _implementors.Length; j++) { var implementor = _implementors[j]; - if (fieldType.IsAssignableFrom(implementor.GetType())) + if (implementor != null && fieldType.IsAssignableFrom(implementor.GetType())) { component = implementor; @@ -61,10 +61,8 @@ namespace Svelto.ES if (component == null) { - Exception e = new Exception("Svelto.ES: An Entity must hold all the components needed for a Node. " + - "Type: " + field.FieldType.Name + " Node: " + node.GetType().Name); - - Utility.Console.LogException(e); + Exception e = new Exception("Svelto.ECS: Implementor not found for a Node. " + + "Implementor Type: " + field.FieldType.Name + " - Node: " + node.GetType().Name + " - EntityDescriptor " + this); throw e; } @@ -75,9 +73,8 @@ namespace Svelto.ES return node; } - readonly object[] _implementors; - - INodeBuilder[] _nodesToBuild; + readonly object[] _implementors; + INodeBuilder[] _nodesToBuild; } public interface INodeBuilder diff --git a/ECS/GenericEntityDescriptor.cs b/ECS/GenericEntityDescriptor.cs new file mode 100644 index 0000000..16c2687 --- /dev/null +++ b/ECS/GenericEntityDescriptor.cs @@ -0,0 +1,127 @@ +namespace Svelto.ECS +{ + class GenericEntityDescriptor : EntityDescriptor + where T : NodeWithID, new() + { + static GenericEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new NodeBuilder() + }; + } + public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + { + } + static INodeBuilder[] _nodesToBuild; + } + + class GenericEntityDescriptor : EntityDescriptor + where T : NodeWithID, new() + where U : NodeWithID, new() + { + static GenericEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new NodeBuilder(), + new NodeBuilder() + }; + } + public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + { + } + static INodeBuilder[] _nodesToBuild; + } + + class GenericEntityDescriptor : EntityDescriptor + where T : NodeWithID, new() + where U : NodeWithID, new() + where V : NodeWithID, new() + { + static GenericEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder() + }; + } + public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + { + } + static INodeBuilder[] _nodesToBuild; + } + + class GenericEntityDescriptor : EntityDescriptor + where T : NodeWithID, new() + where U : NodeWithID, new() + where V : NodeWithID, new() + where W : NodeWithID, new() + { + static GenericEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder() + }; + } + public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + { + } + static INodeBuilder[] _nodesToBuild; + } + + class GenericEntityDescriptor : EntityDescriptor + where T : NodeWithID, new() + where U : NodeWithID, new() + where V : NodeWithID, new() + where W : NodeWithID, new() + where X : NodeWithID, new() + { + static GenericEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder() + }; + } + public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + { + } + static INodeBuilder[] _nodesToBuild; + } + class GenericEntityDescriptor : EntityDescriptor + where T : NodeWithID, new() + where U : NodeWithID, new() + where V : NodeWithID, new() + where W : NodeWithID, new() + where X : NodeWithID, new() + where Y : NodeWithID, new() + { + static GenericEntityDescriptor() + { + _nodesToBuild = new INodeBuilder[] + { + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder(), + new NodeBuilder() + }; + } + public GenericEntityDescriptor(params object[] componentsImplementor) : base(_nodesToBuild, componentsImplementor) + { + } + static INodeBuilder[] _nodesToBuild; + } +} diff --git a/ECS/GenericEntityDescriptor.cs.meta b/ECS/GenericEntityDescriptor.cs.meta new file mode 100644 index 0000000..840728d --- /dev/null +++ b/ECS/GenericEntityDescriptor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: fd70d1b3107162f4790b1b71a129818f +timeCreated: 1484485852 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/IComponent.cs b/ECS/IComponent.cs deleted file mode 100644 index fc5858f..0000000 --- a/ECS/IComponent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Svelto.ES -{ - public interface IComponent - { - } -} \ No newline at end of file diff --git a/ECS/IEngine.cs b/ECS/IEngine.cs index 0d295ef..5bddc66 100644 --- a/ECS/IEngine.cs +++ b/ECS/IEngine.cs @@ -1,4 +1,4 @@ -namespace Svelto.ES +namespace Svelto.ECS { public interface IEngine {} @@ -18,20 +18,4 @@ namespace Svelto.ES { IEngineNodeDB nodesDB { set; } } - - public abstract class SingleNodeEngine : INodeEngine where TNodeType:class, INode - { - void INodeEngine.Add(INode obj) - { - Add(obj as TNodeType); - } - - void INodeEngine.Remove(INode obj) - { - Remove(obj as TNodeType); - } - - protected abstract void Add(TNodeType node); - protected abstract void Remove(TNodeType node); - } } diff --git a/ECS/IEngineNodeDB.cs b/ECS/IEngineNodeDB.cs index 5e7b46f..069d532 100644 --- a/ECS/IEngineNodeDB.cs +++ b/ECS/IEngineNodeDB.cs @@ -1,12 +1,19 @@ using Svelto.DataStructures; -namespace Svelto.ES +namespace Svelto.ECS { public interface IEngineNodeDB { ReadOnlyDictionary QueryIndexableNodes() where T:INode; + bool QueryNode(int ID, out T node) where T:INode; T QueryNode(int ID) where T:INode; + FasterReadOnlyListCast QueryNodes() where T:INode; + + bool QueryNodeFromGroup(int ID, out T node) where T : INode; + T QueryNodeFromGroup(int ID) where T : INode; + FasterReadOnlyListCast QueryNodesFromGroups() where T : INode; } } + diff --git a/ECS/IEnginesRoot.cs b/ECS/IEnginesRoot.cs index 8fa7b5d..72c317a 100644 --- a/ECS/IEnginesRoot.cs +++ b/ECS/IEnginesRoot.cs @@ -1,4 +1,4 @@ -namespace Svelto.ES +namespace Svelto.ECS { public interface IEnginesRoot { @@ -8,5 +8,7 @@ namespace Svelto.ES public interface IEntityFactory { void BuildEntity(int ID, EntityDescriptor ED); + + void BuildEntityGroup(int ID, EntityDescriptor ED); } } diff --git a/ECS/IEntityDescriptorHolder.cs b/ECS/IEntityDescriptorHolder.cs index 9ee697a..2c70278 100644 --- a/ECS/IEntityDescriptorHolder.cs +++ b/ECS/IEntityDescriptorHolder.cs @@ -1,10 +1,11 @@ -namespace Svelto.ES +namespace Svelto.ECS { - /// - /// please use [DisallowMultipleComponent] in your monobehaviours that implement IEntityDescriptorHolder - /// + /// + /// please use [DisallowMultipleComponent] in your monobehaviours that implement IEntityDescriptorHolder + /// public interface IEntityDescriptorHolder { - EntityDescriptor BuildDescriptorType(); + //I must find a nicer solution for the extraImplentors + EntityDescriptor BuildDescriptorType(object[] extraImplentors = null); } } diff --git a/ECS/IEntityDescriptorHolder.cs.meta b/ECS/IEntityDescriptorHolder.cs.meta index cbd5e0b..bf596a5 100644 --- a/ECS/IEntityDescriptorHolder.cs.meta +++ b/ECS/IEntityDescriptorHolder.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5a8db83410a8aaa40b824fdc3d968f67 +guid: 0badb413a22f42a4b9f68e356e88b07f timeCreated: 1463438461 licenseType: Free MonoImporter: diff --git a/ECS/IImplementor.cs b/ECS/IImplementor.cs new file mode 100644 index 0000000..738c8e6 --- /dev/null +++ b/ECS/IImplementor.cs @@ -0,0 +1,6 @@ +namespace Svelto.ECS +{ + public interface IImplementor + { + } +} diff --git a/ECS/IComponent.cs.meta b/ECS/IImplementor.cs.meta similarity index 100% rename from ECS/IComponent.cs.meta rename to ECS/IImplementor.cs.meta diff --git a/ECS/INode.cs b/ECS/INode.cs index 7646bbe..30abb9f 100644 --- a/ECS/INode.cs +++ b/ECS/INode.cs @@ -1,11 +1,11 @@ -namespace Svelto.ES +namespace Svelto.ECS { public interface INode {} public class NodeWithID: INode { - public static TNodeType BuildNode(int ID) where TNodeType: NodeWithID, new() + public static TNodeType BuildNode(int ID) where TNodeType: NodeWithID, new() { return new TNodeType { _ID = ID }; } diff --git a/ECS/IRemoveEntityComponent.cs b/ECS/IRemoveEntityComponent.cs index b6acb5d..a3f1525 100644 --- a/ECS/IRemoveEntityComponent.cs +++ b/ECS/IRemoveEntityComponent.cs @@ -1,6 +1,6 @@ using System; -namespace Svelto.ES +namespace Svelto.ECS { public interface IRemoveEntityComponent { diff --git a/ECS/Profiler.meta b/ECS/Profiler.meta new file mode 100644 index 0000000..3a39d53 --- /dev/null +++ b/ECS/Profiler.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 8d07544bb8b417f4a9eaefe0d4771d89 +folderAsset: yes +timeCreated: 1462355668 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/Editor.meta b/ECS/Profiler/Editor.meta new file mode 100644 index 0000000..48dd9b3 --- /dev/null +++ b/ECS/Profiler/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 71a4fc836cd9bde4bbb936a073804ec5 +folderAsset: yes +timeCreated: 1480683133 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/Editor/EngineProfiler.meta b/ECS/Profiler/Editor/EngineProfiler.meta new file mode 100644 index 0000000..b802d05 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 777a9420fd80746428f9d2c5b718fd2f +folderAsset: yes +timeCreated: 1462351213 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs new file mode 100644 index 0000000..94683c2 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs @@ -0,0 +1,373 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +//This profiler is based on the Entitas Visual Debugging tool +//https://github.com/sschmid/Entitas-CSharp + +namespace Svelto.ECS.Profiler +{ + [CustomEditor(typeof (EngineProfilerBehaviour))] + public class EngineProfilerInspector : Editor + { + enum SORTING_OPTIONS + { + AVERAGE, + MIN, + MAX, + NAME, + NONE + } + + static bool _hideEmptyEngines = true; + static bool _showTickEngines; + static bool _showAddEngines; + static bool _showRemoveEngines; + + static string _systemNameSearchTerm = string.Empty; + + float _axisUpperBounds = 2f; + + string updateTitle = "Update".PadRight(15, ' '); + string lateUpdateTitle = "Late".PadRight(13, ' '); + string fixedupdateTitle = "Fixed".PadRight(15, ' '); + string minTitle = "Min".PadRight(15, ' '); + string maxTitle = "Max".PadRight(15, ' '); + string avgTitle = "Avg".PadRight(15, ' '); + + EnginesMonitor _enginesMonitor; + Queue _engineMonitorData; + const int SYSTEM_MONITOR_DATA_LENGTH = 300; + SORTING_OPTIONS _sortingOption = SORTING_OPTIONS.AVERAGE; + + public override void OnInspectorGUI() + { + var engineProfilerBehaviour = (EngineProfilerBehaviour) target; + EngineInfo[] engines = new EngineInfo[engineProfilerBehaviour.engines.Count]; + engineProfilerBehaviour.engines.CopyTo(engines, 0); + + DrawEnginesMonitor(engines); + DrawEngineList(engineProfilerBehaviour, engines); + EditorUtility.SetDirty(target); + } + + void DrawEngineList(EngineProfilerBehaviour engineProfilerBehaviour, EngineInfo[] engines) + { + ProfilerEditorLayout.BeginVerticalBox(); + { + ProfilerEditorLayout.BeginHorizontal(); + { + if (GUILayout.Button("Reset Durations", GUILayout.Width(120), GUILayout.Height(14))) + { + engineProfilerBehaviour.ResetDurations(); + } + } + ProfilerEditorLayout.EndHorizontal(); + + _sortingOption = (SORTING_OPTIONS) EditorGUILayout.EnumPopup("Sort By:", _sortingOption); + + _hideEmptyEngines = EditorGUILayout.Toggle("Hide empty systems", _hideEmptyEngines); + EditorGUILayout.Space(); + + ProfilerEditorLayout.BeginHorizontal(); + { + _systemNameSearchTerm = EditorGUILayout.TextField("Search", _systemNameSearchTerm); + + const string clearButtonControlName = "Clear Button"; + GUI.SetNextControlName(clearButtonControlName); + if (GUILayout.Button("x", GUILayout.Width(19), GUILayout.Height(14))) + { + _systemNameSearchTerm = string.Empty; + GUI.FocusControl(clearButtonControlName); + } + } + ProfilerEditorLayout.EndHorizontal(); + + _showTickEngines = EditorGUILayout.Foldout(_showTickEngines, "Engines Ticks"); + if (_showTickEngines && ShouldShowSystems(engines)) + { + ProfilerEditorLayout.BeginVerticalBox(); + { + var systemsDrawn = DrawUpdateEngineInfos(engines); + if (systemsDrawn == 0) + { + EditorGUILayout.LabelField(string.Empty); + } + } + ProfilerEditorLayout.EndVertical(); + } + + _showAddEngines = EditorGUILayout.Foldout(_showAddEngines, "Engines Add"); + if (_showAddEngines && ShouldShowSystems(engines)) + { + ProfilerEditorLayout.BeginVerticalBox(); + { + var systemsDrawn = DrawAddEngineInfos(engines); + if (systemsDrawn == 0) + { + EditorGUILayout.LabelField(string.Empty); + } + } + ProfilerEditorLayout.EndVertical(); + } + + _showRemoveEngines = EditorGUILayout.Foldout(_showRemoveEngines, "Engines Remove"); + if (_showRemoveEngines && ShouldShowSystems(engines)) + { + ProfilerEditorLayout.BeginVerticalBox(); + { + var systemsDrawn = DrawRemoveEngineInfos(engines); + if (systemsDrawn == 0) + { + EditorGUILayout.LabelField(string.Empty); + } + } + ProfilerEditorLayout.EndVertical(); + } + } + ProfilerEditorLayout.EndVertical(); + } + + void DrawEnginesMonitor(EngineInfo[] engines) + { + if (_enginesMonitor == null) + { + _enginesMonitor = new EnginesMonitor(SYSTEM_MONITOR_DATA_LENGTH); + _engineMonitorData = new Queue(new float[SYSTEM_MONITOR_DATA_LENGTH]); + if (EditorApplication.update != Repaint) + { + EditorApplication.update += Repaint; + } + } + double totalDuration = 0; + for (int i = 0; i < engines.Length; i++) + { + totalDuration += engines[i].lastUpdateDuration; + } + + ProfilerEditorLayout.BeginVerticalBox(); + { + EditorGUILayout.LabelField("Execution duration", EditorStyles.boldLabel); + + ProfilerEditorLayout.BeginHorizontal(); + { + EditorGUILayout.LabelField("Total", totalDuration.ToString()); + } + ProfilerEditorLayout.EndHorizontal(); + + ProfilerEditorLayout.BeginHorizontal(); + { + _axisUpperBounds = EditorGUILayout.FloatField("Axis Upper Bounds", _axisUpperBounds); + } + ProfilerEditorLayout.EndHorizontal(); + + if (!EditorApplication.isPaused) + { + if (_engineMonitorData.Count >= SYSTEM_MONITOR_DATA_LENGTH) + { + _engineMonitorData.Dequeue(); + } + + _engineMonitorData.Enqueue((float) totalDuration); + } + _enginesMonitor.Draw(_engineMonitorData.ToArray(), 80f, _axisUpperBounds); + } + ProfilerEditorLayout.EndVertical(); + } + + int DrawUpdateEngineInfos(EngineInfo[] engines) + { + if (_sortingOption != SORTING_OPTIONS.NONE) + { + SortUpdateEngines(engines); + } + + string title = + updateTitle.FastConcat(lateUpdateTitle) + .FastConcat(fixedupdateTitle) + .FastConcat(minTitle) + .FastConcat(maxTitle); + EditorGUILayout.LabelField("Engine Name", title, EditorStyles.boldLabel); + + int enginesDrawn = 0; + for (int i = 0; i < engines.Length; i++) + { + EngineInfo engineInfo = engines[i]; + + if (engineInfo.engineName.ToLower().Contains(_systemNameSearchTerm.ToLower())) + { + ProfilerEditorLayout.BeginHorizontal(); + { + var avg = string.Format("{0:0.000}", engineInfo.averageUpdateDuration).PadRight(15); + var avgLate = string.Format("{0:0.000}", engineInfo.averageLateUpdateDuration).PadRight(15); + var avgFixed = string.Format("{0:0.000}", engineInfo.averageFixedUpdateDuration).PadRight(15); + var min = string.Format("{0:0.000}", engineInfo.minUpdateDuration).PadRight(15); + var max = string.Format("{0:0.000}", engineInfo.maxUpdateDuration); + + string output = avg.FastConcat(avgLate).FastConcat(avgFixed).FastConcat(min).FastConcat(max); + + EditorGUILayout.LabelField(engineInfo.engineName, output, GetEngineStyle()); + } + ProfilerEditorLayout.EndHorizontal(); + + enginesDrawn += 1; + } + } + return enginesDrawn; + } + + int DrawAddEngineInfos(EngineInfo[] engines) + { + if (_sortingOption != SORTING_OPTIONS.NONE) + { + SortAddEngines(engines); + } + + string title = avgTitle.FastConcat(minTitle).FastConcat(maxTitle); + EditorGUILayout.LabelField("Engine Name", title, EditorStyles.boldLabel); + + int enginesDrawn = 0; + for (int i = 0; i < engines.Length; i++) + { + EngineInfo engineInfo = engines[i]; + if (engineInfo.engineName.ToLower().Contains(_systemNameSearchTerm.ToLower()) && + !engineInfo.minAddDuration.Equals(0) && !engineInfo.maxAddDuration.Equals(0)) + { + ProfilerEditorLayout.BeginHorizontal(); + { + var avg = string.Format("{0:0.000}", engineInfo.averageAddDuration).PadRight(15); + var min = string.Format("{0:0.000}", engineInfo.minAddDuration).PadRight(15); + var max = string.Format("{0:0.000}", engineInfo.maxAddDuration); + + string output = avg.FastConcat(min).FastConcat(max); + + EditorGUILayout.LabelField(engineInfo.engineName, output, GetEngineStyle()); + } + ProfilerEditorLayout.EndHorizontal(); + + enginesDrawn += 1; + } + } + return enginesDrawn; + } + + int DrawRemoveEngineInfos(EngineInfo[] engines) + { + if (_sortingOption != SORTING_OPTIONS.NONE) + { + SortRemoveEngines(engines); + } + + string title = avgTitle.FastConcat(minTitle).FastConcat(maxTitle); + EditorGUILayout.LabelField("Engine Name", title, EditorStyles.boldLabel); + + int enginesDrawn = 0; + for (int i = 0; i < engines.Length; i++) + { + EngineInfo engineInfo = engines[i]; + if (engineInfo.engineName.ToLower().Contains(_systemNameSearchTerm.ToLower()) && + !engineInfo.minRemoveDuration.Equals(0) && !engineInfo.maxRemoveDuration.Equals(0)) + { + ProfilerEditorLayout.BeginHorizontal(); + { + var avg = string.Format("{0:0.000}", engineInfo.averageRemoveDuration).PadRight(15); + var min = string.Format("{0:0.000}", engineInfo.minRemoveDuration).PadRight(15); + var max = string.Format("{0:0.000}", engineInfo.maxRemoveDuration); + + string output = avg.FastConcat(min).FastConcat(max); + + EditorGUILayout.LabelField(engineInfo.engineName, output, GetEngineStyle()); + } + ProfilerEditorLayout.EndHorizontal(); + + enginesDrawn += 1; + } + } + return enginesDrawn; + } + + static GUIStyle GetEngineStyle() + { + var style = new GUIStyle(GUI.skin.label); + var color = EditorGUIUtility.isProSkin ? Color.white : style.normal.textColor; + + style.normal.textColor = color; + + return style; + } + + static bool ShouldShowSystems(EngineInfo[] engines) + { + return engines.Length > 0; + } + +#region Sorting Engines + void SortUpdateEngines(EngineInfo[] engines) + { + switch (_sortingOption) + { + case SORTING_OPTIONS.AVERAGE: + Array.Sort(engines, + (engine1, engine2) => engine2.averageUpdateDuration.CompareTo(engine1.averageUpdateDuration)); + break; + case SORTING_OPTIONS.MIN: + Array.Sort(engines, + (engine1, engine2) => engine2.minUpdateDuration.CompareTo(engine1.minUpdateDuration)); + break; + case SORTING_OPTIONS.MAX: + Array.Sort(engines, + (engine1, engine2) => engine2.maxUpdateDuration.CompareTo(engine1.maxUpdateDuration)); + break; + case SORTING_OPTIONS.NAME: + Array.Sort(engines, StringComparer.InvariantCulture); + break; + } + } + + void SortAddEngines(EngineInfo[] engines) + { + switch (_sortingOption) + { + case SORTING_OPTIONS.AVERAGE: + Array.Sort(engines, + (engine1, engine2) => engine2.averageAddDuration.CompareTo(engine1.averageAddDuration)); + break; + case SORTING_OPTIONS.MIN: + Array.Sort(engines, + (engine1, engine2) => engine2.minAddDuration.CompareTo(engine1.minAddDuration)); + break; + case SORTING_OPTIONS.MAX: + Array.Sort(engines, + (engine1, engine2) => engine2.maxAddDuration.CompareTo(engine1.maxAddDuration)); + break; + case SORTING_OPTIONS.NAME: + Array.Sort(engines, StringComparer.InvariantCulture); + break; + } + } + + void SortRemoveEngines(EngineInfo[] engines) + { + switch (_sortingOption) + { + case SORTING_OPTIONS.AVERAGE: + Array.Sort(engines, + (engine1, engine2) => engine2.averageRemoveDuration.CompareTo(engine1.averageRemoveDuration)); + break; + case SORTING_OPTIONS.MIN: + Array.Sort(engines, + (engine1, engine2) => engine2.minRemoveDuration.CompareTo(engine1.minRemoveDuration)); + break; + case SORTING_OPTIONS.MAX: + Array.Sort(engines, + (engine1, engine2) => engine2.maxRemoveDuration.CompareTo(engine1.maxRemoveDuration)); + break; + case SORTING_OPTIONS.NAME: + Array.Sort(engines, StringComparer.InvariantCulture); + break; + } + } + } +#endregion +} diff --git a/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs.meta b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs.meta new file mode 100644 index 0000000..02c6f19 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a2202d1747b86dc428310091a9c1a7ef +timeCreated: 1462469401 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs new file mode 100644 index 0000000..2c20e59 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs @@ -0,0 +1,22 @@ +using UnityEditor; + +//This profiler is based on the Entitas Visual Debugging tool +//https://github.com/sschmid/Entitas-CSharp + +namespace Svelto.ECS.Profiler +{ + internal class EngineProfilerMenuItem + { + [MenuItem("Engines/Enable Profiler")] + public static void EnableProfiler() + { + PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, "ENGINE_PROFILER_ENABLED"); + } + + [MenuItem("Engines/Disable Profiler")] + public static void DisableProfiler() + { + PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Standalone, ""); + } + } +} diff --git a/ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs.meta b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs.meta new file mode 100644 index 0000000..b487da4 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/EngineProfilerMenuItem.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e7b6bbeaaa16ab84aaa99c3311199efa +timeCreated: 1462351229 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs b/ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs new file mode 100644 index 0000000..0d568a4 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs @@ -0,0 +1,132 @@ +using System.Linq; +using UnityEditor; +using UnityEngine; + +//This profiler is based on the Entitas Visual Debugging tool +//https://github.com/sschmid/Entitas-CSharp + +namespace Svelto.ECS.Profiler +{ + public class EnginesMonitor + { + public float xBorder = 48; + public float yBorder = 20; + public int rightLinePadding = -15; + public string labelFormat = "{0:0.0}"; + public string axisFormat = "{0:0.0}"; + public int gridLines = 1; + public float axisRounding = 5f; + public float anchorRadius = 1f; + public Color lineColor = Color.magenta; + + readonly GUIStyle _labelTextStyle; + readonly GUIStyle _centeredStyle; + readonly Vector3[] _cachedLinePointVerticies; + readonly Vector3[] _linePoints; + + public EnginesMonitor(int dataLength) + { + _labelTextStyle = new GUIStyle(GUI.skin.label); + _labelTextStyle.alignment = TextAnchor.UpperRight; + _centeredStyle = new GUIStyle(); + _centeredStyle.alignment = TextAnchor.UpperCenter; + _centeredStyle.normal.textColor = Color.white; + _linePoints = new Vector3[dataLength]; + _cachedLinePointVerticies = new[] + { + new Vector3(-1, 1, 0)*anchorRadius, + new Vector3(1, 1, 0)*anchorRadius, + new Vector3(1, -1, 0)*anchorRadius, + new Vector3(-1, -1, 0)*anchorRadius, + }; + } + + public void Draw(float[] data, float height, float axisUpperBounds) + { + axisRounding = axisUpperBounds; + var rect = GUILayoutUtility.GetRect(EditorGUILayout.GetControlRect().width, height); + var top = rect.y + yBorder; + var floor = rect.y + rect.height - yBorder; + var availableHeight = floor - top; + var max = data.Length != 0 ? data.Max() : 0f; + if (max%axisRounding != 0) + { + max = max + axisRounding - (max%axisRounding); + } + + drawGridLines(top, rect.width, availableHeight, max); + drawLine(data, floor, rect.width, availableHeight, max); + } + + void drawGridLines(float top, float width, float availableHeight, float max) + { + var handleColor = Handles.color; + Handles.color = Color.grey; + var n = gridLines + 1; + var lineSpacing = availableHeight/n; + for (int i = 0; i <= n; i++) + { + var lineY = top + (lineSpacing*i); + Handles.DrawLine( + new Vector2(xBorder, lineY), + new Vector2(width - rightLinePadding, lineY) + ); + GUI.Label( + new Rect(0, lineY - 8, xBorder - 2, 50), + string.Format(axisFormat, max*(1f - ((float) i/(float) n))), + _labelTextStyle + ); + } + Handles.color = handleColor; + } + + void drawLine(float[] data, float floor, float width, float availableHeight, float max) + { + var lineWidth = (float) (width - xBorder - rightLinePadding)/data.Length; + var handleColor = Handles.color; + var labelRect = new Rect(); + Vector2 newLine; + bool mousePositionDiscovered = false; + float mouseHoverDataValue = 0; + float linePointScale; + Handles.color = lineColor; + Handles.matrix = Matrix4x4.identity; + HandleUtility.handleMaterial.SetPass(0); + for (int i = 0; i < data.Length; i++) + { + var value = data[i]; + var lineTop = floor - (availableHeight*(value/max)); + newLine = new Vector2(xBorder + (lineWidth*i), lineTop); + _linePoints[i] = new Vector3(newLine.x, newLine.y, 0); + linePointScale = 1f; + if (!mousePositionDiscovered) + { + var anchorPosRadius3 = anchorRadius*3; + var anchorPosRadius6 = anchorRadius*6; + var anchorPos = newLine - (Vector2.up*0.5f); + labelRect = new Rect(anchorPos.x - anchorPosRadius3, anchorPos.y - anchorPosRadius3, + anchorPosRadius6, anchorPosRadius6); + if (labelRect.Contains(Event.current.mousePosition)) + { + mousePositionDiscovered = true; + mouseHoverDataValue = value; + linePointScale = 3f; + } + } + Handles.matrix = Matrix4x4.TRS(_linePoints[i], Quaternion.identity, Vector3.one*linePointScale); + Handles.DrawAAConvexPolygon(_cachedLinePointVerticies); + } + Handles.matrix = Matrix4x4.identity; + Handles.DrawAAPolyLine(2f, data.Length, _linePoints); + + if (mousePositionDiscovered) + { + labelRect.y -= 16; + labelRect.width += 50; + labelRect.x -= 25; + GUI.Label(labelRect, string.Format(labelFormat, mouseHoverDataValue), _centeredStyle); + } + Handles.color = handleColor; + } + } +} diff --git a/ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs.meta b/ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs.meta new file mode 100644 index 0000000..aee00e5 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/EnginesMonitor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 30f9f7f6468a96d4da2525c65ecfe637 +timeCreated: 1467633311 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs b/ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs new file mode 100644 index 0000000..d012cac --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs @@ -0,0 +1,65 @@ +using UnityEditor; +using UnityEngine; + +//This profiler is based on the Entitas Visual Debugging tool +//https://github.com/sschmid/Entitas-CSharp + +namespace Svelto.ECS.Profiler +{ + public static class ProfilerEditorLayout + { + public static void ShowWindow(string title) where T : EditorWindow + { + var window = EditorWindow.GetWindow(true, title); + window.minSize = window.maxSize = new Vector2(415f, 520f); + window.Show(); + } + + public static Texture2D LoadTexture(string label) + { + var guid = AssetDatabase.FindAssets(label)[0]; + if (guid != null) + { + var path = AssetDatabase.GUIDToAssetPath(guid); + return AssetDatabase.LoadAssetAtPath(path); + } + return null; + } + + public static float DrawHeaderTexture(EditorWindow window, Texture2D texture) + { + const int scollBarWidth = 15; + var ratio = texture.width/texture.height; + var width = window.position.width - 8 - scollBarWidth; + var height = width/ratio; + GUI.DrawTexture(new Rect(4, 2, width, height), texture, ScaleMode.ScaleToFit); + + return height; + } + + public static Rect BeginVertical() + { + return EditorGUILayout.BeginVertical(); + } + + public static Rect BeginVerticalBox(GUIStyle style = null) + { + return EditorGUILayout.BeginVertical(style ?? GUI.skin.box); + } + + public static void EndVertical() + { + EditorGUILayout.EndVertical(); + } + + public static Rect BeginHorizontal() + { + return EditorGUILayout.BeginHorizontal(); + } + + public static void EndHorizontal() + { + EditorGUILayout.EndHorizontal(); + } + } +} diff --git a/ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs.meta b/ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs.meta new file mode 100644 index 0000000..77f5e52 --- /dev/null +++ b/ECS/Profiler/Editor/EngineProfiler/ProfilerEditorLayout.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1c9cf391ddfdd92429ce90eeb73a936a +timeCreated: 1462527509 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/EngineInfo.cs b/ECS/Profiler/EngineInfo.cs new file mode 100644 index 0000000..ceffef9 --- /dev/null +++ b/ECS/Profiler/EngineInfo.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; + +//This profiler is based on the Entitas Visual Debugging tool +//https://github.com/sschmid/Entitas-CSharp + +namespace Svelto.ECS.Profiler +{ + public sealed class EngineInfo + { + enum UpdateType + { + Update = 0, + LateUpdate = 1, + FixedUpdate = 2, + } + + readonly IEngine _engine; + readonly string _engineName; + readonly Type _engineType; + + const int NUM_UPDATE_TYPES = 3; + const int NUM_FRAMES_TO_AVERAGE = 10; + + //use a queue to averave out the last 30 frames + Queue[] _updateFrameTimes = new Queue[NUM_UPDATE_TYPES]; + readonly double[] _accumulatedUpdateDuration = new double[NUM_UPDATE_TYPES]; + readonly double[] _lastUpdateDuration = new double[NUM_UPDATE_TYPES]; + + readonly double[] _minUpdateDuration = new double[NUM_UPDATE_TYPES]; + readonly double[] _maxUpdateDuration = new double[NUM_UPDATE_TYPES]; + + double _accumulatedAddDuration; + double _minAddDuration; + double _maxAddDuration; + int _nodesAddedCount; + + double _accumulatedRemoveDuration; + double _minRemoveDuration; + double _maxRemoveDuration; + int _nodesRemovedCount; + + public IEngine engine { get { return _engine; } } + public string engineName { get { return _engineName; } } + public Type engineType { get { return _engineType; } } + + public double lastUpdateDuration { get { return _lastUpdateDuration[(int) UpdateType.Update]; } } + public double lastFixedUpdateDuration { get { return _lastUpdateDuration[(int)UpdateType.LateUpdate]; } } + public double lastLateUpdateDuration { get { return _lastUpdateDuration[(int)UpdateType.FixedUpdate]; } } + + public double minAddDuration { get { return _minAddDuration; } } + public double minRemoveDuration { get { return _minRemoveDuration; } } + public double minUpdateDuration { get { return _minUpdateDuration[(int)UpdateType.Update]; } } + + public double maxAddDuration { get { return _maxAddDuration; } } + public double maxRemoveDuration { get { return _maxRemoveDuration; } } + public double maxUpdateDuration { get { return _maxUpdateDuration[(int)UpdateType.Update]; } } + + public double averageAddDuration { get { return _nodesAddedCount == 0 ? 0 : _accumulatedAddDuration / _nodesAddedCount; } } + public double averageRemoveDuration { get { return _nodesRemovedCount == 0 ? 0 : _accumulatedRemoveDuration / _nodesRemovedCount; } } + public double averageUpdateDuration { get { return _updateFrameTimes[(int)UpdateType.Update].Count == 0 ? 0 : _accumulatedUpdateDuration[(int)UpdateType.Update] / _updateFrameTimes[(int)UpdateType.Update].Count; } } + public double averageLateUpdateDuration { get { return _updateFrameTimes[(int)UpdateType.LateUpdate].Count == 0 ? 0 : _accumulatedUpdateDuration[(int)UpdateType.LateUpdate] / _updateFrameTimes[(int)UpdateType.LateUpdate].Count; } } + public double averageFixedUpdateDuration { get { return _updateFrameTimes[(int)UpdateType.FixedUpdate].Count == 0 ? 0 : _accumulatedUpdateDuration[(int)UpdateType.FixedUpdate] / _updateFrameTimes[(int)UpdateType.FixedUpdate].Count; } } + + public EngineInfo(IEngine engine) + { + _engine = engine; + _engineName = _engine.ToString(); + + int foundNamespace = _engineName.LastIndexOf("."); + _engineName = _engineName.Remove(0, foundNamespace + 1); + + _engineType = engine.GetType(); + + for (int i = 0; i < NUM_UPDATE_TYPES; i++) + { + _updateFrameTimes[i] = new Queue(); + } + ResetDurations(); + } + + public void AddUpdateDuration(double updateDuration) + { + AddUpdateDurationForType(updateDuration, (int)UpdateType.Update); + } + + public void AddLateUpdateDuration(double updateDuration) + { + AddUpdateDurationForType(updateDuration, (int)UpdateType.LateUpdate); + } + + public void AddFixedUpdateDuration(double updateDuration) + { + AddUpdateDurationForType(updateDuration, (int)UpdateType.FixedUpdate); + } + + void AddUpdateDurationForType(double updateDuration, int updateType) + { + if (updateDuration < _minUpdateDuration[updateType] || _minUpdateDuration[updateType] == 0) + { + _minUpdateDuration[updateType] = updateDuration; + } + if (updateDuration > _maxUpdateDuration[updateType]) + { + _maxUpdateDuration[updateType] = updateDuration; + } + + if (_updateFrameTimes[updateType].Count == NUM_FRAMES_TO_AVERAGE) + { + _accumulatedUpdateDuration[updateType] -= _updateFrameTimes[updateType].Dequeue(); + } + + _accumulatedUpdateDuration[updateType] += updateDuration; + _updateFrameTimes[updateType].Enqueue(updateDuration); + _lastUpdateDuration[updateType] = updateDuration; + } + + public void AddAddDuration(double duration) + { + if (duration < _minAddDuration || _minAddDuration == 0) + { + _minAddDuration = duration; + } + if (duration > _maxAddDuration) + { + _maxAddDuration = duration; + } + _accumulatedAddDuration += duration; + _nodesAddedCount += 1; + } + + public void AddRemoveDuration(double duration) + { + if (duration < _minRemoveDuration || _minRemoveDuration == 0) + { + _minRemoveDuration = duration; + } + if (duration > _maxRemoveDuration) + { + _maxRemoveDuration = duration; + } + _accumulatedRemoveDuration += duration; + _nodesRemovedCount += 1; + } + + public void ResetDurations() + { + for (int i = 0; i < NUM_UPDATE_TYPES; i++) + { + _accumulatedUpdateDuration[i] = 0; + _minUpdateDuration[i] = 0; + _maxUpdateDuration[i] = 0; + _updateFrameTimes[i].Clear(); + } + + _accumulatedAddDuration = 0; + _minAddDuration = 0; + _maxAddDuration = 0; + _nodesAddedCount = 0; + + _accumulatedRemoveDuration = 0; + _minRemoveDuration = 0; + _maxRemoveDuration = 0; + _nodesRemovedCount = 0; + } + } +} diff --git a/ECS/Profiler/EngineInfo.cs.meta b/ECS/Profiler/EngineInfo.cs.meta new file mode 100644 index 0000000..2463af8 --- /dev/null +++ b/ECS/Profiler/EngineInfo.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 93c5ba48b51186e44b7094eef7028c90 +timeCreated: 1462357591 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/EngineProfiler.cs b/ECS/Profiler/EngineProfiler.cs new file mode 100644 index 0000000..75e30e1 --- /dev/null +++ b/ECS/Profiler/EngineProfiler.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using UnityEngine; +using Svelto.Ticker.Legacy; + +//This profiler is based on the Entitas Visual Debugging tool +//https://github.com/sschmid/Entitas-CSharp + +namespace Svelto.ECS.Profiler +{ + public sealed class EngineProfiler + { + static readonly Stopwatch _stopwatch = new Stopwatch(); + + public static void MonitorAddDuration(Action, INode> addingFunc, INodeEngine engine, INode node) + { + EngineInfo info; + if (engineInfos.TryGetValue(engine.GetType(), out info)) + { + _stopwatch.Reset(); + _stopwatch.Start(); + addingFunc(engine, node); + _stopwatch.Stop(); + + info.AddAddDuration(_stopwatch.Elapsed.TotalMilliseconds); + } + } + + public static void MonitorRemoveDuration(Action, INode> removeFunc, INodeEngine engine, INode node) + { + EngineInfo info; + if (engineInfos.TryGetValue(engine.GetType(), out info)) + { + _stopwatch.Reset(); + _stopwatch.Start(); + removeFunc(engine, node); + engine.Remove(node); + _stopwatch.Stop(); + + info.AddRemoveDuration(_stopwatch.Elapsed.TotalMilliseconds); + } + } + + public static void MonitorUpdateDuration(ITickable tickable) + { + if (tickable is INodeEngine) + { + EngineInfo info; + if (engineInfos.TryGetValue((tickable as INodeEngine).GetType(), out info)) + { + _stopwatch.Reset(); + _stopwatch.Start(); + tickable.Tick(Time.deltaTime); + _stopwatch.Stop(); + + info.AddUpdateDuration(_stopwatch.Elapsed.TotalMilliseconds); + } + } + else + { + tickable.Tick(Time.deltaTime); + } + } + + public static void MonitorUpdateDuration(IPhysicallyTickable tickable) + { + if (tickable is INodeEngine) + { + EngineInfo info; + if (engineInfos.TryGetValue((tickable as INodeEngine).GetType(), out info)) + { + _stopwatch.Reset(); + _stopwatch.Start(); + tickable.PhysicsTick(Time.fixedDeltaTime); + _stopwatch.Stop(); + + info.AddFixedUpdateDuration(_stopwatch.Elapsed.TotalMilliseconds); + } + } + else + { + tickable.PhysicsTick(Time.fixedDeltaTime); + } + } + + public static void MonitorUpdateDuration(ILateTickable tickable) + { + if (tickable is INodeEngine) + { + EngineInfo info; + if (engineInfos.TryGetValue((tickable as INodeEngine).GetType(), out info)) + { + _stopwatch.Reset(); + _stopwatch.Start(); + tickable.LateTick(Time.deltaTime); + _stopwatch.Stop(); + + info.AddLateUpdateDuration(_stopwatch.Elapsed.TotalMilliseconds); + } + } + else + { + tickable.LateTick(Time.deltaTime); + } + } + + public static void AddEngine(IEngine engine) + { + if (engineInfos.ContainsKey(engine.GetType()) == false) + { + engineInfos.Add(engine.GetType(), new EngineInfo(engine)); + } + } + + public static void ResetDurations() + { + foreach (var engine in engineInfos) + { + engine.Value.ResetDurations(); + } + } + + public static readonly Dictionary engineInfos = new Dictionary(); + } +} diff --git a/ECS/Profiler/EngineProfiler.cs.meta b/ECS/Profiler/EngineProfiler.cs.meta new file mode 100644 index 0000000..7c2b637 --- /dev/null +++ b/ECS/Profiler/EngineProfiler.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 74b59045fe5033b4b98facadd4d6b114 +timeCreated: 1462355668 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Profiler/EngineProfilerBehaviour.cs b/ECS/Profiler/EngineProfilerBehaviour.cs new file mode 100644 index 0000000..9039a2a --- /dev/null +++ b/ECS/Profiler/EngineProfilerBehaviour.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +//This profiler is based on the Entitas Visual Debugging tool +//https://github.com/sschmid/Entitas-CSharp + +namespace Svelto.ECS.Profiler +{ + public class EngineProfilerBehaviour : MonoBehaviour + { + public Dictionary.ValueCollection engines { get { return EngineProfiler.engineInfos.Values; } } + + public void ResetDurations() + { + EngineProfiler.ResetDurations(); + } + } +} diff --git a/ECS/Profiler/EngineProfilerBehaviour.cs.meta b/ECS/Profiler/EngineProfilerBehaviour.cs.meta new file mode 100644 index 0000000..17446f2 --- /dev/null +++ b/ECS/Profiler/EngineProfilerBehaviour.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 688f5cf68b171ef4fa0f6aab49644c48 +timeCreated: 1462469401 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/Sequencer.cs b/ECS/Sequencer.cs new file mode 100644 index 0000000..f9d0efd --- /dev/null +++ b/ECS/Sequencer.cs @@ -0,0 +1,52 @@ +using System; +using Steps = System.Collections.Generic.Dictionary>; + +namespace Svelto.ECS +{ + public interface IStep + { } + + public interface IStep:IStep + { + void Step(ref T token, Enum condition); + } + + public class Sequencer + { + public Sequencer() + {} + + public void SetSequence(Steps steps) + { + _steps = steps; + } + + public void Next(IEngine engine, ref T param) + { + Next(engine, ref param, Condition.always); + } + + public void Next(IEngine engine, ref T param, Enum condition) + { + var tos = _steps[engine]; + var steps = tos[condition]; + + if (steps != null) + for (int i = 0; i < steps.Length; i++) + ((IStep)steps[i]).Step(ref param, condition); + } + + Steps _steps; + } + + public enum Condition + { + always + } + + public enum DamageCondition + { + damage, + dead + } +} \ No newline at end of file diff --git a/ECS/Sequencer.cs.meta b/ECS/Sequencer.cs.meta new file mode 100644 index 0000000..87596cb --- /dev/null +++ b/ECS/Sequencer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4e1f40937ca5027428ed7193729be1a5 +timeCreated: 1484487023 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/ECS/SingleNodeEngine.cs b/ECS/SingleNodeEngine.cs new file mode 100644 index 0000000..f888ee2 --- /dev/null +++ b/ECS/SingleNodeEngine.cs @@ -0,0 +1,20 @@ +namespace Svelto.ECS +{ + public abstract class SingleNodeEngine : INodeEngine where TNodeType : class, INode + { + void INodeEngine.Add(INode obj) + { + Add(obj as TNodeType); + } + + void INodeEngine.Remove(INode obj) + { + Remove(obj as TNodeType); + } + + protected virtual void Add(TNodeType node) + {} + protected virtual void Remove(TNodeType node) + {} + } +} diff --git a/ECS/SingleNodeEngine.cs.meta b/ECS/SingleNodeEngine.cs.meta new file mode 100644 index 0000000..cdb2bea --- /dev/null +++ b/ECS/SingleNodeEngine.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 406a5e818b179b743b582c8fec087ac1 +timeCreated: 1469806920 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Factories.meta b/Factories.meta index 5cf0144..8a250da 100644 --- a/Factories.meta +++ b/Factories.meta @@ -1,8 +1,8 @@ fileFormatVersion: 2 -guid: 354b7801c1d782b47b5d9ff9aba85e83 +guid: 7f7896118ef3c4949898b5c30aea0c47 folderAsset: yes -timeCreated: 1434752394 -licenseType: Free +timeCreated: 1484044925 +licenseType: Pro DefaultImporter: userData: assetBundleName: diff --git a/Observer/Observable.cs b/Observer/Observable.cs deleted file mode 100644 index 9e56fd3..0000000 --- a/Observer/Observable.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; - -namespace Svelto.Observer -{ - public delegate void ObserverAction(ref DispatchType parameter); - - public interface IObservable - { - event Action Notify; - - void Dispatch(); - } - - public interface IObservable - { - event ObserverAction Notify; - - void Dispatch(ref DispatchType parameter); - } - - public class Observable:IObservable - { - public event ObserverAction Notify; - - public void Dispatch(ref DispatchType parameter) - { - if (Notify != null) - Notify(ref parameter); - } - } - - public class Observable:IObservable - { - public event Action Notify; - - public void Dispatch() - { - if (Notify != null) - Notify(); - } - } -} diff --git a/Observer/Observable.cs.meta b/Observer/Observable.cs.meta deleted file mode 100644 index 8f93bb4..0000000 --- a/Observer/Observable.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: e9d378efdf4e5dd439400d001b6775ec -timeCreated: 1440356670 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Observer/Observer.cs b/Observer/Observer.cs deleted file mode 100644 index 7871f47..0000000 --- a/Observer/Observer.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; - -namespace Svelto.Observer.InterNamespace -{ - public abstract class Observer : IObserver - { - protected Observer(Observable observable) - { - observable.Notify += OnObservableDispatched; - - _unsubscribe = () => observable.Notify -= OnObservableDispatched; - } - - public void AddAction(ObserverAction action) - { - _actions += action; - } - - public void RemoveAction(ObserverAction action) - { - _actions -= action; - } - - public void Unsubscribe() - { - _unsubscribe(); - } - - void OnObservableDispatched(ref DispatchType dispatchNotification) - { - if (_actions != null) - { - var actionType = TypeMap(ref dispatchNotification); - - _actions(ref actionType); - } - } - - protected abstract ActionType TypeMap(ref DispatchType dispatchNotification); - - ObserverAction _actions; - Action _unsubscribe; - } -} - -namespace Svelto.Observer.IntraNamespace -{ - public class Observer : InterNamespace.Observer - { - public Observer(Observable observable) : base(observable) - { } - - protected override DispatchType TypeMap(ref DispatchType dispatchNotification) - { - return dispatchNotification; - } - } -} - -namespace Svelto.Observer -{ - public class Observer: IObserver - { - public Observer(Observable observable) - { - observable.Notify += OnObservableDispatched; - - _unsubscribe = () => observable.Notify -= OnObservableDispatched; - } - - public void AddAction(Action action) - { - _actions += action; - } - - public void RemoveAction(Action action) - { - _actions -= action; - } - - public void Unsubscribe() - { - _unsubscribe(); - } - - void OnObservableDispatched() - { - if (_actions != null) - _actions(); - } - - Action _actions; - readonly Action _unsubscribe; - } - - public interface IObserver - { - void AddAction(ObserverAction action); - void RemoveAction(ObserverAction action); - - void Unsubscribe(); - } - - public interface IObserver - { - void AddAction(Action action); - void RemoveAction(Action action); - - void Unsubscribe(); - } -} \ No newline at end of file diff --git a/Observer/Observer.cs.meta b/Observer/Observer.cs.meta deleted file mode 100644 index 5277627..0000000 --- a/Observer/Observer.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: 7b4eff431179a5047a4b9110fe27ecf6 -timeCreated: 1440352946 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Ticker.meta b/Ticker.meta deleted file mode 100644 index 9a9ebf6..0000000 --- a/Ticker.meta +++ /dev/null @@ -1,5 +0,0 @@ -fileFormatVersion: 2 -guid: 86d00787627f3e643bfb37812067fbc5 -folderAsset: yes -DefaultImporter: - userData: diff --git a/Ticker/ITickable.cs b/Ticker/ITickable.cs deleted file mode 100644 index f1c773c..0000000 --- a/Ticker/ITickable.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Svelto.Ticker -{ - public interface ILateTickable : ITickableBase - { - void LateTick(float deltaSec); - } - - public interface IPhysicallyTickable : ITickableBase - { - void PhysicsTick(float deltaSec); - } - - public interface ITickable : ITickableBase - { - void Tick(float deltaSec); - } - - public interface IEndOfFrameTickable : ITickableBase - { - void EndOfFrameTick(float deltaSec); - } - - public interface IIntervaledTickable : ITickableBase - { - void IntervaledTick(); - } - - public interface ITickableBase - { - } -} diff --git a/Ticker/ITicker.cs b/Ticker/ITicker.cs deleted file mode 100644 index 58180d6..0000000 --- a/Ticker/ITicker.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Svelto.Ticker -{ - public interface ITicker - { - void Add(ITickableBase tickable); - void Remove(ITickableBase tickable); - } -} diff --git a/Ticker/IntervaledTickAttribute.cs b/Ticker/IntervaledTickAttribute.cs deleted file mode 100644 index ef3a879..0000000 --- a/Ticker/IntervaledTickAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Svelto.Ticker -{ - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - internal class IntervaledTickAttribute : Attribute - { - public float interval; - - public IntervaledTickAttribute(float intervalTime) - { - interval = intervalTime; - } - } -} diff --git a/Ticker/IntervaledTickAttribute.cs.meta b/Ticker/IntervaledTickAttribute.cs.meta deleted file mode 100644 index e80216c..0000000 --- a/Ticker/IntervaledTickAttribute.cs.meta +++ /dev/null @@ -1,12 +0,0 @@ -fileFormatVersion: 2 -guid: fe6689309725deb4f975fb62e1e061e4 -timeCreated: 1435431616 -licenseType: Free -MonoImporter: - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Ticker/TickBehaviour.cs b/Ticker/TickBehaviour.cs deleted file mode 100644 index 2a59d5d..0000000 --- a/Ticker/TickBehaviour.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -namespace Svelto.Ticker -{ - public class TickBehaviour : MonoBehaviour - { - IEnumerator Start () - { - var waitForEndFrame = new WaitForEndOfFrame(); - - while (true) - { - yield return waitForEndFrame; - - for (int i = _endOfFrameTicked.Count - 1; i >= 0; --i) - { - try - { - _endOfFrameTicked[i].EndOfFrameTick(Time.deltaTime); - } - catch (Exception e) - { - Utility.Console.LogException(e); - } - } - } - } - - internal void Add(ITickable tickable) - { - _ticked.Add(tickable); - } - - internal void AddLate(ILateTickable tickable) - { - _lateTicked.Add(tickable); - } - - internal void AddPhysic(IPhysicallyTickable tickable) - { - _physicallyTicked.Add(tickable); - } - - internal void AddEndOfFrame(IEndOfFrameTickable tickable) - { - _endOfFrameTicked.Add(tickable); - } - - internal void Remove(ITickable tickable) - { - _ticked.Remove(tickable); - } - - internal void RemoveLate(ILateTickable tickable) - { - _lateTicked.Remove(tickable); - } - - internal void RemovePhysic(IPhysicallyTickable tickable) - { - _physicallyTicked.Remove(tickable); - } - - internal void RemoveEndOfFrame(IEndOfFrameTickable tickable) - { - _endOfFrameTicked.Remove(tickable); - } - - void FixedUpdate() - { - for (int i = _physicallyTicked.Count - 1; i >= 0; --i) - { - try - { - _physicallyTicked[i].PhysicsTick(Time.fixedDeltaTime); - } - catch (Exception e) - { - Utility.Console.LogException(e); - } - } - } - - void LateUpdate() - { - for (int i = _lateTicked.Count - 1; i >= 0; --i) - { - try - { - _lateTicked[i].LateTick(Time.deltaTime); - } - catch (Exception e) - { - Utility.Console.LogException(e); - } - } - } - - void Update() - { - for (int i = _ticked.Count - 1; i >= 0; --i) - { - try - { - _ticked[i].Tick(Time.deltaTime); - } - catch (Exception e) - { - Utility.Console.LogException(e); - } - } - } - - internal void AddIntervaled(IIntervaledTickable tickable) - { - var methodInfo = ((Action)tickable.IntervaledTick).Method; - object[] attrs = methodInfo.GetCustomAttributes(typeof(IntervaledTickAttribute), true); - - IEnumerator intervaledTick = IntervaledUpdate(tickable, (attrs[0] as IntervaledTickAttribute).interval); - - _intervalledTicked[tickable] = intervaledTick; - - StartCoroutine(intervaledTick); - } - - internal void RemoveIntervaled(IIntervaledTickable tickable) - { - IEnumerator enumerator; - - if (_intervalledTicked.TryGetValue(tickable, out enumerator)) - { - StopCoroutine(enumerator); - - _intervalledTicked.Remove(tickable); - } - } - - IEnumerator IntervaledUpdate(IIntervaledTickable tickable, float seconds) - { - while (true) { DateTime next = DateTime.UtcNow.AddSeconds(seconds); while (DateTime.UtcNow < next) yield return null; tickable.IntervaledTick(); } - } - - List _lateTicked = new List(); - List _physicallyTicked = new List(); - List _ticked = new List(); - List _endOfFrameTicked = new List(); - - Dictionary _intervalledTicked = new Dictionary(); - } -} diff --git a/Ticker/UnityTicker.cs b/Ticker/UnityTicker.cs deleted file mode 100644 index 8ac0b0a..0000000 --- a/Ticker/UnityTicker.cs +++ /dev/null @@ -1,57 +0,0 @@ -using UnityEngine; - -namespace Svelto.Ticker -{ - class UnityTicker : ITicker - { - public UnityTicker() - { - _ticker = Object.FindObjectOfType(); - - if (_ticker == null) - { - var go = new GameObject("SveltoTicker"); - - _ticker = go.AddComponent(); - } - } - - public void Add(ITickableBase tickable) - { - if (tickable is ITickable) - _ticker.Add(tickable as ITickable); - - if (tickable is IPhysicallyTickable) - _ticker.AddPhysic(tickable as IPhysicallyTickable); - - if (tickable is ILateTickable) - _ticker.AddLate(tickable as ILateTickable); - - if (tickable is IEndOfFrameTickable) - _ticker.AddEndOfFrame(tickable as IEndOfFrameTickable); - - if (tickable is IIntervaledTickable) - _ticker.AddIntervaled(tickable as IIntervaledTickable); - } - - public void Remove(ITickableBase tickable) - { - if (tickable is ITickable) - _ticker.Remove(tickable as ITickable); - - if (tickable is IPhysicallyTickable) - _ticker.RemovePhysic(tickable as IPhysicallyTickable); - - if (tickable is ILateTickable) - _ticker.RemoveLate(tickable as ILateTickable); - - if (tickable is IEndOfFrameTickable) - _ticker.RemoveEndOfFrame(tickable as IEndOfFrameTickable); - - if (tickable is IIntervaledTickable) - _ticker.RemoveIntervaled(tickable as IIntervaledTickable); - } - - readonly TickBehaviour _ticker; - } -} diff --git a/Utilities.meta b/Utilities.meta index 0f482d6..d093762 100644 --- a/Utilities.meta +++ b/Utilities.meta @@ -1,8 +1,8 @@ fileFormatVersion: 2 -guid: 9581aacbfc73076489f471e3e83ba71d +guid: b7374ca2eb247d44c8783d7d23f9b89e folderAsset: yes -timeCreated: 1440947158 -licenseType: Free +timeCreated: 1484394896 +licenseType: Pro DefaultImporter: userData: assetBundleName: diff --git a/Utilities/Print.cs b/Utilities/Print.cs index a693c3a..b3c8e9a 100644 --- a/Utilities/Print.cs +++ b/Utilities/Print.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics; using System.Text; -using Debug = UnityEngine.Debug; +using UnityEngine; public static class FastConcatUtility { @@ -65,20 +65,78 @@ public static class FastConcatUtility return _stringBuilder.ToString(); } } + + public static string FastJoin(this string[] str) + { + lock (_stringBuilder) + { + _stringBuilder.Length = 0; + + for (int i = 0; i < str.Length; i++) + _stringBuilder.Append(str[i]); + + return _stringBuilder.ToString(); + } + } + + public static string FastJoin(this string[] str, string str1) + { + lock (_stringBuilder) + { + _stringBuilder.Length = 0; + + for (int i = 0; i < str.Length; i++) + _stringBuilder.Append(str[i]); + + _stringBuilder.Append(str1); + + return _stringBuilder.ToString(); + } + } } namespace Utility { + public interface ILogger + { + void Log (string txt, string stack = null, LogType type = LogType.Log); + } + + public class SlowLogger : ILogger + { + public void Log(string txt, string stack = null, LogType type = LogType.Log) + { + switch (type) + { + case LogType.Log: + UnityEngine.Debug.Log(stack != null ? txt.FastConcat(stack) : txt); + break; + case LogType.Exception: + UnityEngine.Debug.LogError("Log of exceptions not supported"); + break; + case LogType.Warning: + UnityEngine.Debug.LogWarning(stack != null ? txt.FastConcat(stack) : txt); + break; + case LogType.Error: + UnityEngine.Debug.LogError(stack != null ? txt.FastConcat(stack) : txt); + break; + } + } + } + public static class Console { static StringBuilder _stringBuilder = new StringBuilder(256); + public static ILogger logger = new SlowLogger(); + public static volatile bool BatchLog = false; + public static void Log(string txt) { - Debug.Log(txt); + logger.Log(txt); } - public static void LogError(string txt) + public static void LogError(string txt, bool showCurrentStack = true) { string toPrint; @@ -90,31 +148,34 @@ namespace Utility toPrint = _stringBuilder.ToString(); } - - Debug.LogError(toPrint); - } - public static void LogException(Exception e) - { - LogException(e, null); + logger.Log(toPrint, showCurrentStack == true ? new StackTrace().ToString() : null, LogType.Error); } - public static void LogException(Exception e, UnityEngine.Object obj) + public static void LogError(string txt, string stack) { string toPrint; lock (_stringBuilder) { _stringBuilder.Length = 0; - _stringBuilder.Append("-!!!!!!-> ").Append(e); + _stringBuilder.Append("-!!!!!!-> "); + _stringBuilder.Append(txt); toPrint = _stringBuilder.ToString(); } - Exception ex = new Exception(e.ToString()); + logger.Log(toPrint, stack, LogType.Error); + } + + public static void LogException(Exception e) + { + LogException(e, null); + } - Debug.Log(toPrint); - Debug.LogException(ex, obj); + public static void LogException(Exception e, UnityEngine.Object obj) + { + UnityEngine.Debug.LogException(e, obj); } public static void LogWarning(string txt) @@ -130,11 +191,11 @@ namespace Utility toPrint = _stringBuilder.ToString(); } - Debug.LogWarning(toPrint); + logger.Log(toPrint, null, LogType.Warning); } /// - /// This function should never be used explicitly + /// Use this function if you don't want the message to be batched /// /// public static void SystemLog(string txt) @@ -144,7 +205,7 @@ namespace Utility lock (_stringBuilder) { string currentTimeString = DateTime.UtcNow.ToLongTimeString(); //ensure includes seconds - string processTimeString = (DateTime.Now - Process.GetCurrentProcess().StartTime).ToString(); + string processTimeString = (DateTime.UtcNow - Process.GetCurrentProcess().StartTime).ToString(); _stringBuilder.Length = 0; _stringBuilder.Append("[").Append(currentTimeString); @@ -158,7 +219,7 @@ namespace Utility #if !UNITY_EDITOR System.Console.WriteLine(toPrint); #else - Debug.Log(toPrint); + UnityEngine.Debug.Log(toPrint); #endif } } diff --git a/Utilities/WeakActionStruct.cs b/Utilities/WeakActionStruct.cs new file mode 100644 index 0000000..17158e1 --- /dev/null +++ b/Utilities/WeakActionStruct.cs @@ -0,0 +1,122 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +//careful, you must handle the destruction of the GCHandles! + +namespace BetterWeakEvents +{ + public struct WeakAction : IEquatable> + { + public WeakAction(Action listener) + { + ObjectRef = GCHandle.Alloc(listener.Target, GCHandleType.Weak); + Method = listener.Method; + + if (Method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0) + throw new ArgumentException("Cannot create weak event to anonymous method with closure."); + } + + public bool Invoke(T1 data1, T2 data2) + { + if (ObjectRef.IsAllocated && ObjectRef.Target != null) + { + Method.Invoke(ObjectRef.Target, new object[] { data1, data2 }); + return true; + } + + Release(); + return false; + } + + public bool Equals(WeakAction other) + { + return (Method.Equals(other.Method)); + } + + public void Release() + { + ObjectRef.Free(); + } + + readonly GCHandle ObjectRef; + readonly MethodInfo Method; + } + + public struct WeakAction : IEquatable> + { + public WeakAction(Action listener) + { + ObjectRef = GCHandle.Alloc(listener.Target, GCHandleType.Weak); + Method = listener.Method; + + if (Method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0) + throw new ArgumentException("Cannot create weak event to anonymous method with closure."); + } + + public bool Invoke(T data) + { + if (ObjectRef.IsAllocated && ObjectRef.Target != null) + { + Method.Invoke(ObjectRef.Target, new object[] { data }); + return true; + } + + Release(); + return false; + } + + public bool Equals(WeakAction other) + { + return (Method.Equals(other.Method)); + } + + public void Release() + { + ObjectRef.Free(); + } + + readonly GCHandle ObjectRef; + readonly MethodInfo Method; + } + + public struct WeakAction : IEquatable + { + public WeakAction(Action listener) + { + ObjectRef = GCHandle.Alloc(listener.Target, GCHandleType.Weak); + Method = listener.Method; + + if (Method.DeclaringType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Length != 0) + throw new ArgumentException("Cannot create weak event to anonymous method with closure."); + } + + public bool Invoke() + { + if (ObjectRef.IsAllocated && ObjectRef.Target != null) + { + Method.Invoke(ObjectRef.Target, null); + return true; + } + + Release(); + return false; + } + + public bool Equals(WeakAction other) + { + return (Method.Equals(other.Method)); + } + + public void Release() + { + ObjectRef.Free(); + } + + readonly GCHandle ObjectRef; + readonly MethodInfo Method; + } + + +} \ No newline at end of file diff --git a/Utilities/WeakActionStruct.cs.meta b/Utilities/WeakActionStruct.cs.meta new file mode 100644 index 0000000..49656b3 --- /dev/null +++ b/Utilities/WeakActionStruct.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 1662357721652524593d7b0f731aef45 +timeCreated: 1484483913 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Utilities/WeakEvent.cs b/Utilities/WeakEvent.cs new file mode 100644 index 0000000..e912490 --- /dev/null +++ b/Utilities/WeakEvent.cs @@ -0,0 +1,35 @@ +using Svelto.DataStructures; +using System; + +namespace BetterWeakEvents +{ + public class WeakEvent + { + public static WeakEvent operator+(WeakEvent c1, Action x) + { + c1._subscribers.Add(new WeakAction(x)); + return c1; + } + + public static WeakEvent operator-(WeakEvent c1, Action x) + { + c1._subscribers.UnorderredRemove(new WeakAction(x)); + return c1; + } + + public void Invoke(T1 arg1, T2 arg2) + { + for (int i = 0; i < _subscribers.Count; i++) + if (_subscribers[i].Invoke(arg1, arg2) == false) + _subscribers.UnorderredRemoveAt(i--); + } + + ~WeakEvent() + { + for (int i = 0; i < _subscribers.Count; i++) + _subscribers[i].Release(); + } + + protected FasterList> _subscribers = new FasterList>(); + } +} \ No newline at end of file diff --git a/Utilities/WeakEvent.cs.meta b/Utilities/WeakEvent.cs.meta new file mode 100644 index 0000000..884293e --- /dev/null +++ b/Utilities/WeakEvent.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: e2ee3763e7dffd4459c588eda6867ddd +timeCreated: 1484480295 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: