Mirror of Svelto.ECS because we're a fan of it
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 lines
12KB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Svelto.Common;
  5. using Svelto.DataStructures;
  6. using Svelto.ECS.Internal;
  7. using Svelto.ECS.Schedulers;
  8. namespace Svelto.ECS
  9. {
  10. public sealed partial class EnginesRoot
  11. {
  12. public struct EntitiesSubmitter
  13. {
  14. public EntitiesSubmitter(EnginesRoot enginesRoot) : this()
  15. {
  16. _weakReference = new Svelto.DataStructures.WeakReference<EnginesRoot>(enginesRoot);
  17. }
  18. public bool IsUnused => _weakReference.IsValid == false;
  19. public IEnumerator Invoke(uint maxNumberOfOperationsPerFrame)
  20. {
  21. var entitiesSubmissionScheduler = _weakReference.Target._scheduler;
  22. if (_weakReference.IsValid && entitiesSubmissionScheduler.paused == false)
  23. {
  24. var submitEntityComponents =
  25. _weakReference.Target.SubmitEntityComponents(maxNumberOfOperationsPerFrame);
  26. DBC.ECS.Check.Require(entitiesSubmissionScheduler.isRunning == false
  27. , "A submission started while the previous one was still flushing");
  28. entitiesSubmissionScheduler.isRunning = true;
  29. while (submitEntityComponents.MoveNext() == true)
  30. yield return null;
  31. entitiesSubmissionScheduler.isRunning = false;
  32. ++entitiesSubmissionScheduler.iteration;
  33. }
  34. }
  35. readonly Svelto.DataStructures.WeakReference<EnginesRoot> _weakReference;
  36. }
  37. readonly EntitiesSubmissionScheduler _scheduler;
  38. public EntitiesSubmissionScheduler scheduler => _scheduler;
  39. /// <summary>
  40. /// Engines root contextualize your engines and entities. You don't need to limit yourself to one EngineRoot
  41. /// as multiple engines root could promote separation of scopes. The EntitySubmissionScheduler checks
  42. /// periodically if new entity must be submitted to the database and the engines. It's an external
  43. /// dependencies to be independent by the running platform as the user can define it.
  44. /// The EntitySubmissionScheduler cannot hold an EnginesRoot reference, that's why
  45. /// it must receive a weak reference of the EnginesRoot callback.
  46. /// </summary>
  47. public EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler)
  48. {
  49. _entitiesOperations = new FasterDictionary<ulong, EntitySubmitOperation>();
  50. serializationDescriptorMap = new SerializationDescriptorMap();
  51. _reactiveEnginesAddRemove = new FasterDictionary<RefWrapperType, FasterList<IReactEngine>>();
  52. _reactiveEnginesAddRemoveOnDispose = new FasterDictionary<RefWrapperType, FasterList<IReactEngine>>();
  53. _reactiveEnginesSwap = new FasterDictionary<RefWrapperType, FasterList<IReactEngine>>();
  54. _reactiveEnginesSubmission = new FasterList<IReactOnSubmission>();
  55. _enginesSet = new FasterList<IEngine>();
  56. _enginesTypeSet = new HashSet<Type>();
  57. _disposableEngines = new FasterList<IDisposable>();
  58. _transientEntitiesOperations = new FasterList<EntitySubmitOperation>();
  59. _groupEntityComponentsDB =
  60. new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>();
  61. _groupsPerEntity =
  62. new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>();
  63. _groupedEntityToAdd = new DoubleBufferedEntitiesToAdd();
  64. _entityStreams = EntitiesStreams.Create();
  65. _groupFilters =
  66. new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, GroupFilters>>();
  67. _entitiesDB = new EntitiesDB(this);
  68. _scheduler = entitiesComponentScheduler;
  69. _scheduler.onTick = new EntitiesSubmitter(this);
  70. #if UNITY_NATIVE
  71. AllocateNativeOperations();
  72. #endif
  73. }
  74. public EnginesRoot
  75. (EntitiesSubmissionScheduler entitiesComponentScheduler, bool isDeserializationOnly) :
  76. this(entitiesComponentScheduler)
  77. {
  78. _isDeserializationOnly = isDeserializationOnly;
  79. }
  80. /// <summary>
  81. /// Dispose an EngineRoot once not used anymore, so that all the
  82. /// engines are notified with the entities removed.
  83. /// It's a clean up process.
  84. /// </summary>
  85. public void Dispose()
  86. {
  87. _isDisposing = true;
  88. using (var profiler = new PlatformProfiler("Final Dispose"))
  89. {
  90. //Note: The engines are disposed before the the remove callback to give the chance to behave
  91. //differently if a remove happens as a consequence of a dispose
  92. //The pattern is to implement the IDisposable interface and set a flag in the engine. The
  93. //remove callback will then behave differently according the flag.
  94. foreach (var engine in _disposableEngines)
  95. {
  96. try
  97. {
  98. if (engine is IDisposingEngine dengine)
  99. dengine.isDisposing = true;
  100. engine.Dispose();
  101. }
  102. catch (Exception e)
  103. {
  104. Svelto.Console.LogException(e);
  105. }
  106. }
  107. foreach (var groups in _groupEntityComponentsDB)
  108. {
  109. foreach (var entityList in groups.Value)
  110. try
  111. {
  112. entityList.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemoveOnDispose, profiler
  113. , new ExclusiveGroupStruct(groups.Key));
  114. }
  115. catch (Exception e)
  116. {
  117. Svelto.Console.LogException(e);
  118. }
  119. }
  120. foreach (var groups in _groupEntityComponentsDB)
  121. {
  122. foreach (var entityList in groups.Value)
  123. entityList.Value.Dispose();
  124. }
  125. foreach (var type in _groupFilters)
  126. foreach (var group in type.Value)
  127. group.Value.Dispose();
  128. _groupFilters.Clear();
  129. #if UNITY_NATIVE
  130. _addOperationQueue.Dispose();
  131. _removeOperationQueue.Dispose();
  132. _swapOperationQueue.Dispose();
  133. #endif
  134. _groupEntityComponentsDB.Clear();
  135. _groupsPerEntity.Clear();
  136. _disposableEngines.Clear();
  137. _enginesSet.Clear();
  138. _enginesTypeSet.Clear();
  139. _reactiveEnginesSwap.Clear();
  140. _reactiveEnginesAddRemove.Clear();
  141. _reactiveEnginesAddRemoveOnDispose.Clear();
  142. _reactiveEnginesSubmission.Clear();
  143. _entitiesOperations.Clear();
  144. _transientEntitiesOperations.Clear();
  145. _groupedEntityToAdd.Dispose();
  146. _entityStreams.Dispose();
  147. scheduler.Dispose();
  148. }
  149. GC.SuppressFinalize(this);
  150. }
  151. ~EnginesRoot()
  152. {
  153. Console.LogWarning("Engines Root has been garbage collected, don't forget to call Dispose()!");
  154. Dispose();
  155. }
  156. public void AddEngine(IEngine engine)
  157. {
  158. var type = engine.GetType();
  159. var refWrapper = new RefWrapperType(type);
  160. DBC.ECS.Check.Require(engine != null, "Engine to add is invalid or null");
  161. DBC.ECS.Check.Require(
  162. _enginesTypeSet.Contains(refWrapper) == false
  163. || type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)) == true
  164. , "The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute "
  165. .FastConcat(engine.ToString()));
  166. try
  167. {
  168. if (engine is IReactOnAddAndRemove viewEngine)
  169. CheckReactEngineComponents(viewEngine, _reactiveEnginesAddRemove);
  170. if (engine is IReactOnDispose viewEngineDispose)
  171. CheckReactEngineComponents(viewEngineDispose, _reactiveEnginesAddRemoveOnDispose);
  172. if (engine is IReactOnSwap viewEngineSwap)
  173. CheckReactEngineComponents(viewEngineSwap, _reactiveEnginesSwap);
  174. if (engine is IReactOnSubmission submissionEngine)
  175. _reactiveEnginesSubmission.Add(submissionEngine);
  176. _enginesTypeSet.Add(refWrapper);
  177. _enginesSet.Add(engine);
  178. if (engine is IDisposable)
  179. _disposableEngines.Add(engine as IDisposable);
  180. if (engine is IQueryingEntitiesEngine queryableEntityComponentEngine)
  181. {
  182. queryableEntityComponentEngine.entitiesDB = _entitiesDB;
  183. queryableEntityComponentEngine.Ready();
  184. }
  185. }
  186. catch (Exception e)
  187. {
  188. throw new ECSException("Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " ")
  189. , e);
  190. }
  191. }
  192. void CheckReactEngineComponents<T>(T engine, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines)
  193. where T : class, IReactEngine
  194. {
  195. var interfaces = engine.GetType().GetInterfaces();
  196. foreach (var interf in interfaces)
  197. {
  198. if (interf.IsGenericTypeEx() && typeof(T).IsAssignableFrom(interf))
  199. {
  200. var genericArguments = interf.GetGenericArgumentsEx();
  201. AddEngineToList(engine, genericArguments, engines);
  202. }
  203. }
  204. }
  205. static void AddEngineToList<T>
  206. (T engine, Type[] entityComponentTypes, FasterDictionary<RefWrapperType, FasterList<IReactEngine>> engines)
  207. where T : class, IReactEngine
  208. {
  209. for (var i = 0; i < entityComponentTypes.Length; i++)
  210. {
  211. var type = entityComponentTypes[i];
  212. if (engines.TryGetValue(new RefWrapperType(type), out var list) == false)
  213. {
  214. list = new FasterList<IReactEngine>();
  215. engines.Add(new RefWrapperType(type), list);
  216. }
  217. list.Add(engine);
  218. }
  219. }
  220. readonly FasterDictionary<RefWrapperType, FasterList<IReactEngine>> _reactiveEnginesAddRemove;
  221. readonly FasterDictionary<RefWrapperType, FasterList<IReactEngine>> _reactiveEnginesAddRemoveOnDispose;
  222. readonly FasterDictionary<RefWrapperType, FasterList<IReactEngine>> _reactiveEnginesSwap;
  223. readonly FasterList<IReactOnSubmission> _reactiveEnginesSubmission;
  224. readonly FasterList<IDisposable> _disposableEngines;
  225. readonly FasterList<IEngine> _enginesSet;
  226. readonly HashSet<Type> _enginesTypeSet;
  227. internal bool _isDisposing;
  228. }
  229. }