Mirror of Svelto.ECS because we're a fan of it
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

422 lines
18KB

  1. #if PROFILE_SVELTO && DEBUG
  2. #warning the global define PROFILE_SVELTO should be used only when it's necessary to profile in order to reduce the overhead of debug code. Normally remove this define to get insights when errors happen
  3. #endif
  4. using System;
  5. using System.Collections.Generic;
  6. using DBC.ECS;
  7. using Svelto.Common;
  8. using Svelto.DataStructures;
  9. using Svelto.ECS.Internal;
  10. using Svelto.ECS.Schedulers;
  11. namespace Svelto.ECS
  12. {
  13. public partial class EnginesRoot
  14. {
  15. static EnginesRoot()
  16. {
  17. GroupHashMap.Init();
  18. //SharedDictonary.Init();
  19. SerializationDescriptorMap.Init();
  20. _swapEntities = SwapEntities;
  21. _removeEntities = RemoveEntities;
  22. _removeGroup = RemoveGroup;
  23. _swapGroup = SwapGroup;
  24. }
  25. /// <summary>
  26. /// Engines root contextualize your engines and entities. You don't need to limit yourself to one EngineRoot
  27. /// as multiple engines root could promote separation of scopes. The EntitySubmissionScheduler checks
  28. /// periodically if new entity must be submitted to the database and the engines. It's an external
  29. /// dependencies to be independent by the running platform as the user can define it.
  30. /// The EntitySubmissionScheduler cannot hold an EnginesRoot reference, that's why
  31. /// it must receive a weak reference of the EnginesRoot callback.
  32. /// </summary>
  33. public EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler)
  34. {
  35. _entitiesOperations = new EntitiesOperations();
  36. _idChecker = new FasterDictionary<ExclusiveGroupStruct, HashSet<uint>>();
  37. _cachedRangeOfSubmittedIndices = new FasterList<(uint, uint)>();
  38. _transientEntityIDsLeftAndAffectedByRemoval = new FasterList<uint>();
  39. _transientEntityIDsLeftWithoutDuplicates = new FasterDictionary<uint, int>();
  40. _multipleOperationOnSameEGIDChecker = new FasterDictionary<EGID, uint>();
  41. #if UNITY_NATIVE //because of the thread count, ATM this is only for unity
  42. _nativeSwapOperationQueue = new AtomicNativeBags(Allocator.Persistent);
  43. _nativeRemoveOperationQueue = new AtomicNativeBags(Allocator.Persistent);
  44. _nativeAddOperationQueue = new AtomicNativeBags(Allocator.Persistent);
  45. #endif
  46. _serializationDescriptorMap = new SerializationDescriptorMap();
  47. _reactiveEnginesAdd = new FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnAdd>>>();
  48. _reactiveEnginesAddEx =
  49. new FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnAddEx>>>();
  50. _reactiveEnginesRemove =
  51. new FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnRemove>>>();
  52. _reactiveEnginesRemoveEx =
  53. new FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnRemoveEx>>>();
  54. _reactiveEnginesSwap =
  55. new FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnSwap>>>();
  56. _reactiveEnginesSwapEx =
  57. new FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnSwapEx>>>();
  58. _reactiveEnginesDispose =
  59. new FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnDispose>>>();
  60. _reactiveEnginesSubmission = new FasterList<IReactOnSubmission>();
  61. _reactiveEnginesSubmissionStarted = new FasterList<IReactOnSubmissionStarted>();
  62. _enginesSet = new FasterList<IEngine>();
  63. _enginesTypeSet = new HashSet<Type>();
  64. _disposableEngines = new FasterList<IDisposable>();
  65. _groupEntityComponentsDB =
  66. new FasterDictionary<ExclusiveGroupStruct, FasterDictionary<RefWrapperType, ITypeSafeDictionary>>();
  67. _groupsPerEntity =
  68. new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, ITypeSafeDictionary>>();
  69. _groupedEntityToAdd = new DoubleBufferedEntitiesToAdd();
  70. _entityStreams = EntitiesStreams.Create();
  71. #if SVELTO_LEGACY_FILTERS
  72. _groupFilters =
  73. new FasterDictionary<RefWrapperType, FasterDictionary<ExclusiveGroupStruct, LegacyGroupFilters>>();
  74. #endif
  75. _entityLocator.InitEntityReferenceMap();
  76. _entitiesDB = new EntitiesDB(this, _entityLocator);
  77. InitFilters();
  78. scheduler = entitiesComponentScheduler;
  79. scheduler.onTick = new EntitiesSubmitter(this);
  80. #if UNITY_NATIVE
  81. AllocateNativeOperations();
  82. #endif
  83. }
  84. protected EnginesRoot(EntitiesSubmissionScheduler entitiesComponentScheduler,
  85. EnginesReadyOption enginesWaitForReady): this(entitiesComponentScheduler)
  86. {
  87. _enginesWaitForReady = enginesWaitForReady;
  88. }
  89. public EntitiesSubmissionScheduler scheduler { get; }
  90. /// <summary>
  91. /// Dispose an EngineRoot once not used anymore, so that all the
  92. /// engines are notified with the entities removed.
  93. /// It's a clean up process.
  94. /// </summary>
  95. public void Dispose()
  96. {
  97. Dispose(true);
  98. GC.SuppressFinalize(this);
  99. }
  100. public bool IsValid()
  101. {
  102. return _isDisposed == false;
  103. }
  104. public void AddEngine(IEngine engine)
  105. {
  106. var type = engine.GetType();
  107. var refWrapper = new RefWrapperType(type);
  108. Check.Require(engine != null, "Engine to add is invalid or null");
  109. Check.Require(
  110. _enginesTypeSet.Contains(refWrapper) == false ||
  111. type.ContainsCustomAttribute(typeof(AllowMultipleAttribute)),
  112. "The same engine has been added more than once, if intentional, use [AllowMultiple] class attribute "
  113. .FastConcat(engine.ToString()));
  114. try
  115. {
  116. if (engine is IReactOnAdd viewEngineAdd)
  117. #pragma warning disable CS0612
  118. CheckReactEngineComponents(typeof(IReactOnAdd<>), viewEngineAdd, _reactiveEnginesAdd, type.Name);
  119. #pragma warning restore CS0612
  120. if (engine is IReactOnAddEx viewEngineAddEx)
  121. CheckReactEngineComponents(
  122. typeof(IReactOnAddEx<>), viewEngineAddEx, _reactiveEnginesAddEx, type.Name);
  123. if (engine is IReactOnRemove viewEngineRemove)
  124. CheckReactEngineComponents(
  125. #pragma warning disable CS0612
  126. typeof(IReactOnRemove<>), viewEngineRemove, _reactiveEnginesRemove, type.Name);
  127. #pragma warning restore CS0612
  128. if (engine is IReactOnRemoveEx viewEngineRemoveEx)
  129. CheckReactEngineComponents(
  130. typeof(IReactOnRemoveEx<>), viewEngineRemoveEx, _reactiveEnginesRemoveEx, type.Name);
  131. if (engine is IReactOnDispose viewEngineDispose)
  132. CheckReactEngineComponents(
  133. typeof(IReactOnDispose<>), viewEngineDispose, _reactiveEnginesDispose, type.Name);
  134. if (engine is IReactOnSwap viewEngineSwap)
  135. #pragma warning disable CS0612
  136. CheckReactEngineComponents(typeof(IReactOnSwap<>), viewEngineSwap, _reactiveEnginesSwap, type.Name);
  137. #pragma warning restore CS0612
  138. if (engine is IReactOnSwapEx viewEngineSwapEx)
  139. CheckReactEngineComponents(
  140. typeof(IReactOnSwapEx<>), viewEngineSwapEx, _reactiveEnginesSwapEx, type.Name);
  141. if (engine is IReactOnSubmission submissionEngine)
  142. _reactiveEnginesSubmission.Add(submissionEngine);
  143. if (engine is IReactOnSubmissionStarted submissionEngineStarted)
  144. _reactiveEnginesSubmissionStarted.Add(submissionEngineStarted);
  145. _enginesTypeSet.Add(refWrapper);
  146. _enginesSet.Add(engine);
  147. if (engine is IDisposable)
  148. _disposableEngines.Add(engine as IDisposable);
  149. if (engine is IQueryingEntitiesEngine queryableEntityComponentEngine)
  150. queryableEntityComponentEngine.entitiesDB = _entitiesDB;
  151. if (_enginesWaitForReady == EnginesReadyOption.ReadyAsAdded && engine is IGetReadyEngine getReadyEngine)
  152. getReadyEngine.Ready();
  153. }
  154. catch (Exception e)
  155. {
  156. throw new ECSException(
  157. "Code crashed while adding engine ".FastConcat(engine.GetType().ToString(), " "),
  158. e);
  159. }
  160. }
  161. public void Ready()
  162. {
  163. Check.Require(
  164. _enginesWaitForReady == EnginesReadyOption.WaitForReady,
  165. "The engine has not been initialise to wait for an external ready trigger");
  166. foreach (var engine in _enginesSet)
  167. if (engine is IGetReadyEngine getReadyEngine)
  168. getReadyEngine.Ready();
  169. }
  170. static void AddEngineToList<T>(T engine, Type[] entityComponentTypes,
  171. FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<T>>> engines, string typeName)
  172. where T : class, IReactEngine
  173. {
  174. for (var i = 0; i < entityComponentTypes.Length; i++)
  175. {
  176. var type = entityComponentTypes[i];
  177. if (engines.TryGetValue(new RefWrapperType(type), out var list) == false)
  178. {
  179. list = new FasterList<ReactEngineContainer<T>>();
  180. engines.Add(new RefWrapperType(type), list);
  181. }
  182. list.Add(new ReactEngineContainer<T>(engine, typeName));
  183. }
  184. }
  185. void CheckReactEngineComponents<T>(Type genericDefinition, T engine,
  186. FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<T>>> engines, string typeName)
  187. where T : class, IReactEngine
  188. {
  189. var interfaces = engine.GetType().GetInterfaces();
  190. foreach (var interf in interfaces)
  191. {
  192. if (interf.IsGenericTypeEx() && interf.GetGenericTypeDefinition() == genericDefinition)
  193. {
  194. var genericArguments = interf.GetGenericArgumentsEx();
  195. AddEngineToList(engine, genericArguments, engines, typeName);
  196. }
  197. }
  198. }
  199. void Dispose(bool disposing)
  200. {
  201. using (var profiler = new PlatformProfiler("Final Dispose"))
  202. {
  203. //Note: The engines are disposed before the the remove callback to give the chance to behave
  204. //differently if a remove happens as a consequence of a dispose
  205. //The pattern is to implement the IDisposable interface and set a flag in the engine. The
  206. //remove callback will then behave differently according the flag.
  207. foreach (var engine in _disposableEngines)
  208. try
  209. {
  210. if (engine is IDisposingEngine dengine)
  211. dengine.isDisposing = true;
  212. engine.Dispose();
  213. }
  214. catch (Exception e)
  215. {
  216. Console.LogException(e);
  217. }
  218. foreach (var groups in _groupEntityComponentsDB)
  219. foreach (var entityList in groups.value)
  220. try
  221. {
  222. ITypeSafeDictionary typeSafeDictionary = entityList.value;
  223. typeSafeDictionary.ExecuteEnginesDisposeCallbacks_Group(
  224. _reactiveEnginesDispose, groups.key,
  225. profiler);
  226. }
  227. catch (Exception e)
  228. {
  229. Console.LogException(e);
  230. }
  231. foreach (var groups in _groupEntityComponentsDB)
  232. foreach (var entityList in groups.value)
  233. entityList.value.Dispose();
  234. #if SVELTO_LEGACY_FILTERS
  235. foreach (var type in _groupFilters)
  236. foreach (var group in type.value)
  237. group.value.Dispose();
  238. _groupFilters.Clear();
  239. #endif
  240. DisposeFilters();
  241. #if UNITY_NATIVE
  242. _nativeAddOperationQueue.Dispose();
  243. _nativeRemoveOperationQueue.Dispose();
  244. _nativeSwapOperationQueue.Dispose();
  245. #endif
  246. _groupEntityComponentsDB.Clear();
  247. _groupsPerEntity.Clear();
  248. _disposableEngines.Clear();
  249. _enginesSet.Clear();
  250. _enginesTypeSet.Clear();
  251. _reactiveEnginesSwap.Clear();
  252. _reactiveEnginesAdd.Clear();
  253. _reactiveEnginesRemove.Clear();
  254. _reactiveEnginesDispose.Clear();
  255. _reactiveEnginesSubmission.Clear();
  256. _reactiveEnginesSubmissionStarted.Clear();
  257. _groupedEntityToAdd.Dispose();
  258. _entityLocator.DisposeEntityReferenceMap();
  259. _entityStreams.Dispose();
  260. scheduler.Dispose();
  261. }
  262. _isDisposed = true;
  263. }
  264. void NotifyReactiveEnginesOnSubmission()
  265. {
  266. var enginesCount = _reactiveEnginesSubmission.count;
  267. for (var i = 0; i < enginesCount; i++)
  268. _reactiveEnginesSubmission[i].EntitiesSubmitted();
  269. }
  270. void NotifyReactiveEnginesOnSubmissionStarted()
  271. {
  272. var enginesCount = _reactiveEnginesSubmissionStarted.count;
  273. for (var i = 0; i < enginesCount; i++)
  274. _reactiveEnginesSubmissionStarted[i].EntitiesSubmissionStarting();
  275. }
  276. public readonly struct EntitiesSubmitter
  277. {
  278. public EntitiesSubmitter(EnginesRoot enginesRoot): this()
  279. {
  280. _enginesRoot = new DataStructures.WeakReference<EnginesRoot>(enginesRoot);
  281. }
  282. internal void SubmitEntities()
  283. {
  284. Check.Require(_enginesRoot.IsValid, "ticking an GCed engines root?");
  285. var enginesRootTarget = _enginesRoot.Target;
  286. var entitiesSubmissionScheduler = enginesRootTarget.scheduler;
  287. if (entitiesSubmissionScheduler.paused == false)
  288. {
  289. enginesRootTarget.NotifyReactiveEnginesOnSubmissionStarted();
  290. Check.Require(
  291. entitiesSubmissionScheduler.isRunning == false,
  292. "A submission started while the previous one was still flushing");
  293. entitiesSubmissionScheduler.isRunning = true;
  294. using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission"))
  295. {
  296. var iterations = 0;
  297. var hasEverSubmitted = false;
  298. // We need to clear transient filters before processing callbacks since the callbacks may add
  299. // new entities to these filters.
  300. enginesRootTarget.ClearTransientFilters();
  301. #if UNITY_NATIVE
  302. enginesRootTarget.FlushNativeOperations(profiler);
  303. #endif
  304. while (enginesRootTarget.HasMadeNewStructuralChangesInThisIteration()
  305. && iterations++ < MAX_SUBMISSION_ITERATIONS)
  306. {
  307. hasEverSubmitted = true;
  308. _enginesRoot.Target.SingleSubmission(profiler);
  309. #if UNITY_NATIVE
  310. enginesRootTarget.FlushNativeOperations(profiler);
  311. #endif
  312. }
  313. #if DEBUG && !PROFILE_SVELTO
  314. if (iterations == MAX_SUBMISSION_ITERATIONS)
  315. throw new ECSException("possible circular submission detected");
  316. #endif
  317. if (hasEverSubmitted)
  318. enginesRootTarget.NotifyReactiveEnginesOnSubmission();
  319. }
  320. entitiesSubmissionScheduler.isRunning = false;
  321. ++entitiesSubmissionScheduler.iteration;
  322. }
  323. }
  324. readonly DataStructures.WeakReference<EnginesRoot> _enginesRoot;
  325. }
  326. ~EnginesRoot()
  327. {
  328. Console.LogWarning("Engines Root has been garbage collected, don't forget to call Dispose()!");
  329. Dispose(false);
  330. }
  331. const int MAX_SUBMISSION_ITERATIONS = 10;
  332. readonly FasterList<IDisposable> _disposableEngines;
  333. readonly FasterList<IEngine> _enginesSet;
  334. readonly HashSet<Type> _enginesTypeSet;
  335. readonly EnginesReadyOption _enginesWaitForReady;
  336. readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnAdd>>> _reactiveEnginesAdd;
  337. readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnAddEx>>> _reactiveEnginesAddEx;
  338. readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnRemove>>> _reactiveEnginesRemove;
  339. readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnRemoveEx>>> _reactiveEnginesRemoveEx;
  340. readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnSwap>>> _reactiveEnginesSwap;
  341. readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnSwapEx>>> _reactiveEnginesSwapEx;
  342. readonly FasterDictionary<RefWrapperType, FasterList<ReactEngineContainer<IReactOnDispose>>> _reactiveEnginesDispose;
  343. readonly FasterList<IReactOnSubmission> _reactiveEnginesSubmission;
  344. readonly FasterList<IReactOnSubmissionStarted> _reactiveEnginesSubmissionStarted;
  345. bool _isDisposed;
  346. }
  347. public enum EnginesReadyOption
  348. {
  349. ReadyAsAdded,
  350. WaitForReady
  351. }
  352. }