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.

328 lines
10KB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using Svelto.DataStructures;
  5. using UnityEngine;
  6. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  7. using Svelto.ECS.Profiler;
  8. #endif
  9. namespace Svelto.ECS
  10. {
  11. class Scheduler : MonoBehaviour
  12. {
  13. IEnumerator Start()
  14. {
  15. while (true)
  16. {
  17. yield return new WaitForEndOfFrame();
  18. OnTick();
  19. }
  20. }
  21. internal Action OnTick;
  22. }
  23. public sealed class EnginesRoot : IEnginesRoot, IEntityFactory
  24. {
  25. public EnginesRoot()
  26. {
  27. _nodeEngines = new Dictionary<Type, FasterList<INodeEngine<INode>>>();
  28. _engineRootWeakReference = new WeakReference<EnginesRoot>(this);
  29. _otherEnginesReferences = new FasterList<IEngine>();
  30. _nodesDB = new Dictionary<Type, FasterList<INode>>();
  31. _nodesDBdic = new Dictionary<Type, Dictionary<int, INode>>();
  32. _nodesToAdd = new FasterList<INode>();
  33. _groupNodesToAdd = new FasterList<INode>();
  34. _nodesDBgroups = new Dictionary<Type, FasterList<INode>>();
  35. GameObject go = new GameObject("ECSScheduler");
  36. go.AddComponent<Scheduler>().OnTick += SubmitNodes;
  37. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  38. GameObject debugEngineObject = new GameObject("Engine Debugger");
  39. debugEngineObject.gameObject.AddComponent<EngineProfilerBehaviour>();
  40. #endif
  41. }
  42. void SubmitNodes()
  43. {
  44. int groupNodesCount;
  45. int nodesCount;
  46. bool newNodesHaveBeenAddedWhileIterating;
  47. int startNodes = 0;
  48. int startGroupNodes = 0;
  49. int numberOfReenteringLoops = 0;
  50. do
  51. {
  52. groupNodesCount = _groupNodesToAdd.Count;
  53. nodesCount = _nodesToAdd.Count;
  54. for (int i = startNodes; i < nodesCount; i++)
  55. {
  56. var node = _nodesToAdd[i];
  57. AddNodeToTheDB(node, node.GetType());
  58. }
  59. for (int i = startGroupNodes; i < groupNodesCount; i++)
  60. {
  61. var node = _groupNodesToAdd[i];
  62. AddNodeToGroupDB(node, node.GetType());
  63. }
  64. for (int i = startNodes; i < nodesCount; i++)
  65. {
  66. var node = _nodesToAdd[i];
  67. AddNodeToTheSuitableEngines(node, node.GetType());
  68. }
  69. for (int i = startGroupNodes; i < groupNodesCount; i++)
  70. {
  71. var node = _groupNodesToAdd[i];
  72. AddNodeToTheSuitableEngines(node, node.GetType());
  73. }
  74. newNodesHaveBeenAddedWhileIterating = _groupNodesToAdd.Count > groupNodesCount || _nodesToAdd.Count > nodesCount;
  75. startNodes = nodesCount;
  76. startGroupNodes = groupNodesCount;
  77. if (numberOfReenteringLoops > 5)
  78. throw new Exception("possible infinite loop found creating Entities inside INodesEngine Add method, please consider building entities outside INodesEngine Add method");
  79. numberOfReenteringLoops++;
  80. } while (newNodesHaveBeenAddedWhileIterating);
  81. _nodesToAdd.Clear();
  82. _groupNodesToAdd.Clear();
  83. }
  84. public void AddEngine(IEngine engine)
  85. {
  86. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  87. EngineProfiler.AddEngine(engine);
  88. #endif
  89. if (engine is IQueryableNodeEngine)
  90. (engine as IQueryableNodeEngine).nodesDB = new EngineNodeDB(_nodesDB, _nodesDBdic, _nodesDBgroups);
  91. if (engine is INodesEngine)
  92. {
  93. var nodesEngine = engine as INodesEngine;
  94. AddEngine(nodesEngine, nodesEngine.AcceptedNodes(), _nodeEngines);
  95. return;
  96. }
  97. var baseType = engine.GetType().BaseType;
  98. if (baseType.IsGenericType)
  99. {
  100. var genericType = baseType.GetGenericTypeDefinition();
  101. if (genericType == typeof(SingleNodeEngine<>))
  102. {
  103. AddEngine(engine as INodeEngine<INode>, baseType.GetGenericArguments(), _nodeEngines);
  104. return;
  105. }
  106. }
  107. _otherEnginesReferences.Add(engine);
  108. }
  109. public void BuildEntity(int ID, EntityDescriptor ed)
  110. {
  111. var entityNodes = ed.BuildNodes(ID, (node) =>
  112. {
  113. if (_engineRootWeakReference.IsValid == true)
  114. InternalRemove(node);
  115. });
  116. _nodesToAdd.AddRange(entityNodes);
  117. }
  118. public void BuildEntityGroup(int groupID, EntityDescriptor ed)
  119. {
  120. var entityNodes = ed.BuildNodes(groupID, (node) =>
  121. {
  122. if (_engineRootWeakReference.IsValid == true)
  123. InternalGroupRemove(node);
  124. });
  125. _groupNodesToAdd.AddRange(entityNodes);
  126. }
  127. static void AddEngine<T>(T engine, Type[] types, Dictionary<Type, FasterList<INodeEngine<INode>>> engines) where T : INodeEngine<INode>
  128. {
  129. for (int i = 0; i < types.Length; i++)
  130. {
  131. FasterList<INodeEngine<INode>> list;
  132. var type = types[i];
  133. if (engines.TryGetValue(type, out list) == false)
  134. {
  135. list = new FasterList<INodeEngine<INode>>();
  136. engines.Add(type, list);
  137. }
  138. list.Add(engine);
  139. }
  140. }
  141. void AddNodeToGroupDB(INode node, Type nodeType)
  142. {
  143. FasterList<INode> nodes;
  144. if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == false)
  145. nodes = _nodesDBgroups[nodeType] = new FasterList<INode>();
  146. nodes.Add(node);
  147. AddNodeToNodesDictionary(node, nodeType);
  148. }
  149. void AddNodeToTheDB<T>(T node, Type nodeType) where T : INode
  150. {
  151. FasterList<INode> nodes;
  152. if (_nodesDB.TryGetValue(nodeType, out nodes) == false)
  153. nodes = _nodesDB[nodeType] = new FasterList<INode>();
  154. nodes.Add(node);
  155. AddNodeToNodesDictionary(node, nodeType);
  156. }
  157. void AddNodeToNodesDictionary<T>(T node, Type nodeType) where T : INode
  158. {
  159. if (node is NodeWithID)
  160. {
  161. Dictionary<int, INode> nodesDic;
  162. if (_nodesDBdic.TryGetValue(nodeType, out nodesDic) == false)
  163. nodesDic = _nodesDBdic[nodeType] = new Dictionary<int, INode>();
  164. nodesDic[(node as NodeWithID).ID] = node;
  165. }
  166. }
  167. void AddNodeToTheSuitableEngines<T>(T node, Type nodeType) where T : INode
  168. {
  169. FasterList<INodeEngine<INode>> enginesForNode;
  170. if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
  171. {
  172. for (int j = 0; j < enginesForNode.Count; j++)
  173. {
  174. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  175. EngineProfiler.MonitorAddDuration(AddNodeToEngine, enginesForNode[j], node);
  176. #else
  177. enginesForNode[j].Add(node);
  178. #endif
  179. }
  180. }
  181. }
  182. void RemoveNodeFromTheDB<T>(T node, Type nodeType) where T : INode
  183. {
  184. FasterList<INode> nodes;
  185. if (_nodesDB.TryGetValue(nodeType, out nodes) == true)
  186. nodes.UnorderredRemove(node); //should I remove it from the dictionary if length is zero?
  187. RemoveNodeFromNodesDictionary(node, nodeType);
  188. }
  189. void RemoveNodeFromNodesDictionary<T>(T node, Type nodeType) where T : INode
  190. {
  191. if (node is NodeWithID)
  192. {
  193. Dictionary<int, INode> nodesDic;
  194. if (_nodesDBdic.TryGetValue(nodeType, out nodesDic))
  195. nodesDic.Remove((node as NodeWithID).ID);
  196. }
  197. }
  198. void RemoveNodeFromGroupDB<T>(T node, Type nodeType) where T : INode
  199. {
  200. FasterList<INode> nodes;
  201. if (_nodesDBgroups.TryGetValue(nodeType, out nodes) == true)
  202. nodes.UnorderredRemove(node); //should I remove it from the dictionary if length is zero?
  203. RemoveNodeFromNodesDictionary(node, nodeType);
  204. }
  205. void RemoveNodeFromEngines<T>(T node, Type nodeType) where T : INode
  206. {
  207. FasterList<INodeEngine<INode>> enginesForNode;
  208. if (_nodeEngines.TryGetValue(nodeType, out enginesForNode))
  209. {
  210. for (int j = 0; j < enginesForNode.Count; j++)
  211. {
  212. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  213. EngineProfiler.MonitorRemoveDuration(RemoveNodeFromEngine, enginesForNode[j], node);
  214. #else
  215. enginesForNode[j].Remove(node);
  216. #endif
  217. }
  218. }
  219. }
  220. #if ENGINE_PROFILER_ENABLED && UNITY_EDITOR
  221. void AddNodeToEngine(INodeEngine<INode> engine, INode node)
  222. {
  223. engine.Add(node);
  224. }
  225. void RemoveNodeFromEngine(INodeEngine<INode> engine, INode node)
  226. {
  227. engine.Remove(node);
  228. }
  229. #endif
  230. void InternalRemove<T>(T node) where T : INode
  231. {
  232. Type nodeType = node.GetType();
  233. RemoveNodeFromEngines(node, nodeType);
  234. RemoveNodeFromTheDB(node, node.GetType());
  235. }
  236. void InternalGroupRemove<T>(T node) where T : INode
  237. {
  238. Type nodeType = node.GetType();
  239. RemoveNodeFromEngines(node, nodeType);
  240. RemoveNodeFromGroupDB(node, node.GetType());
  241. }
  242. Dictionary<Type, FasterList<INodeEngine<INode>>> _nodeEngines;
  243. FasterList<IEngine> _otherEnginesReferences;
  244. Dictionary<Type, FasterList<INode>> _nodesDB;
  245. Dictionary<Type, Dictionary<int, INode>> _nodesDBdic;
  246. Dictionary<Type, FasterList<INode>> _nodesDBgroups;
  247. FasterList<INode> _nodesToAdd;
  248. FasterList<INode> _groupNodesToAdd;
  249. WeakReference<EnginesRoot> _engineRootWeakReference;
  250. //integrated pooling system
  251. //add debug panel like Entitas has
  252. //GCHandle should be used to reduce the number of strong references
  253. //datastructure could be thread safe
  254. //future enhancements:
  255. }
  256. }