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.

228 lines
9.9KB

  1. using System.Runtime.CompilerServices;
  2. using Svelto.Common;
  3. using Svelto.DataStructures;
  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 LocatorMap
  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.GetOrCreate(egid.groupID
  73. , () => new SharedSveltoDictionaryNative<uint, EntityReference>(0));
  74. groupMap[egid.entityID] = reference;
  75. }
  76. internal void UpdateEntityReference(EGID from, EGID to)
  77. {
  78. var reference = FetchAndRemoveReference(@from);
  79. _entityReferenceMap[reference.index].egid = to;
  80. var groupMap =
  81. _egidToReferenceMap.GetOrCreate(
  82. to.groupID, () => new SharedSveltoDictionaryNative<uint, EntityReference>(0));
  83. groupMap[to.entityID] = reference;
  84. }
  85. internal void RemoveEntityReference(EGID egid)
  86. {
  87. var reference = FetchAndRemoveReference(@egid);
  88. // Invalidate the entity locator element by bumping its version and setting the egid to point to a not existing element.
  89. ref var entityReferenceMapElement = ref _entityReferenceMap[reference.index];
  90. entityReferenceMapElement.egid = new EGID((uint)(int)_nextFreeIndex, 0);
  91. entityReferenceMapElement.version++;
  92. // Mark the element as the last element used.
  93. _nextFreeIndex.Set((int)reference.index);
  94. }
  95. EntityReference FetchAndRemoveReference(EGID @from)
  96. {
  97. var egidToReference = _egidToReferenceMap[@from.groupID];
  98. var reference = egidToReference[@from.entityID];
  99. egidToReference.Remove(@from.entityID);
  100. return reference;
  101. }
  102. internal void RemoveAllGroupReferenceLocators(ExclusiveGroupStruct groupId)
  103. {
  104. if (_egidToReferenceMap.TryGetValue(groupId, out var groupMap) == false)
  105. return;
  106. // We need to traverse all entities in the group and remove the locator using the egid.
  107. // RemoveLocator would modify the enumerator so this is why we traverse the dictionary from last to first.
  108. foreach (var item in groupMap)
  109. RemoveEntityReference(new EGID(item.Key, groupId));
  110. _egidToReferenceMap.Remove(groupId);
  111. }
  112. internal void UpdateAllGroupReferenceLocators(ExclusiveGroupStruct fromGroupId, ExclusiveGroupStruct toGroupId)
  113. {
  114. if (_egidToReferenceMap.TryGetValue(fromGroupId, out var groupMap) == false)
  115. return;
  116. // We need to traverse all entities in the group and update the locator using the egid.
  117. // UpdateLocator would modify the enumerator so this is why we traverse the dictionary from last to first.
  118. foreach (var item in groupMap)
  119. UpdateEntityReference(new EGID(item.Key, fromGroupId), new EGID(item.Key, toGroupId));
  120. _egidToReferenceMap.Remove(fromGroupId);
  121. }
  122. public EntityReference GetEntityReference(EGID egid)
  123. {
  124. if (_egidToReferenceMap.TryGetValue(egid.groupID, out var groupMap))
  125. {
  126. if (groupMap.TryGetValue(egid.entityID, out var locator))
  127. return locator;
  128. #if DEBUG && !PROFILE_SVELTO
  129. else throw new ECSException($"Entity {egid} does not exist. Are you creating it? Try getting it from initializer.reference.");
  130. #endif
  131. }
  132. return EntityReference.Invalid;
  133. }
  134. public bool TryGetEGID(EntityReference reference, out EGID egid)
  135. {
  136. egid = default;
  137. if (reference == EntityReference.Invalid)
  138. return false;
  139. // Make sure we are querying for the current version of the locator.
  140. // Otherwise the locator is pointing to a removed entity.
  141. if (_entityReferenceMap[reference.index].version == reference.version)
  142. {
  143. egid = _entityReferenceMap[reference.index].egid;
  144. return true;
  145. }
  146. return false;
  147. }
  148. public EGID GetEGID(EntityReference reference)
  149. {
  150. if (reference == EntityReference.Invalid)
  151. throw new ECSException("Invalid Reference");
  152. // Make sure we are querying for the current version of the locator.
  153. // Otherwise the locator is pointing to a removed entity.
  154. if (_entityReferenceMap[reference.index].version != reference.version)
  155. throw new ECSException("outdated Reference");
  156. return _entityReferenceMap[reference.index].egid;
  157. }
  158. internal void PreallocateReferenceMaps(ExclusiveGroupStruct groupID, uint size)
  159. {
  160. _egidToReferenceMap
  161. .GetOrCreate(groupID, () => new SharedSveltoDictionaryNative<uint, EntityReference>(size))
  162. .ResizeTo(size);
  163. _entityReferenceMap.Resize(size);
  164. }
  165. internal void InitEntityReferenceMap()
  166. {
  167. _nextFreeIndex = SharedNativeInt.Create(0, Allocator.Persistent);
  168. _entityReferenceMap =
  169. new NativeDynamicArrayCast<EntityReferenceMapElement>(
  170. NativeDynamicArray.Alloc<EntityReferenceMapElement>());
  171. _egidToReferenceMap =
  172. new SharedSveltoDictionaryNative<ExclusiveGroupStruct,
  173. SharedSveltoDictionaryNative<uint, EntityReference>>(0);
  174. }
  175. internal void DisposeEntityReferenceMap()
  176. {
  177. _nextFreeIndex.Dispose();
  178. _entityReferenceMap.Dispose();
  179. foreach (var element in _egidToReferenceMap)
  180. element.Value.Dispose();
  181. _egidToReferenceMap.Dispose();
  182. }
  183. SharedNativeInt _nextFreeIndex;
  184. NativeDynamicArrayCast<EntityReferenceMapElement> _entityReferenceMap;
  185. SharedSveltoDictionaryNative<ExclusiveGroupStruct, SharedSveltoDictionaryNative<uint, EntityReference>>
  186. _egidToReferenceMap;
  187. }
  188. internal LocatorMap entityLocator => _entityLocator;
  189. LocatorMap _entityLocator;
  190. }
  191. }