Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Entity locator map #52

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions Svelto.ECS/EnginesRoot.Engines.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ public EnginesRoot(IEntitiesSubmissionScheduler entitiesComponentScheduler)
new FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>();
_groupsPerEntity = new FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>>();
_groupedEntityToAdd = new DoubleBufferedEntitiesToAdd();
_entityLocatorMap = new FasterList<EntityLocatorMapElement>();
_egidToLocatorMap = new FasterDictionary<uint, FasterDictionary<uint, EntityLocator>>();

_entitiesStream = new EntitiesStream();
_entitiesDB = new EntitiesDB(_groupEntityComponentsDB, _groupsPerEntity, _entitiesStream);
_entitiesDB = new EntitiesDB(_groupEntityComponentsDB, _groupsPerEntity, _entitiesStream, new EntityLocatorMap(this));

scheduler = entitiesComponentScheduler;
scheduler.onTick = new EntitiesSubmitter(this);
Expand Down Expand Up @@ -88,7 +90,7 @@ public void Dispose()
Svelto.Console.LogException(e);
}
}

foreach (FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>.
KeyValuePairFast groups in _groupEntityComponentsDB)
{
Expand All @@ -104,7 +106,7 @@ public void Dispose()
Svelto.Console.LogException(e);
}
}

foreach (FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>>.
KeyValuePairFast groups in _groupEntityComponentsDB)
{
Expand All @@ -129,7 +131,7 @@ public void Dispose()
#endif
_groupedEntityToAdd.Dispose();
_entitiesStream.Dispose();

scheduler.Dispose();
}

