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

256 行
11KB

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