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.

337 lines
15KB

  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. }