From c8f57253fc2c0c34b96a2426cab5f3a6ee6b9a81 Mon Sep 17 00:00:00 2001 From: sebas77 Date: Sat, 23 Dec 2017 11:15:31 +0000 Subject: [PATCH] - optimize even more - some renaming --- DataStructures/FasterList.cs | 2 +- DataStructures/LockFreeQueue.cs | 65 +++--- .../PriorityQueue/HeapPriorityQueue.cs | 200 +++++++++--------- .../PriorityQueue/IPriorityQueue.cs | 10 +- .../PriorityQueue/PriorityQueueNode.cs | 6 +- .../TypeSafeFasterListForECS.cs | 35 +++ ...{EngineNodeDB.cs => EngineEntityViewDB.cs} | 0 ECS/EnginesRoot.cs | 56 +++-- ECS/EntityDescriptorTemplate.cs | 41 ++-- ...eduler.cs => EntitySubmissionScheduler.cs} | 2 +- ECS/{INode.cs => EntityView.cs} | 0 ECS/{NodeBuilder.cs => EntityViewBuilder.cs} | 21 ++ ...EngineNodeDB.cs => IEngineEntityViewDB.cs} | 0 ECS/IEnginesInterfaces.cs | 9 +- ...desEngine.cs => MultiEntityViewsEngine.cs} | 0 ...odeEngine.cs => SingleEntityViewEngine.cs} | 0 Utilities/FastInvoke.cs | 22 +- 17 files changed, 284 insertions(+), 185 deletions(-) rename ECS/{EngineNodeDB.cs => EngineEntityViewDB.cs} (100%) rename ECS/{NodeSubmissionScheduler.cs => EntitySubmissionScheduler.cs} (71%) rename ECS/{INode.cs => EntityView.cs} (100%) rename ECS/{NodeBuilder.cs => EntityViewBuilder.cs} (73%) rename ECS/{IEngineNodeDB.cs => IEngineEntityViewDB.cs} (100%) rename ECS/{MultiNodesEngine.cs => MultiEntityViewsEngine.cs} (100%) rename ECS/{SingleNodeEngine.cs => SingleEntityViewEngine.cs} (100%) diff --git a/DataStructures/FasterList.cs b/DataStructures/FasterList.cs index bc4c4f1..eed13af 100644 --- a/DataStructures/FasterList.cs +++ b/DataStructures/FasterList.cs @@ -788,7 +788,7 @@ namespace Svelto.DataStructures _buffer = newList; } - public void Trim() + public void Trim() { if (_count < _buffer.Length) Resize(_count); diff --git a/DataStructures/LockFreeQueue.cs b/DataStructures/LockFreeQueue.cs index 4b936be..4c55f39 100644 --- a/DataStructures/LockFreeQueue.cs +++ b/DataStructures/LockFreeQueue.cs @@ -1,13 +1,14 @@ +using System.Collections.Generic; using System.Threading; //from unify wiki namespace Svelto.DataStructures { - public class SingleLinkEntityView + public class SingleLinkNode { // Note; the Next member cannot be a property since // it participates in many CAS operations - public SingleLinkEntityView Next; + public SingleLinkNode Next; public T Item; } @@ -23,33 +24,33 @@ namespace Svelto.DataStructures public class LockFreeLinkPool { - private SingleLinkEntityView head; + private SingleLinkNode head; public LockFreeLinkPool() { - head = new SingleLinkEntityView(); + head = new SingleLinkNode(); } - public void Push(SingleLinkEntityView newEntityView) + public void Push(SingleLinkNode newNode) { - newEntityView.Item = default(T); + newNode.Item = default(T); do { - newEntityView.Next = head.Next; - } while (!SyncMethods.CAS>(ref head.Next, newEntityView.Next, newEntityView)); + newNode.Next = head.Next; + } while (!SyncMethods.CAS>(ref head.Next, newNode.Next, newNode)); return; } - public bool Pop(out SingleLinkEntityView entityView) + public bool Pop(out SingleLinkNode node) { do { - entityView = head.Next; - if (entityView == null) + node = head.Next; + if (node == null) { return false; } - } while (!SyncMethods.CAS>(ref head.Next, entityView, entityView.Next)); + } while (!SyncMethods.CAS>(ref head.Next, node, node.Next)); return true; } } @@ -57,35 +58,35 @@ namespace Svelto.DataStructures public class LockFreeQueue { - SingleLinkEntityView head; - SingleLinkEntityView tail; + SingleLinkNode head; + SingleLinkNode tail; LockFreeLinkPool trash; public LockFreeQueue() { - head = new SingleLinkEntityView(); + head = new SingleLinkNode(); tail = head; trash = new LockFreeLinkPool(); } public void Enqueue(T item) { - SingleLinkEntityView oldTail = null; - SingleLinkEntityView oldTailNext; + SingleLinkNode oldTail = null; + SingleLinkNode oldTailNext; - SingleLinkEntityView newEntityView; - if (!trash.Pop(out newEntityView)) + SingleLinkNode newNode; + if (!trash.Pop(out newNode)) { - newEntityView = new SingleLinkEntityView(); + newNode = new SingleLinkNode(); } else { - newEntityView.Next = null; + newNode.Next = null; } - newEntityView.Item = item; + newNode.Item = item; - bool newEntityViewWasAdded = false; - while (!newEntityViewWasAdded) + bool newNodeWasAdded = false; + while (!newNodeWasAdded) { oldTail = tail; oldTailNext = oldTail.Next; @@ -93,26 +94,26 @@ namespace Svelto.DataStructures if (tail == oldTail) { if (oldTailNext == null) - newEntityViewWasAdded = SyncMethods.CAS>(ref tail.Next, null, newEntityView); + newNodeWasAdded = SyncMethods.CAS>(ref tail.Next, null, newNode); else - SyncMethods.CAS>(ref tail, oldTail, oldTailNext); + SyncMethods.CAS>(ref tail, oldTail, oldTailNext); } } - SyncMethods.CAS>(ref tail, oldTail, newEntityView); + SyncMethods.CAS>(ref tail, oldTail, newNode); } public bool Dequeue(out T item) { item = default(T); - SingleLinkEntityView oldHead = null; + SingleLinkNode oldHead = null; bool haveAdvancedHead = false; while (!haveAdvancedHead) { oldHead = head; - SingleLinkEntityView oldTail = tail; - SingleLinkEntityView oldHeadNext = oldHead.Next; + SingleLinkNode oldTail = tail; + SingleLinkNode oldHeadNext = oldHead.Next; if (oldHead == head) { @@ -122,12 +123,12 @@ namespace Svelto.DataStructures { return false; } - SyncMethods.CAS>(ref tail, oldTail, oldHeadNext); + SyncMethods.CAS>(ref tail, oldTail, oldHeadNext); } else { item = oldHeadNext.Item; - haveAdvancedHead = SyncMethods.CAS>(ref head, oldHead, oldHeadNext); + haveAdvancedHead = SyncMethods.CAS>(ref head, oldHead, oldHeadNext); if (haveAdvancedHead) { trash.Push(oldHead); diff --git a/DataStructures/PriorityQueue/HeapPriorityQueue.cs b/DataStructures/PriorityQueue/HeapPriorityQueue.cs index bd8764b..606acd5 100644 --- a/DataStructures/PriorityQueue/HeapPriorityQueue.cs +++ b/DataStructures/PriorityQueue/HeapPriorityQueue.cs @@ -1,5 +1,7 @@ using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Svelto.DataStructures; namespace Svelto.DataStructures { @@ -7,40 +9,40 @@ 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 PriorityQueueEntityView interface + /// The values in the queue. Must implement the PriorityQueueNode interface public sealed class HeapPriorityQueue : IPriorityQueue - where T : PriorityQueueEntityView + where T : PriorityQueueNode { - private int _numEntityViews; - private readonly FasterList _entityViews; - private long _numEntityViewsEverEnqueued; + private int _numNodes; + private readonly FasterList _nodes; + private long _numNodesEverEnqueued; /// /// Instantiate a new Priority Queue /// - /// The max entityViews ever allowed to be enqueued (going over this will cause an exception) + /// The max nodes ever allowed to be enqueued (going over this will cause an exception) public HeapPriorityQueue() { - _numEntityViews = 0; - _entityViews = new FasterList(); - _numEntityViewsEverEnqueued = 0; + _numNodes = 0; + _nodes = new FasterList(); + _numNodesEverEnqueued = 0; } public HeapPriorityQueue(int initialSize) { - _numEntityViews = 0; - _entityViews = new FasterList(initialSize); - _numEntityViewsEverEnqueued = 0; + _numNodes = 0; + _nodes = new FasterList(initialSize); + _numNodesEverEnqueued = 0; } /// - /// Returns the number of entityViews in the queue. O(1) + /// Returns the number of nodes in the queue. O(1) /// public int Count { get { - return _numEntityViews; + return _numNodes; } } @@ -52,119 +54,119 @@ namespace Svelto.DataStructures { get { - return _entityViews.Count - 1; + return _nodes.Count - 1; } } /// - /// Removes every entityView from the queue. O(n) (So, don't do this often!) + /// 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() { - _entityViews.FastClear(); + _nodes.FastClear(); - _numEntityViews = 0; + _numNodes = 0; } /// - /// Returns (in O(1)!) whether the given entityView is in the queue. O(1) + /// 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 entityView) + public bool Contains(T node) { - return (_entityViews[entityView.QueueIndex] == entityView); + return (_nodes[node.QueueIndex] == node); } /// - /// Enqueue a entityView - .Priority must be set beforehand! O(log n) + /// Enqueue a node - .Priority must be set beforehand! O(log n) /// #if NET_VERSION_4_5 [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif - public void Enqueue(T entityView, double priority) + public void Enqueue(T node, double priority) { - entityView.Priority = priority; - _numEntityViews++; - if (_entityViews.Count < _numEntityViews) - _entityViews.Resize(_numEntityViews + 1); - - _entityViews[_numEntityViews] = entityView; - entityView.QueueIndex = _numEntityViews; - entityView.InsertionIndex = _numEntityViewsEverEnqueued++; - CascadeUp(_entityViews[_numEntityViews]); + 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 entityView1, T entityView2) + private void Swap(T node1, T node2) { - //Swap the entityViews - _entityViews[entityView1.QueueIndex] = entityView2; - _entityViews[entityView2.QueueIndex] = entityView1; + //Swap the nodes + _nodes[node1.QueueIndex] = node2; + _nodes[node2.QueueIndex] = node1; //Swap their indicies - int temp = entityView1.QueueIndex; - entityView1.QueueIndex = entityView2.QueueIndex; - entityView2.QueueIndex = temp; + 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 entityView) + private void CascadeUp(T node) { //aka Heapify-up - int parent = entityView.QueueIndex / 2; + int parent = node.QueueIndex / 2; while(parent >= 1) { - T parentEntityView = _entityViews[parent]; - if(HasHigherPriority(parentEntityView, entityView)) + T parentNode = _nodes[parent]; + if(HasHigherPriority(parentNode, node)) break; - //EntityView has lower priority value, so move it up the heap - Swap(entityView, parentEntityView); //For some reason, this is faster with Swap() rather than (less..?) individual operations, like in CascadeDown() + //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 = entityView.QueueIndex / 2; + parent = node.QueueIndex / 2; } } #if NET_VERSION_4_5 [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif - private void CascadeDown(T entityView) + private void CascadeDown(T node) { //aka Heapify-down T newParent; - int finalQueueIndex = entityView.QueueIndex; + int finalQueueIndex = node.QueueIndex; while(true) { - newParent = entityView; + newParent = node; int childLeftIndex = 2 * finalQueueIndex; - //Check if the left-child is higher-priority than the current entityView - if(childLeftIndex > _numEntityViews) + //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 != entityView twice - entityView.QueueIndex = finalQueueIndex; - _entityViews[finalQueueIndex] = entityView; + //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 = _entityViews[childLeftIndex]; + T childLeft = _nodes[childLeftIndex]; if(HasHigherPriority(childLeft, newParent)) { newParent = childLeft; } - //Check if the right-child is higher-priority than either the current entityView or the left child + //Check if the right-child is higher-priority than either the current node or the left child int childRightIndex = childLeftIndex + 1; - if(childRightIndex <= _numEntityViews) + if(childRightIndex <= _numNodes) { - T childRight = _entityViews[childRightIndex]; + T childRight = _nodes[childRightIndex]; if(HasHigherPriority(childRight, newParent)) { newParent = childRight; @@ -172,11 +174,11 @@ namespace Svelto.DataStructures } //If either of the children has higher (smaller) priority, swap and continue cascading - if(newParent != entityView) + if(newParent != node) { - //Move new parent to its new index. entityView will be moved once, at the end + //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() - _entityViews[finalQueueIndex] = newParent; + _nodes[finalQueueIndex] = newParent; int temp = newParent.QueueIndex; newParent.QueueIndex = finalQueueIndex; @@ -185,8 +187,8 @@ namespace Svelto.DataStructures else { //See note above - entityView.QueueIndex = finalQueueIndex; - _entityViews[finalQueueIndex] = entityView; + node.QueueIndex = finalQueueIndex; + _nodes[finalQueueIndex] = node; break; } } @@ -194,7 +196,7 @@ namespace Svelto.DataStructures /// /// Returns true if 'higher' has higher priority than 'lower', false otherwise. - /// Note that calling HasHigherPriority(entityView, entityView) (ie. both arguments the same entityView) will return false + /// Note that calling HasHigherPriority(node, node) (ie. both arguments the same node) will return false /// #if NET_VERSION_4_5 [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -206,11 +208,11 @@ namespace Svelto.DataStructures } /// - /// Removes the head of the queue (entityView with highest priority; ties are broken by order of insertion), and returns it. O(log n) + /// 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 = _entityViews[1]; + T returnMe = _nodes[1]; Remove(returnMe); return returnMe; } @@ -222,77 +224,77 @@ namespace Svelto.DataStructures { get { - return _entityViews[1]; + return _nodes[1]; } } /// - /// This method must be called on a entityView every time its priority changes while it is in the queue. + /// 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 entityView, double priority) + public void UpdatePriority(T node, double priority) { - entityView.Priority = priority; - OnEntityViewUpdated(entityView); + node.Priority = priority; + OnNodeUpdated(node); } - private void OnEntityViewUpdated(T entityView) + private void OnNodeUpdated(T node) { - //Bubble the updated entityView up or down as appropriate - int parentIndex = entityView.QueueIndex / 2; - T parentEntityView = _entityViews[parentIndex]; + //Bubble the updated node up or down as appropriate + int parentIndex = node.QueueIndex / 2; + T parentNode = _nodes[parentIndex]; - if(parentIndex > 0 && HasHigherPriority(entityView, parentEntityView)) + if(parentIndex > 0 && HasHigherPriority(node, parentNode)) { - CascadeUp(entityView); + CascadeUp(node); } else { - //Note that CascadeDown will be called if parentEntityView == entityView (that is, entityView is the root) - CascadeDown(entityView); + //Note that CascadeDown will be called if parentNode == node (that is, node is the root) + CascadeDown(node); } } /// - /// Removes a entityView from the queue. Note that the entityView does not need to be the head of the queue. O(log n) + /// 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 entityView) + public void Remove(T node) { - if(_numEntityViews <= 1) + if(_numNodes <= 1) { - _entityViews[1] = null; - _numEntityViews = 0; + _nodes[1] = null; + _numNodes = 0; return; } - //Make sure the entityView is the last entityView in the queue + //Make sure the node is the last node in the queue bool wasSwapped = false; - T formerLastEntityView = _entityViews[_numEntityViews]; - if(entityView.QueueIndex != _numEntityViews) + T formerLastNode = _nodes[_numNodes]; + if(node.QueueIndex != _numNodes) { - //Swap the entityView with the last entityView - Swap(entityView, formerLastEntityView); + //Swap the node with the last node + Swap(node, formerLastNode); wasSwapped = true; } - _numEntityViews--; - _entityViews[entityView.QueueIndex] = null; + _numNodes--; + _nodes[node.QueueIndex] = null; if(wasSwapped) { - //Now bubble formerLastEntityView (which is no longer the last entityView) up or down as appropriate - OnEntityViewUpdated(formerLastEntityView); + //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 <= _numEntityViews; i++) - yield return _entityViews[i]; + for(int i = 1; i <= _numNodes; i++) + yield return _nodes[i]; } IEnumerator IEnumerable.GetEnumerator() @@ -306,16 +308,16 @@ namespace Svelto.DataStructures /// public bool IsValidQueue() { - for(int i = 1; i < _entityViews.Count; i++) + for(int i = 1; i < _nodes.Count; i++) { - if(_entityViews[i] != null) + if(_nodes[i] != null) { int childLeftIndex = 2 * i; - if(childLeftIndex < _entityViews.Count && _entityViews[childLeftIndex] != null && HasHigherPriority(_entityViews[childLeftIndex], _entityViews[i])) + if(childLeftIndex < _nodes.Count && _nodes[childLeftIndex] != null && HasHigherPriority(_nodes[childLeftIndex], _nodes[i])) return false; int childRightIndex = childLeftIndex + 1; - if(childRightIndex < _entityViews.Count && _entityViews[childRightIndex] != null && HasHigherPriority(_entityViews[childRightIndex], _entityViews[i])) + if(childRightIndex < _nodes.Count && _nodes[childRightIndex] != null && HasHigherPriority(_nodes[childRightIndex], _nodes[i])) return false; } } diff --git a/DataStructures/PriorityQueue/IPriorityQueue.cs b/DataStructures/PriorityQueue/IPriorityQueue.cs index 58997ff..632820e 100644 --- a/DataStructures/PriorityQueue/IPriorityQueue.cs +++ b/DataStructures/PriorityQueue/IPriorityQueue.cs @@ -8,16 +8,16 @@ namespace Svelto.DataStructures /// (theoretically?) optimize method calls from concrete-types slightly better. /// public interface IPriorityQueue : IEnumerable - where T : PriorityQueueEntityView + where T : PriorityQueueNode { - void Remove(T entityView); - void UpdatePriority(T entityView, double priority); - void Enqueue(T entityView, double priority); + 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 entityView); + bool Contains(T node); } } diff --git a/DataStructures/PriorityQueue/PriorityQueueNode.cs b/DataStructures/PriorityQueue/PriorityQueueNode.cs index 48c5491..b09f3a4 100644 --- a/DataStructures/PriorityQueue/PriorityQueueNode.cs +++ b/DataStructures/PriorityQueue/PriorityQueueNode.cs @@ -1,9 +1,9 @@ namespace Svelto.DataStructures { - public class PriorityQueueEntityView + public class PriorityQueueNode { /// - /// The Priority to insert this entityView at. Must be set BEFORE adding a entityView to the queue + /// The Priority to insert this node at. Must be set BEFORE adding a node to the queue /// public double Priority { get; set; @@ -11,7 +11,7 @@ /// /// Used by the priority queue - do not edit this value. - /// Represents the order the entityView was inserted in + /// Represents the order the node was inserted in /// public long InsertionIndex { get; set; } diff --git a/ECS/DataStructures/TypeSafeFasterListForECS.cs b/ECS/DataStructures/TypeSafeFasterListForECS.cs index 6241937..161ecf4 100644 --- a/ECS/DataStructures/TypeSafeFasterListForECS.cs +++ b/ECS/DataStructures/TypeSafeFasterListForECS.cs @@ -10,10 +10,12 @@ namespace Svelto.ECS.Internal void AddRange(ITypeSafeList entityViewListValue); ITypeSafeList Create(); + ITypeSafeList Create(int size); bool isQueryiableEntityView { get; } bool UnorderedRemove(int entityID); ITypeSafeDictionary CreateIndexedDictionary(); IEntityView[] ToArrayFast(out int count); + void ReserveCapacity(int capacity); } class TypeSafeFasterListForECS: FasterList where T:IEntityView @@ -22,6 +24,11 @@ namespace Svelto.ECS.Internal { _mappedIndices = new Dictionary(); } + + protected TypeSafeFasterListForECS(int size):base(size) + { + _mappedIndices = new Dictionary(); + } public bool UnorderedRemove(int entityID) { @@ -47,11 +54,23 @@ namespace Svelto.ECS.Internal _mappedIndices[this[i].ID] = i; } + public void ReserveCapacity(int capacity) + { + if (this.ToArrayFast().Length < capacity) + Resize(capacity); + } + readonly Dictionary _mappedIndices; } class TypeSafeFasterListForECSForStructs : TypeSafeFasterListForECS, ITypeSafeList where T:struct, IEntityStruct { + public TypeSafeFasterListForECSForStructs(int size):base(size) + {} + + public TypeSafeFasterListForECSForStructs() + {} + public ITypeSafeList Create() { return new TypeSafeFasterListForECSForStructs(); @@ -71,10 +90,21 @@ namespace Svelto.ECS.Internal { throw new Exception("Not Allowed"); } + + public ITypeSafeList Create(int size) + { + return new TypeSafeFasterListForECSForStructs(size); + } } class TypeSafeFasterListForECSForClasses : TypeSafeFasterListForECS, ITypeSafeList where T:EntityView, new() { + public TypeSafeFasterListForECSForClasses(int size):base(size) + {} + + public TypeSafeFasterListForECSForClasses() + {} + public ITypeSafeList Create() { return new TypeSafeFasterListForECSForClasses(); @@ -96,5 +126,10 @@ namespace Svelto.ECS.Internal return this.ToArrayFast(); } + + public ITypeSafeList Create(int size) + { + return new TypeSafeFasterListForECSForClasses(size); + } } } diff --git a/ECS/EngineNodeDB.cs b/ECS/EngineEntityViewDB.cs similarity index 100% rename from ECS/EngineNodeDB.cs rename to ECS/EngineEntityViewDB.cs diff --git a/ECS/EnginesRoot.cs b/ECS/EnginesRoot.cs index 882e57b..5443753 100644 --- a/ECS/EnginesRoot.cs +++ b/ECS/EnginesRoot.cs @@ -18,9 +18,9 @@ using Svelto.ECS.Profiler; namespace Svelto.ECS { - public sealed class EnginesRoot : IEntityFunctions, IEntityFactory, IDisposable + public sealed class EnginesRoot : IDisposable { - public EnginesRoot(EntityViewSubmissionScheduler entityViewScheduler) + public EnginesRoot(EntitySubmissionScheduler entityViewScheduler) { _entityViewEngines = new Dictionary>(); _otherEngines = new FasterList(); @@ -66,13 +66,13 @@ namespace Svelto.ECS return new GenericEntityFunctions(new DataStructures.WeakReference(this)); } - public void BuildEntity(int entityID, object[] implementors = null) where T:IEntityDescriptor, new() + void BuildEntity(int entityID, object[] implementors = null) where T:IEntityDescriptor, new() { EntityFactory.BuildEntityViews (entityID, _entityViewsToAdd.current, EntityDescriptorTemplate.Default, implementors); } - public void BuildEntity(int entityID, EntityDescriptorInfo entityDescriptor, object[] implementors = null) + void BuildEntity(int entityID, EntityDescriptorInfo entityDescriptor, object[] implementors = null) { EntityFactory.BuildEntityViews (entityID, _entityViewsToAdd.current, entityDescriptor, implementors); @@ -103,7 +103,7 @@ namespace Svelto.ECS /// /// /// - public void BuildMetaEntity(int metaEntityID, object[] implementors) where T:IEntityDescriptor, new() + void BuildMetaEntity(int metaEntityID, object[] implementors) where T:IEntityDescriptor, new() { EntityFactory.BuildEntityViews(metaEntityID, _entityViewsToAdd.current, EntityDescriptorTemplate.Default, implementors); @@ -119,7 +119,7 @@ namespace Svelto.ECS /// /// /// - public void BuildEntityInGroup(int entityID, int groupID, object[] implementors = null) where T:IEntityDescriptor, new() + void BuildEntityInGroup(int entityID, int groupID, object[] implementors = null) where T:IEntityDescriptor, new() { EntityFactory.BuildGroupedEntityViews(entityID, groupID, _groupedEntityViewsToAdd.current, @@ -127,14 +127,14 @@ namespace Svelto.ECS implementors); } - public void BuildEntityInGroup(int entityID, int groupID, EntityDescriptorInfo entityDescriptor, object[] implementors = null) + void BuildEntityInGroup(int entityID, int groupID, EntityDescriptorInfo entityDescriptor, object[] implementors = null) { EntityFactory.BuildGroupedEntityViews(entityID, groupID, _groupedEntityViewsToAdd.current, entityDescriptor, implementors); } - public void RemoveEntity(int entityID, IRemoveEntityComponent removeInfo) + void RemoveEntity(int entityID, IRemoveEntityComponent removeInfo) { var removeEntityImplementor = removeInfo as RemoveEntityImplementor; @@ -144,21 +144,44 @@ namespace Svelto.ECS InternalRemove(removeEntityImplementor.removeEntityInfo.descriptor.entityViewsToBuild, entityID, _entityViewsDB); } - public void RemoveEntity(int entityID) where T : IEntityDescriptor, new() + void RemoveEntity(int entityID) where T : IEntityDescriptor, new() { InternalRemove(EntityDescriptorTemplate.Default.descriptor.entityViewsToBuild, entityID, _entityViewsDB); } - public void RemoveMetaEntity(int metaEntityID) where T : IEntityDescriptor, new() + void RemoveMetaEntity(int metaEntityID) where T : IEntityDescriptor, new() { InternalRemove(EntityDescriptorTemplate.Default.descriptor.entityViewsToBuild, metaEntityID, _metaEntityViewsDB); } - public void RemoveEntityFromGroup(int entityID, int groupID) where T : IEntityDescriptor, new() + void RemoveEntityFromGroup(int entityID, int groupID) where T : IEntityDescriptor, new() { InternalRemove(EntityDescriptorTemplate.Default.descriptor.entityViewsToBuild, entityID, _groupEntityViewsDB[groupID]); } - + + void Preallocate(int size) where T : IEntityDescriptor, new() + { + var entityViewsToBuild = EntityDescriptorTemplate.Default.descriptor.entityViewsToBuild; + int count = entityViewsToBuild.Length; + + for (int index = 0; index < count; index++) + { + var entityViewBuilder = entityViewsToBuild[index]; + var entityViewType = entityViewBuilder.GetEntityViewType(); + + ITypeSafeList dbList; + if (_entityViewsDB.TryGetValue(entityViewType, out dbList) == false) + _entityViewsDB[entityViewType] = entityViewBuilder.Preallocate(ref dbList, size); + else + dbList.ReserveCapacity(size); + + if (_entityViewsToAdd.current.TryGetValue(entityViewType, out dbList) == false) + _entityViewsToAdd.current[entityViewType] = entityViewBuilder.Preallocate(ref dbList, size); + else + dbList.ReserveCapacity(size); + } + } + public void AddEngine(IEngine engine) { #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR @@ -323,7 +346,7 @@ namespace Svelto.ECS groupedEntityViewsByType.Add(entityView.Key, entityView.Value); } } - + static void AddEntityViewToDB(Dictionary entityViewsDB, KeyValuePair entityViewList) { ITypeSafeList dbList; @@ -508,7 +531,7 @@ namespace Svelto.ECS readonly DoubleBufferedEntityViews> _metaEntityViewsToAdd; readonly DoubleBufferedEntityViews>> _groupedEntityViewsToAdd; - readonly EntityViewSubmissionScheduler _scheduler; + readonly EntitySubmissionScheduler _scheduler; #if EXPERIMENTAL readonly Type _structEntityViewEngineType; readonly Type _groupedStructEntityViewsEngineType; @@ -580,6 +603,11 @@ namespace Svelto.ECS { _weakEngine.Target.BuildEntityInGroup(entityID, groupID, entityDescriptor, implementors); } + + public void Preallocate(int size) where T : IEntityDescriptor, new() + { + _weakEngine.Target.Preallocate(size); + } } class GenericEntityFunctions : IEntityFunctions diff --git a/ECS/EntityDescriptorTemplate.cs b/ECS/EntityDescriptorTemplate.cs index cceb87d..3c20589 100644 --- a/ECS/EntityDescriptorTemplate.cs +++ b/ECS/EntityDescriptorTemplate.cs @@ -101,17 +101,17 @@ namespace Svelto.ECS.Internal } } - static IEntityView BuildEntityView(int entityID, Dictionary groupedEntityViewsTyped, + static IEntityView BuildEntityView(int entityID, Dictionary entityViewsByType, Type entityViewType, IEntityViewBuilder entityViewBuilderId) { - ITypeSafeList entityViews; + ITypeSafeList entityViewsList; var entityViewsPoolWillBeCreated = - groupedEntityViewsTyped.TryGetValue(entityViewType, out entityViews) == false; - var entityViewObjectToFill = entityViewBuilderId.BuildEntityViewAndAddToList(ref entityViews, entityID); + entityViewsByType.TryGetValue(entityViewType, out entityViewsList) == false; + var entityViewObjectToFill = entityViewBuilderId.BuildEntityViewAndAddToList(ref entityViewsList, entityID); if (entityViewsPoolWillBeCreated) - groupedEntityViewsTyped.Add(entityViewType, entityViews); + entityViewsByType.Add(entityViewType, entityViewsList); return entityViewObjectToFill as IEntityView; } @@ -163,13 +163,13 @@ namespace Svelto.ECS.Internal var keyValuePair = setters[i]; Type fieldType = keyValuePair.Key; - if (fieldType == removeEntityComponentType) - { - keyValuePair.Value(entityView, removeEntity); - } - else + if (fieldType != removeEntityComponentType) { +#if DEBUG && !PROFILER Tuple component; +#else + object component; +#endif if (implementorsByType.TryGetValue(fieldType, out component) == false) { @@ -178,21 +178,28 @@ namespace Svelto.ECS.Internal throw e; } - +#if DEBUG && !PROFILER if (component.item2 > 1) Utility.Console.LogError(DUPLICATE_IMPLEMENTOR_ERROR.FastConcat( "Component Type: ", fieldType.Name, " implementor: ", component.item1.ToString()) + " - EntityView: " + entityView.GetType().Name + " - EntityDescriptor " + entityDescriptorName); - +#endif +#if DEBUG && !PROFILER keyValuePair.Value(entityView, component.item1); +#else + keyValuePair.Value(entityView, component); +#endif + } + else + { + keyValuePair.Value(entityView, removeEntity); } - } implementorsByType.Clear(); } - +#if DEBUG && !PROFILER struct Tuple { public T1 item1; @@ -204,9 +211,13 @@ namespace Svelto.ECS.Internal item2 = v; } } - +#endif //this is used to avoid newing a dictionary every time, but it's used locally only +#if DEBUG && !PROFILER static readonly Dictionary> implementorsByType = new Dictionary>(); +#else + static readonly Dictionary implementorsByType = new Dictionary(); +#endif static Dictionary _cachedTypes = new Dictionary(); const string DUPLICATE_IMPLEMENTOR_ERROR = diff --git a/ECS/NodeSubmissionScheduler.cs b/ECS/EntitySubmissionScheduler.cs similarity index 71% rename from ECS/NodeSubmissionScheduler.cs rename to ECS/EntitySubmissionScheduler.cs index bc62758..cfa8168 100644 --- a/ECS/NodeSubmissionScheduler.cs +++ b/ECS/EntitySubmissionScheduler.cs @@ -2,7 +2,7 @@ using Svelto.WeakEvents; namespace Svelto.ECS.Schedulers { - public abstract class EntityViewSubmissionScheduler + public abstract class EntitySubmissionScheduler { abstract public void Schedule(WeakAction submitEntityViews); } diff --git a/ECS/INode.cs b/ECS/EntityView.cs similarity index 100% rename from ECS/INode.cs rename to ECS/EntityView.cs diff --git a/ECS/NodeBuilder.cs b/ECS/EntityViewBuilder.cs similarity index 73% rename from ECS/NodeBuilder.cs rename to ECS/EntityViewBuilder.cs index 7b4db4b..48ce2dd 100644 --- a/ECS/NodeBuilder.cs +++ b/ECS/EntityViewBuilder.cs @@ -6,6 +6,7 @@ namespace Svelto.ECS public interface IEntityViewBuilder { IEntityView BuildEntityViewAndAddToList(ref ITypeSafeList list, int entityID); + ITypeSafeList Preallocate(ref ITypeSafeList list, int size); Type GetEntityViewType(); } @@ -26,6 +27,16 @@ namespace Svelto.ECS return entityView; } + public ITypeSafeList Preallocate(ref ITypeSafeList list, int size) + { + if (list == null) + list = new TypeSafeFasterListForECSForClasses(size); + else + list.ReserveCapacity(size); + + return list; + } + public Type GetEntityViewType() { return _entityViewType; @@ -51,6 +62,16 @@ namespace Svelto.ECS return null; } + public ITypeSafeList Preallocate(ref ITypeSafeList list, int size) + { + if (list == null) + list = new TypeSafeFasterListForECSForStructs(size); + else + list.ReserveCapacity(size); + + return list; + } + public Type GetEntityViewType() { return _entityViewType; diff --git a/ECS/IEngineNodeDB.cs b/ECS/IEngineEntityViewDB.cs similarity index 100% rename from ECS/IEngineNodeDB.cs rename to ECS/IEngineEntityViewDB.cs diff --git a/ECS/IEnginesInterfaces.cs b/ECS/IEnginesInterfaces.cs index 05ed50c..6c8945c 100644 --- a/ECS/IEnginesInterfaces.cs +++ b/ECS/IEnginesInterfaces.cs @@ -2,6 +2,8 @@ namespace Svelto.ECS { public interface IEntityFactory { + void Preallocate(int size) where T : IEntityDescriptor, new(); + void BuildEntity(int entityID, object[] implementors = null) where T:IEntityDescriptor, new(); void BuildEntity(int entityID, EntityDescriptorInfo entityDescriptor, object[] implementors = null); @@ -20,12 +22,5 @@ namespace Svelto.ECS void RemoveMetaEntity(int metaEntityID) where T:IEntityDescriptor, new(); void RemoveEntityFromGroup(int entityID, int groupID) where T:IEntityDescriptor, new(); -#if EXPERIMENTAL - void SetEntityActiveState(int entityID, bool state) where T:IEntityDescriptor, new(); - - void SetMetaEntityActiveState(int metaEntityID, bool state) where T:IEntityDescriptor, new(); - - void SetEntityInGroupActiveState(int entityID, int group, bool state) where T:IEntityDescriptor, new(); -#endif } } diff --git a/ECS/MultiNodesEngine.cs b/ECS/MultiEntityViewsEngine.cs similarity index 100% rename from ECS/MultiNodesEngine.cs rename to ECS/MultiEntityViewsEngine.cs diff --git a/ECS/SingleNodeEngine.cs b/ECS/SingleEntityViewEngine.cs similarity index 100% rename from ECS/SingleNodeEngine.cs rename to ECS/SingleEntityViewEngine.cs diff --git a/Utilities/FastInvoke.cs b/Utilities/FastInvoke.cs index 48e72f5..c749c1d 100644 --- a/Utilities/FastInvoke.cs +++ b/Utilities/FastInvoke.cs @@ -5,20 +5,26 @@ using System.Reflection.Emit; namespace Svelto.Utilities { //https://stackoverflow.com/questions/321650/how-do-i-set-a-field-value-in-an-c-sharp-expression-tree/321686#321686 + public static class FastInvoke where T : class { public static Action MakeSetter(FieldInfo field) { - DynamicMethod m = new DynamicMethod("setter", typeof(void), new Type[] {typeof(T), typeof(object)}); - ILGenerator cg = m.GetILGenerator(); + if (field.FieldType.IsInterface == true && field.FieldType.IsValueType == false) + { + DynamicMethod m = new DynamicMethod("setter", typeof(void), new Type[] { typeof(T), typeof(object) }); + ILGenerator cg = m.GetILGenerator(); + + // arg0. = arg1 + cg.Emit(OpCodes.Ldarg_0); + cg.Emit(OpCodes.Ldarg_1); + cg.Emit(OpCodes.Stfld, field); + cg.Emit(OpCodes.Ret); - // arg0. = arg1 - cg.Emit(OpCodes.Ldarg_0); - cg.Emit(OpCodes.Ldarg_1); - cg.Emit(OpCodes.Stfld, field); - cg.Emit(OpCodes.Ret); + return (Action)m.CreateDelegate(typeof(Action)); + } - return (Action) m.CreateDelegate(typeof(Action));; + throw new ArgumentException("Svelto.ECS unsupported EntityView field (must be an interface and a class)"); } } } \ No newline at end of file