Expand Down
15 changes: 15 additions & 0 deletions Svelto.ECS/EnginesRoot.Entities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ EntityComponentInitializer BuildEntity
{
CheckAddEntityID(entityID, implementorType);
Check.Require(entityID.groupID != 0, "invalid group detected");
CreateLocator(entityID);

var dic = EntityFactory.BuildGroupedEntities(entityID, _groupedEntityToAdd, componentsToBuild
, implementors);
Expand Down Expand Up @@ -70,6 +71,16 @@ void MoveEntityFromAndToEngines(IComponentBuilder[] componentBuilders, EGID from
{
var fromGroup = GetGroup(fromEntityGID.groupID);

// Update the egid to unique id maps.
if (toEntityGID.HasValue)
{
UpdateLocator(fromEntityGID, toEntityGID.Value);
}
else
{
RemoveLocator(fromEntityGID);
}

//Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor!
if (fromGroup.TryGetValue(new RefWrapper<Type>(ComponentBuilderUtilities.ENTITY_STRUCT_INFO_VIEW)
, out var entityInfoViewDic)
Expand Down Expand Up @@ -192,6 +203,8 @@ void SwapEntitiesBetweenGroups(uint fromIdGroupId, uint toGroupId, in PlatformPr
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> fromGroup = GetGroup(fromIdGroupId);
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> toGroup = GetOrCreateGroup(toGroupId, profiler);

UpdateAllGroupLocators(fromIdGroupId, toGroupId);

foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities
in fromGroup)
{
Expand Down Expand Up @@ -273,6 +286,8 @@ void RemoveGroupAndEntities(uint groupID, in PlatformProfiler profiler)
FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary> dictionariesOfEntities =
_groupEntityComponentsDB[groupID];

RemoveAllGroupLocators(groupID);

foreach (FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>.KeyValuePairFast dictionaryOfEntities in dictionariesOfEntities)
{
dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler
Expand Down
215 changes: 215 additions & 0 deletions Svelto.ECS/EnginesRoot.LocatorMap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
using System.Collections.Specialized;
using System.Runtime.CompilerServices;
using Svelto.DataStructures;

namespace Svelto.ECS
{
public partial class EnginesRoot
{
// The EntityLocatorMap provides a bidirectional map to help locate entities without using an EGID which might
// change in runtime. The Entity Locator map uses a reusable unique identifier struct called EntityLocator to
// find the last known EGID from last entity submission.
class EntityLocatorMap : IEntityLocatorMap
{
public EntityLocatorMap(EnginesRoot enginesRoot)
{
_enginesRoot = new WeakReference<EnginesRoot>(enginesRoot);
}

public EntityLocator GetLocator(EGID egid)
{
return _enginesRoot.Target.GetLocator(egid);
}

public bool TryGetEGID(EntityLocator locator, out EGID egid)
{
return _enginesRoot.Target.TryGetEGID(locator, out egid);
}

WeakReference<EnginesRoot> _enginesRoot;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void CreateLocator(EGID egid)
{
// Check if we need to create a new EntityLocator or whether we can recycle an existing one.s
EntityLocator locator;
if (_nextEntityId == _entityLocatorMap.count)
{
_entityLocatorMap.Add(new EntityLocatorMapElement(egid));
locator = new EntityLocator(_nextEntityId++);
}
else
{
ref var element = ref _entityLocatorMap[_nextEntityId];
locator = new EntityLocator(_nextEntityId, element.version);
// The recycle entities form a linked list, using the egid.entityID to store the next element.
_nextEntityId = element.egid.entityID;
element.egid = egid;
}

// When we create a new one there is nothing to recycle anymore, so we need to update the last recycle entityId.
if (_nextEntityId == _entityLocatorMap.count)
{
_lastEntityId = _entityLocatorMap.count;
}

// Update reverse map from egid to locator.
if (_egidToLocatorMap.TryGetValue(egid.groupID, out var groupMap) == false)
{
groupMap = new FasterDictionary<uint, EntityLocator>();
_egidToLocatorMap[egid.groupID] = groupMap;
}
groupMap[egid.entityID] = locator;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void UpdateLocator(EGID from, EGID to)
{
var locator = GetAndRemoveLocator(from);

#if DEBUG && !PROFILE_SVELTO
if (locator.Equals(EntityLocator.Invalid))
{
throw new ECSException("Unable to update locator from egid: "
.FastConcat(from.ToString(), "to egid: ")
.FastConcat(to.ToString(), ". Locator was not found")
);
}
#endif

_entityLocatorMap[locator.uniqueID].egid = to;

if (_egidToLocatorMap.TryGetValue(to.groupID, out var groupMap) == false)
{
groupMap = new FasterDictionary<uint, EntityLocator>();
_egidToLocatorMap[to.groupID] = groupMap;
}
groupMap[to.entityID] = locator;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void RemoveLocator(EGID egid)
{
var locator = GetAndRemoveLocator(egid);

#if DEBUG && !PROFILE_SVELTO
if (locator.Equals(EntityLocator.Invalid))
{
throw new ECSException("Unable to remove locator for egid: "
.FastConcat(egid.ToString(), ". Locator was not found")
);
}
#endif

// Check if this is the first recycled element.
if (_lastEntityId == _entityLocatorMap.count)
{
_nextEntityId = locator.uniqueID;
}
// Otherwise add it as the last recycled element.
else
{
_entityLocatorMap[_lastEntityId].egid = new EGID(locator.uniqueID, 0);
}

// Invalidate the entity locator element by bumping its version and setting the egid to point to a unexisting element.
_entityLocatorMap[locator.uniqueID].egid = new EGID(_entityLocatorMap.count, 0);
_entityLocatorMap[locator.uniqueID].version++;

// Mark the element as the last element used.
_lastEntityId = locator.uniqueID;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void RemoveAllGroupLocators(uint groupId)
{
if (_egidToLocatorMap.TryGetValue(groupId, out var groupMap) == false)
{
return;
}

// We need to traverse all entities in the group and remove the locator using the egid.
// RemoveLocator would modify the enumerator so this is why we traverse the dictionary from last to first.
var keys = groupMap.unsafeKeys;
for (var i = groupMap.count - 1; true; i--)
{
RemoveLocator(new EGID(keys[i].key, groupId));
if (i == 0) break;
}

_egidToLocatorMap.Remove(groupId);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void UpdateAllGroupLocators(uint fromGroupId, uint toGroupId)
{
if (_egidToLocatorMap.TryGetValue(fromGroupId, out var groupMap) == false)
{
return;
}

// We need to traverse all entities in the group and update the locator using the egid.
// UpdateLocator would modify the enumerator so this is why we traverse the dictionary from last to first.
var keys = groupMap.unsafeKeys;
for (var i = groupMap.count - 1; true; i--)
{
UpdateLocator(new EGID(keys[i].key, fromGroupId), new EGID(keys[i].key, toGroupId));
if (i == 0) break;
}

_egidToLocatorMap.Remove(fromGroupId);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
EntityLocator GetLocator(EGID egid)
{
if (_egidToLocatorMap.TryGetValue(egid.groupID, out var groupMap))
{
if (groupMap.TryGetValue(egid.entityID, out var locator))
{
return locator;
}
}

return EntityLocator.Invalid;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
EntityLocator GetAndRemoveLocator(EGID egid)
{
if (_egidToLocatorMap.TryGetValue(egid.groupID, out var groupMap))
{
if (groupMap.TryGetValue(egid.entityID, out var locator))
{
groupMap.Remove(egid.entityID);
return locator;
}
}

return EntityLocator.Invalid;
}

bool TryGetEGID(EntityLocator locator, out EGID egid)
{
egid = new EGID();
if (locator == EntityLocator.Invalid) return false;
// Make sure we are querying for the current version of the locator.
// Otherwise the locator is pointing to a removed entity.
if (_entityLocatorMap[locator.uniqueID].version == locator.version)
{
egid = _entityLocatorMap[locator.uniqueID].egid;
return true;
}
else
{
return false;
}
}

uint _nextEntityId;
uint _lastEntityId;
readonly FasterList<EntityLocatorMapElement> _entityLocatorMap;
readonly FasterDictionary<uint, FasterDictionary<uint, EntityLocator>> _egidToLocatorMap;
}
}
30 changes: 23 additions & 7 deletions Svelto.ECS/EntitiesDB.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,25 @@ public partial class EntitiesDB
internal EntitiesDB
(FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDictionary>> groupEntityComponentsDB
, FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> groupsPerEntity
, EntitiesStream entityStream)
, EntitiesStream entityStream
, IEntityLocatorMap entityLocatorMap)
{
_groupEntityComponentsDB = groupEntityComponentsDB;
_groupsPerEntity = groupsPerEntity;
_entityStream = entityStream;
_entityLocatorMap = entityLocatorMap;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool FindEGID(EntityLocator entityLocator, out EGID egid)
{
return _entityLocatorMap.TryGetEGID(entityLocator, out egid);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public EntityLocator GetLocator(EGID egid)
{
return _entityLocatorMap.GetLocator(egid);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -75,7 +89,7 @@ public EntityCollection<T1, T2> QueryEntities<T1, T2>(ExclusiveGroupStruct group
.FastConcat("'. Entity 2: ' count: ".FastConcat(T2entities.count)
.FastConcat(" ", typeof(T2).ToString())
.FastConcat("' group: ", groupStruct.ToName())));
#endif
#endif

return new EntityCollection<T1, T2>(T1entities, T2entities);
}
Expand All @@ -96,16 +110,16 @@ public EntityCollection<T1, T2, T3> QueryEntities<T1, T2, T3>(ExclusiveGroupStru
.FastConcat(T2entities.count)
.FastConcat(" Entity 3: ".FastConcat(typeof(T3).ToString()))
.FastConcat(" count: ").FastConcat(T3entities.count)));
#endif
#endif

return new EntityCollection<T1, T2, T3>(T1entities, T2entities, T3entities);
}

public int IterateOverGroupsAndCount<T>
(in LocalFasterReadOnlyList<ExclusiveGroupStruct> groups) where T : struct, IEntityComponent
{
int count = 0;

for (int i = 0; i < groups.count; i++)
{
count += Count<T>(groups[i]);
Expand Down Expand Up @@ -154,7 +168,7 @@ public EGIDMapper<T> QueryMappedEntities<T>(ExclusiveGroupStruct groupStructId)

return (typeSafeDictionary as ITypeSafeDictionary<T>).ToEGIDMapper(groupStructId);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryQueryMappedEntities<T>
(ExclusiveGroupStruct groupStructId, out EGIDMapper<T> mapper) where T : struct, IEntityComponent
Expand Down Expand Up @@ -210,7 +224,7 @@ public int Count<T>(ExclusiveGroupStruct groupStruct) where T : struct, IEntityC
{
if (SafeQueryEntityDictionary<T>(groupStruct, out var typeSafeDictionary) == false)
return 0;

return (int) typeSafeDictionary.count;
}

Expand Down Expand Up @@ -324,5 +338,7 @@ readonly FasterDictionary<uint, FasterDictionary<RefWrapper<Type>, ITypeSafeDict
//needed to be able to track in which groups a specific entity type can be found.
//may change in future as it could be expanded to support queries
readonly FasterDictionary<RefWrapper<Type>, FasterDictionary<uint, ITypeSafeDictionary>> _groupsPerEntity;

readonly IEntityLocatorMap _entityLocatorMap;
}
}
Loading