diff --git a/Svelto.Common b/Svelto.Common index f6d6d21..177d50a 160000 --- a/Svelto.Common +++ b/Svelto.Common @@ -1 +1 @@ -Subproject commit f6d6d2169a310ed3f9ab4a99a4c8ab5b6469ed51 +Subproject commit 177d50a45b2ba05f4bb655432d649330e127bb56 diff --git a/Svelto.ECS/CHANGELOG.md b/Svelto.ECS/CHANGELOG.md index 41df00d..d9ac49f 100644 --- a/Svelto.ECS/CHANGELOG.md +++ b/Svelto.ECS/CHANGELOG.md @@ -1,7 +1,15 @@ # Changelog All notable changes to this project will be documented in this file. I created this file with Svelto.ECS version 3.1. -## [3.1.1 +## [3.1.2] + +### Changed + +* improved async entity submission code (still experimental) +* improved native entity operations debug info +* + +## [3.1.1] ### Changed diff --git a/Svelto.ECS/Core/ComponentBuilder.cs b/Svelto.ECS/Core/ComponentBuilder.cs index a948d2d..bd23c5d 100644 --- a/Svelto.ECS/Core/ComponentBuilder.cs +++ b/Svelto.ECS/Core/ComponentBuilder.cs @@ -21,8 +21,10 @@ namespace Svelto.ECS _initializer = initializer; } + public bool isUnmanaged => IS_UNMANAGED; + public void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID egid, - IEnumerable implementors) + IEnumerable implementors) { if (dictionary == null) dictionary = TypeSafeDictionaryFactory.Create(); @@ -77,7 +79,7 @@ namespace Svelto.ECS IS_ENTITY_VIEW_COMPONENT = typeof(IEntityViewComponent).IsAssignableFrom(ENTITY_COMPONENT_TYPE); HAS_EGID = typeof(INeedEGID).IsAssignableFrom(ENTITY_COMPONENT_TYPE); ENTITY_COMPONENT_NAME = ENTITY_COMPONENT_TYPE.ToString(); - var IS_UNMANAGED = ENTITY_COMPONENT_TYPE.IsUnmanagedEx(); + IS_UNMANAGED = ENTITY_COMPONENT_TYPE.IsUnmanagedEx(); if (IS_UNMANAGED) EntityComponentIDMap.Register(new Filler()); @@ -102,9 +104,10 @@ namespace Svelto.ECS public static readonly bool HAS_EGID; internal static readonly bool IS_ENTITY_VIEW_COMPONENT; - static readonly T DEFAULT_IT; - static readonly string ENTITY_COMPONENT_NAME; - + static readonly T DEFAULT_IT; + static readonly string ENTITY_COMPONENT_NAME; + static bool IS_UNMANAGED; + /// /// Note: this static class will hold forever the references of the entities implementors. These references /// are not even cleared when the engines root is destroyed, as they are static references. diff --git a/Svelto.ECS/Core/EnginesRoot.Engines.cs b/Svelto.ECS/Core/EnginesRoot.Engines.cs index b7f7e8c..30f1123 100644 --- a/Svelto.ECS/Core/EnginesRoot.Engines.cs +++ b/Svelto.ECS/Core/EnginesRoot.Engines.cs @@ -145,9 +145,9 @@ namespace Svelto.ECS _groupFilters.Clear(); #if UNITY_NATIVE - _addOperationQueue.Dispose(); - _removeOperationQueue.Dispose(); - _swapOperationQueue.Dispose(); + _nativeAddOperationQueue.Dispose(); + _nativeRemoveOperationQueue.Dispose(); + _nativeSwapOperationQueue.Dispose(); #endif _groupEntityComponentsDB.Clear(); _groupsPerEntity.Clear(); diff --git a/Svelto.ECS/Core/EnginesRoot.Submission.cs b/Svelto.ECS/Core/EnginesRoot.Submission.cs index 8c22000..4f31ec9 100644 --- a/Svelto.ECS/Core/EnginesRoot.Submission.cs +++ b/Svelto.ECS/Core/EnginesRoot.Submission.cs @@ -148,7 +148,8 @@ namespace Svelto.ECS { var groupID = groupToSubmit.Key; var groupDB = _groupEntityComponentsDB[groupID]; - +//entityComponentsToSubmit is the array of components found in the groupID per component type. +//if there are N entities to submit, and M components type to add for each entity, this foreach will run NxM times. foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID]) { var realDic = groupDB[new RefWrapperType(entityComponentsToSubmit.Key)]; @@ -156,7 +157,7 @@ namespace Svelto.ECS entityComponentsToSubmit.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesAddRemove, realDic, null, new ExclusiveGroupStruct(groupID), in profiler); - ++numberOfOperations; + numberOfOperations += entityComponentsToSubmit.Value.count; if (numberOfOperations >= maxNumberOfOperations) { diff --git a/Svelto.ECS/Core/EntityDescriptor/EntityDescriptorExtension.cs b/Svelto.ECS/Core/EntityDescriptor/EntityDescriptorExtension.cs new file mode 100644 index 0000000..982e0ff --- /dev/null +++ b/Svelto.ECS/Core/EntityDescriptor/EntityDescriptorExtension.cs @@ -0,0 +1,14 @@ +namespace Svelto.ECS +{ + public static class EntityDescriptorExtension + { + public static bool IsUnmanaged(this IEntityDescriptor descriptor) + { + foreach (var component in descriptor.componentsToBuild) + if (component.isUnmanaged == false) + return false; + + return true; + } + } +} \ No newline at end of file diff --git a/Svelto.ECS/Core/IComponentBuilder.cs b/Svelto.ECS/Core/IComponentBuilder.cs index 0b532c5..cc242e4 100644 --- a/Svelto.ECS/Core/IComponentBuilder.cs +++ b/Svelto.ECS/Core/IComponentBuilder.cs @@ -11,5 +11,6 @@ namespace Svelto.ECS ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size); Type GetEntityComponentType(); + bool isUnmanaged { get; } } } \ No newline at end of file diff --git a/Svelto.ECS/Core/SimpleEntitiesSubmissionScheduler.cs b/Svelto.ECS/Core/SimpleEntitiesSubmissionScheduler.cs index d1c27bd..293c09e 100644 --- a/Svelto.ECS/Core/SimpleEntitiesSubmissionScheduler.cs +++ b/Svelto.ECS/Core/SimpleEntitiesSubmissionScheduler.cs @@ -20,6 +20,17 @@ namespace Svelto.ECS.Schedulers yield return null; } } + + public IEnumerator SubmitEntitiesAsync(uint maxNumberOfOperationsPerFrame) + { + if (paused == false) + { + var submitEntities = _onTick.Invoke(maxNumberOfOperationsPerFrame); + + while (submitEntities.MoveNext()) + yield return null; + } + } public void SubmitEntities() { diff --git a/Svelto.ECS/Extensions/ProcessorCount.cs b/Svelto.ECS/Extensions/ProcessorCount.cs index 4df4450..5fdbb53 100644 --- a/Svelto.ECS/Extensions/ProcessorCount.cs +++ b/Svelto.ECS/Extensions/ProcessorCount.cs @@ -2,7 +2,7 @@ using System; namespace Svelto.ECS { - internal static class ProcessorCount + public static class ProcessorCount { public static readonly int processorCount = Environment.ProcessorCount; diff --git a/Svelto.ECS/Extensions/Unity/DOTS/Native/EnginesRoot.NativeOperation.cs b/Svelto.ECS/Extensions/Unity/DOTS/Native/EnginesRoot.NativeOperation.cs index 136129c..5fc9658 100644 --- a/Svelto.ECS/Extensions/Unity/DOTS/Native/EnginesRoot.NativeOperation.cs +++ b/Svelto.ECS/Extensions/Unity/DOTS/Native/EnginesRoot.NativeOperation.cs @@ -1,57 +1,60 @@ #if UNITY_NATIVE using System; +using DBC.ECS; using Svelto.Common; using Svelto.DataStructures; using Svelto.ECS.DataStructures; +using Svelto.ECS.Internal; namespace Svelto.ECS { public partial class EnginesRoot { //todo: I very likely don't need to create one for each native entity factory, the same can be reused - readonly AtomicNativeBags _addOperationQueue = new AtomicNativeBags(Common.Allocator.Persistent); - - readonly AtomicNativeBags _removeOperationQueue = new AtomicNativeBags(Common.Allocator.Persistent); - - readonly AtomicNativeBags _swapOperationQueue = new AtomicNativeBags(Common.Allocator.Persistent); + readonly AtomicNativeBags _nativeAddOperationQueue = new AtomicNativeBags(Common.Allocator.Persistent); + readonly AtomicNativeBags _nativeRemoveOperationQueue = new AtomicNativeBags(Common.Allocator.Persistent); + readonly AtomicNativeBags _nativeSwapOperationQueue = new AtomicNativeBags(Common.Allocator.Persistent); NativeEntityRemove ProvideNativeEntityRemoveQueue(string memberName) where T : IEntityDescriptor, new() { + //DBC.ECS.Check.Require(EntityDescriptorTemplate.descriptor.IsUnmanaged(), "can't remove entities with not native types"); //todo: remove operation array and store entity descriptor hash in the return value //todo I maybe able to provide a _nativeSwap.SwapEntity _nativeRemoveOperations.Add(new NativeOperationRemove( EntityDescriptorTemplate.descriptor.componentsToBuild, TypeCache.type , memberName)); - return new NativeEntityRemove(_removeOperationQueue, _nativeRemoveOperations.count - 1); + return new NativeEntityRemove(_nativeRemoveOperationQueue, _nativeRemoveOperations.count - 1); } NativeEntitySwap ProvideNativeEntitySwapQueue(string memberName) where T : IEntityDescriptor, new() { + // DBC.ECS.Check.Require(EntityDescriptorTemplate.descriptor.IsUnmanaged(), "can't swap entities with not native types"); //todo: remove operation array and store entity descriptor hash in the return value _nativeSwapOperations.Add(new NativeOperationSwap(EntityDescriptorTemplate.descriptor.componentsToBuild , TypeCache.type, memberName)); - return new NativeEntitySwap(_swapOperationQueue, _nativeSwapOperations.count - 1); + return new NativeEntitySwap(_nativeSwapOperationQueue, _nativeSwapOperations.count - 1); } NativeEntityFactory ProvideNativeEntityFactoryQueue(string memberName) where T : IEntityDescriptor, new() { + DBC.ECS.Check.Require(EntityDescriptorTemplate.descriptor.IsUnmanaged(), "can't build entities with not native types"); //todo: remove operation array and store entity descriptor hash in the return value _nativeAddOperations.Add( - new NativeOperationBuild(EntityDescriptorTemplate.descriptor.componentsToBuild, TypeCache.type)); + new NativeOperationBuild(EntityDescriptorTemplate.descriptor.componentsToBuild, TypeCache.type, memberName)); - return new NativeEntityFactory(_addOperationQueue, _nativeAddOperations.count - 1); + return new NativeEntityFactory(_nativeAddOperationQueue, _nativeAddOperations.count - 1); } void NativeOperationSubmission(in PlatformProfiler profiler) { using (profiler.Sample("Native Remove/Swap Operations")) { - var removeBuffersCount = _removeOperationQueue.count; + var removeBuffersCount = _nativeRemoveOperationQueue.count; for (int i = 0; i < removeBuffersCount; i++) { - ref var buffer = ref _removeOperationQueue.GetBuffer(i); + ref var buffer = ref _nativeRemoveOperationQueue.GetBuffer(i); while (buffer.IsEmpty() == false) { @@ -66,10 +69,10 @@ namespace Svelto.ECS } } - var swapBuffersCount = _swapOperationQueue.count; + var swapBuffersCount = _nativeSwapOperationQueue.count; for (int i = 0; i < swapBuffersCount; i++) { - ref var buffer = ref _swapOperationQueue.GetBuffer(i); + ref var buffer = ref _nativeSwapOperationQueue.GetBuffer(i); while (buffer.IsEmpty() == false) { @@ -93,19 +96,27 @@ namespace Svelto.ECS using (profiler.Sample("Native Add Operations")) { - var addBuffersCount = _addOperationQueue.count; + var addBuffersCount = _nativeAddOperationQueue.count; for (int i = 0; i < addBuffersCount; i++) { - ref var buffer = ref _addOperationQueue.GetBuffer(i); + ref var buffer = ref _nativeAddOperationQueue.GetBuffer(i); while (buffer.IsEmpty() == false) { var componentsIndex = buffer.Dequeue(); var egid = buffer.Dequeue(); var componentCounts = buffer.Dequeue(); + + var componentBuilders = _nativeAddOperations[componentsIndex].components; + var entityDescriptorType = _nativeAddOperations[componentsIndex].entityDescriptorType; + CheckAddEntityID(egid, entityDescriptorType, _nativeAddOperations[componentsIndex].caller); + + Check.Require(egid.groupID != 0, "invalid group detected, are you using new ExclusiveGroupStruct() instead of new ExclusiveGroup()?"); - var init = BuildEntity(egid, _nativeAddOperations[componentsIndex].components - , _nativeAddOperations[componentsIndex].entityDescriptorType); + var dic = EntityFactory.BuildGroupedEntities(egid, _groupedEntityToAdd, componentBuilders + , null, entityDescriptorType); + + var init = new EntityInitializer(egid, dic); //only called if Init is called on the initialized (there is something to init) while (componentCounts > 0) @@ -152,11 +163,14 @@ namespace Svelto.ECS { internal readonly IComponentBuilder[] components; internal readonly Type entityDescriptorType; + internal readonly string caller; - public NativeOperationBuild(IComponentBuilder[] descriptorComponentsToBuild, Type entityDescriptorType) + public NativeOperationBuild + (IComponentBuilder[] descriptorComponentsToBuild, Type entityDescriptorType, string caller) { this.entityDescriptorType = entityDescriptorType; components = descriptorComponentsToBuild; + this.caller = caller; } } diff --git a/Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEntityFactory.cs b/Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEntityFactory.cs index dc83142..ff3eddc 100644 --- a/Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEntityFactory.cs +++ b/Svelto.ECS/Extensions/Unity/DOTS/Native/NativeEntityFactory.cs @@ -5,9 +5,6 @@ namespace Svelto.ECS { public readonly struct NativeEntityFactory { - readonly AtomicNativeBags _addOperationQueue; - readonly int _index; - internal NativeEntityFactory(AtomicNativeBags addOperationQueue, int index) { _index = index; @@ -36,6 +33,9 @@ namespace Svelto.ECS return new NativeEntityInitializer(unsafeBuffer, index); } + + readonly AtomicNativeBags _addOperationQueue; + readonly int _index; } } #endif \ No newline at end of file diff --git a/Svelto.ECS/Extensions/Unity/DOTS/UECS/SveltoUECSEntitiesSubmissionGroup.cs b/Svelto.ECS/Extensions/Unity/DOTS/UECS/SveltoUECSEntitiesSubmissionGroup.cs index dd1c619..b9bfbdc 100644 --- a/Svelto.ECS/Extensions/Unity/DOTS/UECS/SveltoUECSEntitiesSubmissionGroup.cs +++ b/Svelto.ECS/Extensions/Unity/DOTS/UECS/SveltoUECSEntitiesSubmissionGroup.cs @@ -1,4 +1,6 @@ #if UNITY_ECS +using System; +using System.Collections; using Svelto.Common; using Svelto.DataStructures; using Svelto.ECS.Schedulers; @@ -28,6 +30,24 @@ namespace Svelto.ECS.Extensions.Unity public void SubmitEntities(JobHandle jobHandle) { + JobHandle RefHelper() + { + //execute submission engines and complete jobs because of this I don't need to do _ECBSystem.AddJobHandleForProducer(Dependency); + using (var profiler = new PlatformProfiler("SveltoUECSEntitiesSubmissionGroup")) + { + for (var index = 0; index < _engines.count; index++) + { + ref var engine = ref _engines[index]; + using (profiler.Sample(engine.name)) + { + jobHandle = engine.Execute(jobHandle); + } + } + } + + return jobHandle; + } + if (_submissionScheduler.paused) return; @@ -44,19 +64,55 @@ namespace Svelto.ECS.Extensions.Unity //Submit Svelto Entities, calls Add/Remove/MoveTo that can be used by the IUECSSubmissionEngines _submissionScheduler.SubmitEntities(); - //execute submission engines and complete jobs because of this I don't need to do _ECBSystem.AddJobHandleForProducer(Dependency); - using (var profiler = new PlatformProfiler("SveltoUECSEntitiesSubmissionGroup")) + jobHandle = RefHelper(); + + //Sync Point as we must be sure that jobs that create/swap/remove entities are done + jobHandle.Complete(); + + //flush command buffer + _ECBSystem.Update(); + } + + public IEnumerator SubmitEntities(JobHandle jobHandle, uint maxEntities) + { + JobHandle RefHelper() { - for (var index = 0; index < _engines.count; index++) + //execute submission engines and complete jobs because of this I don't need to do _ECBSystem.AddJobHandleForProducer(Dependency); + using (var profiler = new PlatformProfiler("SveltoUECSEntitiesSubmissionGroup")) { - ref var engine = ref _engines[index]; - using (profiler.Sample(engine.name)) + for (var index = 0; index < _engines.count; index++) { - jobHandle = engine.Execute(jobHandle); + ref var engine = ref _engines[index]; + using (profiler.Sample(engine.name)) + { + jobHandle = engine.Execute(jobHandle); + } } } + + return jobHandle; + } + + if (_submissionScheduler.paused) + yield break; + + jobHandle.Complete(); + + //prepare the entity command buffer to be used by the registered engines + var entityCommandBuffer = _ECBSystem.CreateCommandBuffer(); + + foreach (var system in _engines) + { + system.ECB = entityCommandBuffer; } + //Submit Svelto Entities, calls Add/Remove/MoveTo that can be used by the IUECSSubmissionEngines + var submitEntitiesAsync = _submissionScheduler.SubmitEntitiesAsync(maxEntities); + while (submitEntitiesAsync.MoveNext()) + yield return null; + + jobHandle = RefHelper(); + //Sync Point as we must be sure that jobs that create/swap/remove entities are done jobHandle.Complete(); diff --git a/Svelto.ECS/package.json b/Svelto.ECS/package.json index 022bbbe..4c9ab3f 100644 --- a/Svelto.ECS/package.json +++ b/Svelto.ECS/package.json @@ -3,13 +3,13 @@ "category": "Svelto", "description": "Svelto ECS C# Lightweight Data Oriented Entity Component System Framework", "dependencies": { - "com.sebaslab.svelto.common": "3.1.1" + "com.sebaslab.svelto.common": "3.1.2" }, "keywords": [ "svelto" ], "name": "com.sebaslab.svelto.ecs", - "version": "3.1.1", + "version": "3.1.2", "type": "library", "unity": "2019.3" } \ No newline at end of file