Mirror of Svelto.ECS because we're a fan of it
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

404 líneas
19KB

  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. public abstract class GroupCompound<G1, G2, G3, G4>: ITouchedByReflection
  20. where G1 : GroupTag<G1>
  21. where G2 : GroupTag<G2>
  22. where G3 : GroupTag<G3>
  23. where G4 : GroupTag<G4>
  24. {
  25. static GroupCompound()
  26. {
  27. //avoid race conditions if compounds are using on multiple thread
  28. if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
  29. GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value == false)
  30. {
  31. System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(
  32. typeof(GroupCompound<G1, G2, G3, G4>).TypeHandle);
  33. var group = new ExclusiveGroup(
  34. GroupTag<G1>.bitmask | GroupTag<G2>.bitmask | GroupTag<G3>.bitmask | GroupTag<G4>.bitmask
  35. | bitmask);
  36. _Groups = new FasterList<ExclusiveGroupStruct>(1);
  37. _Groups.Add(group);
  38. #if DEBUG
  39. var name =
  40. $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name}-{typeof(G4).Name} ID {(uint)group.id}";
  41. GroupNamesMap.idToName[group] = name;
  42. #endif
  43. //The hashname is independent from the actual group ID. this is fundamental because it is want
  44. //guarantees the hash to be the same across different machines
  45. GroupHashMap.RegisterGroup(group, typeof(GroupCompound<G1, G2, G3, G4>).FullName);
  46. //ToArrayFast is theoretically not correct, but since multiple 0s are ignored and we don't care if we
  47. //add one, we avoid an allocation
  48. _GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
  49. GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = true;
  50. //all the permutations must share the same group and group hashset. Warm them up, avoid call the
  51. //constructors again, set the desired value
  52. GroupCompound<G1, G2, G4, G3>._Groups = _Groups;
  53. GroupCompound<G1, G3, G2, G4>._Groups = _Groups;
  54. GroupCompound<G1, G3, G4, G2>._Groups = _Groups;
  55. GroupCompound<G1, G4, G2, G3>._Groups = _Groups;
  56. GroupCompound<G2, G1, G3, G4>._Groups = _Groups;
  57. GroupCompound<G2, G3, G4, G1>._Groups = _Groups;
  58. GroupCompound<G3, G1, G2, G4>._Groups = _Groups;
  59. GroupCompound<G4, G1, G2, G3>._Groups = _Groups;
  60. GroupCompound<G1, G4, G3, G2>._Groups = _Groups;
  61. GroupCompound<G2, G1, G4, G3>._Groups = _Groups;
  62. GroupCompound<G2, G4, G3, G1>._Groups = _Groups;
  63. GroupCompound<G3, G1, G4, G2>._Groups = _Groups;
  64. GroupCompound<G4, G1, G3, G2>._Groups = _Groups;
  65. GroupCompound<G2, G3, G1, G4>._Groups = _Groups;
  66. GroupCompound<G3, G4, G1, G2>._Groups = _Groups;
  67. GroupCompound<G2, G4, G1, G3>._Groups = _Groups;
  68. GroupCompound<G3, G2, G1, G4>._Groups = _Groups;
  69. GroupCompound<G3, G2, G4, G1>._Groups = _Groups;
  70. GroupCompound<G3, G4, G2, G1>._Groups = _Groups;
  71. GroupCompound<G4, G2, G1, G3>._Groups = _Groups;
  72. GroupCompound<G4, G2, G3, G1>._Groups = _Groups;
  73. GroupCompound<G4, G3, G1, G2>._Groups = _Groups;
  74. GroupCompound<G4, G3, G2, G1>._Groups = _Groups;
  75. //all the permutations are warmed up now
  76. GroupCompoundInitializer.skipStaticCompoundConstructorsWith4Tags.Value = false;
  77. GroupCompound<G1, G2, G4, G3>._GroupsHashSet = _GroupsHashSet;
  78. GroupCompound<G1, G3, G2, G4>._GroupsHashSet = _GroupsHashSet;
  79. GroupCompound<G1, G3, G4, G2>._GroupsHashSet = _GroupsHashSet;
  80. GroupCompound<G1, G4, G2, G3>._GroupsHashSet = _GroupsHashSet;
  81. GroupCompound<G2, G1, G3, G4>._GroupsHashSet = _GroupsHashSet;
  82. GroupCompound<G2, G3, G4, G1>._GroupsHashSet = _GroupsHashSet;
  83. GroupCompound<G3, G1, G2, G4>._GroupsHashSet = _GroupsHashSet;
  84. GroupCompound<G4, G1, G2, G3>._GroupsHashSet = _GroupsHashSet;
  85. GroupCompound<G1, G4, G3, G2>._GroupsHashSet = _GroupsHashSet;
  86. GroupCompound<G2, G1, G4, G3>._GroupsHashSet = _GroupsHashSet;
  87. GroupCompound<G2, G4, G3, G1>._GroupsHashSet = _GroupsHashSet;
  88. GroupCompound<G3, G1, G4, G2>._GroupsHashSet = _GroupsHashSet;
  89. GroupCompound<G4, G1, G3, G2>._GroupsHashSet = _GroupsHashSet;
  90. GroupCompound<G2, G3, G1, G4>._GroupsHashSet = _GroupsHashSet;
  91. GroupCompound<G3, G4, G1, G2>._GroupsHashSet = _GroupsHashSet;
  92. GroupCompound<G2, G4, G1, G3>._GroupsHashSet = _GroupsHashSet;
  93. GroupCompound<G3, G2, G1, G4>._GroupsHashSet = _GroupsHashSet;
  94. GroupCompound<G3, G2, G4, G1>._GroupsHashSet = _GroupsHashSet;
  95. GroupCompound<G3, G4, G2, G1>._GroupsHashSet = _GroupsHashSet;
  96. GroupCompound<G4, G2, G1, G3>._GroupsHashSet = _GroupsHashSet;
  97. GroupCompound<G4, G2, G3, G1>._GroupsHashSet = _GroupsHashSet;
  98. GroupCompound<G4, G3, G1, G2>._GroupsHashSet = _GroupsHashSet;
  99. GroupCompound<G4, G3, G2, G1>._GroupsHashSet = _GroupsHashSet;
  100. GroupCompound<G1, G2, G3>.Add(group);
  101. GroupCompound<G1, G2, G4>.Add(group);
  102. GroupCompound<G1, G3, G4>.Add(group);
  103. GroupCompound<G2, G3, G4>.Add(group);
  104. GroupCompound<G1, G2>.Add(group); //<G1/G2> and <G2/G1> must share the same array
  105. GroupCompound<G1, G3>.Add(group);
  106. GroupCompound<G1, G4>.Add(group);
  107. GroupCompound<G2, G3>.Add(group);
  108. GroupCompound<G2, G4>.Add(group);
  109. GroupCompound<G3, G4>.Add(group);
  110. //This is done here to be sure that the group is added once per group tag
  111. //(if done inside the previous group compound it would be added multiple times)
  112. GroupTag<G1>.Add(group);
  113. GroupTag<G2>.Add(group);
  114. GroupTag<G3>.Add(group);
  115. GroupTag<G4>.Add(group);
  116. }
  117. }
  118. public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
  119. new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
  120. public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
  121. public static bool Includes(ExclusiveGroupStruct group)
  122. {
  123. DBC.ECS.Check.Require(group != ExclusiveGroupStruct.Invalid, "invalid group passed");
  124. return _GroupsHashSet.Contains(group);
  125. }
  126. internal static void Add(ExclusiveGroupStruct group)
  127. {
  128. #if DEBUG && !PROFILE_SVELTO
  129. for (var i = 0; i < _Groups.count; ++i)
  130. if (_Groups[i] == group)
  131. throw new System.Exception("this test must be transformed in unit test");
  132. #endif
  133. _Groups.Add(group);
  134. _GroupsHashSet.Add(group);
  135. }
  136. static readonly FasterList<ExclusiveGroupStruct> _Groups;
  137. static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;
  138. //we are changing this with Interlocked, so it cannot be readonly
  139. static int isInitializing;
  140. protected internal static ExclusiveGroupBitmask bitmask;
  141. }
  142. public abstract class GroupCompound<G1, G2, G3>: ITouchedByReflection
  143. where G1 : GroupTag<G1>
  144. where G2 : GroupTag<G2>
  145. where G3 : GroupTag<G3>
  146. {
  147. static GroupCompound()
  148. {
  149. if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
  150. GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value == false)
  151. {
  152. System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(
  153. typeof(GroupCompound<G1, G2, G3>).TypeHandle);
  154. var group = new ExclusiveGroup(
  155. GroupTag<G1>.bitmask | GroupTag<G2>.bitmask | GroupTag<G3>.bitmask | bitmask);
  156. _Groups = new FasterList<ExclusiveGroupStruct>(1);
  157. _Groups.Add(group);
  158. #if DEBUG
  159. var name = $"Compound: {typeof(G1).Name}-{typeof(G2).Name}-{typeof(G3).Name} ID {(uint)group.id}";
  160. GroupNamesMap.idToName[group] = name;
  161. #endif
  162. //The hashname is independent from the actual group ID. this is fundamental because it is want
  163. //guarantees the hash to be the same across different machines
  164. GroupHashMap.RegisterGroup(group, typeof(GroupCompound<G1, G2, G3>).FullName);
  165. _GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
  166. GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = true;
  167. //all the combinations must share the same group and group hashset
  168. GroupCompound<G3, G1, G2>._Groups = _Groups;
  169. GroupCompound<G2, G3, G1>._Groups = _Groups;
  170. GroupCompound<G3, G2, G1>._Groups = _Groups;
  171. GroupCompound<G1, G3, G2>._Groups = _Groups;
  172. GroupCompound<G2, G1, G3>._Groups = _Groups;
  173. //all the constructor have been called now
  174. GroupCompoundInitializer.skipStaticCompoundConstructorsWith3Tags.Value = false;
  175. GroupCompound<G3, G1, G2>._GroupsHashSet = _GroupsHashSet;
  176. GroupCompound<G2, G3, G1>._GroupsHashSet = _GroupsHashSet;
  177. GroupCompound<G3, G2, G1>._GroupsHashSet = _GroupsHashSet;
  178. GroupCompound<G1, G3, G2>._GroupsHashSet = _GroupsHashSet;
  179. GroupCompound<G2, G1, G3>._GroupsHashSet = _GroupsHashSet;
  180. GroupCompound<G1, G2>.Add(group); //<G1/G2> and <G2/G1> must share the same array
  181. GroupCompound<G1, G3>.Add(group);
  182. GroupCompound<G2, G3>.Add(group);
  183. //This is done here to be sure that the group is added once per group tag
  184. //(if done inside the previous group compound it would be added multiple times)
  185. GroupTag<G1>.Add(group);
  186. GroupTag<G2>.Add(group);
  187. GroupTag<G3>.Add(group);
  188. }
  189. }
  190. public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
  191. new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
  192. public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
  193. public static bool Includes(ExclusiveGroupStruct group)
  194. {
  195. DBC.ECS.Check.Require(group != ExclusiveGroupStruct.Invalid, "invalid group passed");
  196. return _GroupsHashSet.Contains(group);
  197. }
  198. internal static void Add(ExclusiveGroupStruct group)
  199. {
  200. #if DEBUG && !PROFILE_SVELTO
  201. for (var i = 0; i < _Groups.count; ++i)
  202. if (_Groups[i] == group)
  203. throw new System.Exception("this test must be transformed in unit test");
  204. #endif
  205. _Groups.Add(group);
  206. _GroupsHashSet.Add(group);
  207. }
  208. static readonly FasterList<ExclusiveGroupStruct> _Groups;
  209. static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;
  210. //we are changing this with Interlocked, so it cannot be readonly
  211. static int isInitializing;
  212. protected internal static ExclusiveGroupBitmask bitmask;
  213. }
  214. public abstract class GroupCompound<G1, G2>: ITouchedByReflection
  215. where G1 : GroupTag<G1>
  216. where G2 : GroupTag<G2>
  217. {
  218. static GroupCompound()
  219. {
  220. if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0 &&
  221. GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value == false)
  222. {
  223. System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(
  224. typeof(GroupCompound<G1, G2>).TypeHandle);
  225. var group = new ExclusiveGroup(GroupTag<G1>.bitmask | GroupTag<G2>.bitmask | bitmask);
  226. _Groups = new FasterList<ExclusiveGroupStruct>(1);
  227. _Groups.Add(group);
  228. #if DEBUG
  229. GroupNamesMap.idToName[group] = $"Compound: {typeof(G1).Name}-{typeof(G2).Name} ID {group.id}";
  230. #endif
  231. //The hashname is independent from the actual group ID. this is fundamental because it is want
  232. //guarantees the hash to be the same across different machines
  233. GroupHashMap.RegisterGroup(group, typeof(GroupCompound<G1, G2>).FullName);
  234. _GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
  235. GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = true;
  236. GroupCompound<G2, G1>._Groups = _Groups;
  237. GroupCompoundInitializer.skipStaticCompoundConstructorsWith2Tags.Value = false;
  238. GroupCompound<G2, G1>._GroupsHashSet = _GroupsHashSet;
  239. //every abstract group preemptively adds this group, it may or may not be empty in future
  240. GroupTag<G1>.Add(group);
  241. GroupTag<G2>.Add(group);
  242. }
  243. }
  244. public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
  245. new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
  246. public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
  247. //TODO there is an overlap between this method and ExclusiveGroupExtensions FoundIn
  248. public static bool Includes(ExclusiveGroupStruct group)
  249. {
  250. DBC.ECS.Check.Require(group != ExclusiveGroupStruct.Invalid, "invalid group passed");
  251. return _GroupsHashSet.Contains(group);
  252. }
  253. internal static void Add(ExclusiveGroupStruct group)
  254. {
  255. #if DEBUG && !PROFILE_SVELTO
  256. for (var i = 0; i < _Groups.count; ++i)
  257. if (_Groups[i] == group)
  258. throw new System.Exception("this test must be transformed in unit test");
  259. #endif
  260. _Groups.Add(group);
  261. _GroupsHashSet.Add(group);
  262. }
  263. static readonly FasterList<ExclusiveGroupStruct> _Groups;
  264. static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;
  265. static int isInitializing;
  266. protected internal static ExclusiveGroupBitmask bitmask;
  267. }
  268. /// <summary>
  269. /// A Group Tag holds initially just a group, itself. However the number of groups can grow with the number of
  270. /// combinations of GroupTags including this one. This because a GroupTag is an adjective and different entities
  271. /// can use the same adjective together with other ones. However since I need to be able to iterate over all the
  272. /// groups with the same adjective, a group tag needs to hold all the groups sharing it.
  273. /// </summary>
  274. /// <typeparam name="T"></typeparam>
  275. public abstract class GroupTag<T>: ITouchedByReflection
  276. where T : GroupTag<T>
  277. {
  278. static GroupTag()
  279. {
  280. if (Interlocked.CompareExchange(ref isInitializing, 1, 0) == 0)
  281. {
  282. //Allow to call GroupTag static constructors like
  283. // public class Dead: GroupTag<Dead>
  284. // {
  285. // static Dead()
  286. // {
  287. // bitmask = ExclusiveGroupBitmask.DISABLED_BIT;
  288. // }
  289. // };
  290. System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle);
  291. var group = new ExclusiveGroup(bitmask);
  292. _Groups.Add(group);
  293. #if DEBUG
  294. var typeInfo = typeof(T);
  295. var name = $"Compound: {typeInfo.Name} ID {(uint)group.id}";
  296. #if !PROFILE_SVELTO
  297. var typeInfoBaseType = typeInfo.BaseType;
  298. if (typeInfoBaseType.GenericTypeArguments[0] !=
  299. 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
  300. throw new ECSException("Invalid Group Tag declared");
  301. #endif
  302. GroupNamesMap.idToName[group] = name;
  303. #endif
  304. //The hashname is independent from the actual group ID. this is fundamental because it is want
  305. //guarantees the hash to be the same across different machines
  306. GroupHashMap.RegisterGroup(group, typeof(GroupTag<T>).FullName);
  307. _GroupsHashSet = new HashSet<ExclusiveGroupStruct>(_Groups.ToArrayFast(out _));
  308. }
  309. }
  310. public static FasterReadOnlyList<ExclusiveGroupStruct> Groups =>
  311. new FasterReadOnlyList<ExclusiveGroupStruct>(_Groups);
  312. public static ExclusiveBuildGroup BuildGroup => new ExclusiveBuildGroup(_Groups[0]);
  313. public static bool Includes(ExclusiveGroupStruct group)
  314. {
  315. DBC.ECS.Check.Require(group != ExclusiveGroupStruct.Invalid, "invalid group passed");
  316. return _GroupsHashSet.Contains(group);
  317. }
  318. //Each time a new combination of group tags is found a new group is added.
  319. internal static void Add(ExclusiveGroupStruct group)
  320. {
  321. #if DEBUG && !PROFILE_SVELTO
  322. for (var i = 0; i < _Groups.count; ++i)
  323. if (_Groups[i] == group)
  324. throw new System.Exception("this test must be transformed in unit test");
  325. #endif
  326. _Groups.Add(group);
  327. _GroupsHashSet.Add(group);
  328. }
  329. static readonly FasterList<ExclusiveGroupStruct> _Groups = new FasterList<ExclusiveGroupStruct>(1);
  330. static readonly HashSet<ExclusiveGroupStruct> _GroupsHashSet;
  331. //we are changing this with Interlocked, so it cannot be readonly
  332. static int isInitializing;
  333. protected internal static ExclusiveGroupBitmask bitmask;
  334. }
  335. }