diff --git a/DBC.cs b/DBC.cs index d73fee3..e9fae93 100644 --- a/DBC.cs +++ b/DBC.cs @@ -1,7 +1,8 @@ -using System; -#if PROFILER || !DEBUG +#if DISABLE_DBC || !DEBUG || PROFILER +#define DISABLE_CHECKS using System.Diagnostics; #endif +using System; namespace DBC.ECS { @@ -59,7 +60,7 @@ namespace DBC.ECS /// /// Precondition check. /// -#if PROFILER || !DEBUG +#if DISABLE_DBC [Conditional("__NEVER_DEFINED__")] #endif public static void Require(bool assertion, string message) @@ -79,7 +80,7 @@ namespace DBC.ECS /// Precondition check. /// /// -#if PROFILER || !DEBUG +#if DISABLE_DBC [Conditional("__NEVER_DEFINED__")] #endif public static void Require(bool assertion, string message, Exception inner) @@ -99,7 +100,7 @@ namespace DBC.ECS /// Precondition check. /// /// -#if PROFILER || !DEBUG +#if DISABLE_DBC [Conditional("__NEVER_DEFINED__")] #endif public static void Require(bool assertion) @@ -119,7 +120,7 @@ namespace DBC.ECS /// Postcondition check. /// /// -#if PROFILER || !DEBUG +#if DISABLE_DBC [Conditional("__NEVER_DEFINED__")] #endif public static void Ensure(bool assertion, string message) @@ -139,7 +140,7 @@ namespace DBC.ECS /// Postcondition check. /// /// -#if PROFILER || !DEBUG +#if DISABLE_DBC [Conditional("__NEVER_DEFINED__")] #endif public static void Ensure(bool assertion, string message, Exception inner) @@ -159,7 +160,7 @@ namespace DBC.ECS /// Postcondition check. /// /// -#if PROFILER || !DEBUG +#if DISABLE_DBC [Conditional("__NEVER_DEFINED__")] #endif public static void Ensure(bool assertion) @@ -179,7 +180,7 @@ namespace DBC.ECS /// Invariant check. /// /// -#if PROFILER || !DEBUG +#if DISABLE_DBC [Conditional("__NEVER_DEFINED__")] #endif public static void Invariant(bool assertion, string message) @@ -199,7 +200,7 @@ namespace DBC.ECS /// Invariant check. /// /// -#if PROFILER || !DEBUG +#if DISABLE_DBC [Conditional("__NEVER_DEFINED__")] #endif public static void Invariant(bool assertion, string message, Exception inner) @@ -219,7 +220,7 @@ namespace DBC.ECS /// Invariant check. /// /// -#if PROFILER || !DEBUG +#if DISABLE_DBC [Conditional("__NEVER_DEFINED__")] #endif public static void Invariant(bool assertion) @@ -238,7 +239,7 @@ namespace DBC.ECS /// /// Assertion check. /// -#if PROFILER || !DEBUG +#if DISABLE_DBC [Conditional("__NEVER_DEFINED__")] #endif public static void Assert(bool assertion, string message) @@ -258,7 +259,7 @@ namespace DBC.ECS /// Assertion check. /// /// -#if PROFILER || !DEBUG +#if DISABLE_DBC [Conditional("__NEVER_DEFINED__")] #endif public static void Assert(bool assertion, string message, Exception inner) @@ -278,7 +279,7 @@ namespace DBC.ECS /// Assertion check. /// /// -#if PROFILER || !DEBUG +#if DISABLE_DBC [Conditional("__NEVER_DEFINED__")] #endif public static void Assert(bool assertion) diff --git a/Svelto.ECS/EnginesRoot.Engines.cs b/Svelto.ECS/EnginesRoot.Engines.cs index 1438ee0..0ca68ef 100644 --- a/Svelto.ECS/EnginesRoot.Engines.cs +++ b/Svelto.ECS/EnginesRoot.Engines.cs @@ -14,17 +14,18 @@ namespace Svelto.ECS { public partial class EnginesRoot : IDisposable { +#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR static EnginesRoot() { -#if ENGINE_PROFILER_ENABLED && UNITY_EDITOR + /// /// I still need to find a good solution for this. Need to move somewhere else /// UnityEngine.GameObject debugEngineObject = new UnityEngine.GameObject("Svelto.ECS.Profiler"); debugEngineObject.gameObject.AddComponent(); UnityEngine.GameObject.DontDestroyOnLoad(debugEngineObject); -#endif } +#endif /// /// Engines root contextualize your engines and entities. You don't need to limit yourself to one EngineRoot @@ -34,21 +35,22 @@ namespace Svelto.ECS /// The EntitySubmissionScheduler cannot hold an EnginesRoot reference, that's why /// it must receive a weak reference of the EnginesRoot callback. /// - public EnginesRoot(EntitySubmissionScheduler entityViewScheduler) + public EnginesRoot(IEntitySubmissionScheduler entityViewScheduler) { _entitiesOperations = new FasterList(); _entityEngines = new Dictionary>(); _otherEngines = new FasterList(); _disposableEngines = new FasterList(); + _transientEntitiesOperations = new FasterList(); _groupEntityDB = new FasterDictionary>(); - _groupedGroups = new Dictionary>(); + _groupsPerEntity = new Dictionary>(); _groupedEntityToAdd = new DoubleBufferedEntitiesToAdd>>(); - _DB = new EntitiesDB(_groupEntityDB, _groupedGroups); + _DB = new EntitiesDB(_groupEntityDB, _groupsPerEntity); _scheduler = entityViewScheduler; - _scheduler.Schedule(new WeakAction(SubmitEntityViews)); + _scheduler.onTick = new WeakAction(SubmitEntityViews); } public void AddEngine(IEngine engine) diff --git a/Svelto.ECS/EnginesRoot.Entities.cs b/Svelto.ECS/EnginesRoot.Entities.cs index 34f05f6..61c54db 100644 --- a/Svelto.ECS/EnginesRoot.Entities.cs +++ b/Svelto.ECS/EnginesRoot.Entities.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using Svelto.DataStructures.Experimental; + using Svelto.DataStructures; + using Svelto.DataStructures.Experimental; using Svelto.ECS.Internal; #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR @@ -58,11 +59,9 @@ namespace Svelto.ECS #endif var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd.current, - descriptorEntitiesToBuild, + descriptorEntitiesToBuild, implementors); - _newEntitiesBuiltToProcess++; - return new EntityStructInitializer(entityID, dic); } @@ -96,7 +95,6 @@ namespace Svelto.ECS } } } - ///-------------------------------------------- void Preallocate(int groupID, int size) where T : IEntityDescriptor, new() @@ -188,8 +186,8 @@ namespace Svelto.ECS } FasterDictionary groupedGroup; - if (_groupedGroups.TryGetValue(entityType, out groupedGroup) == false) - groupedGroup = _groupedGroups[entityType] = new FasterDictionary(); + if (_groupsPerEntity.TryGetValue(entityType, out groupedGroup) == false) + groupedGroup = _groupsPerEntity[entityType] = new FasterDictionary(); groupedGroup[toGroupID] = dictionaryOfEntities; } @@ -202,7 +200,7 @@ namespace Svelto.ECS if (fromTypeSafeDictionary.Count == 0) //clean up { - _groupedGroups[entityType].Remove(entityGID.groupID); + _groupsPerEntity[entityType].Remove(entityGID.groupID); //I don't remove the group if empty on purpose, in case it needs to be reused //however I trim it to save memory @@ -216,7 +214,7 @@ namespace Svelto.ECS foreach (var dictionaryOfEntities in dictionariesOfEntities) { dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_entityEngines); - var groupedGroupOfEntities = _groupedGroups[dictionaryOfEntities.Key]; + var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key]; groupedGroupOfEntities.Remove(groupID); } @@ -240,7 +238,6 @@ namespace Svelto.ECS } readonly EntitiesDB _DB; - int _newEntitiesBuiltToProcess; Type _entityInfoView = typeof(EntityInfoView); } diff --git a/Svelto.ECS/EnginesRoot.Submission.cs b/Svelto.ECS/EnginesRoot.Submission.cs index 5db8dd5..f73459e 100644 --- a/Svelto.ECS/EnginesRoot.Submission.cs +++ b/Svelto.ECS/EnginesRoot.Submission.cs @@ -1,9 +1,12 @@ -using System; +using System; using System.Collections.Generic; +using System.Diagnostics; +using Svelto.Common; using Svelto.DataStructures; using Svelto.DataStructures.Experimental; using Svelto.ECS.Internal; using Svelto.ECS.Schedulers; +using Console = Svelto.Utilities.Console; #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR using Svelto.ECS.Profiler; @@ -15,69 +18,87 @@ namespace Svelto.ECS { void SubmitEntityViews() { - var entitiesOperations = _entitiesOperations.ToArrayFast(); - for (int i = 0; i < _entitiesOperations.Count; i++) + using (new PlatformProfiler("Svelto.ECS submit")) { + if (_entitiesOperations.Count > 0) + { + _transientEntitiesOperations.FastClear(); + _transientEntitiesOperations.AddRange(_entitiesOperations); + _entitiesOperations.FastClear(); + var entitiesOperations = _transientEntitiesOperations.ToArrayFast(); + for (var i = 0; i < _transientEntitiesOperations.Count; i++) + { + try + { + switch (entitiesOperations[i].type) + { + case EntitySubmitOperationType.Swap: + SwapEntityGroup(entitiesOperations[i].builders, entitiesOperations[i].id, + entitiesOperations[i].fromGroupID, entitiesOperations[i].toGroupID); + break; + case EntitySubmitOperationType.Remove: + MoveEntity(entitiesOperations[i].builders, + new EGID(entitiesOperations[i].id, entitiesOperations[i].fromGroupID)); + break; + case EntitySubmitOperationType.RemoveGroup: + RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromGroupID); + break; + } + } + catch (Exception e) + { +#if DEBUG + var str = "Entity ".FastConcat(entitiesOperations[i].type.ToString(), + " with used ID is about to be built: ") + .FastConcat(" id: ") + .FastConcat(entitiesOperations[i].id) + .FastConcat(" from groupid: ") + .FastConcat(entitiesOperations[i].fromGroupID) + .FastConcat(" to groupid: ") + .FastConcat(entitiesOperations[i].toGroupID); + + Console.LogError(e.Message.FastConcat(" ", str, " ", entitiesOperations[i].trace)); + + throw; +#else + Console.LogException(e); +#endif + } + } + } + try { - switch (entitiesOperations[i].type) + if (_groupedEntityToAdd.current.Count > 0) { - case EntitySubmitOperationType.Swap: - SwapEntityGroup(entitiesOperations[i].builders, entitiesOperations[i].id, - entitiesOperations[i].fromGroupID, entitiesOperations[i].toGroupID); - break; - case EntitySubmitOperationType.Remove: - MoveEntity(entitiesOperations[i].builders, - new EGID(entitiesOperations[i].id, entitiesOperations[i].fromGroupID)); - break; - case EntitySubmitOperationType.RemoveGroup: - RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromGroupID); - break; + //use other as source from now on + //current will be use to write new entityViews + _groupedEntityToAdd.Swap(); + + //Note: if N entity of the same type are added on the same frame + //the Add callback is called N times on the same frame. + //if the Add callback builds a new entity, that entity will not + //be available in the database until the N callbacks are done + //solving it could be complicated as callback and database update + //must be interleaved. + + AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd.other); + + //other can be cleared now, but let's avoid deleting the dictionary every time + _groupedEntityToAdd.ClearOther(); } } - catch (ECSException e) + catch (Exception e) { - Svelto.Utilities.Console.LogError(e.Message.FastConcat(" ", entitiesOperations[i].trace)); - + Console.LogException(e); #if DEBUG throw; -#endif +#endif } } - - _entitiesOperations.FastClear(); - - int numberOfReenteringLoops = 0; - - //are there new entities built to process? - while ( _newEntitiesBuiltToProcess > 0) - { - _newEntitiesBuiltToProcess = 0; - //use other as source from now on - //current will be use to write new entityViews - _groupedEntityToAdd.Swap(); - - //Note: if N entity of the same type are added on the same frame - //the Add callback is called N times on the same frame. - //if the Add calback builds a new entity, that entity will not - //be available in the database until the N callbacks are done - //solving it could be complicated as callback and database update - //must be interleaved. - if (_groupedEntityToAdd.other.Count > 0) - AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd.other); - - //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"); - - numberOfReenteringLoops++; - } } - //todo: groupsToSubmit can be simplified as data structure? - void AddEntityViewsToTheDBAndSuitableEngines(FasterDictionary> groupsOfEntitiesToSubmit) + void AddEntityViewsToTheDBAndSuitableEngines(FasterDictionary> groupsOfEntitiesToSubmit) { //each group is indexed by entity view type. for each type there is a dictionary indexed by entityID foreach (var groupOfEntitiesToSubmit in groupsOfEntitiesToSubmit) @@ -97,8 +118,8 @@ namespace Svelto.ECS if (groupDB.TryGetValue(entityViewTypeSafeDictionary.Key, out dbDic) == false) dbDic = groupDB[entityViewTypeSafeDictionary.Key] = entityViewTypeSafeDictionary.Value.Create(); - if (_groupedGroups.TryGetValue(entityViewTypeSafeDictionary.Key, out groupedGroup) == false) - groupedGroup = _groupedGroups[entityViewTypeSafeDictionary.Key] = + if (_groupsPerEntity.TryGetValue(entityViewTypeSafeDictionary.Key, out groupedGroup) == false) + groupedGroup = _groupsPerEntity[entityViewTypeSafeDictionary.Key] = new FasterDictionary(); //Fill the DB with the entity views generate this frame. @@ -117,20 +138,25 @@ namespace Svelto.ECS } } } - + + readonly FasterList _entitiesOperations; + + readonly DoubleBufferedEntitiesToAdd>> + _groupedEntityToAdd; + + //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are + //found indexed by group id + readonly Dictionary> _groupsPerEntity; //yes I am being sarcastic + //one datastructure rule them all: //split by group //split by type per group. It's possible to get all the entities of a give type T per group thanks //to the FasterDictionary capabilities OR it's possible to get a specific entityView indexed by //ID. This ID doesn't need to be the EGID, it can be just the entityID - + //for each group id, save a dictionary indexed by entity type of entities indexed by id readonly FasterDictionary> _groupEntityDB; - //for each entity view type, return the groups (dictionary of entities indexed by entity id) where they are found indexed by group id - readonly Dictionary> _groupedGroups; //yes I am being sarcastic - readonly DoubleBufferedEntitiesToAdd>> - _groupedEntityToAdd; - readonly EntitySubmissionScheduler _scheduler; - readonly FasterList _entitiesOperations; + readonly IEntitySubmissionScheduler _scheduler; + readonly FasterList _transientEntitiesOperations; } } \ No newline at end of file diff --git a/Svelto.ECS/EntityBuilder.cs b/Svelto.ECS/EntityBuilder.cs index 29f631b..a464007 100644 --- a/Svelto.ECS/EntityBuilder.cs +++ b/Svelto.ECS/EntityBuilder.cs @@ -41,7 +41,7 @@ namespace Svelto.ECS SubCheckFields(fieldFieldType); } - if (type.Assembly == Assembly.GetCallingAssembly()) + if (type.Assembly == Assembly.GetCallingAssembly() && type != typeof(Svelto.ECS.EGID)) { var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); @@ -141,7 +141,8 @@ namespace Svelto.ECS public class EntityStructException : Exception { - public EntityStructException(Type fieldType):base("EntityStruct must contains only value types and no public methods! " + fieldType.ToString()) + public EntityStructException(Type fieldType) : + base("EntityStruct must contains only value types and no public methods! " + fieldType.ToString()) {} } } \ No newline at end of file diff --git a/Svelto.ECS/EntitySubmissionScheduler.cs b/Svelto.ECS/EntitySubmissionScheduler.cs index 09335fd..93e4b4c 100644 --- a/Svelto.ECS/EntitySubmissionScheduler.cs +++ b/Svelto.ECS/EntitySubmissionScheduler.cs @@ -2,8 +2,8 @@ using Svelto.WeakEvents; namespace Svelto.ECS.Schedulers { - public abstract class EntitySubmissionScheduler + public interface IEntitySubmissionScheduler { - public abstract void Schedule(WeakAction submitEntityViews); + WeakAction onTick { set; } } } \ No newline at end of file diff --git a/Svelto.ECS/EntitySubmitOperation.cs b/Svelto.ECS/EntitySubmitOperation.cs index 6a1510a..66c098c 100644 --- a/Svelto.ECS/EntitySubmitOperation.cs +++ b/Svelto.ECS/EntitySubmitOperation.cs @@ -13,7 +13,11 @@ namespace Svelto.ECS public string trace; #endif - public EntitySubmitOperation(EntitySubmitOperationType operation, int entityId, int fromGroupId, int toGroupId, IEntityBuilder[] builders) + public EntitySubmitOperation(EntitySubmitOperationType operation, + int entityId, + int fromGroupId, + int toGroupId, + IEntityBuilder[] builders) { type = operation; this.builders = builders; @@ -30,6 +34,6 @@ namespace Svelto.ECS { Swap, Remove, - RemoveGroup + RemoveGroup, } } \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs b/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs index fbaa798..3da402a 100644 --- a/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs +++ b/Svelto.ECS/Extensions/Unity/UnityEntitySubmissionScheduler.cs @@ -7,21 +7,8 @@ namespace Svelto.ECS.Schedulers.Unity { //The EntitySubmissionScheduler has been introduced to make the entity views submission logic platform independent //You can customize the scheduler if you wish - - public class UnityEntitySubmissionScheduler : EntitySubmissionScheduler + public class UnityEntitySubmissionScheduler : IEntitySubmissionScheduler { - public UnityEntitySubmissionScheduler() - { - GameObject go = new GameObject("ECSScheduler"); - - _scheduler = go.AddComponent(); - } - - public override void Schedule(WeakAction submitEntityViews) - { - _scheduler.OnTick = submitEntityViews; - } - class Scheduler : MonoBehaviour { IEnumerator Start() @@ -30,19 +17,34 @@ namespace Svelto.ECS.Schedulers.Unity { yield return _wait; - if (OnTick.IsValid) - OnTick.Invoke(); + if (onTick.IsValid) + onTick.Invoke(); else yield break; + } } - internal WeakAction OnTick; - readonly WaitForEndOfFrame _wait = new WaitForEndOfFrame(); + + public WeakAction onTick; + } + + public WeakAction onTick + { + set + { + if (_scheduler == null) + { + GameObject go = new GameObject("ECSScheduler"); + + _scheduler = go.AddComponent(); + } + _scheduler.onTick = value; + } } - readonly Scheduler _scheduler; + Scheduler _scheduler; } } #endif \ No newline at end of file diff --git a/Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs b/Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs index 57d3b97..d316c5c 100644 --- a/Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs +++ b/Svelto.ECS/SimpleSubmissionEntityViewScheduler.cs @@ -5,18 +5,13 @@ namespace Svelto.ECS { //This scheduler shouldn't be used in production and it's meant to be //used for Unit Tests only - public class SimpleSubmissionEntityViewScheduler : EntitySubmissionScheduler + public class SimpleSubmissionEntityViewScheduler : IEntitySubmissionScheduler { public void SubmitEntities() { - _submitEntityViews.Invoke(); + onTick.Invoke(); } - - public override void Schedule(WeakAction submitEntityViews) - { - _submitEntityViews = submitEntityViews; - } - - WeakAction _submitEntityViews; + + public WeakAction onTick { set; private get; } } } \ No newline at end of file