@@ -193,7 +193,8 @@ namespace Svelto.DataStructures | |||
{ | |||
public FasterListThreadSafe(FasterList<T> list) | |||
{ | |||
_list = list; | |||
if (list == null) throw new ArgumentException("invalid list"); | |||
_list = list; | |||
_lockQ = new ReaderWriterLockSlim(); | |||
} | |||
@@ -288,14 +289,14 @@ namespace Svelto.DataStructures | |||
public void CopyTo(T[] array, int arrayIndex) | |||
{ | |||
_lockQ.EnterWriteLock(); | |||
_lockQ.EnterReadLock(); | |||
try | |||
{ | |||
_list.CopyTo(array, arrayIndex); | |||
} | |||
finally | |||
{ | |||
_lockQ.ExitWriteLock(); | |||
_lockQ.ExitReadLock(); | |||
} | |||
} | |||
@@ -351,6 +352,19 @@ namespace Svelto.DataStructures | |||
} | |||
} | |||
public void UnorderredRemoveAt(int index) | |||
{ | |||
_lockQ.EnterWriteLock(); | |||
try | |||
{ | |||
_list.UnorderredRemoveAt(index); | |||
} | |||
finally | |||
{ | |||
_lockQ.ExitWriteLock(); | |||
} | |||
} | |||
IEnumerator<T> IEnumerable<T>.GetEnumerator() | |||
{ | |||
throw new NotImplementedException(); | |||
@@ -368,8 +382,6 @@ namespace Svelto.DataStructures | |||
public struct FasterReadOnlyListCast<T, U> : IList<U> where U:T | |||
{ | |||
public static FasterList<T> DefaultList = new FasterList<T>(); | |||
public int Count { get { return _list.Count; } } | |||
public bool IsReadOnly { get { return true; } } | |||
@@ -402,7 +414,7 @@ namespace Svelto.DataStructures | |||
public void CopyTo(U[] array, int arrayIndex) | |||
{ | |||
throw new NotImplementedException(); | |||
Array.Copy(_list.ToArrayFast(), 0, array, arrayIndex, _list.Count); | |||
} | |||
public bool Remove(U item) | |||
@@ -440,6 +452,8 @@ namespace Svelto.DataStructures | |||
public class FasterList<T> : IList<T> | |||
{ | |||
public static FasterList<T> DefaultList = new FasterList<T>(); | |||
const int MIN_SIZE = 4; | |||
public int Count | |||
@@ -752,14 +766,15 @@ namespace Svelto.DataStructures | |||
Resize(_count); | |||
} | |||
public bool Reuse(int index, out T result) | |||
public bool Reuse<U>(int index, out U result) | |||
where U:class, T | |||
{ | |||
result = default(T); | |||
result = default(U); | |||
if (index >= _buffer.Length) | |||
return false; | |||
result = _buffer[index]; | |||
result = (U)_buffer[index]; | |||
return result != null; | |||
} | |||
@@ -13,9 +13,9 @@ namespace Svelto.DataStructures | |||
public sealed class HeapPriorityQueue<T> : IPriorityQueue<T> | |||
where T : PriorityQueueNode | |||
{ | |||
private int _numNodes; | |||
private readonly FasterList<T> _nodes; | |||
private long _numNodesEverEnqueued; | |||
int _numNodes; | |||
private readonly FasterList<T> _nodes; | |||
long _numNodesEverEnqueued; | |||
/// <summary> | |||
/// Instantiate a new Priority Queue | |||
@@ -101,10 +101,10 @@ namespace Svelto.DataStructures | |||
CascadeUp(_nodes[_numNodes]); | |||
} | |||
#if NET_VERSION_4_5 | |||
#if NET_VERSION_4_5 | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
#endif | |||
private void Swap(T node1, T node2) | |||
#endif | |||
void Swap(T node1, T node2) | |||
{ | |||
//Swap the nodes | |||
_nodes[node1.QueueIndex] = node2; | |||
@@ -117,14 +117,14 @@ namespace Svelto.DataStructures | |||
} | |||
//Performance appears to be slightly better when this is NOT inlined o_O | |||
private void CascadeUp(T node) | |||
void CascadeUp(T node) | |||
{ | |||
//aka Heapify-up | |||
int parent = node.QueueIndex / 2; | |||
while(parent >= 1) | |||
while (parent >= 1) | |||
{ | |||
T parentNode = _nodes[parent]; | |||
if(HasHigherPriority(parentNode, node)) | |||
if (HasHigherPriority(parentNode, node)) | |||
break; | |||
//Node has lower priority value, so move it up the heap | |||
@@ -134,9 +134,9 @@ namespace Svelto.DataStructures | |||
} | |||
} | |||
#if NET_VERSION_4_5 | |||
#if NET_VERSION_4_5 | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
#endif | |||
#endif | |||
private void CascadeDown(T node) | |||
{ | |||
//aka Heapify-down | |||
@@ -198,10 +198,10 @@ namespace Svelto.DataStructures | |||
/// 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 | |||
/// </summary> | |||
#if NET_VERSION_4_5 | |||
#if NET_VERSION_4_5 | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
#endif | |||
private bool HasHigherPriority(T higher, T lower) | |||
#endif | |||
bool HasHigherPriority(T higher, T lower) | |||
{ | |||
return (higher.Priority < lower.Priority || | |||
(higher.Priority == lower.Priority && higher.InsertionIndex < lower.InsertionIndex)); | |||
@@ -242,13 +242,13 @@ namespace Svelto.DataStructures | |||
OnNodeUpdated(node); | |||
} | |||
private void OnNodeUpdated(T node) | |||
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)) | |||
if (parentIndex > 0 && HasHigherPriority(node, parentNode)) | |||
{ | |||
CascadeUp(node); | |||
} | |||
@@ -31,10 +31,15 @@ namespace Svelto.DataStructures | |||
{ | |||
get | |||
{ | |||
using (new ReadOnlyLock(dictionaryLock)) | |||
LockQ.EnterReadLock(); | |||
try | |||
{ | |||
return dict.Count; | |||
} | |||
finally | |||
{ | |||
LockQ.ExitReadLock(); | |||
} | |||
} | |||
} | |||
@@ -42,10 +47,15 @@ namespace Svelto.DataStructures | |||
{ | |||
get | |||
{ | |||
using (new ReadOnlyLock(dictionaryLock)) | |||
LockQ.EnterReadLock(); | |||
try | |||
{ | |||
return dict.IsReadOnly; | |||
} | |||
finally | |||
{ | |||
LockQ.ExitReadLock(); | |||
} | |||
} | |||
} | |||
@@ -53,10 +63,15 @@ namespace Svelto.DataStructures | |||
{ | |||
get | |||
{ | |||
using (new ReadOnlyLock(dictionaryLock)) | |||
LockQ.EnterReadLock(); | |||
try | |||
{ | |||
return new FasterList<TKey>(dict.Keys); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitReadLock(); | |||
} | |||
} | |||
} | |||
@@ -64,10 +79,15 @@ namespace Svelto.DataStructures | |||
{ | |||
get | |||
{ | |||
using (new ReadOnlyLock(dictionaryLock)) | |||
LockQ.EnterReadLock(); | |||
try | |||
{ | |||
return new FasterList<TValue>(dict.Values); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitReadLock(); | |||
} | |||
} | |||
} | |||
@@ -75,91 +95,146 @@ namespace Svelto.DataStructures | |||
{ | |||
get | |||
{ | |||
using (new ReadOnlyLock(dictionaryLock)) | |||
LockQ.EnterReadLock(); | |||
try | |||
{ | |||
return dict[key]; | |||
} | |||
finally | |||
{ | |||
LockQ.ExitReadLock(); | |||
} | |||
} | |||
set | |||
{ | |||
using (new WriteLock(dictionaryLock)) | |||
LockQ.EnterWriteLock(); | |||
try | |||
{ | |||
dict[key] = value; | |||
} | |||
finally | |||
{ | |||
LockQ.ExitWriteLock(); | |||
} | |||
} | |||
} | |||
public virtual void Add(KeyValuePair<TKey, TValue> item) | |||
{ | |||
using (new WriteLock(dictionaryLock)) | |||
LockQ.EnterWriteLock(); | |||
try | |||
{ | |||
dict.Add(item); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitWriteLock(); | |||
} | |||
} | |||
public virtual void Clear() | |||
{ | |||
using (new WriteLock(dictionaryLock)) | |||
LockQ.EnterWriteLock(); | |||
try | |||
{ | |||
dict.Clear(); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitWriteLock(); | |||
} | |||
} | |||
public virtual bool Contains(KeyValuePair<TKey, TValue> item) | |||
{ | |||
using (new ReadOnlyLock(dictionaryLock)) | |||
LockQ.EnterReadLock(); | |||
try | |||
{ | |||
return dict.Contains(item); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitReadLock(); | |||
} | |||
} | |||
public virtual void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) | |||
{ | |||
using (new ReadOnlyLock(dictionaryLock)) | |||
LockQ.EnterReadLock(); | |||
try | |||
{ | |||
dict.CopyTo(array, arrayIndex); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitReadLock(); | |||
} | |||
} | |||
public virtual bool Remove(KeyValuePair<TKey, TValue> item) | |||
{ | |||
using (new WriteLock(dictionaryLock)) | |||
LockQ.EnterWriteLock(); | |||
try | |||
{ | |||
return dict.Remove(item); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitWriteLock(); | |||
} | |||
} | |||
public virtual void Add(TKey key, TValue value) | |||
{ | |||
using (new WriteLock(dictionaryLock)) | |||
LockQ.EnterWriteLock(); | |||
try | |||
{ | |||
dict.Add(key, value); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitWriteLock(); | |||
} | |||
} | |||
public virtual bool ContainsKey(TKey key) | |||
{ | |||
using (new ReadOnlyLock(dictionaryLock)) | |||
LockQ.EnterReadLock(); | |||
try | |||
{ | |||
return dict.ContainsKey(key); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitReadLock(); | |||
} | |||
} | |||
public virtual bool Remove(TKey key) | |||
{ | |||
using (new WriteLock(dictionaryLock)) | |||
LockQ.EnterWriteLock(); | |||
try | |||
{ | |||
return dict.Remove(key); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitWriteLock(); | |||
} | |||
} | |||
public virtual bool TryGetValue(TKey key, out TValue value) | |||
{ | |||
using (new ReadOnlyLock(dictionaryLock)) | |||
LockQ.EnterReadLock(); | |||
try | |||
{ | |||
return dict.TryGetValue(key, out value); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitReadLock(); | |||
} | |||
} | |||
/// <summary> | |||
@@ -169,7 +244,8 @@ namespace Svelto.DataStructures | |||
/// <param name = "newValue">New Value</param> | |||
public void MergeSafe(TKey key, TValue newValue) | |||
{ | |||
using (new WriteLock(dictionaryLock)) | |||
LockQ.EnterWriteLock(); | |||
try | |||
{ | |||
// take a writelock immediately since we will always be writing | |||
if (dict.ContainsKey(key)) | |||
@@ -177,6 +253,10 @@ namespace Svelto.DataStructures | |||
dict.Add(key, newValue); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitWriteLock(); | |||
} | |||
} | |||
/// <summary> | |||
@@ -185,132 +265,29 @@ namespace Svelto.DataStructures | |||
/// <param name = "key">Key to remove</param> | |||
public void RemoveSafe(TKey key) | |||
{ | |||
using (new ReadLock(dictionaryLock)) | |||
LockQ.EnterReadLock(); | |||
try | |||
{ | |||
if (dict.ContainsKey(key)) | |||
using (new WriteLock(dictionaryLock)) | |||
{ | |||
dict.Remove(key); | |||
} | |||
LockQ.EnterWriteLock(); | |||
try | |||
{ | |||
dict.Remove(key); | |||
} | |||
finally | |||
{ | |||
LockQ.ExitWriteLock(); | |||
} | |||
} | |||
finally | |||
{ | |||
LockQ.ExitReadLock(); | |||
} | |||
} | |||
// This is the internal dictionary that we are wrapping | |||
readonly IDictionary<TKey, TValue> dict; | |||
[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); | |||
} | |||
readonly ReaderWriterLockSlim LockQ = new ReaderWriterLockSlim(); | |||
} | |||
} |
@@ -10,29 +10,39 @@ namespace Svelto.ECS | |||
Dictionary<Type, Dictionary<int, INode>> nodesDBdic, | |||
Dictionary<Type, FasterList<INode>> nodesDBgroups) | |||
{ | |||
_nodesDB = nodesDB; | |||
_nodesDBdic = nodesDBdic; | |||
_nodesDBgroups = nodesDBgroups; | |||
_nodesDB = new DataStructures.WeakReference<Dictionary<Type, FasterList<INode>>>(nodesDB); | |||
_nodesDBdic = new DataStructures.WeakReference<Dictionary<Type, Dictionary<int, INode>>>(nodesDBdic); | |||
_nodesDBgroups = new DataStructures.WeakReference<Dictionary<Type, FasterList<INode>>>(nodesDBgroups); | |||
} | |||
public FasterReadOnlyListCast<INode, T> QueryNodes<T>() where T:INode | |||
{ | |||
var type = typeof(T); | |||
if (_nodesDB.ContainsKey(type) == false) | |||
if (_nodesDB.IsValid == false || _nodesDB.Target.ContainsKey(type) == false) | |||
return RetrieveEmptyNodeList<T>(); | |||
return new FasterReadOnlyListCast<INode, T>(_nodesDB[type]); | |||
return new FasterReadOnlyListCast<INode, T>(_nodesDB.Target[type]); | |||
} | |||
/* public FasterReadOnlyList<T> QueryStructNodes<T>() where T:struct | |||
{ | |||
var type = typeof(T); | |||
if (_nodesDBStructs.ContainsKey(type) == false) | |||
return RetrieveEmptyStructNodeList<T>(); | |||
return new FasterReadOnlyList<T>(((StructNodeList<T>)(_nodesDBStructs[type])).list); | |||
}*/ | |||
public ReadOnlyDictionary<int, INode> QueryIndexableNodes<T>() where T:INode | |||
{ | |||
var type = typeof(T); | |||
if (_nodesDBdic.ContainsKey(type) == false) | |||
if (_nodesDB.IsValid == false || _nodesDBdic.Target.ContainsKey(type) == false) | |||
return _defaultEmptyNodeDict; | |||
return new ReadOnlyDictionary<int, INode>(_nodesDBdic[type]); | |||
return new ReadOnlyDictionary<int, INode>(_nodesDBdic.Target[type]); | |||
} | |||
public T QueryNodeFromGroup<T>(int groupID) where T : INode | |||
@@ -49,10 +59,10 @@ namespace Svelto.ECS | |||
{ | |||
var type = typeof(T); | |||
if (_nodesDBgroups.ContainsKey(type) == false) | |||
if (_nodesDBgroups.IsValid == false || _nodesDBgroups.Target.ContainsKey(type) == false) | |||
return RetrieveEmptyNodeList<T>(); | |||
return new FasterReadOnlyListCast<INode, T>(_nodesDBgroups[type]); | |||
return new FasterReadOnlyListCast<INode, T>(_nodesDBgroups.Target[type]); | |||
} | |||
public bool QueryNode<T>(int ID, out T node) where T:INode | |||
@@ -61,7 +71,7 @@ namespace Svelto.ECS | |||
INode internalNode; | |||
if (_nodesDBdic.ContainsKey(type) && _nodesDBdic[type].TryGetValue(ID, out internalNode)) | |||
if (_nodesDBdic.IsValid && _nodesDBdic.Target.ContainsKey(type) && _nodesDBdic.Target[type].TryGetValue(ID, out internalNode)) | |||
{ | |||
node = (T)internalNode; | |||
@@ -79,7 +89,7 @@ namespace Svelto.ECS | |||
INode internalNode; | |||
if (_nodesDBdic.ContainsKey(type) && _nodesDBdic[type].TryGetValue(ID, out internalNode)) | |||
if (_nodesDBdic.IsValid && _nodesDBdic.Target.ContainsKey(type) && _nodesDBdic.Target[type].TryGetValue(ID, out internalNode)) | |||
return (T)internalNode; | |||
throw new Exception("Node Not Found"); | |||
@@ -87,17 +97,31 @@ namespace Svelto.ECS | |||
static FasterReadOnlyListCast<INode, T> RetrieveEmptyNodeList<T>() where T : INode | |||
{ | |||
return new FasterReadOnlyListCast<INode, T>(FasterReadOnlyListCast<INode, T>.DefaultList); | |||
return new FasterReadOnlyListCast<INode, T>(FasterList<INode>.DefaultList); | |||
} | |||
static FasterReadOnlyList<T> RetrieveEmptyStructNodeList<T>() where T : struct | |||
{ | |||
return new FasterReadOnlyList<T>(FasterList<T>.DefaultList); | |||
} | |||
Dictionary<Type, FasterList<INode>> _nodesDB; | |||
Dictionary<Type, Dictionary<int, INode>> _nodesDBdic; | |||
Dictionary<Type, FasterList<INode>> _nodesDBgroups; | |||
Svelto.DataStructures.WeakReference<Dictionary<Type, FasterList<INode>>> _nodesDB; | |||
Svelto.DataStructures.WeakReference<Dictionary<Type, Dictionary<int, INode>>> _nodesDBdic; | |||
Svelto.DataStructures.WeakReference<Dictionary<Type, FasterList<INode>>> _nodesDBgroups; | |||
// Dictionary<Type, StructNodeList> _nodesDBStructs; | |||
//Dictionary<Type, ThreadSafeFasterList<INode>> _nodesDB; | |||
//Dictionary<Type, ThreadsSafeDictionary<int, INode>> _nodesDBdic; | |||
// Dictionary<Type, ThreadSafeFasterList<INode>> _nodesDBgroups; | |||
ReadOnlyDictionary<int, INode> _defaultEmptyNodeDict = new ReadOnlyDictionary<int, INode>(new Dictionary<int, INode>()); | |||
class StructNodeList | |||
{ } | |||
class StructNodeList<T> : StructNodeList where T : struct | |||
{ | |||
public FasterList<T> list = new FasterList<T>(); | |||
} | |||
} | |||
} |
@@ -4,7 +4,8 @@ using System.Collections.Generic; | |||
using Svelto.DataStructures; | |||
using UnityEngine; | |||
using WeakReference = Svelto.DataStructures.WeakReference<Svelto.ECS.EnginesRoot>; | |||
using Svelto.ECS.NodeSchedulers; | |||
using Svelto.ECS.Internal; | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
using Svelto.ECS.Profiler; | |||
#endif | |||
@@ -14,26 +15,11 @@ using System.Reflection; | |||
namespace Svelto.ECS | |||
{ | |||
class Scheduler : MonoBehaviour | |||
{ | |||
IEnumerator Start() | |||
{ | |||
while (true) | |||
{ | |||
yield return new WaitForEndOfFrame(); | |||
OnTick(); | |||
} | |||
} | |||
internal Action OnTick; | |||
} | |||
public sealed class EnginesRoot : IEnginesRoot, IEntityFactory | |||
{ | |||
public EnginesRoot() | |||
public EnginesRoot(NodeSubmissionScheduler nodeScheduler) | |||
{ | |||
_nodeEngines = new Dictionary<Type, FasterList<INodeEngine<INode>>>(); | |||
_nodeEngines = new Dictionary<Type, FasterList<INodeEngine>>(); | |||
_engineRootWeakReference = new WeakReference(this); | |||
_otherEnginesReferences = new FasterList<IEngine>(); | |||
@@ -45,9 +31,8 @@ namespace Svelto.ECS | |||
_nodesDBgroups = new Dictionary<Type, FasterList<INode>>(); | |||
GameObject go = new GameObject("ECSScheduler"); | |||
go.AddComponent<Scheduler>().OnTick += SubmitNodes; | |||
_scheduler = nodeScheduler; | |||
_scheduler.Schedule(SubmitNodes); | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
GameObject debugEngineObject = new GameObject("Engine Debugger"); | |||
@@ -109,6 +94,28 @@ namespace Svelto.ECS | |||
_groupNodesToAdd.Clear(); | |||
} | |||
public void AddEngine<T>(INodeEngine<T> engine) where T:class, INode | |||
{ | |||
AddEngine(new NodeEngineWrapper<T>(engine)); | |||
if (engine is IQueryableNodeEngine) | |||
(engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _nodesDBgroups); | |||
} | |||
public void AddEngine<T, U>(INodeEngine<T, U> engine) where T:class, INode where U:class, INode | |||
{ | |||
AddEngine(new NodeEngineWrapper<T, U>(engine)); | |||
AddEngine((INodeEngine<U>)(engine)); | |||
} | |||
public void AddEngine<T, U, V>(INodeEngine<T, U, V> engine) where T:class, INode | |||
where U:class, INode | |||
where V:class, INode | |||
{ | |||
AddEngine(new NodeEngineWrapper<T, U, V>(engine)); | |||
AddEngine((INodeEngine<U, V>)(engine)); | |||
} | |||
public void AddEngine(IEngine engine) | |||
{ | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
@@ -125,9 +132,7 @@ namespace Svelto.ECS | |||
} | |||
else | |||
{ | |||
var engineType = engine.GetType(); | |||
// Type baseInterface = null; | |||
#if !NETFX_CORE | |||
var baseType = engineType.BaseType; | |||
@@ -138,12 +143,28 @@ namespace Svelto.ECS | |||
if (baseType.IsConstructedGenericType | |||
#endif | |||
&& baseType.GetGenericTypeDefinition() == typeof (SingleNodeEngine<>)) | |||
&& baseType.GetGenericTypeDefinition() == typeof(SingleNodeEngine<>)) | |||
{ | |||
AddEngine(engine as INodeEngine<INode>, baseType.GetGenericArguments(), _nodeEngines); | |||
AddEngine(engine as INodeEngine, baseType.GetGenericArguments(), _nodeEngines); | |||
} | |||
else | |||
_otherEnginesReferences.Add(engine); | |||
{ | |||
bool found = false; | |||
for (int i = 0, maxLength = engineType.GetInterfaces().Length; i < maxLength; i++) | |||
{ | |||
var type = engineType.GetInterfaces()[i]; | |||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(INodeEngine<>)) | |||
{ | |||
AddEngine(engine as INodeEngine, type.GetGenericArguments(), _nodeEngines); | |||
found = true; | |||
} | |||
} | |||
if (found == false) | |||
_otherEnginesReferences.Add(engine); | |||
} | |||
} | |||
if (engine is ICallBackOnAddEngine) | |||
@@ -152,10 +173,10 @@ namespace Svelto.ECS | |||
public void BuildEntity(int ID, EntityDescriptor ed) | |||
{ | |||
var entityNodes = ed.BuildNodes(ID, (node) => | |||
var entityNodes = ed.BuildNodes(ID, (nodes) => | |||
{ | |||
if (_engineRootWeakReference.IsValid == true) | |||
InternalRemove(node); | |||
InternalRemove(nodes); | |||
}); | |||
_nodesToAdd.AddRange(entityNodes); | |||
@@ -176,26 +197,26 @@ namespace Svelto.ECS | |||
/// <param name="ed"></param> | |||
public void BuildEntityGroup(int groupID, EntityDescriptor ed) | |||
{ | |||
var entityNodes = ed.BuildNodes(groupID, (node) => | |||
var entityNodes = ed.BuildNodes(groupID, (nodes) => | |||
{ | |||
if (_engineRootWeakReference.IsValid == true) | |||
InternalGroupRemove(node); | |||
InternalGroupRemove(nodes); | |||
}); | |||
_groupNodesToAdd.AddRange(entityNodes); | |||
} | |||
static void AddEngine<T>(T engine, Type[] types, Dictionary<Type, FasterList<INodeEngine<INode>>> engines) where T : INodeEngine<INode> | |||
static void AddEngine(INodeEngine engine, Type[] types, Dictionary<Type, FasterList<INodeEngine>> engines) | |||
{ | |||
for (int i = 0; i < types.Length; i++) | |||
{ | |||
FasterList<INodeEngine<INode>> list; | |||
FasterList<INodeEngine> list; | |||
var type = types[i]; | |||
if (engines.TryGetValue(type, out list) == false) | |||
{ | |||
list = new FasterList<INodeEngine<INode>>(); | |||
list = new FasterList<INodeEngine>(); | |||
engines.Add(type, list); | |||
} | |||
@@ -215,7 +236,7 @@ namespace Svelto.ECS | |||
AddNodeToNodesDictionary(node, nodeType); | |||
} | |||
void AddNodeToTheDB<T>(T node, Type nodeType) where T : INode | |||
void AddNodeToTheDB(INode node, Type nodeType) | |||
{ | |||
FasterList<INode> nodes; | |||
if (_nodesDB.TryGetValue(nodeType, out nodes) == false) | |||
@@ -226,7 +247,7 @@ namespace Svelto.ECS | |||
AddNodeToNodesDictionary(node, nodeType); | |||
} | |||
void AddNodeToNodesDictionary<T>(T node, Type nodeType) where T : INode | |||
void AddNodeToNodesDictionary(INode node, Type nodeType) | |||
{ | |||
if (node is NodeWithID) | |||
{ | |||
@@ -238,9 +259,9 @@ namespace Svelto.ECS | |||
} | |||
} | |||
void AddNodeToTheSuitableEngines<T>(T node, Type nodeType) where T : INode | |||
void AddNodeToTheSuitableEngines(INode node, Type nodeType) | |||
{ | |||
FasterList<INodeEngine<INode>> enginesForNode; | |||
FasterList<INodeEngine> enginesForNode; | |||
if (_nodeEngines.TryGetValue(nodeType, out enginesForNode)) | |||
{ | |||
@@ -255,91 +276,85 @@ namespace Svelto.ECS | |||
} | |||
} | |||
void RemoveNodeFromTheDB<T>(T node, Type nodeType) where T : INode | |||
{ | |||
FasterList<INode> nodes; | |||
if (_nodesDB.TryGetValue(nodeType, out nodes) == true) | |||
nodes.UnorderredRemove(node); //should I remove it from the dictionary if length is zero? | |||
RemoveNodeFromNodesDictionary(node, nodeType); | |||
} | |||
void RemoveNodeFromNodesDictionary<T>(T node, Type nodeType) where T : INode | |||
void RemoveNodesFromDB(Dictionary<Type, FasterList<INode>> DB, FasterReadOnlyList<INode> nodes) | |||
{ | |||
if (node is NodeWithID) | |||
for (int i = 0; i < nodes.Count; i++) | |||
{ | |||
Dictionary<int, INode> nodesDic; | |||
FasterList<INode> nodesInDB; | |||
if (_nodesDBdic.TryGetValue(nodeType, out nodesDic)) | |||
nodesDic.Remove((node as NodeWithID).ID); | |||
} | |||
} | |||
var node = nodes[i]; | |||
var nodeType = node.GetType(); | |||
if (DB.TryGetValue(nodeType, out nodesInDB) == true) | |||
nodesInDB.UnorderredRemove(node); //should I remove it from the dictionary if length is zero? | |||
void RemoveNodeFromGroupDB<T>(T node, Type nodeType) where T : INode | |||
{ | |||
FasterList<INode> nodes; | |||
if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == true) | |||
nodes.UnorderredRemove(node); //should I remove it from the dictionary if length is zero? | |||
if (node is NodeWithID) | |||
{ | |||
Dictionary<int, INode> nodesDic; | |||
RemoveNodeFromNodesDictionary(node, nodeType); | |||
if (_nodesDBdic.TryGetValue(nodeType, out nodesDic)) | |||
nodesDic.Remove((node as NodeWithID).ID); | |||
} | |||
} | |||
} | |||
void RemoveNodeFromEngines<T>(T node, Type nodeType) where T : INode | |||
void RemoveNodesFromEngines(FasterReadOnlyList<INode> nodes) | |||
{ | |||
FasterList<INodeEngine<INode>> enginesForNode; | |||
if (_nodeEngines.TryGetValue(nodeType, out enginesForNode)) | |||
for (int i = 0; i < nodes.Count; i++) | |||
{ | |||
for (int j = 0; j < enginesForNode.Count; j++) | |||
FasterList<INodeEngine> enginesForNode; | |||
var node = nodes[i]; | |||
if (_nodeEngines.TryGetValue(node.GetType(), out enginesForNode)) | |||
{ | |||
for (int j = 0; j < enginesForNode.Count; j++) | |||
{ | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, enginesForNode[j], node); | |||
EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, enginesForNode[j], node); | |||
#else | |||
enginesForNode[j].Remove(node); | |||
enginesForNode[j].Remove(node); | |||
#endif | |||
} | |||
} | |||
} | |||
} | |||
#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR | |||
void AddNodeToEngine(INodeEngine<INode> engine, INode node) | |||
static void AddNodeToEngine(INodeEngine engine, INode node) | |||
{ | |||
engine.Add(node); | |||
} | |||
void RemoveNodeFromEngine(INodeEngine<INode> engine, INode node) | |||
static void RemoveNodeFromEngine(INodeEngine engine, INode node) | |||
{ | |||
engine.Remove(node); | |||
} | |||
#endif | |||
void InternalRemove<T>(T node) where T : INode | |||
{ | |||
Type nodeType = node.GetType(); | |||
RemoveNodeFromEngines(node, nodeType); | |||
RemoveNodeFromTheDB(node, node.GetType()); | |||
void InternalRemove(FasterReadOnlyList<INode> nodes) | |||
{ | |||
RemoveNodesFromEngines(nodes); | |||
RemoveNodesFromDB(_nodesDB, nodes); | |||
} | |||
void InternalGroupRemove<T>(T node) where T : INode | |||
void InternalGroupRemove(FasterReadOnlyList<INode> nodes) | |||
{ | |||
Type nodeType = node.GetType(); | |||
RemoveNodeFromEngines(node, nodeType); | |||
RemoveNodeFromGroupDB(node, node.GetType()); | |||
RemoveNodesFromEngines(nodes); | |||
RemoveNodesFromDB(_nodesDBgroups, nodes); | |||
} | |||
Dictionary<Type, FasterList<INodeEngine<INode>>> _nodeEngines; | |||
Dictionary<Type, FasterList<INodeEngine>> _nodeEngines; | |||
FasterList<IEngine> _otherEnginesReferences; | |||
Dictionary<Type, FasterList<INode>> _nodesDB; | |||
Dictionary<Type, Dictionary<int, INode>> _nodesDBdic; | |||
Dictionary<Type, FasterList<INode>> _nodesDBgroups; | |||
Dictionary<Type, Dictionary<int, INode>> _nodesDBdic; | |||
FasterList<INode> _nodesToAdd; | |||
FasterList<INode> _groupNodesToAdd; | |||
WeakReference _engineRootWeakReference; | |||
NodeSubmissionScheduler _scheduler; | |||
} | |||
} | |||
@@ -15,6 +15,11 @@ namespace Svelto.ECS | |||
_nodesToBuild = nodesToBuild; | |||
} | |||
/* protected EntityDescriptor(IStructNodeBuilder[] structNodesToBuild) | |||
{ | |||
_structNodesToBuild = structNodesToBuild; | |||
}*/ | |||
public void AddImplementors(params object[] componentsImplementor) | |||
{ | |||
var implementors = new object[componentsImplementor.Length + _implementors.Length]; | |||
@@ -25,23 +30,22 @@ namespace Svelto.ECS | |||
_implementors = implementors; | |||
} | |||
public virtual FasterList<INode> BuildNodes(int ID, Action<INode> removeAction) | |||
public virtual FasterList<INode> BuildNodes(int ID, Action<FasterReadOnlyList<INode>> removeAction) | |||
{ | |||
var nodes = new FasterList<INode>(); | |||
for (int index = 0; index < _nodesToBuild.Length; index++) | |||
for (int index = _nodesToBuild.Length - 1; index >= 0; index--) | |||
{ | |||
var nodeBuilder = _nodesToBuild[index]; | |||
var node = FillNode(nodeBuilder.Build(ID), () => | |||
{ | |||
for (int i = 0; i < nodes.Count; i++) | |||
removeAction(nodes[i]); | |||
removeAction(new FasterReadOnlyList<INode>()); | |||
nodes.Clear(); | |||
} | |||
); | |||
nodes.Add (node); | |||
nodes.Add(node); | |||
} | |||
return nodes; | |||
@@ -88,7 +92,8 @@ namespace Svelto.ECS | |||
object[] _implementors; | |||
readonly INodeBuilder[] _nodesToBuild; | |||
readonly INodeBuilder[] _nodesToBuild; | |||
// readonly IStructNodeBuilder[] _structNodesToBuild; | |||
} | |||
public interface INodeBuilder | |||
@@ -105,4 +110,15 @@ namespace Svelto.ECS | |||
return (NodeType)node; | |||
} | |||
} | |||
/* | |||
public interface IStructNodeBuilder | |||
{} | |||
public class StructNodeBuilder<NodeType> : IStructNodeBuilder where NodeType : struct | |||
{ | |||
public NodeType Build() | |||
{ | |||
return new NodeType(); | |||
} | |||
}*/ | |||
} |
@@ -9,7 +9,25 @@ namespace Svelto.ECS | |||
void Remove(TNodeType obj); | |||
} | |||
public interface INodesEngine : INodeEngine<INode> | |||
public interface INodeEngine<in T, in U>:INodeEngine<U> where T:INode where U:INode | |||
{ | |||
void Add(T obj); | |||
void Remove(T obj); | |||
} | |||
public interface INodeEngine<in T, in U, in V>:INodeEngine<U, V> where T:INode where U:INode where V:INode | |||
{ | |||
void Add(T obj); | |||
void Remove(T obj); | |||
} | |||
public interface INodeEngine:IEngine | |||
{ | |||
void Add(INode obj); | |||
void Remove(INode obj); | |||
} | |||
public interface INodesEngine : INodeEngine | |||
{ | |||
System.Type[] AcceptedNodes(); | |||
} | |||
@@ -11,6 +11,8 @@ namespace Svelto.ECS | |||
FasterReadOnlyListCast<INode, T> QueryNodes<T>() where T:INode; | |||
// FasterReadOnlyList<T> QueryStructNodes<T>() where T : struct; | |||
bool QueryNodeFromGroup<T>(int ID, out T node) where T : INode; | |||
T QueryNodeFromGroup<T>(int ID) where T : INode; | |||
FasterReadOnlyListCast<INode, T> QueryNodesFromGroups<T>() where T : INode; | |||
@@ -0,0 +1,66 @@ | |||
using System; | |||
namespace Svelto.ECS.Internal | |||
{ | |||
class NodeEngineWrapper<T> : SingleNodeEngine<T> where T : class, INode | |||
{ | |||
INodeEngine<T> engine; | |||
public NodeEngineWrapper(INodeEngine<T> engine) | |||
{ | |||
this.engine = engine; | |||
} | |||
protected override void Add(T node) | |||
{ | |||
engine.Add((T)node); | |||
} | |||
protected override void Remove(T node) | |||
{ | |||
engine.Remove((T)node); | |||
} | |||
} | |||
class NodeEngineWrapper<T, U>: SingleNodeEngine<T> where T : class, INode where U : class, INode | |||
{ | |||
INodeEngine<T, U> engine; | |||
public NodeEngineWrapper(INodeEngine<T, U> engine) | |||
{ | |||
this.engine = engine; | |||
} | |||
protected override void Add(T node) | |||
{ | |||
engine.Add((T)node); | |||
} | |||
protected override void Remove(T node) | |||
{ | |||
engine.Remove((T)node); | |||
} | |||
} | |||
class NodeEngineWrapper<T, U, V>: SingleNodeEngine<T> where T : class, INode | |||
where U : class, INode | |||
where V : class, INode | |||
{ | |||
INodeEngine<T, U, V> engine; | |||
public NodeEngineWrapper(INodeEngine<T, U, V> engine) | |||
{ | |||
this.engine = engine; | |||
} | |||
protected override void Add(T node) | |||
{ | |||
engine.Add((T)node); | |||
} | |||
protected override void Remove(T node) | |||
{ | |||
engine.Remove((T)node); | |||
} | |||
} | |||
} |
@@ -0,0 +1,9 @@ | |||
using System; | |||
namespace Svelto.ECS.NodeSchedulers | |||
{ | |||
public abstract class NodeSubmissionScheduler | |||
{ | |||
abstract public void Schedule(Action submitNodes); | |||
} | |||
} |
@@ -5,7 +5,7 @@ using UnityEditor; | |||
namespace Svelto.ECS.Profiler | |||
{ | |||
internal class EngineProfilerMenuItem | |||
class EngineProfilerMenuItem | |||
{ | |||
[MenuItem("Engines/Enable Profiler")] | |||
public static void EnableProfiler() | |||
@@ -11,7 +11,7 @@ namespace Svelto.ECS.Profiler | |||
{ | |||
static readonly Stopwatch _stopwatch = new Stopwatch(); | |||
public static void MonitorAddDuration(Action<INodeEngine<INode>, INode> addingFunc, INodeEngine<INode> engine, INode node) | |||
public static void MonitorAddDuration(Action<INodeEngine, INode> addingFunc, INodeEngine engine, INode node) | |||
{ | |||
EngineInfo info; | |||
if (engineInfos.TryGetValue(engine.GetType(), out info)) | |||
@@ -25,7 +25,7 @@ namespace Svelto.ECS.Profiler | |||
} | |||
} | |||
public static void MonitorRemoveDuration(Action<INodeEngine<INode>, INode> removeFunc, INodeEngine<INode> engine, INode node) | |||
public static void MonitorRemoveDuration(Action<INodeEngine, INode> removeFunc, INodeEngine engine, INode node) | |||
{ | |||
EngineInfo info; | |||
if (engineInfos.TryGetValue(engine.GetType(), out info)) | |||
@@ -1,13 +1,13 @@ | |||
namespace Svelto.ECS | |||
{ | |||
public abstract class SingleNodeEngine<TNodeType> : INodeEngine<INode> where TNodeType : class, INode | |||
public abstract class SingleNodeEngine<TNodeType> : INodeEngine where TNodeType : class, INode | |||
{ | |||
void INodeEngine<INode>.Add(INode obj) | |||
void INodeEngine.Add(INode obj) | |||
{ | |||
Add(obj as TNodeType); | |||
} | |||
void INodeEngine<INode>.Remove(INode obj) | |||
void INodeEngine.Remove(INode obj) | |||
{ | |||
Remove(obj as TNodeType); | |||
} | |||
@@ -0,0 +1,38 @@ | |||
using System; | |||
using System.Collections; | |||
using UnityEngine; | |||
namespace Svelto.ECS.NodeSchedulers | |||
{ | |||
public class UnitySumbmissionNodeScheduler : NodeSubmissionScheduler | |||
{ | |||
public UnitySumbmissionNodeScheduler() | |||
{ | |||
GameObject go = new GameObject("ECSScheduler"); | |||
_scheduler = go.AddComponent<Scheduler>(); | |||
} | |||
public override void Schedule(Action submitNodes) | |||
{ | |||
_scheduler.OnTick += submitNodes; | |||
} | |||
class Scheduler : MonoBehaviour | |||
{ | |||
IEnumerator Start() | |||
{ | |||
while (true) | |||
{ | |||
yield return new WaitForEndOfFrame(); | |||
OnTick(); | |||
} | |||
} | |||
internal Action OnTick; | |||
} | |||
Scheduler _scheduler; | |||
} | |||
} |
@@ -324,18 +324,18 @@ namespace DesignByContract | |||
useAssertions = value; | |||
} | |||
} | |||
#endregion // Interface | |||
#region Implementation | |||
#endregion // Interface | |||
// No creation | |||
private Check() {} | |||
#region Implementation | |||
/// <summary> | |||
/// Is exception handling being used? | |||
/// </summary> | |||
private static bool UseExceptions | |||
// No creation | |||
Check() { } | |||
/// <summary> | |||
/// Is exception handling being used? | |||
/// </summary> | |||
private static bool UseExceptions | |||
{ | |||
get | |||
{ | |||
@@ -343,15 +343,15 @@ namespace DesignByContract | |||
} | |||
} | |||
// Are trace assertion statements being used? | |||
// Default is to use exception handling. | |||
private static bool useAssertions = false; | |||
// Are trace assertion statements being used? | |||
// Default is to use exception handling. | |||
static bool useAssertions = false; | |||
#endregion // Implementation | |||
#endregion // Implementation | |||
} // End Check | |||
} // End Check | |||
internal class Trace | |||
class Trace | |||
{ | |||
internal static void Assert(bool assertion, string v) | |||
{ | |||