From 89cc341f6f0dc6406ffd1a085d45f8db0f371eee Mon Sep 17 00:00:00 2001 From: sebas77 Date: Sat, 28 Jul 2018 14:20:04 +0100 Subject: [PATCH] Sequencer cannot pass data anymore, just the EGID More debug info Better memory usage Query entity must throw an exception if entity is not found runtime validation for EntityStructs --- .../DataStructures/TypeSafeDictionary.cs | 8 ++- .../EnginesRoot.DoubleBufferedEntityViews.cs | 22 ++++-- Svelto.ECS/EnginesRoot.Engines.cs | 2 +- Svelto.ECS/EnginesRoot.Entities.cs | 52 ++++++++++++-- .../EnginesRoot.GenericEntityFunctions.cs | 2 +- Svelto.ECS/EnginesRoot.Submission.cs | 16 ++--- Svelto.ECS/EntitiesDB.cs | 42 +++++++----- Svelto.ECS/EntityFactory.cs | 2 + Svelto.ECS/EntityViewBuilder.cs | 42 +++++++----- Svelto.ECS/EntityViewUtility.cs | 10 +-- Svelto.ECS/IEntitiesDB.cs | 2 - Svelto.ECS/Sequencer.cs | 67 ++++++------------- 12 files changed, 163 insertions(+), 104 deletions(-) diff --git a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs index c53e17a..95ceceb 100644 --- a/Svelto.ECS/DataStructures/TypeSafeDictionary.cs +++ b/Svelto.ECS/DataStructures/TypeSafeDictionary.cs @@ -29,10 +29,11 @@ namespace Svelto.ECS.Internal void AddEntitiesToEngines(Dictionary> entityViewEnginesDB); void AddCapacity(int size); - bool Remove(int idGid); int Count { get; } void Trim(); + void Clear(); + bool Has(int entityIdEntityId); } class TypeSafeDictionary : FasterDictionary, ITypeSafeDictionary where TValue : IEntityStruct @@ -74,6 +75,11 @@ namespace Svelto.ECS.Internal } } + public bool Has(int entityIdEntityId) + { + return ContainsKey(entityIdEntityId); + } + void AddEntityViewToEngines(Dictionary> entityViewEnginesDB, ref TValue entity) { FasterList entityViewsEngines; diff --git a/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs b/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs index 380a4a3..285ec57 100644 --- a/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs +++ b/Svelto.ECS/EnginesRoot.DoubleBufferedEntityViews.cs @@ -1,4 +1,7 @@ -using System.Collections; +using System; +using System.Collections; +using System.Collections.Generic; +using Svelto.ECS.Internal; #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR using Svelto.ECS.Profiler; @@ -8,12 +11,12 @@ namespace Svelto.ECS { public partial class EnginesRoot { - class DoubleBufferedEntityViews where T : class, IDictionary, new() + class DoubleBufferedEntitiesToAdd where T : Dictionary>, new() { readonly T _entityViewsToAddBufferA = new T(); readonly T _entityViewsToAddBufferB = new T(); - internal DoubleBufferedEntityViews() + internal DoubleBufferedEntitiesToAdd() { this.other = _entityViewsToAddBufferA; this.current = _entityViewsToAddBufferB; @@ -21,13 +24,24 @@ namespace Svelto.ECS internal T other; internal T current; - + internal void Swap() { var toSwap = other; other = current; current = toSwap; } + + public void ClearOther() + { + foreach (var item in other) + { + foreach (var subitem in item.Value) + { + subitem.Value.Clear(); + } + } + } } } } \ No newline at end of file diff --git a/Svelto.ECS/EnginesRoot.Engines.cs b/Svelto.ECS/EnginesRoot.Engines.cs index 0cca849..45a3085 100644 --- a/Svelto.ECS/EnginesRoot.Engines.cs +++ b/Svelto.ECS/EnginesRoot.Engines.cs @@ -42,7 +42,7 @@ namespace Svelto.ECS _groupEntityDB = new Dictionary>(); _groupEntityDB[ExclusiveGroup.StandardEntitiesGroup] = new Dictionary(); _groupedGroups = new Dictionary>(); - _groupedEntityToAdd = new DoubleBufferedEntityViews>>(); + _groupedEntityToAdd = new DoubleBufferedEntitiesToAdd>>(); _DB = new entitiesDB(_groupEntityDB, _groupedGroups); diff --git a/Svelto.ECS/EnginesRoot.Entities.cs b/Svelto.ECS/EnginesRoot.Entities.cs index 8ed5a3b..0d4cde2 100644 --- a/Svelto.ECS/EnginesRoot.Entities.cs +++ b/Svelto.ECS/EnginesRoot.Entities.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using Svelto.DataStructures.Experimental; using Svelto.ECS.Internal; @@ -40,10 +41,16 @@ namespace Svelto.ECS EntityStructInitializer BuildEntity(EGID entityID, object[] implementors) where T : IEntityDescriptor, new() { + var descriptorEntitiesToBuild = EntityDescriptorTemplate.descriptor.entitiesToBuild; +#if DEBUG && !PROFILER + CheckEntityID(entityID, descriptorEntitiesToBuild); +#endif var dic = EntityFactory.BuildGroupedEntityViews(entityID, _groupedEntityToAdd.current, - EntityDescriptorTemplate.descriptor.entitiesToBuild, + descriptorEntitiesToBuild, implementors); + + _newEntitiesBuiltToProcess++; return new EntityStructInitializer(entityID, dic); } @@ -52,13 +59,48 @@ namespace Svelto.ECS IEntityBuilder[] entityToBuild, object[] implementors) { +#if DEBUG && !PROFILER + CheckEntityID(entityID, entityToBuild); +#endif var dic = EntityFactory.BuildGroupedEntityViews(entityID, _groupedEntityToAdd.current, entityToBuild, implementors); + _newEntitiesBuiltToProcess++; + return new EntityStructInitializer(entityID, dic); } + + void CheckEntityID(EGID entityID, IEntityBuilder[] descriptorEntitiesToBuild) + { + Dictionary @group; + if (_groupEntityDB.TryGetValue(entityID.groupID, out @group) == true) + { + for (int i = 0; i < descriptorEntitiesToBuild.Length; i++) + { + CheckEntityID(entityID, descriptorEntitiesToBuild[i].GetEntityType(), @group); + } + CheckEntityID(entityID, _typeEntityInfoView, @group); + } + } + + static void CheckEntityID(EGID entityID, Type entityType, Dictionary @group) + { + ITypeSafeDictionary entities; + if (@group.TryGetValue(entityType, out entities)) + { + if (entities.Has(entityID.entityID) == true) + { + Utility.Console.LogError("Entity with used ID is about to be built: " + .FastConcat(entityType) + .FastConcat(" id: ") + .FastConcat(entityID.entityID) + .FastConcat(" groupid: ") + .FastConcat(entityID.groupID)); + } + } + } ///-------------------------------------------- @@ -181,7 +223,7 @@ namespace Svelto.ECS MoveEntity(new EGID(entityID, fromGroupID), toGroupID, toGroup); } - EGID SwapFirstEntityGroup(int fromGroupID, int toGroupId) + EGID SwapFirstEntityInGroup(int fromGroupID, int toGroupId) { var firstID = ((TypeSafeDictionary) _groupEntityDB[fromGroupID][_typeEntityInfoView]).FasterValues[0].ID.entityID; @@ -191,10 +233,10 @@ namespace Svelto.ECS return new EGID(firstID, toGroupId); } - readonly entitiesDB _DB; - readonly DoubleBufferedEntityViews>> _groupedEntityToAdd; + readonly entitiesDB _DB; - static readonly Type _typeEntityInfoView = typeof(EntityInfoView); + static readonly Type _typeEntityInfoView = typeof(EntityInfoView); + int _newEntitiesBuiltToProcess; } public struct EntityStructInitializer diff --git a/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs b/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs index 9904927..ef255a5 100644 --- a/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs +++ b/Svelto.ECS/EnginesRoot.GenericEntityFunctions.cs @@ -54,7 +54,7 @@ namespace Svelto.ECS public EGID SwapFirstEntityGroup(int fromGroupID, int toGroupID) { - return _weakReference.Target.SwapFirstEntityGroup( fromGroupID, toGroupID); + return _weakReference.Target.SwapFirstEntityInGroup( fromGroupID, toGroupID); } } } diff --git a/Svelto.ECS/EnginesRoot.Submission.cs b/Svelto.ECS/EnginesRoot.Submission.cs index c9f94bc..eb3ce77 100644 --- a/Svelto.ECS/EnginesRoot.Submission.cs +++ b/Svelto.ECS/EnginesRoot.Submission.cs @@ -14,11 +14,10 @@ namespace Svelto.ECS { void SubmitEntityViews() { - bool newEntityViewsHaveBeenAddedWhileIterating = _groupedEntityToAdd.current.Count > 0; - int numberOfReenteringLoops = 0; - while (newEntityViewsHaveBeenAddedWhileIterating) + //are there new entities built to process? + while ( _newEntitiesBuiltToProcess > 0) { //use other as source from now on //current will be use to write new entityViews @@ -33,15 +32,13 @@ namespace Svelto.ECS if (_groupedEntityToAdd.other.Count > 0) AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd.other); - //other can be cleared now - _groupedEntityToAdd.other.Clear(); - - //has current new entityViews? - newEntityViewsHaveBeenAddedWhileIterating = _groupedEntityToAdd.current.Count > 0; + //other can be cleared now, but let's avoid deleting the dictionary every time + _groupedEntityToAdd.ClearOther(); if (numberOfReenteringLoops > 5) throw new Exception("possible infinite loop found creating Entities inside IEntityViewsEngine Add method, please consider building entities outside IEntityViewsEngine Add method"); + _newEntitiesBuiltToProcess = 0; numberOfReenteringLoops++; } } @@ -70,7 +67,7 @@ namespace Svelto.ECS if (_groupedGroups.TryGetValue(entityViewTypeSafeDictionary.Key, out groupedGroup) == false) groupedGroup = _groupedGroups[entityViewTypeSafeDictionary.Key] = new FasterDictionary(); - //type safe copy + //Fill the DB with the entity views generate this frame. dbDic.FillWithIndexedEntities(entityViewTypeSafeDictionary.Value); groupedGroup[groupID] = dbDic; } @@ -95,6 +92,7 @@ namespace Svelto.ECS readonly Dictionary> _groupEntityDB; readonly Dictionary> _groupedGroups; //yes I am being sarcastic + readonly DoubleBufferedEntitiesToAdd>> _groupedEntityToAdd; readonly EntitySubmissionScheduler _scheduler; } } \ No newline at end of file diff --git a/Svelto.ECS/EntitiesDB.cs b/Svelto.ECS/EntitiesDB.cs index ceac8ba..e446ae9 100644 --- a/Svelto.ECS/EntitiesDB.cs +++ b/Svelto.ECS/EntitiesDB.cs @@ -57,27 +57,16 @@ namespace Svelto.ECS.Internal public T[] QueryEntitiesAndIndex(EGID entityGID, out uint index) where T : IEntityStruct { - TypeSafeDictionary casted; - if (!FindSafeDictionary(entityGID, out casted)) - { - index = 0; - return null; - } - - if (casted == null || casted.TryFindElementIndex(entityGID.entityID, out index) == false) - { - index = 0; - return null; - } - - int count; + T[] array; + if ((array = QueryEntitiesAndIndexInternal(entityGID, out index)) != null) + return array; - return QueryEntities(entityGID.groupID, out count); + throw new Exception("Entity not found id: ".FastConcat(entityGID.entityID).FastConcat(" groupID: ").FastConcat(entityGID.groupID)); } - + public bool TryQueryEntitiesAndIndex(EGID entityGid, out uint index, out T[] array) where T : IEntityStruct { - if ((array = QueryEntitiesAndIndex(entityGid, out index)) != null) + if ((array = QueryEntitiesAndIndexInternal(entityGid, out index)) != null) return true; return false; @@ -275,7 +264,26 @@ namespace Svelto.ECS.Internal return false; } + + T[] QueryEntitiesAndIndexInternal(EGID entityGID, out uint index) where T : IEntityStruct + { + TypeSafeDictionary casted; + if (!FindSafeDictionary(entityGID, out casted)) + { + index = 0; + return null; + } + if (casted == null || casted.TryFindElementIndex(entityGID.entityID, out index) == false) + { + index = 0; + return null; + } + + int count; + + return QueryEntities(entityGID.groupID, out count); + } static ReadOnlyCollectionStruct RetrieveEmptyEntityViewList() { return ReadOnlyCollectionStruct.DefaultList; diff --git a/Svelto.ECS/EntityFactory.cs b/Svelto.ECS/EntityFactory.cs index 679cd19..8a83d35 100644 --- a/Svelto.ECS/EntityFactory.cs +++ b/Svelto.ECS/EntityFactory.cs @@ -48,6 +48,7 @@ namespace Svelto.ECS.Internal } _builder._initializer = new EntityInfoView {entityToBuild = entityToBuild}; + BuildEntityView(entityID, @group, _viewType, _builder, null); } @@ -62,6 +63,7 @@ namespace Svelto.ECS.Internal //passing the undefined entityViewsByType inside the entityViewBuilder will allow //it to be created with the correct type and casted back to the undefined list. //that's how the list will be eventually of the target type. + entityBuilder.BuildEntityViewAndAddToList(ref safeDictionary, entityID, implementors); if (entityViewsPoolWillBeCreated) diff --git a/Svelto.ECS/EntityViewBuilder.cs b/Svelto.ECS/EntityViewBuilder.cs index bdaddb0..790b0a3 100644 --- a/Svelto.ECS/EntityViewBuilder.cs +++ b/Svelto.ECS/EntityViewBuilder.cs @@ -18,20 +18,7 @@ namespace Svelto.ECS #if DEBUG && !PROFILER if (needsReflection == false && typeof(T) != typeof(EntityInfoView)) { - var type = typeof(T); - - var fields = type.GetFields(BindingFlags.Public | - BindingFlags.Instance); - - for (int i = fields.Length - 1; i >= 0; --i) - { - var field = fields[i]; - - if (field.FieldType.IsPrimitive == true || field.FieldType.IsValueType == true) - continue; - - throw new EntityStructException(field.FieldType); - } + CheckFields(typeof(T)); } #endif if (needsReflection == true) @@ -39,7 +26,32 @@ namespace Svelto.ECS EntityView.InitCache(); } } - +#if DEBUG && !PROFILER + static void CheckFields(Type type) + { + var fields = type.GetFields(BindingFlags.Public | + BindingFlags.Instance); + + for (int i = fields.Length - 1; i >= 0; --i) + { + var field = fields[i]; + + var fieldFieldType = field.FieldType; + if (fieldFieldType.IsPrimitive == true || fieldFieldType.IsValueType == true) + { + if (fieldFieldType.IsValueType && !fieldFieldType.IsEnum) + { + CheckFields(fieldFieldType); + } + + continue; + } + + throw new EntityStructException(fieldFieldType); + } + } +#endif + public void BuildEntityViewAndAddToList(ref ITypeSafeDictionary dictionary, EGID entityID, object[] implementors) { if (dictionary == null) diff --git a/Svelto.ECS/EntityViewUtility.cs b/Svelto.ECS/EntityViewUtility.cs index 33d1840..261d9c3 100644 --- a/Svelto.ECS/EntityViewUtility.cs +++ b/Svelto.ECS/EntityViewUtility.cs @@ -20,7 +20,8 @@ static class EntityViewUtility .NoVirt.ToArrayFast(entityViewBlazingFastReflection, out count); #if DEBUG && !PROFILER if (count == 0) - throw new Exception(NO_COMPONENTS_EXCEPTION.FastConcat("Type ", entityDescriptorName, " entityView ", entityBuilder.GetEntityType().ToString())); + throw new Exception(NO_COMPONENTS_EXCEPTION.FastConcat("Type ", entityDescriptorName, " entityView ", + entityBuilder.GetEntityType().ToString())); #endif for (var index = 0; index < implementors.Length; index++) { @@ -52,7 +53,8 @@ static class EntityViewUtility #if DEBUG && !PROFILER else { - Utility.Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat("Type ", entityDescriptorName, " entityView ", entityBuilder.GetEntityType().ToString())); + Utility.Console.Log(NULL_IMPLEMENTOR_ERROR.FastConcat("Type ", entityDescriptorName, " entityView ", + entityBuilder.GetEntityType().ToString())); } #endif } @@ -71,8 +73,8 @@ static class EntityViewUtility if (implementorsByType.TryGetValue(fieldType, out component) == false) { var e = new Exception(NOT_FOUND_EXCEPTION + " Component Type: " + fieldType.Name + - " - EntityView: " + - entityBuilder.GetEntityType().Name + " - EntityDescriptor " + entityDescriptorName); + " - EntityView: " + entityBuilder.GetEntityType().Name + + " - EntityDescriptor " + entityDescriptorName); throw e; } diff --git a/Svelto.ECS/IEntitiesDB.cs b/Svelto.ECS/IEntitiesDB.cs index 433b28f..56a064c 100644 --- a/Svelto.ECS/IEntitiesDB.cs +++ b/Svelto.ECS/IEntitiesDB.cs @@ -40,7 +40,6 @@ namespace Svelto.ECS //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; @@ -48,7 +47,6 @@ namespace Svelto.ECS 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; diff --git a/Svelto.ECS/Sequencer.cs b/Svelto.ECS/Sequencer.cs index 154002e..ba4c25d 100644 --- a/Svelto.ECS/Sequencer.cs +++ b/Svelto.ECS/Sequencer.cs @@ -7,39 +7,30 @@ namespace Svelto.ECS public class Steps : Dictionary {} - public class To : Dictionary + public class To : Dictionary where C : struct, IConvertible { - public void Add(IStep engine) + public void Add(C condition, IStep engine) { - Add(Condition.Always, new [] {engine}); + Add(condition, new [] {engine}); } - public void Add(IStep[] engines) + public void Add(C condition, params IStep[] engines) { - Add(Condition.Always, engines); + Add(condition, engines); + } + public void Add(params IStep[] engines) + { + Add(default(C), engines); } } - public class To : Dictionary where C:struct,IConvertible - {} - public interface IStep {} - public interface IStep:IStep + public interface IStep:IStep where C:struct,IConvertible { - void Step(ref T token, int condition); + void Step(C condition, EGID id); } - - public interface IStep:IStep where C:struct,IConvertible - { - void Step(ref T token, C condition); - } - - public interface IEnumStep:IStep - { - void Step(ref T token, Enum condition); - } - + public abstract class Sequencer { public void SetSequence(Steps steps) @@ -47,39 +38,25 @@ namespace Svelto.ECS _steps = steps; } - public void Next(IEngine engine, ref T param) - { - Next(engine, ref param, Condition.Always); - } - - public void Next(IEngine engine, ref T param, int condition) + public void Next(IEngine engine, EGID id) { - int branch = condition; - var steps = (_steps[engine] as Dictionary)[branch]; - - if (steps != null) - for (int i = 0; i < steps.Length; i++) - ((IStep)steps[i]).Step(ref param, condition); + Next(engine, Condition.Always, id); } - public void Next(IEngine engine, ref T param, Enum condition) + public void Next(IEngine engine) { - int branch = Convert.ToInt32(condition); - var steps = (_steps[engine] as Dictionary)[branch]; - - if (steps != null) - for (int i = 0; i < steps.Length; i++) - ((IEnumStep)steps[i]).Step(ref param, condition); + Next(engine, Condition.Always); } - public void Next(IEngine engine, ref T param, C condition) where C:struct,IConvertible + public void Next(IEngine engine, C condition, EGID id = new EGID()) where C:struct,IConvertible { C branch = condition; var steps = (_steps[engine] as Dictionary)[branch]; - if (steps != null) - for (int i = 0; i < steps.Length; i++) - ((IStep)steps[i]).Step(ref param, condition); + if (steps == null) return; + + for (var i = 0; i < steps.Length; i++) + ((IStep)steps[i]).Step(condition, id); } Steps _steps; @@ -89,4 +66,4 @@ namespace Svelto.ECS { public const int Always = 0; } -} \ No newline at end of file +} \ No newline at end of file