diff --git a/com.sebaslab.svelto.common b/com.sebaslab.svelto.common index 114ce56..b3b6ad7 160000 --- a/com.sebaslab.svelto.common +++ b/com.sebaslab.svelto.common @@ -1 +1 @@ -Subproject commit 114ce56f5efe5b5a2a8326ef55587ca668e72503 +Subproject commit b3b6ad7f922cfdac70d1ee5c28c580ddd451a1ff diff --git a/com.sebaslab.svelto.ecs/CHANGELOG.md b/com.sebaslab.svelto.ecs/CHANGELOG.md index c0234e9..da1b1d3 100644 --- a/com.sebaslab.svelto.ecs/CHANGELOG.md +++ b/com.sebaslab.svelto.ecs/CHANGELOG.md @@ -1,6 +1,11 @@ # Svelto.ECS Changelog All notable changes to this project will be documented in this file. Changes are listed in random order of importance. +## [3.4.5] - 04-2023 +* improved code after internal data refactoring and fixed some bugs +* Added static DynamicEntityDescriptor method to create a new DynamicEntityDescriptor without passing an array of components +* factory method to build entities without entity descriptor is now internal + ## [3.4.4] - 04-2023 * refactored internal datastructures diff --git a/com.sebaslab.svelto.ecs/Core/ComponentBuilder.cs b/com.sebaslab.svelto.ecs/Core/ComponentBuilder.cs index 2ccafdc..c9a53e3 100644 --- a/com.sebaslab.svelto.ecs/Core/ComponentBuilder.cs +++ b/com.sebaslab.svelto.ecs/Core/ComponentBuilder.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Reflection; -using System.Runtime.CompilerServices; using System.Threading; using DBC.ECS; using Svelto.Common; @@ -26,104 +24,6 @@ namespace Svelto.ECS } } - public static class BurstCompatibleCounter - { - public static int counter; - } - - static public class ComponentTypeMap - { - static readonly FasterDictionary, ComponentID> _componentTypeMap = new FasterDictionary, ComponentID>(); - static readonly FasterDictionary _reverseComponentTypeMap = new FasterDictionary(); - - public static void Add(Type type, ComponentID idData) - { - _componentTypeMap.Add(type, idData); - _reverseComponentTypeMap.Add(idData, type); - } - - public static ComponentID FetchID(Type type) - { - return _componentTypeMap[type]; - } - - public static Type FetchType(ComponentID id) - { - return _reverseComponentTypeMap[id]; - } - } - - public class ComponentTypeID where T : struct, _IInternalEntityComponent - { - static readonly SharedStaticWrapper> _id; - - public static ComponentID id - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _id.Data; - } - - //todo: any reason to not do this? If I don't, I cannot Create filters in ready functions and - //I have to remove the CreateFilter method - static ComponentTypeID() - { - Init(); - } - -#if UNITY_BURST - [Unity.Burst.BurstDiscard] - //SharedStatic values must be initialized from not burstified code -#endif - static void Init() - { - _id.Data = Interlocked.Increment(ref BurstCompatibleCounter.counter); - ComponentTypeMap.Add(typeof(T), id); - } - } - - sealed class ComponentIDDebugProxy - { - public ComponentIDDebugProxy(ComponentID id) - { - this._id = id; - } - - public Type type => ComponentTypeMap.FetchType(_id); - - readonly ComponentID _id; - } - - [DebuggerTypeProxy(typeof(ComponentIDDebugProxy))] - public struct ComponentID: IEquatable - { - public static implicit operator int(ComponentID id) - { - return id._id; - } - - public static implicit operator uint(ComponentID id) - { - return (uint)id._id; - } - - public static implicit operator ComponentID(int id) - { - return new ComponentID() {_id = id}; - } - - public bool Equals(ComponentID other) - { - return _id == other._id; - } - - public override int GetHashCode() - { - return _id; - } - - int _id; - } - public class ComponentBuilder : IComponentBuilder where T : struct, _IInternalEntityComponent { internal static readonly Type ENTITY_COMPONENT_TYPE; diff --git a/com.sebaslab.svelto.ecs/Core/ComponentID.cs b/com.sebaslab.svelto.ecs/Core/ComponentID.cs new file mode 100644 index 0000000..7d0d046 --- /dev/null +++ b/com.sebaslab.svelto.ecs/Core/ComponentID.cs @@ -0,0 +1,52 @@ +using System; +using System.Diagnostics; + +namespace Svelto.ECS +{ + sealed class ComponentIDDebugProxy + { + public ComponentIDDebugProxy(ComponentID id) + { + this._id = id; + } + + public Type type => ComponentTypeMap.FetchType(_id); + + readonly ComponentID _id; + } + + + [DebuggerTypeProxy(typeof(ComponentIDDebugProxy))] + public struct ComponentID: IEquatable + { + public static implicit operator int(ComponentID id) + { + return id._id; + } + + public static implicit operator uint(ComponentID id) + { + return (uint)id._id; + } + + public static implicit operator ComponentID(int id) + { + return new ComponentID() + { + _id = id + }; + } + + public bool Equals(ComponentID other) + { + return _id == other._id; + } + + public override int GetHashCode() + { + return _id; + } + + int _id; + } +} \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/Core/ComponentTypeID.cs b/com.sebaslab.svelto.ecs/Core/ComponentTypeID.cs new file mode 100644 index 0000000..da40c66 --- /dev/null +++ b/com.sebaslab.svelto.ecs/Core/ComponentTypeID.cs @@ -0,0 +1,38 @@ +using System.Runtime.CompilerServices; +using System.Threading; +using Svelto.Common; +using Svelto.ECS.Internal; + +namespace Svelto.ECS +{ + public static class BurstCompatibleCounter + { + public static int counter; + } + + public class ComponentTypeID where T : struct, _IInternalEntityComponent + { + static readonly SharedStaticWrapper> _id; + + public static ComponentID id + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _id.Data; + } + + static ComponentTypeID() + { + Init(); + } + +#if UNITY_BURST + [Unity.Burst.BurstDiscard] + //SharedStatic values must be initialized from not burstified code +#endif + static void Init() + { + _id.Data = Interlocked.Increment(ref BurstCompatibleCounter.counter); + ComponentTypeMap.Add(typeof(T), id); + } + } +} \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/Core/ComponentTypeMap.cs b/com.sebaslab.svelto.ecs/Core/ComponentTypeMap.cs new file mode 100644 index 0000000..5f17eb9 --- /dev/null +++ b/com.sebaslab.svelto.ecs/Core/ComponentTypeMap.cs @@ -0,0 +1,38 @@ +using System; +using System.Runtime.CompilerServices; +using Svelto.DataStructures; + +namespace Svelto.ECS +{ + public static class ComponentTypeMap + { + static readonly FasterDictionary, ComponentID> _componentTypeMap = new FasterDictionary, ComponentID>(); + static readonly FasterDictionary _reverseComponentTypeMap = new FasterDictionary(); + + public static void Add(Type type, ComponentID idData) + { + _componentTypeMap.Add(type, idData); + _reverseComponentTypeMap.Add(idData, type); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ComponentID FetchID(Type type) + { + if (_componentTypeMap.TryGetValue(type, out var index) == false) + { + //if warming up is working correctly, this should never happen + var componentType = typeof(ComponentTypeID<>).MakeGenericType(type); + System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(componentType.TypeHandle); + return _componentTypeMap[type]; + } + + return index; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Type FetchType(ComponentID id) + { + return _reverseComponentTypeMap[id]; + } + } +} \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFactory.cs b/com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFactory.cs index 8577dff..00f068e 100644 --- a/com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFactory.cs +++ b/com.sebaslab.svelto.ecs/Core/EnginesRoot.GenericEntityFactory.cs @@ -7,7 +7,7 @@ namespace Svelto.ECS { public partial class EnginesRoot { - class GenericEntityFactory : IEntityFactory + class GenericEntityFactory : IEntityFactory, IEntitySerializationFactory { public GenericEntityFactory(EnginesRoot weakReference) { diff --git a/com.sebaslab.svelto.ecs/Core/EntityDescriptor/DynamicEntityDescriptor.cs b/com.sebaslab.svelto.ecs/Core/EntityDescriptor/DynamicEntityDescriptor.cs index ea65ef8..0be72db 100644 --- a/com.sebaslab.svelto.ecs/Core/EntityDescriptor/DynamicEntityDescriptor.cs +++ b/com.sebaslab.svelto.ecs/Core/EntityDescriptor/DynamicEntityDescriptor.cs @@ -14,40 +14,48 @@ namespace Svelto.ECS /// public struct DynamicEntityDescriptor : IDynamicEntityDescriptor where TType : IEntityDescriptor, new() { - internal DynamicEntityDescriptor(bool isExtendible) : this() + public static DynamicEntityDescriptor CreateDynamicEntityDescriptor() { + var entityDescriptor = new DynamicEntityDescriptor(); + var defaultEntities = EntityDescriptorTemplate.realDescriptor.componentsToBuild; var length = defaultEntities.Length; if (FetchEntityInfoComponent(defaultEntities) == -1) { - _componentsToBuild = new IComponentBuilder[length + 1]; + entityDescriptor._componentsToBuild = new IComponentBuilder[length + 1]; - Array.Copy(defaultEntities, 0, _componentsToBuild, 0, length); + Array.Copy(defaultEntities, 0, entityDescriptor._componentsToBuild, 0, length); //assign it after otherwise the previous copy will overwrite the value in case the item //is already present - _componentsToBuild[length] = new ComponentBuilder(new EntityInfoComponent + entityDescriptor._componentsToBuild[length] = new ComponentBuilder(new EntityInfoComponent { - componentsToBuild = _componentsToBuild + componentsToBuild = entityDescriptor._componentsToBuild }); } else { - _componentsToBuild = new IComponentBuilder[length]; + entityDescriptor._componentsToBuild = new IComponentBuilder[length]; - Array.Copy(defaultEntities, 0, _componentsToBuild, 0, length); + Array.Copy(defaultEntities, 0, entityDescriptor._componentsToBuild, 0, length); } + + return entityDescriptor; } - public DynamicEntityDescriptor(IComponentBuilder[] extraEntityBuilders) : this(true) + public DynamicEntityDescriptor(IComponentBuilder[] extraEntityBuilders) { + this = CreateDynamicEntityDescriptor(); + var extraEntitiesLength = extraEntityBuilders.Length; _componentsToBuild = Construct(extraEntitiesLength, extraEntityBuilders); } - public DynamicEntityDescriptor(FasterList extraEntityBuilders) : this(true) + public DynamicEntityDescriptor(FasterList extraEntityBuilders) { + this = CreateDynamicEntityDescriptor(); + var extraEntities = extraEntityBuilders.ToArrayFast(out _); var extraEntitiesLength = extraEntityBuilders.count; diff --git a/com.sebaslab.svelto.ecs/Core/EntityDescriptor/ExtendibleEntityDescriptor.cs b/com.sebaslab.svelto.ecs/Core/EntityDescriptor/ExtendibleEntityDescriptor.cs index 5ad8f49..33c106f 100644 --- a/com.sebaslab.svelto.ecs/Core/EntityDescriptor/ExtendibleEntityDescriptor.cs +++ b/com.sebaslab.svelto.ecs/Core/EntityDescriptor/ExtendibleEntityDescriptor.cs @@ -38,7 +38,7 @@ namespace Svelto.ECS protected ExtendibleEntityDescriptor() { - _dynamicDescriptor = new DynamicEntityDescriptor(true); + _dynamicDescriptor = DynamicEntityDescriptor.CreateDynamicEntityDescriptor(); } protected ExtendibleEntityDescriptor ExtendWith() where T : IEntityDescriptor, new() diff --git a/com.sebaslab.svelto.ecs/Core/Filters/Legacy/EntitiesDB.LegacyFilters.cs b/com.sebaslab.svelto.ecs/Core/Filters/Legacy/EntitiesDB.LegacyFilters.cs index bc2e74f..2a1867e 100644 --- a/com.sebaslab.svelto.ecs/Core/Filters/Legacy/EntitiesDB.LegacyFilters.cs +++ b/com.sebaslab.svelto.ecs/Core/Filters/Legacy/EntitiesDB.LegacyFilters.cs @@ -127,6 +127,7 @@ namespace Svelto.ECS } public void ClearFilter(int filterID, ExclusiveGroupStruct exclusiveGroupStruct) + where T : struct, _IInternalEntityComponent { if (_filtersLegacy.TryGetValue(ComponentTypeID.id, out var fasterDictionary)) { @@ -137,14 +138,14 @@ namespace Svelto.ECS } } - public void ClearFilters(int filterID) + public void ClearFilters(int filterID) where T : struct, _IInternalEntityComponent { if (_filtersLegacy.TryGetValue(ComponentTypeID.id, out var fasterDictionary)) foreach (var filtersPerGroup in fasterDictionary) filtersPerGroup.value.ClearFilter(filterID); } - public void DisposeFilters(ExclusiveGroupStruct exclusiveGroupStruct) + public void DisposeFilters(ExclusiveGroupStruct exclusiveGroupStruct) where T : struct, _IInternalEntityComponent { if (_filtersLegacy.TryGetValue(ComponentTypeID.id, out var fasterDictionary)) { @@ -153,7 +154,7 @@ namespace Svelto.ECS } } - public void DisposeFilters() + public void DisposeFilters() where T : struct, _IInternalEntityComponent { if (_filtersLegacy.TryGetValue(ComponentTypeID.id, out var fasterDictionary)) foreach (var filtersPerGroup in fasterDictionary) @@ -162,7 +163,7 @@ namespace Svelto.ECS _filtersLegacy.Remove(ComponentTypeID.id); } - public void DisposeFilterForGroup(int resetFilterID, ExclusiveGroupStruct group) + public void DisposeFilterForGroup(int resetFilterID, ExclusiveGroupStruct group) where T : struct, _IInternalEntityComponent { if (_filtersLegacy.TryGetValue(ComponentTypeID.id, out var fasterDictionary)) fasterDictionary[@group].DisposeFilter(resetFilterID); @@ -185,14 +186,13 @@ namespace Svelto.ECS public bool AddEntityToFilter(int filtersID, EGID egid, N mapper) where N : IEGIDMapper { - ref var filter = - ref CreateOrGetFilterForGroup(filtersID, egid.groupID, new RefWrapperType(mapper.entityType)); + ref var filter = ref CreateOrGetFilterForGroup(filtersID, egid.groupID, ComponentTypeMap.FetchID(mapper.entityType)); return filter.Add(egid.entityID, mapper); } internal ref LegacyFilterGroup CreateOrGetFilterForGroup(int filterID, ExclusiveGroupStruct groupID, - RefWrapperType refWrapper) + ComponentID refWrapper) { var fasterDictionary = _filtersLegacy.GetOrAdd(refWrapper, () => new FasterDictionary()); diff --git a/com.sebaslab.svelto.ecs/Core/Groups/EntityDescriptorsWarmup.cs b/com.sebaslab.svelto.ecs/Core/Groups/EntityDescriptorsWarmup.cs index 7125bfb..9d26d97 100644 --- a/com.sebaslab.svelto.ecs/Core/Groups/EntityDescriptorsWarmup.cs +++ b/com.sebaslab.svelto.ecs/Core/Groups/EntityDescriptorsWarmup.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using Svelto.ECS.Internal; namespace Svelto.ECS { @@ -15,35 +16,47 @@ namespace Svelto.ECS List assemblies = AssemblyUtility.GetCompatibleAssemblies(); foreach (Assembly assembly in assemblies) { - var typeOfEntityDescriptors = typeof(IEntityDescriptor); + var typeOfEntityDescriptors = typeof(IEntityDescriptor); - foreach (Type type in AssemblyUtility.GetTypesSafe(assembly)) + foreach (Type type in AssemblyUtility.GetTypesSafe(assembly)) + { + if (type.IsInterface == false && typeOfEntityDescriptors.IsAssignableFrom(type)) //IsClass and IsSealed and IsAbstract means only static classes { - if (typeOfEntityDescriptors.IsAssignableFrom(type)) //IsClass and IsSealed and IsAbstract means only static classes + try { + //the main goal of this iteration is to warm up the component builders and descriptors + //note: I could have just instanced the entity descriptor, but in this way I will warm up the EntityDescriptorTemplate too var warmup = typeof(EntityDescriptorTemplate<>).MakeGenericType(type); - try - { - System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(warmup.TypeHandle); - } - catch - { - continue; - } - PropertyInfo field = warmup.GetProperty("descriptor", BindingFlags.Static | BindingFlags.Public); - object value = field.GetValue(null); // pass null because the field is static + System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(warmup.TypeHandle); + } + catch (Exception e) + { + continue; + } + } + } + + var typeOfComponents = typeof(_IInternalEntityComponent); -// cast the value to your descriptor type - IEntityDescriptor descriptor = (IEntityDescriptor)value; - foreach (IComponentBuilder component in descriptor.componentsToBuild) - { - var typeArguments = component.GetEntityComponentType(); - var warmup2 = typeof(ComponentTypeID<>).MakeGenericType(typeArguments); - System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(warmup2.TypeHandle); - } + //the main goal of this iteration is to warm up the component IDs + foreach (Type type in AssemblyUtility.GetTypesSafe(assembly)) + { + if (type.IsInterface == false && typeOfComponents.IsAssignableFrom(type)) //IsClass and IsSealed and IsAbstract means only static classes + { + try + { + var componentType = typeof(ComponentTypeID<>).MakeGenericType(type); + //is called only once ever, even if runs multiple times. + //this warms up the component builder. There could be different implementation of components builders for the same component type in theory + System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(componentType.TypeHandle); + } + catch (Exception e) + { + continue; } } + } } } } diff --git a/com.sebaslab.svelto.ecs/Core/IEntityFactory.cs b/com.sebaslab.svelto.ecs/Core/IEntityFactory.cs index 276c707..a722877 100644 --- a/com.sebaslab.svelto.ecs/Core/IEntityFactory.cs +++ b/com.sebaslab.svelto.ecs/Core/IEntityFactory.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; namespace Svelto.ECS @@ -52,11 +51,6 @@ namespace Svelto.ECS EntityInitializer BuildEntity(EGID egid, T entityDescriptor, IEnumerable implementors = null, [System.Runtime.CompilerServices.CallerMemberName] string caller = null) where T : IEntityDescriptor; - //Todo: analyze if this can be internal or just related to serialization - EntityInitializer BuildEntity(EGID egid, IComponentBuilder[] componentsToBuild, Type descriptorType, - IEnumerable implementors = null, - [System.Runtime.CompilerServices.CallerMemberName] string caller = null); - #if UNITY_NATIVE Svelto.ECS.Native.NativeEntityFactory ToNative([System.Runtime.CompilerServices.CallerMemberName] string callerName = null) where T : IEntityDescriptor, new(); diff --git a/com.sebaslab.svelto.ecs/Core/IEntitySerializationFactory.cs b/com.sebaslab.svelto.ecs/Core/IEntitySerializationFactory.cs new file mode 100644 index 0000000..12a0f74 --- /dev/null +++ b/com.sebaslab.svelto.ecs/Core/IEntitySerializationFactory.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace Svelto.ECS +{ + interface IEntitySerializationFactory + { + EntityInitializer BuildEntity(EGID egid, IComponentBuilder[] componentsToBuild, Type descriptorType, + IEnumerable implementors = null, + [System.Runtime.CompilerServices.CallerMemberName] string caller = null); + } +} \ No newline at end of file diff --git a/com.sebaslab.svelto.ecs/Extensions/Svelto/Legacy/EntitiesDBFiltersExtension.cs b/com.sebaslab.svelto.ecs/Extensions/Svelto/Legacy/EntitiesDBFiltersExtension.cs index 7f9ddaf..8ff82f5 100644 --- a/com.sebaslab.svelto.ecs/Extensions/Svelto/Legacy/EntitiesDBFiltersExtension.cs +++ b/com.sebaslab.svelto.ecs/Extensions/Svelto/Legacy/EntitiesDBFiltersExtension.cs @@ -9,7 +9,7 @@ namespace Svelto.ECS public static bool AddEntityToFilter(this EntitiesDB.LegacyFilters legacyFilters, int filtersID, EGID egid, N mapper) where N : IEGIDMultiMapper { ref var filter = - ref legacyFilters.CreateOrGetFilterForGroup(filtersID, egid.groupID, new RefWrapperType(mapper.entityType)); + ref legacyFilters.CreateOrGetFilterForGroup(filtersID, egid.groupID, ComponentTypeMap.FetchID(mapper.entityType)); return filter.Add(egid.entityID, mapper); } diff --git a/com.sebaslab.svelto.ecs/Serialization/DefaultVersioningFactory.cs b/com.sebaslab.svelto.ecs/Serialization/DefaultVersioningFactory.cs index 862ce8c..10a5442 100644 --- a/com.sebaslab.svelto.ecs/Serialization/DefaultVersioningFactory.cs +++ b/com.sebaslab.svelto.ecs/Serialization/DefaultVersioningFactory.cs @@ -16,7 +16,7 @@ namespace Svelto.ECS.Serialization ? entityDescriptor.componentsToSerialize : entityDescriptor.componentsToBuild; - var initializer = factory.BuildEntity(egid, entityDescriptorEntitiesToSerialize, TypeCache.type); + var initializer = (factory as IEntitySerializationFactory).BuildEntity(egid, entityDescriptorEntitiesToSerialize, TypeCache.type); entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref initializer, serializationType); diff --git a/com.sebaslab.svelto.ecs/Svelto.ECS.csproj b/com.sebaslab.svelto.ecs/Svelto.ECS.csproj index 87fa700..9a0b63e 100644 --- a/com.sebaslab.svelto.ecs/Svelto.ECS.csproj +++ b/com.sebaslab.svelto.ecs/Svelto.ECS.csproj @@ -1,7 +1,7 @@  Svelto.ECS - 10 + 9 netstandard2.1 Svelto 3.4 diff --git a/com.sebaslab.svelto.ecs/package.json b/com.sebaslab.svelto.ecs/package.json index 8cc7fb1..e454774 100644 --- a/com.sebaslab.svelto.ecs/package.json +++ b/com.sebaslab.svelto.ecs/package.json @@ -19,7 +19,7 @@ "svelto.ecs" ], "name": "com.sebaslab.svelto.ecs", - "version": "3.4.4", + "version": "3.4.5", "type": "library", "unity": "2020.3" } diff --git a/com.sebaslab.svelto.ecs/version.json b/com.sebaslab.svelto.ecs/version.json index 28ff4ef..d86a467 100644 --- a/com.sebaslab.svelto.ecs/version.json +++ b/com.sebaslab.svelto.ecs/version.json @@ -1,3 +1,3 @@ { - "version": "3.4.4" + "version": "3.4.5" }