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.

233 lines
10KB

  1. using System.Runtime.CompilerServices;
  2. using Svelto.Common;
  3. using Svelto.DataStructures.Native;
  4. using Svelto.ECS.DataStructures;
  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 in 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.
  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. if (_entityReferenceMap[reference.index].version != reference.version ||
  64. _entityReferenceMap[reference.index].egid.groupID != ExclusiveGroupStruct.Invalid)
  65. {
  66. throw new ECSException("Entity reference already set. This should never happen, please report it.");
  67. }
  68. #endif
  69. _entityReferenceMap[reference.index] = new EntityReferenceMapElement(egid, reference.version);
  70. // Update reverse map from egid to locator.
  71. var groupMap =
  72. _egidToReferenceMap.GetOrAdd(egid.groupID
  73. , () => 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 =
  82. _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);
  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. var egidToReference = _egidToReferenceMap[@from.groupID];
  101. var reference = egidToReference[@from.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 throw new ECSException($"Entity {egid} does not exist. Are you creating it? Try getting it from initializer.reference.");
  135. #endif
  136. }
  137. return EntityReference.Invalid;
  138. }
  139. public bool TryGetEGID(EntityReference reference, out EGID egid)
  140. {
  141. egid = default;
  142. if (reference == EntityReference.Invalid)
  143. return false;
  144. // Make sure we are querying for the current version of the locator.
  145. // Otherwise the locator is pointing to a removed entity.
  146. if (_entityReferenceMap[reference.index].version == reference.version)
  147. {
  148. egid = _entityReferenceMap[reference.index].egid;
  149. return true;
  150. }
  151. return false;
  152. }
  153. public EGID GetEGID(EntityReference reference)
  154. {
  155. if (reference == EntityReference.Invalid)
  156. throw new ECSException("Invalid Reference");
  157. // Make sure we are querying for the current version of the locator.
  158. // Otherwise the locator is pointing to a removed entity.
  159. if (_entityReferenceMap[reference.index].version != reference.version)
  160. throw new ECSException("outdated Reference");
  161. return _entityReferenceMap[reference.index].egid;
  162. }
  163. internal void PreallocateReferenceMaps(ExclusiveGroupStruct groupID, uint size)
  164. {
  165. _egidToReferenceMap
  166. .GetOrAdd(groupID, () => new SharedSveltoDictionaryNative<uint, EntityReference>(size))
  167. .EnsureCapacity(size);
  168. _entityReferenceMap.Resize(size);
  169. }
  170. internal void InitEntityReferenceMap()
  171. {
  172. _nextFreeIndex = SharedNativeInt.Create(0, Allocator.Persistent);
  173. _entityReferenceMap =
  174. new NativeDynamicArrayCast<EntityReferenceMapElement>(
  175. NativeDynamicArray.Alloc<EntityReferenceMapElement>());
  176. _egidToReferenceMap =
  177. new SharedSveltoDictionaryNative<ExclusiveGroupStruct,
  178. SharedSveltoDictionaryNative<uint, EntityReference>>(0);
  179. }
  180. internal void DisposeEntityReferenceMap()
  181. {
  182. _nextFreeIndex.Dispose();
  183. _entityReferenceMap.Dispose();
  184. foreach (var element in _egidToReferenceMap)
  185. element.value.Dispose();
  186. _egidToReferenceMap.Dispose();
  187. }
  188. SharedNativeInt _nextFreeIndex;
  189. NativeDynamicArrayCast<EntityReferenceMapElement> _entityReferenceMap;
  190. SharedSveltoDictionaryNative<ExclusiveGroupStruct, SharedSveltoDictionaryNative<uint, EntityReference>>
  191. _egidToReferenceMap;
  192. }
  193. EntityReferenceMap entityLocator => _entityLocator;
  194. EntityReferenceMap _entityLocator;
  195. }
  196. }