diff --git a/Svelto.ECS/EGIDMapper.cs b/Svelto.ECS/EGIDMapper.cs index cec4ee9..f9057fd 100644 --- a/Svelto.ECS/EGIDMapper.cs +++ b/Svelto.ECS/EGIDMapper.cs @@ -6,9 +6,11 @@ namespace Svelto.ECS { internal TypeSafeDictionary map; - public uint this[EGID index] + public T[] entities(EGID id, out uint index) { - get { return map.FindElementIndex(index.entityID); } + int count; + index = map.FindElementIndex(id.entityID); + return map.GetFasterValuesBuffer(out count); } } } \ No newline at end of file diff --git a/Svelto.ECS/EntitiesDB.cs b/Svelto.ECS/EntitiesDB.cs index c584eaf..175e84a 100644 --- a/Svelto.ECS/EntitiesDB.cs +++ b/Svelto.ECS/EntitiesDB.cs @@ -42,17 +42,25 @@ namespace Svelto.ECS.Internal return typeSafeDictionary.GetFasterValuesBuffer(out count); } - public T[] QueryEntities(int groupID, ref EGIDMapper mapper) where T : IEntityStruct + public EGIDMapper QueryMappedEntities() where T : IEntityStruct + { + return QueryMappedEntities(ExclusiveGroup.StandardEntitiesGroup); + } + + public EGIDMapper QueryMappedEntities(int groupID) where T : IEntityStruct { TypeSafeDictionary typeSafeDictionary; if (QueryEntitySafeDictionary(groupID, out typeSafeDictionary) == false) throw new EntitiesDBException("Entity group not found type: ".FastConcat(typeof(T)).FastConcat(" groupID: ").FastConcat(groupID)); - + + EGIDMapper mapper; mapper.map = typeSafeDictionary; int count; - return typeSafeDictionary.GetFasterValuesBuffer(out count); + typeSafeDictionary.GetFasterValuesBuffer(out count); + + return mapper; } public T[] QueryEntitiesAndIndex(EGID entityGID, out uint index) where T : IEntityStruct @@ -130,11 +138,22 @@ namespace Svelto.ECS.Internal public void ExecuteOnEntities(int groupID, ActionRef action) where T : IEntityStruct { - int count; - var entities = QueryEntities(groupID, out count); - - for (int i = 0; i < count; i++) + int count; + TypeSafeDictionary typeSafeDictionary; + if (QueryEntitySafeDictionary(@groupID, out typeSafeDictionary) == false) return; + + var entities = typeSafeDictionary.GetFasterValuesBuffer(out count); + + for (var i = 0; i < count; i++) action(ref entities[i]); + + SafetyChecks(typeSafeDictionary, count); + } + + static void SafetyChecks(TypeSafeDictionary typeSafeDictionary, int count) where T : IEntityStruct + { + if (typeSafeDictionary.Count != count) + throw new EntitiesDBException("Entities cannot be swapped or removed during an iteration"); } public void ExecuteOnEntities(ActionRef action) where T : IEntityStruct @@ -144,17 +163,27 @@ namespace Svelto.ECS.Internal public void ExecuteOnEntities(int groupID, ref W value, ActionRef action) where T : IEntityStruct { - int count; - var entities = QueryEntities(groupID, out count); - - for (int i = 0; i < count; i++) + int count; + TypeSafeDictionary typeSafeDictionary; + if (QueryEntitySafeDictionary(@groupID, out typeSafeDictionary) == false) return; + + var entities = typeSafeDictionary.GetFasterValuesBuffer(out count); + + for (var i = 0; i < count; i++) action(ref entities[i], ref value); + + SafetyChecks(typeSafeDictionary, count); } public void ExecuteOnEntities(ref W value, ActionRef action) where T : IEntityStruct { ExecuteOnEntities(ExclusiveGroup.StandardEntitiesGroup, ref value, action); } + + public void ExecuteOnEntities(W value, ActionRef action) where T : IEntityStruct where T1 : IEntityStruct + { + ExecuteOnEntities(ExclusiveGroup.StandardEntitiesGroup, ref value, action); + } public void ExecuteOnAllEntities(ActionRef action) where T : IEntityStruct { @@ -167,11 +196,15 @@ namespace Svelto.ECS.Internal for (int j = 0; j < count; j++) { int innerCount; - var safedic = typeSafeDictionaries[j]; - var casted = safedic as TypeSafeDictionary; + var typeSafeDictionary = typeSafeDictionaries[j]; + var casted = typeSafeDictionary as TypeSafeDictionary; + var entities = casted.GetFasterValuesBuffer(out innerCount); + for (int i = 0; i < innerCount; i++) action(ref entities[i]); + + SafetyChecks(casted, count); } } } @@ -187,11 +220,15 @@ namespace Svelto.ECS.Internal for (int j = 0; j < count; j++) { int innerCount; - var safedic = typeSafeDictionaries[j]; - var casted = safedic as TypeSafeDictionary; + var typeSafeDictionary = typeSafeDictionaries[j]; + var casted = typeSafeDictionary as TypeSafeDictionary; + var entities = casted.GetFasterValuesBuffer(out innerCount); + for (int i = 0; i < innerCount; i++) action(ref entities[i], ref value); + + SafetyChecks(casted, count); } } } @@ -203,20 +240,46 @@ namespace Svelto.ECS.Internal if (QueryEntitySafeDictionary(@group, out typeSafeDictionary) == false) return; var entities = typeSafeDictionary.GetFasterValuesBuffer(out count); + + EGIDMapper map = QueryMappedEntities(group); + + for (var i = 0; i < count; i++) + { + uint index; + action(ref entities[i], ref map.entities(entities[i].ID, out index)[index]); + } + + SafetyChecks(typeSafeDictionary, count); + } + + public void ExecuteOnEntities(int group, ref W value, ActionRef action) where T : IEntityStruct where T1 : IEntityStruct + { + int count; + TypeSafeDictionary typeSafeDictionary; + if (QueryEntitySafeDictionary(@group, out typeSafeDictionary) == false) return; + + var entities = typeSafeDictionary.GetFasterValuesBuffer(out count); + + EGIDMapper map = QueryMappedEntities(group); for (var i = 0; i < count; i++) { uint index; - action(ref entities[i], ref QueryEntitiesAndIndex(entities[i].ID, out index)[index]); - if (typeSafeDictionary.Count != count) - throw new EntitiesDBException("Entities cannot be swapped or removed during an iteration"); + action(ref entities[i], ref map.entities(entities[i].ID, out index)[index], ref value); } + + SafetyChecks(typeSafeDictionary, count); } public void ExecuteOnEntities(ActionRef action) where T : IEntityStruct where T1 : IEntityStruct { ExecuteOnEntities(ExclusiveGroup.StandardEntitiesGroup, action); } + + public void ExecuteOnEntities(ref W value, ActionRef action) where T : IEntityStruct where T1 : IEntityStruct + { + ExecuteOnEntities(ExclusiveGroup.StandardEntitiesGroup, ref value, action); + } public bool Exists(EGID entityGID) where T : IEntityStruct { diff --git a/Svelto.ECS/IEntitiesDB.cs b/Svelto.ECS/IEntitiesDB.cs index 7289d0c..a8f7940 100644 --- a/Svelto.ECS/IEntitiesDB.cs +++ b/Svelto.ECS/IEntitiesDB.cs @@ -1,4 +1,3 @@ -using System; using Svelto.DataStructures; using Svelto.Utilities; @@ -30,41 +29,95 @@ namespace Svelto.ECS /// over EntityView /// T QueryEntityView(EGID egid) where T : class, IEntityStruct; - - //to use with EntityViews, EntityStructs and EntityViewStructs + /// + /// Fast and raw (therefore not safe) return of entities buffer + /// Modifying a buffer would compromise the integrity of the whole DB + /// so they are meant to be used only in performance critical path + /// + /// + /// + /// T[] QueryEntities(out int count) where T : IEntityStruct; - T[] QueryEntities(int group, out int count) where T : IEntityStruct; - T[] QueryEntities(int groupID, ref EGIDMapper mapper) where T : IEntityStruct; - - T[] QueryEntitiesAndIndex(EGID entityGid, out uint index) where T : IEntityStruct; - bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) where T : IEntityStruct; - - //to use with EntityViews, EntityStructs and EntityViewStructs - - void ExecuteOnEntity(EGID egid, ActionRef action) where T : IEntityStruct; - void ExecuteOnEntity(int id, ActionRef action) where T : IEntityStruct; - void ExecuteOnEntity(int id, int groupid, ActionRef action) where T : IEntityStruct; - + T[] QueryEntities(int group, out int count) where T : IEntityStruct; + /// + /// this version returns a mapped version of the entity array so that is possible to find the + /// index of the entity inside the returned buffer through it's EGID + /// However mapping can be slow so it must be used for not performance critical paths + /// + /// + /// + /// + /// + EGIDMapper QueryMappedEntities(int groupID) where T : IEntityStruct; + EGIDMapper QueryMappedEntities() where T : IEntityStruct; + /// + /// Execute an action on entities. Be sure that the action is not capturing variables + /// otherwise you will allocate memory which will have a great impact on the execution performance. + /// ExecuteOnEntities can be used to iterate safely over entities, several checks are in place + /// to be sure that everything will be done correctly. + /// Cache friendliness is guaranteed if only Entity Structs are used, but + /// + /// + /// + /// void ExecuteOnEntities(int groupID, ActionRef action) where T : IEntityStruct; void ExecuteOnEntities(ActionRef action) where T : IEntityStruct; - - void ExecuteOnEntity(EGID egid, ref W value, ActionRef action) where T : IEntityStruct; - void ExecuteOnEntity(int id, ref W value, ActionRef action) where T : IEntityStruct; - void ExecuteOnEntity(int id, int groupid, ref W value, ActionRef action) where T : IEntityStruct; - void ExecuteOnEntities(int groupID, ref W value, ActionRef action) where T : IEntityStruct; void ExecuteOnEntities(ref W value, ActionRef action) where T : IEntityStruct; - - void ExecuteOnAllEntities(ActionRef action) where T : IEntityStruct; - void ExecuteOnAllEntities(ref W value, ActionRef action) where T : IEntityStruct; - + /// + /// This specialized version allows to execute actions on multiple entity views or entity structs + /// Safety checks are in place. This function doesn't guarantee cache + /// friendliness even if just EntityStructs are used. + /// + /// + /// + /// + /// void ExecuteOnEntities(int groupID, ActionRef action) where T : IEntityStruct where T1 : IEntityStruct; void ExecuteOnEntities(ActionRef action) where T : IEntityStruct where T1 : IEntityStruct; + + void ExecuteOnEntities(int groupID, ref W value, ActionRef action) where T : IEntityStruct where T1 : IEntityStruct; + void ExecuteOnEntities(ref W value, ActionRef action) where T : IEntityStruct where T1 : IEntityStruct; + void ExecuteOnEntities(W value, ActionRef action) where T : IEntityStruct where T1 : IEntityStruct; + /// + /// Execute an action on ALL the entities regardless the group. This function doesn't guarantee cache + /// friendliness even if just EntityStructs are used. + /// Safety checks are in place + /// + /// + /// + void ExecuteOnAllEntities(ActionRef action) where T : IEntityStruct; + void ExecuteOnAllEntities(ref W value, ActionRef action) where T : IEntityStruct; + /// + /// ECS is meant to work on a set of Entities. Working on a single entity is sometime necessary, but using + /// the following functions inside a loop would be a mistake as performance can be significantly impacted + /// return the buffer and the index of the entity inside the buffer using the input EGID + /// + /// + /// + /// + /// + T[] QueryEntitiesAndIndex(EGID entityGid, out uint index) where T : IEntityStruct; + bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) where T : IEntityStruct; + /// + /// ECS is meant to work on a set of Entities. Working on a single entity is sometime necessary, but using + /// the following functions inside a loop would be a mistake as performance can be significantly impacted + /// Execute an action on a specific Entity. Be sure that the action is not capturing variables + /// otherwise you will allocate memory which will have a great impact on the execution performance + /// + /// + /// + /// + void ExecuteOnEntity(EGID egid, ActionRef action) where T : IEntityStruct; + void ExecuteOnEntity(int id, ActionRef action) where T : IEntityStruct; + void ExecuteOnEntity(int id, int groupid, ActionRef action) where T : IEntityStruct; + void ExecuteOnEntity(EGID egid, ref W value, ActionRef action) where T : IEntityStruct; + void ExecuteOnEntity(int id, ref W value, ActionRef action) where T : IEntityStruct; + void ExecuteOnEntity(int id, int groupid, ref W value, ActionRef action) where T : IEntityStruct; bool Exists(EGID egid) where T : IEntityStruct; bool HasAny() where T:IEntityStruct; bool HasAny(int group) where T:IEntityStruct; - } } \ No newline at end of file