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.

341 lines
16KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading;
  4. using Svelto.DataStructures;
  5. namespace Svelto.ECS
  6. {
  7. /// <summary>
  8. /// This mechanism is not for thread-safety but to be sure that all the permutations of group tags always
  9. /// point to the same group ID.
  10. /// A group compound can generate several permutation of tags, so that the order of the tag doesn't matter,
  11. /// but for this to work, each permutation must be identified by the same ID (generated by the unique combination)
  12. /// </summary>
  13. static class GroupCompoundInitializer
  14. {
  15. internal static readonly ThreadLocal<bool> skipStaticCompoundConstructorsWith4Tags = new ThreadLocal<bool>();
  16. internal static readonly ThreadLocal<bool> skipStaticCompoundConstructorsWith3Tags = new ThreadLocal<bool>();
  17. internal static readonly ThreadLocal<bool> skipStaticCompoundConstructorsWith2Tags = new ThreadLocal<bool>();
  18. }
  19. public abstract class GroupCompound<G1, G2, G3, G4> where G1 : GroupTag<G1>
  20. where G2 : GroupTag<G2>
  21. where G3 : GroupTag<G3>
  22. where G4 : GroupTag<G4>
  23. {
  24. static readonly FasterList<ExclusiveGroupStruct> _Groups;
  25. static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;
  26. public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
  27. new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
  28. public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
  29. static int isInitializing;
  30. static GroupCompound()
  31. {
  32. //avoid race conditions if compounds are using on multiple thread
  33. if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0
  34. && GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value == false)
  35. {
  36. _Groups = new FasterList<ExclusiveGroupStruct>(1);
  37. var group = new ExclusiveGroup();
  38. _Groups.Add(group);
  39. var name =
  40. $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint) group}";
  41. #if DEBUG
  42. GroupNamesMap.idToName[(uint) group] = name;
  43. #endif
  44. GroupHashMap.RegisterGroup(group, name);
  45. _GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
  46. GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = true;
  47. //all the permutations must share the same group and group hashset. Warm them up, avoid call the
  48. //constructors again, set the desired value
  49. GroupCompound<G1, G2, G4, G3>._Groups = _Groups;
  50. GroupCompound<G1, G3, G2, G4>._Groups = _Groups;
  51. GroupCompound<G1, G3, G4, G2>._Groups = _Groups;
  52. GroupCompound<G1, G4, G2, G3>._Groups = _Groups;
  53. GroupCompound<G2, G1, G3, G4>._Groups = _Groups;
  54. GroupCompound<G2, G3, G4, G1>._Groups = _Groups;
  55. GroupCompound<G3, G1, G2, G4>._Groups = _Groups;
  56. GroupCompound<G4, G1, G2, G3>._Groups = _Groups;
  57. GroupCompound<G1, G4, G3, G2>._Groups = _Groups;
  58. GroupCompound<G2, G1, G4, G3>._Groups = _Groups;
  59. GroupCompound<G2, G4, G3, G1>._Groups = _Groups;
  60. GroupCompound<G3, G1, G4, G2>._Groups = _Groups;
  61. GroupCompound<G4, G1, G3, G2>._Groups = _Groups;
  62. GroupCompound<G2, G3, G1, G4>._Groups = _Groups;
  63. GroupCompound<G3, G4, G1, G2>._Groups = _Groups;
  64. GroupCompound<G2, G4, G1, G3>._Groups = _Groups;
  65. GroupCompound<G3, G2, G1, G4>._Groups = _Groups;
  66. GroupCompound<G3, G2, G4, G1>._Groups = _Groups;
  67. GroupCompound<G3, G4, G2, G1>._Groups = _Groups;
  68. GroupCompound<G4, G2, G1, G3>._Groups = _Groups;
  69. GroupCompound<G4, G2, G3, G1>._Groups = _Groups;
  70. GroupCompound<G4, G3, G1, G2>._Groups = _Groups;
  71. GroupCompound<G4, G3, G2, G1>._Groups = _Groups;
  72. //all the permutations are warmed up now
  73. GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = false;
  74. GroupCompound<G1, G2, G4, G3>._GroupsHashSet = _GroupsHashSet;
  75. GroupCompound<G1, G3, G2, G4>._GroupsHashSet = _GroupsHashSet;
  76. GroupCompound<G1, G3, G4, G2>._GroupsHashSet = _GroupsHashSet;
  77. GroupCompound<G1, G4, G2, G3>._GroupsHashSet = _GroupsHashSet;
  78. GroupCompound<G2, G1, G3, G4>._GroupsHashSet = _GroupsHashSet;
  79. GroupCompound<G2, G3, G4, G1>._GroupsHashSet = _GroupsHashSet;
  80. GroupCompound<G3, G1, G2, G4>._GroupsHashSet = _GroupsHashSet;
  81. GroupCompound<G4, G1, G2, G3>._GroupsHashSet = _GroupsHashSet;
  82. GroupCompound<G1, G4, G3, G2>._GroupsHashSet = _GroupsHashSet;
  83. GroupCompound<G2, G1, G4, G3>._GroupsHashSet = _GroupsHashSet;
  84. GroupCompound<G2, G4, G3, G1>._GroupsHashSet = _GroupsHashSet;
  85. GroupCompound<G3, G1, G4, G2>._GroupsHashSet = _GroupsHashSet;
  86. GroupCompound<G4, G1, G3, G2>._GroupsHashSet = _GroupsHashSet;
  87. GroupCompound<G2, G3, G1, G4>._GroupsHashSet = _GroupsHashSet;
  88. GroupCompound<G3, G4, G1, G2>._GroupsHashSet = _GroupsHashSet;
  89. GroupCompound<G2, G4, G1, G3>._GroupsHashSet = _GroupsHashSet;
  90. GroupCompound<G3, G2, G1, G4>._GroupsHashSet = _GroupsHashSet;
  91. GroupCompound<G3, G2, G4, G1>._GroupsHashSet = _GroupsHashSet;
  92. GroupCompound<G3, G4, G2, G1>._GroupsHashSet = _GroupsHashSet;
  93. GroupCompound<G4, G2, G1, G3>._GroupsHashSet = _GroupsHashSet;
  94. GroupCompound<G4, G2, G3, G1>._GroupsHashSet = _GroupsHashSet;
  95. GroupCompound<G4, G3, G1, G2>._GroupsHashSet = _GroupsHashSet;
  96. GroupCompound<G4, G3, G2, G1>._GroupsHashSet = _GroupsHashSet;
  97. GroupCompound<G1, G2, G3>.Add(group);
  98. GroupCompound<G1, G2, G4>.Add(group);
  99. GroupCompound<G1, G3, G4>.Add(group);
  100. GroupCompound<G2, G3, G4>.Add(group);
  101. GroupCompound<G1, G2>.Add(group); //<G1/G2> and <G2/G1> must share the same array
  102. GroupCompound<G1, G3>.Add(group);
  103. GroupCompound<G1, G4>.Add(group);
  104. GroupCompound<G2, G3>.Add(group);
  105. GroupCompound<G2, G4>.Add(group);
  106. GroupCompound<G3, G4>.Add(group);
  107. //This is done here to be sure that the group is added once per group tag
  108. //(if done inside the previous group compound it would be added multiple times)
  109. GroupTag<G1>.Add(group);
  110. GroupTag<G2>.Add(group);
  111. GroupTag<G3>.Add(group);
  112. GroupTag<G4>.Add(group);
  113. }
  114. }
  115. internal static void Add(ExclusiveGroupStruct @group)
  116. {
  117. for (int i = 0; i < _Groups.count; ++i)
  118. if (_Groups[i] == group)
  119. throw new Exception("temporary must be transformed in unit test");
  120. _Groups.Add(group);
  121. _GroupsHashSet.Add(group);
  122. }
  123. public static bool Includes(ExclusiveGroupStruct @group)
  124. {
  125. return _GroupsHashSet.Contains(@group);
  126. }
  127. }
  128. public abstract class GroupCompound<G1, G2, G3>
  129. where G1 : GroupTag<G1> where G2 : GroupTag<G2> where G3 : GroupTag<G3>
  130. {
  131. static readonly FasterList<ExclusiveGroupStruct> _Groups;
  132. static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;
  133. public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
  134. new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
  135. public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
  136. static int isInitializing;
  137. internal static void Add(ExclusiveGroupStruct group)
  138. {
  139. for (var i = 0; i < _Groups.count; ++i)
  140. if (_Groups[i] == group)
  141. throw new Exception("temporary must be transformed in unit test");
  142. _Groups.Add(group);
  143. _GroupsHashSet.Add(group);
  144. }
  145. public static bool Includes(ExclusiveGroupStruct @group)
  146. {
  147. return _GroupsHashSet.Contains(@group);
  148. }
  149. static GroupCompound()
  150. {
  151. if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
  152. GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value == false)
  153. {
  154. _Groups = new FasterList<ExclusiveGroupStruct>(1);
  155. var group = new ExclusiveGroup();
  156. _Groups.Add(group);
  157. var name = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name} ID {(uint) group}";
  158. #if DEBUG
  159. GroupNamesMap.idToName[(uint) group] = name;
  160. #endif
  161. GroupHashMap.RegisterGroup(group, name);
  162. _GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
  163. GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = true;
  164. //all the combinations must share the same group and group hashset
  165. GroupCompound<G3, G1, G2>._Groups = _Groups;
  166. GroupCompound<G2, G3, G1>._Groups = _Groups;
  167. GroupCompound<G3, G2, G1>._Groups = _Groups;
  168. GroupCompound<G1, G3, G2>._Groups = _Groups;
  169. GroupCompound<G2, G1, G3>._Groups = _Groups;
  170. //all the constructor have been called now
  171. GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = false;
  172. GroupCompound<G3, G1, G2>._GroupsHashSet = _GroupsHashSet;
  173. GroupCompound<G2, G3, G1>._GroupsHashSet = _GroupsHashSet;
  174. GroupCompound<G3, G2, G1>._GroupsHashSet = _GroupsHashSet;
  175. GroupCompound<G1, G3, G2>._GroupsHashSet = _GroupsHashSet;
  176. GroupCompound<G2, G1, G3>._GroupsHashSet = _GroupsHashSet;
  177. GroupCompound<G1, G2>.Add(group); //<G1/G2> and <G2/G1> must share the same array
  178. GroupCompound<G1, G3>.Add(group);
  179. GroupCompound<G2, G3>.Add(group);
  180. //This is done here to be sure that the group is added once per group tag
  181. //(if done inside the previous group compound it would be added multiple times)
  182. GroupTag<G1>.Add(group);
  183. GroupTag<G2>.Add(group);
  184. GroupTag<G3>.Add(group);
  185. }
  186. }
  187. }
  188. public abstract class GroupCompound<G1, G2> where G1 : GroupTag<G1> where G2 : GroupTag<G2>
  189. {
  190. static readonly FasterList<ExclusiveGroupStruct> _Groups;
  191. static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;
  192. public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
  193. new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
  194. public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
  195. static int isInitializing;
  196. internal static void Add(ExclusiveGroupStruct group)
  197. {
  198. for (var i = 0; i < _Groups.count; ++i)
  199. if (_Groups[i] == group)
  200. throw new Exception("temporary must be transformed in unit test");
  201. _Groups.Add(group);
  202. _GroupsHashSet.Add(group);
  203. }
  204. public static bool Includes(ExclusiveGroupStruct @group)
  205. {
  206. return _GroupsHashSet.Contains(@group);
  207. }
  208. static GroupCompound()
  209. {
  210. if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
  211. GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value == false)
  212. {
  213. var group = new ExclusiveGroup();
  214. _Groups = new FasterList<ExclusiveGroupStruct>(1);
  215. _Groups.Add(group);
  216. var groupName = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {(uint) group}";
  217. #if DEBUG
  218. GroupNamesMap.idToName[(uint) group] = groupName;
  219. #endif
  220. GroupHashMap.RegisterGroup(group, groupName);
  221. _GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
  222. GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = true;
  223. GroupCompound<G2, G1>._Groups = _Groups;
  224. GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = false;
  225. GroupCompound<G2, G1>._GroupsHashSet = _GroupsHashSet;
  226. //every abstract group preemptively adds this group, it may or may not be empty in future
  227. GroupTag<G1>.Add(group);
  228. GroupTag<G2>.Add(group);
  229. }
  230. }
  231. }
  232. /// <summary>
  233. ///A Group Tag holds initially just a group, itself. However the number of groups can grow with the number of
  234. ///combinations of GroupTags including this one. This because a GroupTag is an adjective and different entities
  235. ///can use the same adjective together with other ones. However since I need to be able to iterate over all the
  236. ///groups with the same adjective, a group tag needs to hold all the groups sharing it.
  237. /// </summary>
  238. /// <typeparam name="T"></typeparam>
  239. public abstract class GroupTag<T> where T : GroupTag<T>
  240. {
  241. static readonly FasterList<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1);
  242. static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;
  243. public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
  244. new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
  245. public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
  246. static int isInitializing;
  247. static GroupTag()
  248. {
  249. if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0)
  250. {
  251. var group = new ExclusiveGroup();
  252. _Groups.Add(group);
  253. var typeInfo = typeof(T);
  254. var name = $"Compound: {typeInfo.Name} ID {(uint) @group}";
  255. #if DEBUG
  256. var typeInfoBaseType = typeInfo.BaseType;
  257. if (typeInfoBaseType.GenericTypeArguments[0] != typeInfo)
  258. throw new ECSException("Invalid Group Tag declared");
  259. GroupNamesMap.idToName[(uint) group] = name;
  260. #endif
  261. GroupHashMap.RegisterGroup(group, name);
  262. _GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
  263. }
  264. }
  265. //Each time a new combination of group tags is found a new group is added.
  266. internal static void Add(ExclusiveGroupStruct group)
  267. {
  268. for (var i = 0; i < _Groups.count; ++i)
  269. if (_Groups[i] == group)
  270. throw new Exception("temporary must be transformed in unit test");
  271. _Groups.Add(group);
  272. _GroupsHashSet.Add(group);
  273. }
  274. public static bool Includes(ExclusiveGroupStruct @group)
  275. {
  276. return _GroupsHashSet.Contains(@group);
  277. }
  278. }
  279. }