@@ -788,7 +788,7 @@ namespace Svelto.DataStructures | |||
_buffer = newList; | |||
} | |||
public void Trim() | |||
public void Trim() | |||
{ | |||
if (_count < _buffer.Length) | |||
Resize(_count); | |||
@@ -1,13 +1,14 @@ | |||
using System.Collections.Generic; | |||
using System.Threading; | |||
//from unify wiki | |||
namespace Svelto.DataStructures | |||
{ | |||
public class SingleLinkEntityView<T> | |||
public class SingleLinkNode<T> | |||
{ | |||
// Note; the Next member cannot be a property since | |||
// it participates in many CAS operations | |||
public SingleLinkEntityView<T> Next; | |||
public SingleLinkNode<T> Next; | |||
public T Item; | |||
} | |||
@@ -23,33 +24,33 @@ namespace Svelto.DataStructures | |||
public class LockFreeLinkPool<T> | |||
{ | |||
private SingleLinkEntityView<T> head; | |||
private SingleLinkNode<T> head; | |||
public LockFreeLinkPool() | |||
{ | |||
head = new SingleLinkEntityView<T>(); | |||
head = new SingleLinkNode<T>(); | |||
} | |||
public void Push(SingleLinkEntityView<T> newEntityView) | |||
public void Push(SingleLinkNode<T> newNode) | |||
{ | |||
newEntityView.Item = default(T); | |||
newNode.Item = default(T); | |||
do | |||
{ | |||
newEntityView.Next = head.Next; | |||
} while (!SyncMethods.CAS<SingleLinkEntityView<T>>(ref head.Next, newEntityView.Next, newEntityView)); | |||
newNode.Next = head.Next; | |||
} while (!SyncMethods.CAS<SingleLinkNode<T>>(ref head.Next, newNode.Next, newNode)); | |||
return; | |||
} | |||
public bool Pop(out SingleLinkEntityView<T> entityView) | |||
public bool Pop(out SingleLinkNode<T> node) | |||
{ | |||
do | |||
{ | |||
entityView = head.Next; | |||
if (entityView == null) | |||
node = head.Next; | |||
if (node == null) | |||
{ | |||
return false; | |||
} | |||
} while (!SyncMethods.CAS<SingleLinkEntityView<T>>(ref head.Next, entityView, entityView.Next)); | |||
} while (!SyncMethods.CAS<SingleLinkNode<T>>(ref head.Next, node, node.Next)); | |||
return true; | |||
} | |||
} | |||
@@ -57,35 +58,35 @@ namespace Svelto.DataStructures | |||
public class LockFreeQueue<T> | |||
{ | |||
SingleLinkEntityView<T> head; | |||
SingleLinkEntityView<T> tail; | |||
SingleLinkNode<T> head; | |||
SingleLinkNode<T> tail; | |||
LockFreeLinkPool<T> trash; | |||
public LockFreeQueue() | |||
{ | |||
head = new SingleLinkEntityView<T>(); | |||
head = new SingleLinkNode<T>(); | |||
tail = head; | |||
trash = new LockFreeLinkPool<T>(); | |||
} | |||
public void Enqueue(T item) | |||
{ | |||
SingleLinkEntityView<T> oldTail = null; | |||
SingleLinkEntityView<T> oldTailNext; | |||
SingleLinkNode<T> oldTail = null; | |||
SingleLinkNode<T> oldTailNext; | |||
SingleLinkEntityView<T> newEntityView; | |||
if (!trash.Pop(out newEntityView)) | |||
SingleLinkNode<T> newNode; | |||
if (!trash.Pop(out newNode)) | |||
{ | |||
newEntityView = new SingleLinkEntityView<T>(); | |||
newNode = new SingleLinkNode<T>(); | |||
} | |||
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<SingleLinkEntityView<T>>(ref tail.Next, null, newEntityView); | |||
newNodeWasAdded = SyncMethods.CAS<SingleLinkNode<T>>(ref tail.Next, null, newNode); | |||
else | |||
SyncMethods.CAS<SingleLinkEntityView<T>>(ref tail, oldTail, oldTailNext); | |||
SyncMethods.CAS<SingleLinkNode<T>>(ref tail, oldTail, oldTailNext); | |||
} | |||
} | |||
SyncMethods.CAS<SingleLinkEntityView<T>>(ref tail, oldTail, newEntityView); | |||
SyncMethods.CAS<SingleLinkNode<T>>(ref tail, oldTail, newNode); | |||
} | |||
public bool Dequeue(out T item) | |||
{ | |||
item = default(T); | |||
SingleLinkEntityView<T> oldHead = null; | |||
SingleLinkNode<T> oldHead = null; | |||
bool haveAdvancedHead = false; | |||
while (!haveAdvancedHead) | |||
{ | |||
oldHead = head; | |||
SingleLinkEntityView<T> oldTail = tail; | |||
SingleLinkEntityView<T> oldHeadNext = oldHead.Next; | |||
SingleLinkNode<T> oldTail = tail; | |||
SingleLinkNode<T> oldHeadNext = oldHead.Next; | |||
if (oldHead == head) | |||
{ | |||
@@ -122,12 +123,12 @@ namespace Svelto.DataStructures | |||
{ | |||
return false; | |||
} | |||
SyncMethods.CAS<SingleLinkEntityView<T>>(ref tail, oldTail, oldHeadNext); | |||
SyncMethods.CAS<SingleLinkNode<T>>(ref tail, oldTail, oldHeadNext); | |||
} | |||
else | |||
{ | |||
item = oldHeadNext.Item; | |||
haveAdvancedHead = SyncMethods.CAS<SingleLinkEntityView<T>>(ref head, oldHead, oldHeadNext); | |||
haveAdvancedHead = SyncMethods.CAS<SingleLinkNode<T>>(ref head, oldHead, oldHeadNext); | |||
if (haveAdvancedHead) | |||
{ | |||
trash.Push(oldHead); | |||
@@ -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 | |||
/// </summary> | |||
/// <typeparam name="T">The values in the queue. Must implement the PriorityQueueEntityView interface</typeparam> | |||
/// <typeparam name="T">The values in the queue. Must implement the PriorityQueueNode interface</typeparam> | |||
public sealed class HeapPriorityQueue<T> : IPriorityQueue<T> | |||
where T : PriorityQueueEntityView | |||
where T : PriorityQueueNode | |||
{ | |||
private int _numEntityViews; | |||
private readonly FasterList<T> _entityViews; | |||
private long _numEntityViewsEverEnqueued; | |||
private int _numNodes; | |||
private readonly FasterList<T> _nodes; | |||
private long _numNodesEverEnqueued; | |||
/// <summary> | |||
/// Instantiate a new Priority Queue | |||
/// </summary> | |||
/// <param name="maxEntityViews">The max entityViews ever allowed to be enqueued (going over this will cause an exception)</param> | |||
/// <param name="maxNodes">The max nodes ever allowed to be enqueued (going over this will cause an exception)</param> | |||
public HeapPriorityQueue() | |||
{ | |||
_numEntityViews = 0; | |||
_entityViews = new FasterList<T>(); | |||
_numEntityViewsEverEnqueued = 0; | |||
_numNodes = 0; | |||
_nodes = new FasterList<T>(); | |||
_numNodesEverEnqueued = 0; | |||
} | |||
public HeapPriorityQueue(int initialSize) | |||
{ | |||
_numEntityViews = 0; | |||
_entityViews = new FasterList<T>(initialSize); | |||
_numEntityViewsEverEnqueued = 0; | |||
_numNodes = 0; | |||
_nodes = new FasterList<T>(initialSize); | |||
_numNodesEverEnqueued = 0; | |||
} | |||
/// <summary> | |||
/// Returns the number of entityViews in the queue. O(1) | |||
/// Returns the number of nodes in the queue. O(1) | |||
/// </summary> | |||
public int Count | |||
{ | |||
get | |||
{ | |||
return _numEntityViews; | |||
return _numNodes; | |||
} | |||
} | |||
@@ -52,119 +54,119 @@ namespace Svelto.DataStructures | |||
{ | |||
get | |||
{ | |||
return _entityViews.Count - 1; | |||
return _nodes.Count - 1; | |||
} | |||
} | |||
/// <summary> | |||
/// 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!) | |||
/// </summary> | |||
#if NET_VERSION_4_5 | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
#endif | |||
public void Clear() | |||
{ | |||
_entityViews.FastClear(); | |||
_nodes.FastClear(); | |||
_numEntityViews = 0; | |||
_numNodes = 0; | |||
} | |||
/// <summary> | |||
/// 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) | |||
/// </summary> | |||
#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); | |||
} | |||
/// <summary> | |||
/// Enqueue a entityView - .Priority must be set beforehand! O(log n) | |||
/// Enqueue a node - .Priority must be set beforehand! O(log n) | |||
/// </summary> | |||
#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 | |||
/// <summary> | |||
/// 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 | |||
/// </summary> | |||
#if NET_VERSION_4_5 | |||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||
@@ -206,11 +208,11 @@ namespace Svelto.DataStructures | |||
} | |||
/// <summary> | |||
/// 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) | |||
/// </summary> | |||
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]; | |||
} | |||
} | |||
/// <summary> | |||
/// 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. | |||
/// <b>Forgetting to call this method will result in a corrupted queue!</b> | |||
/// O(log n) | |||
/// </summary> | |||
#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); | |||
} | |||
} | |||
/// <summary> | |||
/// 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) | |||
/// </summary> | |||
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<T> 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 | |||
/// </summary> | |||
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; | |||
} | |||
} | |||
@@ -8,16 +8,16 @@ namespace Svelto.DataStructures | |||
/// (theoretically?) optimize method calls from concrete-types slightly better. | |||
/// </summary> | |||
public interface IPriorityQueue<T> : IEnumerable<T> | |||
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); | |||
} | |||
} |
@@ -1,9 +1,9 @@ | |||
namespace Svelto.DataStructures | |||
{ | |||
public class PriorityQueueEntityView | |||
public class PriorityQueueNode | |||
{ | |||
/// <summary> | |||
/// 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 | |||
/// </summary> | |||
public double Priority { get; | |||
set; | |||
@@ -11,7 +11,7 @@ | |||
/// <summary> | |||
/// <b>Used by the priority queue - do not edit this value.</b> | |||
/// Represents the order the entityView was inserted in | |||
/// Represents the order the node was inserted in | |||
/// </summary> | |||
public long InsertionIndex { get; set; } | |||
@@ -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<T>: FasterList<T> where T:IEntityView | |||
@@ -22,6 +24,11 @@ namespace Svelto.ECS.Internal | |||
{ | |||
_mappedIndices = new Dictionary<int, int>(); | |||
} | |||
protected TypeSafeFasterListForECS(int size):base(size) | |||
{ | |||
_mappedIndices = new Dictionary<int, int>(); | |||
} | |||
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<int, int> _mappedIndices; | |||
} | |||
class TypeSafeFasterListForECSForStructs<T> : TypeSafeFasterListForECS<T>, ITypeSafeList where T:struct, IEntityStruct | |||
{ | |||
public TypeSafeFasterListForECSForStructs(int size):base(size) | |||
{} | |||
public TypeSafeFasterListForECSForStructs() | |||
{} | |||
public ITypeSafeList Create() | |||
{ | |||
return new TypeSafeFasterListForECSForStructs<T>(); | |||
@@ -71,10 +90,21 @@ namespace Svelto.ECS.Internal | |||
{ | |||
throw new Exception("Not Allowed"); | |||
} | |||
public ITypeSafeList Create(int size) | |||
{ | |||
return new TypeSafeFasterListForECSForStructs<T>(size); | |||
} | |||
} | |||
class TypeSafeFasterListForECSForClasses<T> : TypeSafeFasterListForECS<T>, ITypeSafeList where T:EntityView, new() | |||
{ | |||
public TypeSafeFasterListForECSForClasses(int size):base(size) | |||
{} | |||
public TypeSafeFasterListForECSForClasses() | |||
{} | |||
public ITypeSafeList Create() | |||
{ | |||
return new TypeSafeFasterListForECSForClasses<T>(); | |||
@@ -96,5 +126,10 @@ namespace Svelto.ECS.Internal | |||
return this.ToArrayFast(); | |||
} | |||
public ITypeSafeList Create(int size) | |||
{ | |||
return new TypeSafeFasterListForECSForClasses<T>(size); | |||
} | |||
} | |||
} |
@@ -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<Type, FasterList<IHandleEntityViewEngine>>(); | |||
_otherEngines = new FasterList<IEngine>(); | |||
@@ -66,13 +66,13 @@ namespace Svelto.ECS | |||
return new GenericEntityFunctions(new DataStructures.WeakReference<EnginesRoot>(this)); | |||
} | |||
public void BuildEntity<T>(int entityID, object[] implementors = null) where T:IEntityDescriptor, new() | |||
void BuildEntity<T>(int entityID, object[] implementors = null) where T:IEntityDescriptor, new() | |||
{ | |||
EntityFactory.BuildEntityViews | |||
(entityID, _entityViewsToAdd.current, EntityDescriptorTemplate<T>.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 | |||
/// <param name="metaEntityID"></param> | |||
/// <param name="ed"></param> | |||
/// <param name="implementors"></param> | |||
public void BuildMetaEntity<T>(int metaEntityID, object[] implementors) where T:IEntityDescriptor, new() | |||
void BuildMetaEntity<T>(int metaEntityID, object[] implementors) where T:IEntityDescriptor, new() | |||
{ | |||
EntityFactory.BuildEntityViews(metaEntityID, _entityViewsToAdd.current, | |||
EntityDescriptorTemplate<T>.Default, implementors); | |||
@@ -119,7 +119,7 @@ namespace Svelto.ECS | |||
/// <param name="groupID"></param> | |||
/// <param name="ed"></param> | |||
/// <param name="implementors"></param> | |||
public void BuildEntityInGroup<T>(int entityID, int groupID, object[] implementors = null) where T:IEntityDescriptor, new() | |||
void BuildEntityInGroup<T>(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<T>(int entityID) where T : IEntityDescriptor, new() | |||
void RemoveEntity<T>(int entityID) where T : IEntityDescriptor, new() | |||
{ | |||
InternalRemove(EntityDescriptorTemplate<T>.Default.descriptor.entityViewsToBuild, entityID, _entityViewsDB); | |||
} | |||
public void RemoveMetaEntity<T>(int metaEntityID) where T : IEntityDescriptor, new() | |||
void RemoveMetaEntity<T>(int metaEntityID) where T : IEntityDescriptor, new() | |||
{ | |||
InternalRemove(EntityDescriptorTemplate<T>.Default.descriptor.entityViewsToBuild, metaEntityID, _metaEntityViewsDB); | |||
} | |||
public void RemoveEntityFromGroup<T>(int entityID, int groupID) where T : IEntityDescriptor, new() | |||
void RemoveEntityFromGroup<T>(int entityID, int groupID) where T : IEntityDescriptor, new() | |||
{ | |||
InternalRemove(EntityDescriptorTemplate<T>.Default.descriptor.entityViewsToBuild, entityID, _groupEntityViewsDB[groupID]); | |||
} | |||
void Preallocate<T>(int size) where T : IEntityDescriptor, new() | |||
{ | |||
var entityViewsToBuild = EntityDescriptorTemplate<T>.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<Type, ITypeSafeList> entityViewsDB, KeyValuePair<Type, ITypeSafeList> entityViewList) | |||
{ | |||
ITypeSafeList dbList; | |||
@@ -508,7 +531,7 @@ namespace Svelto.ECS | |||
readonly DoubleBufferedEntityViews<Dictionary<Type, ITypeSafeList>> _metaEntityViewsToAdd; | |||
readonly DoubleBufferedEntityViews<Dictionary<int, Dictionary<Type, ITypeSafeList>>> _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<T>(int size) where T : IEntityDescriptor, new() | |||
{ | |||
_weakEngine.Target.Preallocate<T>(size); | |||
} | |||
} | |||
class GenericEntityFunctions : IEntityFunctions | |||
@@ -101,17 +101,17 @@ namespace Svelto.ECS.Internal | |||
} | |||
} | |||
static IEntityView BuildEntityView(int entityID, Dictionary<Type, ITypeSafeList> groupedEntityViewsTyped, | |||
static IEntityView BuildEntityView(int entityID, Dictionary<Type, ITypeSafeList> 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<object, int> 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<T1, T2> | |||
{ | |||
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<Type, Tuple<object, int>> implementorsByType = new Dictionary<Type, Tuple<object, int>>(); | |||
#else | |||
static readonly Dictionary<Type, object> implementorsByType = new Dictionary<Type, object>(); | |||
#endif | |||
static Dictionary<Type, Type[]> _cachedTypes = new Dictionary<Type, Type[]>(); | |||
const string DUPLICATE_IMPLEMENTOR_ERROR = | |||
@@ -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); | |||
} |
@@ -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<EntityViewType>(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<EntityViewType>(size); | |||
else | |||
list.ReserveCapacity(size); | |||
return list; | |||
} | |||
public Type GetEntityViewType() | |||
{ | |||
return _entityViewType; |
@@ -2,6 +2,8 @@ namespace Svelto.ECS | |||
{ | |||
public interface IEntityFactory | |||
{ | |||
void Preallocate<T>(int size) where T : IEntityDescriptor, new(); | |||
void BuildEntity<T>(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<T>(int metaEntityID) where T:IEntityDescriptor, new(); | |||
void RemoveEntityFromGroup<T>(int entityID, int groupID) where T:IEntityDescriptor, new(); | |||
#if EXPERIMENTAL | |||
void SetEntityActiveState<T>(int entityID, bool state) where T:IEntityDescriptor, new(); | |||
void SetMetaEntityActiveState<T>(int metaEntityID, bool state) where T:IEntityDescriptor, new(); | |||
void SetEntityInGroupActiveState<T>(int entityID, int group, bool state) where T:IEntityDescriptor, new(); | |||
#endif | |||
} | |||
} |
@@ -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<T> where T : class | |||
{ | |||
public static Action<T, object> 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.<field> = arg1 | |||
cg.Emit(OpCodes.Ldarg_0); | |||
cg.Emit(OpCodes.Ldarg_1); | |||
cg.Emit(OpCodes.Stfld, field); | |||
cg.Emit(OpCodes.Ret); | |||
// arg0.<field> = arg1 | |||
cg.Emit(OpCodes.Ldarg_0); | |||
cg.Emit(OpCodes.Ldarg_1); | |||
cg.Emit(OpCodes.Stfld, field); | |||
cg.Emit(OpCodes.Ret); | |||
return (Action<T, object>)m.CreateDelegate(typeof(Action<T, object>)); | |||
} | |||
return (Action<T, object>) m.CreateDelegate(typeof(Action<T, object>));; | |||
throw new ArgumentException("<color=orange>Svelto.ECS</color> unsupported EntityView field (must be an interface and a class)"); | |||
} | |||
} | |||
} |