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.

182 lines
8.7KB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using System.Runtime.CompilerServices;
  6. using System.Text;
  7. using Svelto.ECS.Serialization;
  8. namespace Svelto.ECS
  9. {
  10. public static class GroupHashMap
  11. {
  12. /// <summary>
  13. /// c# Static constructors are guaranteed to be thread safe
  14. /// The runtime guarantees that a static constructor is only called once. So even if a type is called by multiple threads at the same time,
  15. /// the static constructor is always executed one time. To get a better understanding how this works, it helps to know what purpose it serves.
  16. ///
  17. /// Warmup the group hash map. This will call all the static constructors of the group types
  18. /// </summary>
  19. internal static void WarmUp()
  20. {
  21. List<Assembly> assemblies = AssemblyUtility.GetCompatibleAssemblies();
  22. foreach (Assembly assembly in assemblies)
  23. {
  24. var typeOfExclusiveGroup = typeof(ExclusiveGroup);
  25. var typeOfExclusiveGroupStruct = typeof(ExclusiveGroupStruct);
  26. var typeOfExclusiveBuildGroup = typeof(ExclusiveBuildGroup);
  27. var typesSafe = AssemblyUtility.GetTypesSafe(assembly);
  28. foreach (Type type in typesSafe)
  29. {
  30. CheckForGroupCompounds(type);
  31. //Search inside static types
  32. if (type != null && type.IsClass && type.IsSealed
  33. && type.IsAbstract) //IsClass and IsSealed and IsAbstract means only static classes
  34. {
  35. var subClasses = type.GetNestedTypes();
  36. foreach (var subclass in subClasses)
  37. {
  38. CheckForGroupCompounds(subclass);
  39. }
  40. var fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
  41. foreach (var field in fields)
  42. {
  43. if ((typeOfExclusiveGroup.IsAssignableFrom(field.FieldType)
  44. || typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType)
  45. || typeOfExclusiveBuildGroup.IsAssignableFrom(field.FieldType)))
  46. {
  47. uint groupIDAndBitMask;
  48. int range = 0;
  49. if (typeOfExclusiveGroup.IsAssignableFrom(field.FieldType))
  50. {
  51. var group = (ExclusiveGroup)field.GetValue(null);
  52. groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask();
  53. range = @group._range;
  54. }
  55. else if (typeOfExclusiveGroupStruct.IsAssignableFrom(field.FieldType))
  56. {
  57. var group = (ExclusiveGroupStruct)field.GetValue(null);
  58. groupIDAndBitMask = @group.ToIDAndBitmask();
  59. }
  60. else
  61. {
  62. var group = (ExclusiveBuildGroup)field.GetValue(null);
  63. groupIDAndBitMask = ((ExclusiveGroupStruct)@group).ToIDAndBitmask();
  64. }
  65. {
  66. var bitMask = (byte)(groupIDAndBitMask >> 24);
  67. var groupID = groupIDAndBitMask & 0xFFFFFF;
  68. ExclusiveGroupStruct group = new ExclusiveGroupStruct(groupID, bitMask);
  69. #if DEBUG && !PROFILE_SVELTO
  70. if (GroupNamesMap.idToName.ContainsKey(@group) == false)
  71. GroupNamesMap.idToName[@group] =
  72. $"{type.FullName}.{field.Name} id: {@group.id}";
  73. #endif
  74. //The hashname is independent from the actual group ID. this is fundamental because it is want
  75. //guarantees the hash to be the same across different machines
  76. RegisterGroup(@group, $"{type.FullName}.{field.Name}");
  77. for (uint i = 1; i < range; i++)
  78. {
  79. var exclusiveGroupStruct = group + i;
  80. #if DEBUG && !PROFILE_SVELTO
  81. if (GroupNamesMap.idToName.ContainsKey(exclusiveGroupStruct) == false)
  82. GroupNamesMap.idToName[exclusiveGroupStruct] =
  83. $"{type.FullName}.{field.Name} id: {@group.id + i}";
  84. #endif
  85. RegisterGroup(exclusiveGroupStruct, $"{type.FullName}.{field.Name} id: {@group.id + i}");
  86. }
  87. }
  88. }
  89. }
  90. }
  91. }
  92. }
  93. }
  94. static void CheckForGroupCompounds(Type type)
  95. {
  96. if (typeof(ITouchedByReflection).IsAssignableFrom(type))
  97. {
  98. //this calls the static constructor, but only once. Static constructors won't be called
  99. //more than once with this
  100. CallStaticConstructorsRecursively(type);
  101. }
  102. static void CallStaticConstructorsRecursively(Type type)
  103. {
  104. // Check if the current type has a static constructor
  105. // type.TypeInitializer.Invoke(null, null); //calling Invoke will force the static constructor to be called even if already called, this is a problem because GroupTag and Compound throw an exception if called multiple times
  106. RuntimeHelpers.RunClassConstructor(type.TypeHandle); //this will call the static constructor only once
  107. // Recursively check the base types
  108. Type baseType = type.BaseType;
  109. if (baseType != null && baseType != typeof(object)) //second if means we got the the end of the hierarchy
  110. {
  111. CallStaticConstructorsRecursively(baseType);
  112. }
  113. }
  114. }
  115. /// <summary>
  116. /// The hashname is independent from the actual group ID. this is fundamental because it
  117. /// guarantees the hash to be the same across different machines
  118. /// </summary>
  119. /// <param name="exclusiveGroupStruct"></param>
  120. /// <param name="name"></param>
  121. /// <exception cref="ECSException"></exception>
  122. internal static void RegisterGroup(ExclusiveGroupStruct exclusiveGroupStruct, string name)
  123. {
  124. //Group already registered by another field referencing the same group, can happen because
  125. //the group poked is a group compound which static constructor is already been called at this point
  126. if (_hashByGroups.ContainsKey(exclusiveGroupStruct))
  127. return;
  128. var nameHash = DesignatedHash.Hash(Encoding.ASCII.GetBytes(name));
  129. if (_groupsByHash.ContainsKey(nameHash))
  130. throw new ECSException($"Group hash collision with {name} and {_groupsByHash[nameHash]}");
  131. Console.LogDebug($"Registering group {name} with ID {exclusiveGroupStruct.id} to {nameHash}");
  132. _groupsByHash.Add(nameHash, exclusiveGroupStruct);
  133. _hashByGroups.Add(exclusiveGroupStruct, nameHash);
  134. }
  135. public static uint GetHashFromGroup(ExclusiveGroupStruct groupStruct)
  136. {
  137. #if DEBUG && !PROFILE_SVELTO
  138. if (_hashByGroups.ContainsKey(groupStruct) == false)
  139. throw new ECSException($"Attempted to get hash from unregistered group {groupStruct}");
  140. #endif
  141. return _hashByGroups[groupStruct];
  142. }
  143. public static ExclusiveGroupStruct GetGroupFromHash(uint groupHash)
  144. {
  145. #if DEBUG && !PROFILE_SVELTO
  146. if (_groupsByHash.ContainsKey(groupHash) == false)
  147. throw new ECSException($"Attempted to get group from unregistered hash {groupHash}");
  148. #endif
  149. return _groupsByHash[groupHash];
  150. }
  151. static readonly Dictionary<uint, ExclusiveGroupStruct> _groupsByHash;
  152. static readonly Dictionary<ExclusiveGroupStruct, uint> _hashByGroups;
  153. static GroupHashMap()
  154. {
  155. _groupsByHash = new Dictionary<uint, ExclusiveGroupStruct>();
  156. _hashByGroups = new Dictionary<ExclusiveGroupStruct, uint>();
  157. }
  158. }
  159. }