Mirror of Svelto.ECS because we're a fan of it
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

255 行
11KB

  1. using System.Runtime.CompilerServices;
  2. using Svelto.Common;
  3. using Svelto.DataStructures;
  4. using Svelto.DataStructures.Native;
  5. using Svelto.ECS.Reference;
  6. namespace Svelto.ECS
  7. {
  8. // The EntityLocatorMap provides a bidirectional map to help locate entities without using an EGID which might
  9. // change at runtime. The Entity Locator map uses a reusable unique identifier struct called EntityLocator to
  10. // find the last known EGID from last entity submission.
  11. public partial class EnginesRoot
  12. {
  13. public struct EntityReferenceMap
  14. {
  15. internal EntityReference ClaimReference()
  16. {
  17. int tempFreeIndex;
  18. int newFreeIndex;
  19. uint version;
  20. do
  21. {
  22. tempFreeIndex = _nextFreeIndex;
  23. // Check if we need to create a new EntityLocator or whether we can recycle an existing one.
  24. if ((uint)tempFreeIndex >= _entityReferenceMap.count)
  25. {
  26. newFreeIndex = tempFreeIndex + 1;
  27. version = 0;
  28. }
  29. else
  30. {
  31. ref EntityReferenceMapElement element = ref _entityReferenceMap[tempFreeIndex];
  32. // The recycle entities form a linked list, using the egid.entityID to store the next element.
  33. newFreeIndex = (int)element.egid.entityID;
  34. version = element.version;
  35. }
  36. } while (tempFreeIndex != _nextFreeIndex.CompareExchange(newFreeIndex, tempFreeIndex));
  37. #if DEBUG && !PROFILE_SVELTO
  38. // This code should be safe since we own the tempFreeIndex, this allows us to later check that nothing went wrong.
  39. if (tempFreeIndex < _entityReferenceMap.count)
  40. {
  41. _entityReferenceMap[tempFreeIndex] = new EntityReferenceMapElement(new EGID(0, 0), version);
  42. }
  43. #endif
  44. return new EntityReference((uint)tempFreeIndex + 1, version);
  45. }
  46. internal void SetReference(EntityReference reference, EGID egid)
  47. {
  48. // Since references can be claimed in parallel now, it might happen that they are set out of order,
  49. // so we need to resize instead of add. TODO: what did this comment mean?
  50. if (reference.index >= _entityReferenceMap.count)
  51. {
  52. #if DEBUG && !PROFILE_SVELTO //THIS IS TO VALIDATE DATE DBC LIKE
  53. for (var i = _entityReferenceMap.count; i <= reference.index; i++)
  54. {
  55. _entityReferenceMap.Add(new EntityReferenceMapElement(default, 0));
  56. }
  57. #else
  58. _entityReferenceMap.AddAt(reference.index);
  59. #endif
  60. }
  61. #if DEBUG && !PROFILE_SVELTO
  62. // These debug tests should be enough to detect if indices are being used correctly under native factories
  63. ref var entityReferenceMapElement = ref _entityReferenceMap[reference.index];
  64. if (entityReferenceMapElement.version != reference.version
  65. || entityReferenceMapElement.egid.groupID != ExclusiveGroupStruct.Invalid)
  66. {
  67. throw new ECSException("Entity reference already set. This should never happen, please report it.");
  68. }
  69. #endif
  70. _entityReferenceMap[reference.index] = new EntityReferenceMapElement(egid, reference.version);
  71. // Update reverse map from egid to locator.
  72. var groupMap = _egidToReferenceMap.GetOrAdd(
  73. egid.groupID, () => new SharedSveltoDictionaryNative<uint, EntityReference>(0));
  74. groupMap[egid.entityID] = reference;
  75. }
  76. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  77. internal void UpdateEntityReference(EGID from, EGID to)
  78. {
  79. var reference = FetchAndRemoveReference(@from);
  80. _entityReferenceMap[reference.index].egid = to;
  81. var groupMap = _egidToReferenceMap.GetOrAdd(
  82. to.groupID, () => new SharedSveltoDictionaryNative<uint, EntityReference>(0));
  83. groupMap[to.entityID] = reference;
  84. }
  85. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  86. internal void RemoveEntityReference(EGID egid)
  87. {
  88. var reference = FetchAndRemoveReference(@egid);
  89. // Invalidate the entity locator element by bumping its version and setting the egid to point to a not existing element.
  90. ref var entityReferenceMapElement = ref _entityReferenceMap[reference.index];
  91. entityReferenceMapElement.egid = new EGID((uint)(int)_nextFreeIndex, 0); //keep the free linked list updated
  92. entityReferenceMapElement.version++;
  93. // Mark the element as the last element used.
  94. _nextFreeIndex.Set((int)reference.index);
  95. }
  96. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  97. EntityReference FetchAndRemoveReference(EGID @from)
  98. {
  99. SharedSveltoDictionaryNative<uint, EntityReference> egidToReference = _egidToReferenceMap[@from.groupID];
  100. EntityReference reference = egidToReference[@from.entityID]; //todo: double searching fro entityID
  101. egidToReference.Remove(@from.entityID);
  102. return reference;
  103. }
  104. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  105. internal void RemoveAllGroupReferenceLocators(ExclusiveGroupStruct groupId)
  106. {
  107. if (_egidToReferenceMap.TryGetValue(groupId, out var groupMap) == false)
  108. return;
  109. // We need to traverse all entities in the group and remove the locator using the egid.
  110. // RemoveLocator would modify the enumerator so this is why we traverse the dictionary from last to first.
  111. foreach (var item in groupMap)
  112. RemoveEntityReference(new EGID(item.key, groupId));
  113. _egidToReferenceMap.Remove(groupId);
  114. }
  115. [MethodImpl(MethodImplOptions.AggressiveInlining)]
  116. internal void UpdateAllGroupReferenceLocators(ExclusiveGroupStruct fromGroupId, ExclusiveGroupStruct toGroupId)
  117. {
  118. if (_egidToReferenceMap.TryGetValue(fromGroupId, out var groupMap) == false)
  119. return;
  120. // We need to traverse all entities in the group and update the locator using the egid.
  121. // UpdateLocator would modify the enumerator so this is why we traverse the dictionary from last to first.
  122. foreach (var item in groupMap)
  123. UpdateEntityReference(new EGID(item.key, fromGroupId), new EGID(item.key, toGroupId));
  124. _egidToReferenceMap.Remove(fromGroupId);
  125. }
  126. public EntityReference GetEntityReference(EGID egid)
  127. {
  128. if (_egidToReferenceMap.TryGetValue(egid.groupID, out var groupMap))
  129. {
  130. if (groupMap.TryGetValue(egid.entityID, out var locator))
  131. return locator;
  132. #if DEBUG && !PROFILE_SVELTO
  133. else
  134. throw new ECSException(
  135. $"Entity {egid} does not exist. Are you creating it? Try getting it from initializer.reference.");
  136. #endif
  137. }
  138. return EntityReference.Invalid;
  139. }
  140. public SharedSveltoDictionaryNative<uint, EntityReference> GetEntityReferenceMap(ExclusiveGroupStruct groupID)
  141. {
  142. if (_egidToReferenceMap.TryGetValue(groupID, out var groupMap) == false)
  143. throw new ECSException("reference group map not found");
  144. return groupMap;
  145. }
  146. public bool TryGetEGID(EntityReference reference, out EGID egid)
  147. {
  148. egid = default;
  149. if (reference == EntityReference.Invalid)
  150. return false;
  151. // Make sure we are querying for the current version of the locator.
  152. // Otherwise the locator is pointing to a removed entity.
  153. ref var entityReferenceMapElement = ref _entityReferenceMap[reference.index];
  154. if (entityReferenceMapElement.version == reference.version)
  155. {
  156. egid = entityReferenceMapElement.egid;
  157. return true;
  158. }
  159. return false;
  160. }
  161. public EGID GetEGID(EntityReference reference)
  162. {
  163. #if DEBUG && !PROFILE_SVELTO
  164. if (reference == EntityReference.Invalid)
  165. throw new ECSException("Invalid Reference");
  166. #endif
  167. // Make sure we are querying for the current version of the locator.
  168. // Otherwise the locator is pointing to a removed entity.
  169. ref var entityReferenceMapElement = ref _entityReferenceMap[reference.index];
  170. #if DEBUG && !PROFILE_SVELTO
  171. if (entityReferenceMapElement.version != reference.version)
  172. throw new ECSException("outdated Reference");
  173. #endif
  174. return entityReferenceMapElement.egid;
  175. }
  176. internal void PreallocateReferenceMaps(ExclusiveGroupStruct groupID, uint size)
  177. {
  178. _egidToReferenceMap.GetOrAdd(
  179. groupID, () => new SharedSveltoDictionaryNative<uint, EntityReference>(size)).EnsureCapacity(size);
  180. _entityReferenceMap.Resize(size);
  181. }
  182. internal void InitEntityReferenceMap()
  183. {
  184. _nextFreeIndex = SharedNativeInt.Create(0, Allocator.Persistent);
  185. _entityReferenceMap =
  186. new NativeDynamicArrayCast<EntityReferenceMapElement>(
  187. NativeDynamicArray.Alloc<EntityReferenceMapElement>());
  188. _egidToReferenceMap =
  189. new SharedSveltoDictionaryNative<ExclusiveGroupStruct,
  190. SharedSveltoDictionaryNative<uint, EntityReference>>(0);
  191. }
  192. internal void DisposeEntityReferenceMap()
  193. {
  194. _nextFreeIndex.Dispose();
  195. _entityReferenceMap.Dispose();
  196. foreach (var element in _egidToReferenceMap)
  197. element.value.Dispose();
  198. _egidToReferenceMap.Dispose();
  199. }
  200. SharedNativeInt _nextFreeIndex;
  201. NativeDynamicArrayCast<EntityReferenceMapElement> _entityReferenceMap;
  202. //todo: this should be just one dictionary <EGID, REference> it's a double one to be
  203. //able to remove entire groups at once. IT's wasteful since the operation is very rare
  204. //we should find an alternative solution
  205. //alternatively since the groups are guaranteed to be sequential an array should be used instead
  206. //than a dictionary for groups. It could be a good case to implement a 4k chunk based sparseset
  207. SharedSveltoDictionaryNative<ExclusiveGroupStruct, SharedSveltoDictionaryNative<uint, EntityReference>>
  208. _egidToReferenceMap;
  209. }
  210. EntityReferenceMap entityLocator => _entityLocator;
  211. EntityReferenceMap _entityLocator;
  212. }
  213. }