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.

369 lines
17KB

  1. using System.Collections.Generic;
  2. using System.Threading;
  3. using Svelto.DataStructures;
  4. namespace Svelto.ECS
  5. {
  6. /// <summary>
  7. /// This mechanism is not for thread-safety but to be sure that all the permutations of group tags always
  8. /// point to the same group ID.
  9. /// A group compound can generate several permutation of tags, so that the order of the tag doesn't matter,
  10. /// but for this to work, each permutation must be identified by the same ID (generated by the unique combination)
  11. /// </summary>
  12. static class GroupCompoundInitializer
  13. {
  14. internal static readonly ThreadLocal<bool> skipStaticCompoundConstructorsWith4Tags = new ThreadLocal<bool>();
  15. internal static readonly ThreadLocal<bool> skipStaticCompoundConstructorsWith3Tags = new ThreadLocal<bool>();
  16. internal static readonly ThreadLocal<bool> skipStaticCompoundConstructorsWith2Tags = new ThreadLocal<bool>();
  17. }
  18. interface ITouchedByReflection
  19. {
  20. }
  21. public abstract class GroupCompound<G1, G2, G3, G4> : ITouchedByReflection where G1 : GroupTag<G1>
  22. where G2 : GroupTag<G2>
  23. where G3 : GroupTag<G3>
  24. where G4 : GroupTag<G4>
  25. {
  26. static GroupCompound()
  27. {
  28. //avoid race conditions if compounds are using on multiple thread
  29. if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
  30. GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value == false)
  31. {
  32. var
  33. group =
  34. new ExclusiveGroup(); //todo: it's a bit of a waste to create a class here even if this is a static constructor
  35. _Groups = new FasterList<ExclusiveGroupStruct>(1);
  36. _Groups.Add(group);
  37. #if DEBUG && !PROFILE_SVELTO
  38. var name =
  39. $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint)group.id}";
  40. GroupNamesMap.idToName[group] = name;
  41. #endif
  42. //The hashname is independent from the actual group ID. this is fundamental because it is want
  43. //guarantees the hash to be the same across different machines
  44. GroupHashMap.RegisterGroup(group, typeof(GroupCompound<G1, G2, G3, G4>).FullName);
  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. public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
  116. new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
  117. public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
  118. public static bool Includes(ExclusiveGroupStruct group)
  119. {
  120. return _GroupsHashSet.Contains(group);
  121. }
  122. internal static void Add(ExclusiveGroupStruct group)
  123. {
  124. #if DEBUG && !PROFILE_SVELTO
  125. for (var i = 0; i < _Groups.count; ++i)
  126. if (_Groups[i] == group)
  127. throw new System.Exception("this test must be transformed in unit test");
  128. #endif
  129. _Groups.Add(group);
  130. _GroupsHashSet.Add(group);
  131. }
  132. static readonly FasterList<ExclusiveGroupStruct> _Groups;
  133. static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;
  134. //we are changing this with Interlocked, so it cannot be readonly
  135. static int isInitializing;
  136. }
  137. public abstract class GroupCompound<G1, G2, G3> : ITouchedByReflection where G1 : GroupTag<G1>
  138. where G2 : GroupTag<G2>
  139. where G3 : GroupTag<G3>
  140. {
  141. static GroupCompound()
  142. {
  143. if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
  144. GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value == false)
  145. {
  146. var
  147. group =
  148. new ExclusiveGroup(); //todo: it's a bit of a waste to create a class here even if this is a static constructor
  149. _Groups = new FasterList<ExclusiveGroupStruct>(1);
  150. _Groups.Add(group);
  151. #if DEBUG && !PROFILE_SVELTO
  152. var name = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name} ID {(uint)group.id}";
  153. GroupNamesMap.idToName[group] = name;
  154. #endif
  155. //The hashname is independent from the actual group ID. this is fundamental because it is want
  156. //guarantees the hash to be the same across different machines
  157. GroupHashMap.RegisterGroup(group, typeof(GroupCompound<G1, G2, G3>).FullName);
  158. _GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
  159. GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = true;
  160. //all the combinations must share the same group and group hashset
  161. GroupCompound<G3, G1, G2>._Groups = _Groups;
  162. GroupCompound<G2, G3, G1>._Groups = _Groups;
  163. GroupCompound<G3, G2, G1>._Groups = _Groups;
  164. GroupCompound<G1, G3, G2>._Groups = _Groups;
  165. GroupCompound<G2, G1, G3>._Groups = _Groups;
  166. //all the constructor have been called now
  167. GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = false;
  168. GroupCompound<G3, G1, G2>._GroupsHashSet = _GroupsHashSet;
  169. GroupCompound<G2, G3, G1>._GroupsHashSet = _GroupsHashSet;
  170. GroupCompound<G3, G2, G1>._GroupsHashSet = _GroupsHashSet;
  171. GroupCompound<G1, G3, G2>._GroupsHashSet = _GroupsHashSet;
  172. GroupCompound<G2, G1, G3>._GroupsHashSet = _GroupsHashSet;
  173. GroupCompound<G1, G2>.Add(group); //<G1/G2> and <G2/G1> must share the same array
  174. GroupCompound<G1, G3>.Add(group);
  175. GroupCompound<G2, G3>.Add(group);
  176. //This is done here to be sure that the group is added once per group tag
  177. //(if done inside the previous group compound it would be added multiple times)
  178. GroupTag<G1>.Add(group);
  179. GroupTag<G2>.Add(group);
  180. GroupTag<G3>.Add(group);
  181. }
  182. }
  183. public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
  184. new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
  185. public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
  186. public static bool Includes(ExclusiveGroupStruct group)
  187. {
  188. return _GroupsHashSet.Contains(group);
  189. }
  190. internal static void Add(ExclusiveGroupStruct group)
  191. {
  192. #if DEBUG && !PROFILE_SVELTO
  193. for (var i = 0; i < _Groups.count; ++i)
  194. if (_Groups[i] == group)
  195. throw new System.Exception("this test must be transformed in unit test");
  196. #endif
  197. _Groups.Add(group);
  198. _GroupsHashSet.Add(group);
  199. }
  200. static readonly FasterList<ExclusiveGroupStruct> _Groups;
  201. static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;
  202. //we are changing this with Interlocked, so it cannot be readonly
  203. static int isInitializing;
  204. }
  205. public abstract class GroupCompound<G1, G2> : ITouchedByReflection where G1 : GroupTag<G1> where G2 : GroupTag<G2>
  206. {
  207. static GroupCompound()
  208. {
  209. if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
  210. GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value == false)
  211. {
  212. var
  213. group =
  214. new ExclusiveGroup(); //todo: it's a bit of a waste to create a class here even if this is a static constructor
  215. _Groups = new FasterList<ExclusiveGroupStruct>(1);
  216. _Groups.Add(group);
  217. #if DEBUG && !PROFILE_SVELTO
  218. GroupNamesMap.idToName[group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {group.id}";
  219. #endif
  220. //The hashname is independent from the actual group ID. this is fundamental because it is want
  221. //guarantees the hash to be the same across different machines
  222. GroupHashMap.RegisterGroup(group, typeof(GroupCompound<G1, G2>).FullName);
  223. _GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
  224. GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = true;
  225. GroupCompound<G2, G1>._Groups = _Groups;
  226. GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = false;
  227. GroupCompound<G2, G1>._GroupsHashSet = _GroupsHashSet;
  228. //every abstract group preemptively adds this group, it may or may not be empty in future
  229. GroupTag<G1>.Add(group);
  230. GroupTag<G2>.Add(group);
  231. }
  232. }
  233. public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
  234. new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
  235. public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
  236. public static bool Includes(ExclusiveGroupStruct group)
  237. {
  238. return _GroupsHashSet.Contains(group);
  239. }
  240. internal static void Add(ExclusiveGroupStruct group)
  241. {
  242. #if DEBUG && !PROFILE_SVELTO
  243. for (var i = 0; i < _Groups.count; ++i)
  244. if (_Groups[i] == group)
  245. throw new System.Exception("this test must be transformed in unit test");
  246. #endif
  247. _Groups.Add(group);
  248. _GroupsHashSet.Add(group);
  249. }
  250. static readonly FasterList<ExclusiveGroupStruct> _Groups;
  251. static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;
  252. static int isInitializing;
  253. }
  254. /// <summary>
  255. /// A Group Tag holds initially just a group, itself. However the number of groups can grow with the number of
  256. /// combinations of GroupTags including this one. This because a GroupTag is an adjective and different entities
  257. /// can use the same adjective together with other ones. However since I need to be able to iterate over all the
  258. /// groups with the same adjective, a group tag needs to hold all the groups sharing it.
  259. /// </summary>
  260. /// <typeparam name="T"></typeparam>
  261. public abstract class GroupTag<T> : ITouchedByReflection where T : GroupTag<T>
  262. {
  263. static GroupTag()
  264. {
  265. if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0)
  266. {
  267. var group = new ExclusiveGroup();
  268. _Groups.Add(group);
  269. #if DEBUG && !PROFILE_SVELTO
  270. var typeInfo = typeof(T);
  271. var name = $"Compound: {typeInfo.Name} ID {(uint)group.id}";
  272. var typeInfoBaseType = typeInfo.BaseType;
  273. if (typeInfoBaseType.GenericTypeArguments[0] !=
  274. typeInfo) //todo: this should shield from using a pattern different than public class GROUP_NAME : GroupTag<GROUP_NAME> {} however I am not sure it's working
  275. throw new ECSException("Invalid Group Tag declared");
  276. GroupNamesMap.idToName[group] = name;
  277. #endif
  278. //The hashname is independent from the actual group ID. this is fundamental because it is want
  279. //guarantees the hash to be the same across different machines
  280. GroupHashMap.RegisterGroup(group, typeof(GroupTag<T>).FullName);
  281. _GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
  282. }
  283. }
  284. public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
  285. new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
  286. public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
  287. public static bool Includes(ExclusiveGroupStruct group)
  288. {
  289. return _GroupsHashSet.Contains(group);
  290. }
  291. //Each time a new combination of group tags is found a new group is added.
  292. internal static void Add(ExclusiveGroupStruct group)
  293. {
  294. #if DEBUG && !PROFILE_SVELTO
  295. for (var i = 0; i < _Groups.count; ++i)
  296. if (_Groups[i] == group)
  297. throw new System.Exception("this test must be transformed in unit test");
  298. #endif
  299. _Groups.Add(group);
  300. _GroupsHashSet.Add(group);
  301. }
  302. static readonly FasterList<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1);
  303. static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;
  304. //we are changing this with Interlocked, so it cannot be readonly
  305. static int isInitializing;
  306. }
  307. }