Mirror of Svelto.ECS because we're a fan of it
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

EnginesRoot.Engines.cs 15KB

Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
před 4 roky
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
před 4 roky
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
před 4 roky
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
před 4 roky
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
před 4 roky
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
před 4 roky
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
před 4 roky
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
před 4 roky
Svelto.ECS 2.9 changes (random order of importance): New Serialization framework more thorough disposing of the EnginesRoot an EnginesRoot reference should never be held, unless it’s a weak reference. The code changed to stick to this rule IReactOnAddAndRemove callbacks are now guaranteed to be called after all the entity structs generated by the same entity have been added and before any is removed. both functions pass the EGID of the analysing entity by parameter now, so that the entity struct won’t need to implement INeedEGID for this sole purpose. The IReactOnSwap MovedFrom method has been removed, it is now redundant. Entities built or removed during the IReactOnAddAndRemove callbacks are now added and removed immediately and not on the next submission like used to happen. This avoid some awkward checks that were previously needed inside engines. EntityStreams can get (optionally) the EGID of the entity published, so that the EntityStruct won’t need an INeedEGID for this sole purpose. Groups are not trimmed anymore when they are emptied to avoid allocations. Removed a bunch of run-time allocations that weren’t supposed to happen in Release and/or when the Profile define is used in editor (for debugging reasons Svelto.ECS may need to use strings at run-time, but Svelto.ECS is allocation zero in Release and when the Profile keyword is used) A improved the DynamicEntityDescriptor and ExtendibleEntityDescriptor code and more notably, introduced the new method ExtendedWith<> to facilitate the writing of modular and reusable entity descriptors. Several minor code design improvements/optimisations
před 4 roky
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. using System;
  2. using System.Collections.Generic;
  3. using Svelto.Common;
  4. using Svelto.DataStructures;
  5. using Svelto.ECS.Internal;
  6. using Svelto.ECS.Schedulers;
  7. namespace Svelto.ECS
  8. {
  9. public sealed partial class EnginesRoot
  10. {
  11. static EnginesRoot()
  12. {
  13. GroupHashMap.Init();
  14. SerializationDescriptorMap.Init();
  15. }
  16. /// <summary>
  17. /// Engines root contextualize your engines and entities. You don't need to limit yourself to one EngineRoot
  18. /// as multiple engines root could promote separation of scopes. The EntitySubmissionScheduler checks
  19. /// periodically if new entity must be submitted to the database and the engines. It's an external
  20. /// dependencies to be independent by the running platform as the user can define it.
  21. /// The EntitySubmissionScheduler cannot hold an EnginesRoot reference, that's why
  22. /// it must receive a weak reference of the EnginesRoot callback.
  23. /// </summary>
  24. public EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler)
  25. {
  26. _entitiesOperations = new FasterDictionary<ulong, EntitySubmitOperation>();
  27. #if UNITY_NATIVE //because of the thread count, ATM this is only for unity
  28. _nativeSwapOperationQueue = new DataStructures.AtomicNativeBags(Allocator.Persistent);
  29. _nativeRemoveOperationQueue = new DataStructures.AtomicNativeBags(Allocator.Persistent);
  30. _nativeAddOperationQueue = new DataStructures.AtomicNativeBags(Allocator.Persistent);
  31. #endif
  32. _serializationDescriptorMap = new SerializationDescriptorMap();
  33. _maxNumberOfOperationsPerFrame = uint.MaxValue;
  34. _reactiveEnginesAddRemove = new FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer>>();
  35. _reactiveEnginesAddRemoveOnDispose =
  36. new FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer>>();
  37. _reactiveEnginesSwap = new FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer>>();
  38. _reactiveEnginesSubmission = new FasterList<IReactOnSubmission>();
  39. _enginesSet = new FasterList<IEngine>();
  40. _enginesTypeSet = new HashSet<Type>();
  41. _disposableEngines = new FasterList<IDisposable>();
  42. _transientEntitiesOperations = new FasterList<EntitySubmitOperation>();
  43. _groupEntityComponentsDB =
  44. new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>();
  45. _groupsPerEntity =
  46. new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>();
  47. _groupedEntityToAdd = new DoubleBufferedEntitiesToAdd();
  48. _entityStreams = EntitiesStreams.Create();
  49. _groupFilters =
  50. new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>();
  51. _entityLocator.InitEntityReferenceMap();
  52. _entitiesDB = new EntitiesDB(this,_entityLocator);
  53. scheduler = entitiesComponentScheduler;
  54. scheduler.onTick = new EntitiesSubmitter(this);
  55. #if UNITY_NATIVE
  56. AllocateNativeOperations();
  57. #endif
  58. }
  59. public EnginesRoot
  60. (EntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly) :
  61. this(entitiesComponentScheduler)
  62. {
  63. _isDeserializationOnly = isDeserializationOnly;
  64. }
  65. public EntitiesSubmissionScheduler scheduler { get; }
  66. /// <summary>
  67. /// Dispose an EngineRoot once not used anymore, so that all the
  68. /// engines are notified with the entities removed.
  69. /// It's a clean up process.
  70. /// </summary>
  71. public void Dispose()
  72. {
  73. _isDisposing = true;
  74. using (var profiler = new PlatformProfiler("Final Dispose"))
  75. {
  76. //Note: The engines are disposed before the the remove callback to give the chance to behave
  77. //differently if a remove happens as a consequence of a dispose
  78. //The pattern is to implement the IDisposable interface and set a flag in the engine. The
  79. //remove callback will then behave differently according the flag.
  80. foreach (var engine in _disposableEngines)
  81. try
  82. {
  83. if (engine is IDisposingEngine dengine)
  84. dengine.isDisposing = true;
  85. engine.Dispose();
  86. }
  87. catch (Exception e)
  88. {
  89. Console.LogException(e);
  90. }
  91. foreach (var groups in _groupEntityComponentsDB)
  92. foreach (var entityList in groups.Value)
  93. try
  94. {
  95. entityList.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemoveOnDispose, profiler
  96. , new ExclusiveGroupStruct(groups.Key));
  97. }
  98. catch (Exception e)
  99. {
  100. Console.LogException(e);
  101. }
  102. foreach (var groups in _groupEntityComponentsDB)
  103. foreach (var entityList in groups.Value)
  104. entityList.Value.Dispose();
  105. foreach (var type in _groupFilters)
  106. foreach (var group in type.Value)
  107. group.Value.Dispose();
  108. _groupFilters.Clear();
  109. #if UNITY_NATIVE
  110. _nativeAddOperationQueue.Dispose();
  111. _nativeRemoveOperationQueue.Dispose();
  112. _nativeSwapOperationQueue.Dispose();
  113. #endif
  114. _groupEntityComponentsDB.Clear();
  115. _groupsPerEntity.Clear();
  116. _disposableEngines.Clear();
  117. _enginesSet.Clear();
  118. _enginesTypeSet.Clear();
  119. _reactiveEnginesSwap.Clear();
  120. _reactiveEnginesAddRemove.Clear();
  121. _reactiveEnginesAddRemoveOnDispose.Clear();
  122. _reactiveEnginesSubmission.Clear();
  123. _entitiesOperations.Clear();
  124. _transientEntitiesOperations.Clear();
  125. _groupedEntityToAdd.Dispose();
  126. _entityLocator.DisposeEntityReferenceMap();
  127. _entityStreams.Dispose();
  128. scheduler.Dispose();
  129. }
  130. GC.SuppressFinalize(this);
  131. }
  132. public void AddEngine(IEngine engine)
  133. {
  134. var type = engine.GetType();
  135. var refWrapper = new RefWrapperType(type);
  136. DBC.ECS.Check.Require(engine != null, "Engine to add is invalid or null");
  137. DBC.ECS.Check.Require(
  138. _enginesTypeSet.Contains(refWrapper) == false
  139. || type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true
  140. , "The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute "
  141. .FastConcat(engine.ToString()));
  142. try
  143. {
  144. if (engine is IReactOnAddAndRemove viewEngine)
  145. CheckReactEngineComponents(viewEngine, _reactiveEnginesAddRemove, type.Name);
  146. if (engine is IReactOnDispose viewEngineDispose)
  147. CheckReactEngineComponents(viewEngineDispose, _reactiveEnginesAddRemoveOnDispose, type.Name);
  148. if (engine is IReactOnSwap viewEngineSwap)
  149. CheckReactEngineComponents(viewEngineSwap, _reactiveEnginesSwap, type.Name);
  150. if (engine is IReactOnSubmission submissionEngine)
  151. _reactiveEnginesSubmission.Add(submissionEngine);
  152. _enginesTypeSet.Add(refWrapper);
  153. _enginesSet.Add(engine);
  154. if (engine is IDisposable)
  155. _disposableEngines.Add(engine as IDisposable);
  156. if (engine is IQueryingEntitiesEngine queryableEntityComponentEngine)
  157. {
  158. queryableEntityComponentEngine.entitiesDB = _entitiesDB;
  159. queryableEntityComponentEngine.Ready();
  160. }
  161. }
  162. catch (Exception e)
  163. {
  164. throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " ")
  165. , e);
  166. }
  167. }
  168. void NotifyReactiveEnginesOnSubmission()
  169. {
  170. var enginesCount = _reactiveEnginesSubmission.count;
  171. for (var i = 0; i < enginesCount; i++)
  172. _reactiveEnginesSubmission[i].EntitiesSubmitted();
  173. }
  174. ~EnginesRoot()
  175. {
  176. Console.LogWarning("Engines Root has been garbage collected, don't forget to call Dispose()!");
  177. Dispose();
  178. }
  179. void CheckReactEngineComponents<T>
  180. (T engine, FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer>> engines, string typeName)
  181. where T : class, IReactEngine
  182. {
  183. var interfaces = engine.GetType().GetInterfaces();
  184. foreach (var interf in interfaces)
  185. if (interf.IsGenericTypeEx() && typeof(T).IsAssignableFrom(interf))
  186. {
  187. var genericArguments = interf.GetGenericArgumentsEx();
  188. AddEngineToList(engine, genericArguments, engines, typeName);
  189. }
  190. }
  191. static void AddEngineToList<T>
  192. (T engine, Type[] entityComponentTypes
  193. , FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer>> engines, string typeName)
  194. where T : class, IReactEngine
  195. {
  196. for (var i = 0; i < entityComponentTypes.Length; i++)
  197. {
  198. var type = entityComponentTypes[i];
  199. if (engines.TryGetValue(new RefWrapperType(type), out var list) == false)
  200. {
  201. list = new FasterList<ReactEngineContainer>();
  202. engines.Add(new RefWrapperType(type), list);
  203. }
  204. list.Add(new ReactEngineContainer(engine, typeName));
  205. }
  206. }
  207. internal bool _isDisposing;
  208. readonly FasterList<IDisposable> _disposableEngines;
  209. readonly FasterList<IEngine> _enginesSet;
  210. readonly HashSet<Type> _enginesTypeSet;
  211. readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer>> _reactiveEnginesAddRemove;
  212. readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer>> _reactiveEnginesAddRemoveOnDispose;
  213. readonly FasterList<IReactOnSubmission> _reactiveEnginesSubmission;
  214. readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer>> _reactiveEnginesSwap;
  215. public struct EntitiesSubmitter
  216. {
  217. public EntitiesSubmitter(EnginesRoot enginesRoot) : this()
  218. {
  219. _enginesRoot = new Svelto.DataStructures.WeakReference<EnginesRoot>(enginesRoot);
  220. _privateSubmitEntities =
  221. _enginesRoot.Target.SingleSubmission(new PlatformProfiler());
  222. submitEntities = Invoke(); //this must be last to capture all the variables
  223. }
  224. IEnumerator<bool> Invoke()
  225. {
  226. while (true)
  227. {
  228. DBC.ECS.Check.Require(_enginesRoot.IsValid, "ticking an GCed engines root?");
  229. var enginesRootTarget = _enginesRoot.Target;
  230. var entitiesSubmissionScheduler = enginesRootTarget.scheduler;
  231. if (entitiesSubmissionScheduler.paused == false)
  232. {
  233. DBC.ECS.Check.Require(entitiesSubmissionScheduler.isRunning == false
  234. , "A submission started while the previous one was still flushing");
  235. entitiesSubmissionScheduler.isRunning = true;
  236. using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission"))
  237. {
  238. var iterations = 0;
  239. var hasEverSubmitted = false;
  240. #if UNITY_NATIVE
  241. enginesRootTarget.FlushNativeOperations(profiler);
  242. #endif
  243. //todo: proper unit test structural changes made as result of add/remove callbacks
  244. while (enginesRootTarget.HasMadeNewStructuralChangesInThisIteration() && iterations++ < 5)
  245. {
  246. hasEverSubmitted = true;
  247. while (true)
  248. {
  249. _privateSubmitEntities.MoveNext();
  250. if (_privateSubmitEntities.Current == true)
  251. {
  252. using (profiler.Yield())
  253. {
  254. yield return true;
  255. }
  256. }
  257. else
  258. break;
  259. }
  260. #if UNITY_NATIVE
  261. if (enginesRootTarget.HasMadeNewStructuralChangesInThisIteration())
  262. enginesRootTarget.FlushNativeOperations(profiler);
  263. #endif
  264. }
  265. #if DEBUG && !PROFILE_SVELTO
  266. if (iterations == 5)
  267. throw new ECSException("possible circular submission detected");
  268. #endif
  269. if (hasEverSubmitted)
  270. enginesRootTarget.NotifyReactiveEnginesOnSubmission();
  271. }
  272. entitiesSubmissionScheduler.isRunning = false;
  273. ++entitiesSubmissionScheduler.iteration;
  274. }
  275. yield return false;
  276. }
  277. }
  278. public uint maxNumberOfOperationsPerFrame
  279. {
  280. set => _enginesRoot.Target._maxNumberOfOperationsPerFrame = value;
  281. }
  282. readonly Svelto.DataStructures.WeakReference<EnginesRoot> _enginesRoot;
  283. internal readonly IEnumerator<bool> submitEntities;
  284. readonly IEnumerator<bool> _privateSubmitEntities;
  285. }
  286. }
  287. }