@@ -1 +1 @@ | |||
Subproject commit f6d6d2169a310ed3f9ab4a99a4c8ab5b6469ed51 | |||
Subproject commit 177d50a45b2ba05f4bb655432d649330e127bb56 |
@@ -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 | |||
@@ -21,8 +21,10 @@ namespace Svelto.ECS | |||
_initializer = initializer; | |||
} | |||
public bool isUnmanaged => IS_UNMANAGED; | |||
public void BuildEntityAndAddToList(ref ITypeSafeDictionary dictionary, EGID egid, | |||
IEnumerable<object> implementors) | |||
IEnumerable<object> implementors) | |||
{ | |||
if (dictionary == null) | |||
dictionary = TypeSafeDictionaryFactory<T>.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<T>(new Filler<T>()); | |||
@@ -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; | |||
/// <summary> | |||
/// 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. | |||
@@ -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(); | |||
@@ -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) | |||
{ | |||
@@ -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; | |||
} | |||
} | |||
} |
@@ -11,5 +11,6 @@ namespace Svelto.ECS | |||
ITypeSafeDictionary Preallocate(ref ITypeSafeDictionary dictionary, uint size); | |||
Type GetEntityComponentType(); | |||
bool isUnmanaged { get; } | |||
} | |||
} |
@@ -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() | |||
{ | |||
@@ -2,7 +2,7 @@ using System; | |||
namespace Svelto.ECS | |||
{ | |||
internal static class ProcessorCount | |||
public static class ProcessorCount | |||
{ | |||
public static readonly int processorCount = Environment.ProcessorCount; | |||
@@ -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<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
//DBC.ECS.Check.Require(EntityDescriptorTemplate<T>.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<entityDescriptor> | |||
_nativeRemoveOperations.Add(new NativeOperationRemove( | |||
EntityDescriptorTemplate<T>.descriptor.componentsToBuild, TypeCache<T>.type | |||
, memberName)); | |||
return new NativeEntityRemove(_removeOperationQueue, _nativeRemoveOperations.count - 1); | |||
return new NativeEntityRemove(_nativeRemoveOperationQueue, _nativeRemoveOperations.count - 1); | |||
} | |||
NativeEntitySwap ProvideNativeEntitySwapQueue<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
// DBC.ECS.Check.Require(EntityDescriptorTemplate<T>.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<T>.descriptor.componentsToBuild | |||
, TypeCache<T>.type, memberName)); | |||
return new NativeEntitySwap(_swapOperationQueue, _nativeSwapOperations.count - 1); | |||
return new NativeEntitySwap(_nativeSwapOperationQueue, _nativeSwapOperations.count - 1); | |||
} | |||
NativeEntityFactory ProvideNativeEntityFactoryQueue<T>(string memberName) where T : IEntityDescriptor, new() | |||
{ | |||
DBC.ECS.Check.Require(EntityDescriptorTemplate<T>.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<T>.descriptor.componentsToBuild, TypeCache<T>.type)); | |||
new NativeOperationBuild(EntityDescriptorTemplate<T>.descriptor.componentsToBuild, TypeCache<T>.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<uint>(); | |||
var egid = buffer.Dequeue<EGID>(); | |||
var componentCounts = buffer.Dequeue<uint>(); | |||
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; | |||
} | |||
} | |||
@@ -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 |
@@ -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(); | |||
@@ -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" | |||
} |