public JobHandle Execute(JobHandle inputHandles) { var engines = _engines; JobHandle combinedHandles = inputHandles; using (var profiler = new PlatformProfiler(_name)) { for (var index = 0; index < engines.count; index++) { ref var engine = ref engines[index]; using (profiler.Sample(engine.name)) { combinedHandles = engine.Execute(inputHandles); } } }
public JobHandle Execute(JobHandle combinedHandles, ref Param _param) { var engines = _engines; using (var profiler = new PlatformProfiler(_name)) { for (var index = 0; index < engines.count; index++) { var engine = engines[index]; using (profiler.Sample(engine.name)) combinedHandles = JobHandle.CombineDependencies(combinedHandles, engine.Execute(combinedHandles, ref _param)); } } return(combinedHandles); }
public JobHandle Execute(JobHandle inputHandles) { var sequenceItems = _instancedSequence.items; JobHandle combinedHandles = inputHandles; using (var profiler = new PlatformProfiler(_name)) { for (var index = 0; index < sequenceItems.count; index++) { var engine = sequenceItems[index]; using (profiler.Sample(engine.name)) combinedHandles = engine.Execute(combinedHandles); } } return(combinedHandles); }
void PreSubmissionPhase(ref JobHandle jobHandle, PlatformProfiler profiler) { using (profiler.Sample("Complete All Pending Jobs")) jobHandle.Complete(); //sync-point _entityCommandBuffer = new EntityCommandBuffer((Allocator)Common.Allocator.TempJob); foreach (var system in _submissionEngines) { system.entityCommandBuffer = new EntityCommandBufferForSvelto(_entityCommandBuffer, World.EntityManager); } foreach (var system in _sveltoOnDotsHandleLifeTimeEngines) { system.entityCommandBuffer = new EntityCommandBufferForSvelto(_entityCommandBuffer, World.EntityManager); } }
void RemoveGroupAndEntitiesFromDB(int groupID) { using (var profiler = new PlatformProfiler("Remove Group")) { var dictionariesOfEntities = _groupEntityDB[groupID]; foreach (var dictionaryOfEntities in dictionariesOfEntities) { var platformProfiler = profiler; dictionaryOfEntities.Value.RemoveEntitiesFromEngines(_entityEngines, ref platformProfiler); var groupedGroupOfEntities = _groupsPerEntity[dictionaryOfEntities.Key]; groupedGroupOfEntities.Remove(groupID); } //careful, in this case I assume you really don't want to use this group anymore //so I remove it from the database _groupEntityDB.Remove(groupID); } }
void SubmitEntityComponents() { using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission")) { int iterations = 0; do { SingleSubmission(profiler); } while ((_groupedEntityToAdd.currentEntitiesCreatedPerGroup.count > 0 || _entitiesOperations.Count > 0) && ++iterations < 5); #if DEBUG && !PROFILE_SVELTO if (iterations == 5) { throw new ECSException("possible circular submission detected"); } #endif } }
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); }
void PreSubmissionPhase(ref JobHandle jobHandle, PlatformProfiler profiler) { JobHandle BeforeECBFlushEngines() { JobHandle jobHandle = default; //execute submission engines and complete jobs because of this I don't need to do _ECBSystem.AddJobHandleForProducer(Dependency); for (var index = 0; index < _beforeSubmissionEngines.count; index++) { ref var engine = ref _beforeSubmissionEngines[index]; using (profiler.Sample(engine.name)) { jobHandle = JobHandle.CombineDependencies(jobHandle, engine.BeforeSubmissionUpdate(jobHandle)); } } return(jobHandle); }
public void SubmitEntities(JobHandle jobHandle) { if (_submissionScheduler.paused) { return; } using (var profiler = new PlatformProfiler("SveltoUECSEntitiesSubmissionGroup - PreSubmissionPhase")) { PreSubmissionPhase(ref jobHandle, profiler); //Submit Svelto Entities, calls Add/Remove/MoveTo that can be used by the IUECSSubmissionEngines using (profiler.Sample("Submit svelto entities")) { _submissionScheduler.SubmitEntities(); } AfterSubmissionPhase(profiler); } }
/// <summary> /// Dispose an EngineRoot once not used anymore, so that all the /// engines are notified with the entities removed. /// It's a clean up process. /// </summary> public void Dispose() { using (var profiler = new PlatformProfiler("Final Dispose")) { foreach (FasterDictionary <uint, FasterDictionary <RefWrapper <Type>, ITypeSafeDictionary> > .KeyValuePairFast groups in _groupEntityComponentsDB) { foreach (FasterDictionary <RefWrapper <Type>, ITypeSafeDictionary> .KeyValuePairFast entityList in groups.Value) { entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler, new ExclusiveGroupStruct(groups.Key)); entityList.Value.Dispose(); } } _groupEntityComponentsDB.Clear(); _groupsPerEntity.Clear(); foreach (var engine in _disposableEngines) { engine.Dispose(); } _disposableEngines.Clear(); _enginesSet.Clear(); _enginesTypeSet.Clear(); _reactiveEnginesSwap.Clear(); _reactiveEnginesAddRemove.Clear(); _entitiesOperations.Clear(); _transientEntitiesOperations.Clear(); scheduler.Dispose(); #if DEBUG && !PROFILE_SVELTO _idCheckers.Clear(); #endif _groupedEntityToAdd = null; _entitiesStream.Dispose(); } GC.SuppressFinalize(this); }
///-------------------------------------------- /// void MoveEntityFromAndToEngines(IEntityBuilder[] entityBuilders, EGID fromEntityGID, EGID?toEntityGID) { using (var sampler = new PlatformProfiler("Move Entity From Engines")) { var fromGroup = GetGroup(fromEntityGID.groupID); //Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor! if (fromGroup.TryGetValue(new RefWrapper <Type>(EntityBuilderUtilities.ENTITY_STRUCT_INFO_VIEW), out var entityInfoViewDic) && (entityInfoViewDic as ITypeSafeDictionary <EntityStructInfoView>).TryGetValue(fromEntityGID.entityID, out var entityInfoView)) { MoveEntityViews(fromEntityGID, toEntityGID, entityInfoView.entitiesToBuild, fromGroup, sampler); } //otherwise it's a normal static entity descriptor else { MoveEntityViews(fromEntityGID, toEntityGID, entityBuilders, fromGroup, sampler); } } }
///-------------------------------------------- /// void MoveEntityFromAndToEngines(IComponentBuilder[] componentBuilders, EGID fromEntityGID, EGID?toEntityGID) { using (var sampler = new PlatformProfiler("Move Entity From Engines")) { var fromGroup = GetGroup(fromEntityGID.groupID); //Check if there is an EntityInfo linked to this entity, if so it's a DynamicEntityDescriptor! if (fromGroup.TryGetValue(new RefWrapperType(ComponentBuilderUtilities.ENTITY_INFO_COMPONENT) , out var entityInfoDic) && (entityInfoDic as ITypeSafeDictionary <EntityInfoComponent>).TryGetValue( fromEntityGID.entityID, out var entityInfo)) { MoveEntityComponents(fromEntityGID, toEntityGID, entityInfo.componentsToBuild, fromGroup , sampler); } //otherwise it's a normal static entity descriptor else { MoveEntityComponents(fromEntityGID, toEntityGID, componentBuilders, fromGroup, sampler); } } }
/// <summary> /// Dispose an EngineRoot once not used anymore, so that all the /// engines are notified with the entities removed. /// It's a clean up process. /// </summary> public void Dispose() { using (var profiler = new PlatformProfiler("Final Dispose")) { foreach (var groups in _groupEntityViewsDB) { foreach (var entityList in groups.Value) { entityList.Value.RemoveEntitiesFromEngines(_reactiveEnginesAddRemove, profiler, new ExclusiveGroupStruct(groups.Key)); } } _groupEntityViewsDB.Clear(); _groupsPerEntity.Clear(); foreach (var engine in _disposableEngines) { engine.Dispose(); } _disposableEngines.Clear(); _enginesSet.Clear(); _enginesTypeSet.Clear(); _reactiveEnginesSwap.Clear(); _reactiveEnginesAddRemove.Clear(); _entitiesOperations.Clear(); _transientEntitiesOperations.Clear(); _scheduler.Dispose(); #if DEBUG && !PROFILER _idCheckers.Clear(); #endif _groupedEntityToAdd = null; _entitiesStream.Dispose(); } GC.SuppressFinalize(this); }
IEnumerator SubmitEntityComponents(uint maxNumberOfOperations) { using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission")) { int iterations = 0; do { var submitEntityComponents = SingleSubmission(profiler, maxNumberOfOperations); while (submitEntityComponents.MoveNext() == true) { yield return(null); } } while ((_groupedEntityToAdd.currentEntitiesCreatedPerGroup.count > 0 || _entitiesOperations.count > 0) && ++iterations < 5); #if DEBUG && !PROFILE_SVELTO if (iterations == 5) { throw new ECSException("possible circular submission detected"); } #endif } }
//Right, when you record a command outside of a job using the regular ECB, you don't pass it a sort key. //We instead use a constant for the main thread that is actually set to Int32.MaxValue. Where as the commands //that are recording from jobs with the ParallelWriter, get a lower value sort key from the job. Because we //playback the commands in order based on this sort key, the ParallelWriter commands end up happening before //the main thread commands. This is where your error is coming from because the Instantiate command happens at //the end because it's sort key is Int32.MaxValue. //We don't recommend mixing the main thread and ParallelWriter commands in a single ECB for this reason. public void SubmitEntities(JobHandle jobHandle) { if (_submissionScheduler.paused == true) { return; } using (var profiler = new PlatformProfiler("SveltoDOTSEntitiesSubmissionGroup")) { using (profiler.Sample("PreSubmissionPhase")) { PreSubmissionPhase(ref jobHandle, profiler); } //Submit Svelto Entities, calls Add/Remove/MoveTo that can be used by the IDOTS ECSSubmissionEngines _submissionScheduler.SubmitEntities(); using (profiler.Sample("AfterSubmissionPhase")) { AfterSubmissionPhase(profiler); } } }
public void MoveEntityFromDictionaryAndEngines(EGID fromEntityGid, EGID?toEntityID, ITypeSafeDictionary toGroup, Dictionary <Type, FasterList <IHandleEntityViewEngineAbstracted> > engines, ref PlatformProfiler profiler) { var valueIndex = GetValueIndex(fromEntityGid.entityID); if (engines != null) { RemoveEntityViewFromEngines(engines, ref _values[valueIndex], ref profiler, toGroup != null); } if (toGroup != null) { var toGroupCasted = toGroup as TypeSafeDictionary <TValue>; ref var entity = ref _values[valueIndex]; var previousGroup = fromEntityGid.groupID; /// /// NOTE I WOULD EVENTUALLY NEED TO REUSE THE REAL ID OF THE REMOVING ELEMENT /// SO THAT I CAN DECREASE THE GLOBAL GROUP COUNT /// // entity.ID = EGID.UPDATE_REAL_ID_AND_GROUP(entity.ID, toEntityID.groupID, entityCount); if (HasEgid) { (entity as INeedEGID).ID = toEntityID.Value; } var index = toGroupCasted.Add(fromEntityGid.entityID, ref entity); if (engines != null) { AddEntityViewToEngines(engines, ref toGroupCasted._values[index], previousGroup, ref profiler); } }
/// <summary> /// Dispose an EngineRoot once not used anymore, so that all the /// engines are notified with the entities removed. /// It's a clean up process. /// </summary> public void Dispose() { var profiler = new PlatformProfiler(); using (profiler.StartNewSession("Final Dispose")) { foreach (var groups in _groupEntityDB) { foreach (var entityList in groups.Value) { try { entityList.Value.RemoveEntitiesFromEngines(_entityEngines, ref profiler); } catch (Exception e) { Console.LogException(e); } } } foreach (var engine in _disposableEngines) { try { engine.Dispose(); } catch (Exception e) { Console.LogException(e); } } } GC.SuppressFinalize(this); }
internal ContinuationEnumerator Run <TRunner>(TRunner runner, ref TTask task) where TRunner : class, IRunner <LeanSveltoTask <TTask> > { using (var profiler = new PlatformProfiler("LeanSveltoTask.Run")) { using (profiler.Sample("create task")) _sveltoTask = new SveltoTaskWrapper <TTask, IRunner <LeanSveltoTask <TTask> > >(ref task, runner); #if DEBUG && !PROFILE_SVELTO DBC.Tasks.Check.Require(IS_TASK_STRUCT == true || task != null, "A valid enumerator is required to enable a LeanSveltTask ".FastConcat(ToString())); DBC.Tasks.Check.Require(runner != null, "SetScheduler function has never been called"); #endif using (profiler.Sample("fetch enumerator")) _continuationEnumerator = new ContinuationEnumerator(ContinuationPool.RetrieveFromPool()); _threadSafeSveltoTaskStates.started = true; using (profiler.Sample("StartCoroutine")) runner.StartCoroutine(this); return(_continuationEnumerator); } }
void Dispose(bool disposing) { _isDisposing = disposing; if (disposing == false) { return; } using (var profiler = new PlatformProfiler("Final Dispose")) { //Note: The engines are disposed before the the remove callback to give the chance to behave //differently if a remove happens as a consequence of a dispose //The pattern is to implement the IDisposable interface and set a flag in the engine. The //remove callback will then behave differently according the flag. foreach (var engine in _disposableEngines) { try { if (engine is IDisposingEngine dengine) { dengine.isDisposing = true; } engine.Dispose(); } catch (Exception e) { Console.LogException(e); } } foreach (var groups in _groupEntityComponentsDB) { foreach (var entityList in groups.value) { try { ITypeSafeDictionary typeSafeDictionary = entityList.value; typeSafeDictionary.ExecuteEnginesDisposeCallbacks_Group(_reactiveEnginesDispose, groups.key, profiler); } catch (Exception e) { Console.LogException(e); } } } foreach (var groups in _groupEntityComponentsDB) { foreach (var entityList in groups.value) { entityList.value.Dispose(); } } foreach (var type in _groupFilters) { foreach (var group in type.value) { group.value.Dispose(); } } _groupFilters.Clear(); DisposeFilters(); #if UNITY_NATIVE _nativeAddOperationQueue.Dispose(); _nativeRemoveOperationQueue.Dispose(); _nativeSwapOperationQueue.Dispose(); #endif _groupEntityComponentsDB.Clear(); _groupsPerEntity.Clear(); _disposableEngines.Clear(); _enginesSet.Clear(); _enginesTypeSet.Clear(); _reactiveEnginesSwap.Clear(); _reactiveEnginesAdd.Clear(); _reactiveEnginesRemove.Clear(); _reactiveEnginesDispose.Clear(); _reactiveEnginesSubmission.Clear(); _groupedEntityToAdd.Dispose(); _entityLocator.DisposeEntityReferenceMap(); _entityStreams.Dispose(); scheduler.Dispose(); } }
void SubmitEntityViews() { using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission")) { if (_entitiesOperations.Count > 0) { using (profiler.Sample("Remove and Swap operations")) { #if DEBUG && !PROFILER _entitiesOperationsDebug.Clear(); #endif _transientEntitiesOperations.FastClear(); _transientEntitiesOperations.AddRange(_entitiesOperations); _entitiesOperations.FastClear(); var entitiesOperations = _transientEntitiesOperations.ToArrayFast(); for (var i = 0; i < _transientEntitiesOperations.Count; i++) { try { switch (entitiesOperations[i].type) { case EntitySubmitOperationType.Swap: SwapEntityGroup(entitiesOperations[i].builders, entitiesOperations[i].entityDescriptor, new EGID(entitiesOperations[i].ID, entitiesOperations[i].fromGroupID), new EGID( entitiesOperations[i].toID, entitiesOperations[i].toGroupID)); break; case EntitySubmitOperationType.Remove: MoveEntity(entitiesOperations[i].builders, new EGID(entitiesOperations[i].ID, entitiesOperations[i].fromGroupID), entitiesOperations[i].entityDescriptor, new EGID()); break; case EntitySubmitOperationType.RemoveGroup: RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromGroupID); break; } } catch (Exception e) { #if DEBUG && !PROFILER var str = "Crash while executing Entity Operation" .FastConcat(entitiesOperations[i].type.ToString()) .FastConcat(" id: ") .FastConcat(entitiesOperations[i].ID) .FastConcat(" to id: ") .FastConcat(entitiesOperations[i].toID) .FastConcat(" from groupid: ") .FastConcat(entitiesOperations[i].fromGroupID) .FastConcat(" to groupid: ") .FastConcat(entitiesOperations[i].toGroupID); #if RELAXED_ECS Console.LogException(str.FastConcat(" ", entitiesOperations[i].trace), e); #else throw new ECSException(str.FastConcat(" ").FastConcat(entitiesOperations[i].trace), e); #endif #else var str = "Entity Operation is ".FastConcat(entitiesOperations[i].type.ToString()) .FastConcat(" id: ") .FastConcat(entitiesOperations[i].ID) .FastConcat(" to id: ") .FastConcat(entitiesOperations[i].toID) .FastConcat(" from groupid: ") .FastConcat(entitiesOperations[i].fromGroupID) .FastConcat(" to groupid: ") .FastConcat(entitiesOperations[i].toGroupID); Console.LogException(str, e); #endif } } } } if (_groupedEntityToAdd.current.Count > 0) { using (profiler.Sample("Add operations")) { //use other as source from now on current will be use to write new entityViews _groupedEntityToAdd.Swap(); try { //Note: if N entity of the same type are added on the same frame the Add callback is called N //times on the same frame. if the Add callback builds a new entity, that entity will not //be available in the database until the N callbacks are done solving it could be complicated as //callback and database update must be interleaved. AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd.other, profiler); } #if !DEBUG catch (Exception e) { Console.LogException(e); } #endif finally { //other can be cleared now, but let's avoid deleting the dictionary every time _groupedEntityToAdd.ClearOther(); } } } } }
/// <summary> /// Todo: it would be probably better to split even further the logic between submission and callbacks /// Something to do when I will optimize the callbacks /// </summary> /// <param name="profiler"></param> /// <param name="maxNumberOfOperations"></param> IEnumerator SingleSubmission(PlatformProfiler profiler, uint maxNumberOfOperations) { #if UNITY_NATIVE NativeOperationSubmission(profiler); #endif ClearChecks(); bool entitiesAreSubmitted = false; uint numberOfOperations = 0; if (_entitiesOperations.count > 0) { using (profiler.Sample("Remove and Swap operations")) { _transientEntitiesOperations.FastClear(); _entitiesOperations.CopyValuesTo(_transientEntitiesOperations); _entitiesOperations.FastClear(); EntitySubmitOperation[] entitiesOperations = _transientEntitiesOperations.ToArrayFast(out var count); for (var i = 0; i < count; i++) { try { switch (entitiesOperations[i].type) { case EntitySubmitOperationType.Swap: MoveEntityFromAndToEngines(entitiesOperations[i].builders, entitiesOperations[i].fromID, entitiesOperations[i].toID); break; case EntitySubmitOperationType.Remove: MoveEntityFromAndToEngines(entitiesOperations[i].builders, entitiesOperations[i].fromID, null); break; case EntitySubmitOperationType.RemoveGroup: RemoveEntitiesFromGroup( entitiesOperations[i].fromID.groupID, profiler); break; case EntitySubmitOperationType.SwapGroup: SwapEntitiesBetweenGroups(entitiesOperations[i].fromID.groupID, entitiesOperations[i].toID.groupID, profiler); break; } } catch { var str = "Crash while executing Entity Operation " .FastConcat(entitiesOperations[i].type.ToString()); Svelto.Console.LogError(str.FastConcat(" ") #if DEBUG && !PROFILE_SVELTO .FastConcat(entitiesOperations[i].trace.ToString()) #endif ); throw; } ++numberOfOperations; if ((uint)numberOfOperations >= (uint)maxNumberOfOperations) { yield return(null); numberOfOperations = 0; } } } entitiesAreSubmitted = true; } _groupedEntityToAdd.Swap(); if (_groupedEntityToAdd.otherEntitiesCreatedPerGroup.count > 0) { using (profiler.Sample("Add operations")) { try { using (profiler.Sample("Add entities to database")) { //each group is indexed by entity view type. for each type there is a dictionary indexed by entityID foreach (var groupToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup) { var groupID = groupToSubmit.Key; var groupDB = GetOrCreateGroup(groupID, profiler); //add the entityComponents in the group foreach (var entityComponentsToSubmit in _groupedEntityToAdd.other[groupID]) { var type = entityComponentsToSubmit.Key; var targetTypeSafeDictionary = entityComponentsToSubmit.Value; var wrapper = new RefWrapperType(type); ITypeSafeDictionary dbDic = GetOrCreateTypeSafeDictionary(groupID, groupDB, wrapper, targetTypeSafeDictionary); //Fill the DB with the entity components generate this frame. dbDic.AddEntitiesFromDictionary(targetTypeSafeDictionary, groupID); } } } //then submit everything in the engines, so that the DB is up to date with all the entity components //created by the entity built using (profiler.Sample("Add entities to engines")) { foreach (var groupToSubmit in _groupedEntityToAdd.otherEntitiesCreatedPerGroup) { 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)]; entityComponentsToSubmit.Value.ExecuteEnginesAddOrSwapCallbacks(_reactiveEnginesAddRemove, realDic, null, new ExclusiveGroupStruct(groupID), in profiler); numberOfOperations += entityComponentsToSubmit.Value.count; if (numberOfOperations >= maxNumberOfOperations) { yield return(null); numberOfOperations = 0; } } } } } finally { using (profiler.Sample("clear double buffering")) { //other can be cleared now, but let's avoid deleting the dictionary every time _groupedEntityToAdd.ClearOther(); } } } entitiesAreSubmitted = true; } if (entitiesAreSubmitted) { var enginesCount = _reactiveEnginesSubmission.count; for (int i = 0; i < enginesCount; i++) { _reactiveEnginesSubmission[i].EntitiesSubmitted(); } } }
void SubmitEntityViews() { using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission")) { if (_entitiesOperations.Count > 0) { using (profiler.Sample("Remove and Swap operations")) { _transientEntitiesOperations.FastClear(); var entitySubmitOperations = _entitiesOperations.GetValuesArray(out var count); _transientEntitiesOperations.AddRange(entitySubmitOperations, count); _entitiesOperations.FastClear(); var entitiesOperations = _transientEntitiesOperations.ToArrayFast(); for (var i = 0; i < _transientEntitiesOperations.Count; i++) { try { switch (entitiesOperations[i].type) { case EntitySubmitOperationType.Swap: SwapEntityGroup(entitiesOperations[i].builders, entitiesOperations[i].entityDescriptor, entitiesOperations[i].fromID, entitiesOperations[i].toID); break; case EntitySubmitOperationType.Remove: MoveEntity(entitiesOperations[i].builders, entitiesOperations[i].fromID, entitiesOperations[i].entityDescriptor, null); break; case EntitySubmitOperationType.RemoveGroup: if (entitiesOperations[i].entityDescriptor == null) { RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromID.groupID); } else { RemoveGroupAndEntitiesFromDB(entitiesOperations[i].fromID.groupID, entitiesOperations[i].entityDescriptor); } break; } } catch (Exception e) { var str = "Crash while executing Entity Operation " .FastConcat(entitiesOperations[i].type.ToString()); #if RELAXED_ECS && !PROFILER Console.LogException(str.FastConcat(" " #if DEBUG && !PROFILER , entitiesOperations[i].trace #endif ), e); #else throw new ECSException(str.FastConcat(" ") #if DEBUG && !PROFILER .FastConcat(entitiesOperations[i].trace) #endif , e); #endif } } } } if (_groupedEntityToAdd.currentEntitiesCreatedPerGroup.Count > 0) { using (profiler.Sample("Add operations")) { //use other as source from now on current will be use to write new entityViews _groupedEntityToAdd.Swap(); try { //Note: if N entity of the same type are added on the same frame the Add callback is called //N times on the same frame. if the Add callback builds a new entity, that entity will not //be available in the database until the N callbacks are done. Solving this could be //complicated as callback and database update must be interleaved. AddEntityViewsToTheDBAndSuitableEngines(_groupedEntityToAdd, profiler); } finally { //other can be cleared now, but let's avoid deleting the dictionary every time _groupedEntityToAdd.ClearOther(); } } } } }
void FixedUpdate() { using (var platform = new PlatformProfiler("Physic tasks")) ExecuteRoutines(_physicRoutines, platform); }
void LateUpdate() { using (var platform = new PlatformProfiler("Late tasks")) ExecuteRoutines(_lateRoutines, platform); }
public void OnGUI() { using (var platform = new PlatformProfiler("onGui tasks")) ExecuteRoutines(_onGuiRoutines, platform); }
static void ExecuteRoutines(ThreadSafeFasterList <FasterList <IProcessSveltoTasks> > list, PlatformProfiler profiler) { var orderedRoutines = list.ToArrayFast(out var orderedCount); for (int ii = 0; ii < orderedCount; ii++) { if (orderedRoutines[ii] == null) { continue; } var routines = orderedRoutines[ii]; for (int i = 0; i < routines.count; i++) { var ret = routines[i].MoveNext(profiler); if (ret == false) { routines.UnorderedRemoveAt(i); i--; } } } }
IEnumerator <bool> Invoke() { while (true) { DBC.ECS.Check.Require(_enginesRoot.IsValid, "ticking an GCed engines root?"); var enginesRootTarget = _enginesRoot.Target; var entitiesSubmissionScheduler = enginesRootTarget.scheduler; if (entitiesSubmissionScheduler.paused == false) { DBC.ECS.Check.Require(entitiesSubmissionScheduler.isRunning == false , "A submission started while the previous one was still flushing"); entitiesSubmissionScheduler.isRunning = true; using (var profiler = new PlatformProfiler("Svelto.ECS - Entities Submission")) { var iterations = 0; var hasEverSubmitted = false; #if UNITY_NATIVE enginesRootTarget.FlushNativeOperations(profiler); #endif //todo: proper unit test structural changes made as result of add/remove callbacks while (enginesRootTarget.HasMadeNewStructuralChangesInThisIteration() && iterations++ < 5) { hasEverSubmitted = true; while (true) { _privateSubmitEntities.MoveNext(); if (_privateSubmitEntities.Current == true) { using (profiler.Yield()) { yield return(true); } } else { break; } } #if UNITY_NATIVE if (enginesRootTarget.HasMadeNewStructuralChangesInThisIteration()) { enginesRootTarget.FlushNativeOperations(profiler); } #endif } #if DEBUG && !PROFILE_SVELTO if (iterations == 5) { throw new ECSException("possible circular submission detected"); } #endif if (hasEverSubmitted) { enginesRootTarget.NotifyReactiveEnginesOnSubmission(); } } entitiesSubmissionScheduler.isRunning = false; ++entitiesSubmissionScheduler.iteration; } yield return(false); } }
void MoveEntityView(EGID entityGID, EGID?toEntityGID, Dictionary <Type, ITypeSafeDictionary> toGroup, ref Dictionary <Type, ITypeSafeDictionary> fromGroup, Type entityViewType, PlatformProfiler profiler) { if (fromGroup.TryGetValue(entityViewType, out var fromTypeSafeDictionary) == false) { throw new ECSException("no entities in from group eid: ".FastConcat(entityGID.entityID) .FastConcat(" group: ").FastConcat(entityGID.groupID)); } ITypeSafeDictionary toEntitiesDictionary = null; //in case we want to move to a new group, otherwise is just a remove if (toEntityGID != null) { if (toGroup.TryGetValue(entityViewType, out toEntitiesDictionary) == false) { toEntitiesDictionary = fromTypeSafeDictionary.Create(); toGroup.Add(entityViewType, toEntitiesDictionary); } if (_groupsPerEntity.TryGetValue(entityViewType, out var groupedGroup) == false) { groupedGroup = _groupsPerEntity[entityViewType] = new FasterDictionary <uint, ITypeSafeDictionary>(); } groupedGroup[toEntityGID.Value.groupID] = toEntitiesDictionary; } if (fromTypeSafeDictionary.Has(entityGID.entityID) == false) { throw new EntityNotFoundException(entityGID, entityViewType); } fromTypeSafeDictionary.MoveEntityFromDictionaryAndEngines(entityGID, toEntityGID, toEntitiesDictionary, _entityEngines, ref profiler); if (fromTypeSafeDictionary.Count == 0) //clean up { _groupsPerEntity[entityViewType].Remove(entityGID.groupID); //I don't remove the group if empty on purpose, in case it needs to be reused however I trim it to save //memory fromTypeSafeDictionary.Trim(); } }
/// <summary> /// Dispose an EngineRoot once not used anymore, so that all the /// engines are notified with the entities removed. /// It's a clean up process. /// </summary> public void Dispose() { _isDisposing = true; using (var profiler = new PlatformProfiler("Final Dispose")) { //Note: The engines are disposed before the the remove callback to give the chance to behave //differently if a remove happens as a consequence of a dispose //The pattern is to implement the IDisposable interface and set a flag in the engine. The //remove callback will then behave differently according the flag. foreach (var engine in _disposableEngines) { try { if (engine is IDisposingEngine dengine) { dengine.isDisposing = true; } engine.Dispose(); } catch (Exception e) { Svelto.Console.LogException(e); } } foreach (var groups in _groupEntityComponentsDB) { foreach (var entityList in groups.Value) { try { entityList.Value.ExecuteEnginesRemoveCallbacks(_reactiveEnginesAddRemoveOnDispose, profiler , new ExclusiveGroupStruct(groups.Key)); } catch (Exception e) { Svelto.Console.LogException(e); } } } foreach (var groups in _groupEntityComponentsDB) { foreach (var entityList in groups.Value) { entityList.Value.Dispose(); } } foreach (var type in _groupFilters) { foreach (var group in type.Value) { group.Value.Dispose(); } } _groupFilters.Clear(); #if UNITY_NATIVE _nativeAddOperationQueue.Dispose(); _nativeRemoveOperationQueue.Dispose(); _nativeSwapOperationQueue.Dispose(); #endif _groupEntityComponentsDB.Clear(); _groupsPerEntity.Clear(); _disposableEngines.Clear(); _enginesSet.Clear(); _enginesTypeSet.Clear(); _reactiveEnginesSwap.Clear(); _reactiveEnginesAddRemove.Clear(); _reactiveEnginesAddRemoveOnDispose.Clear(); _reactiveEnginesSubmission.Clear(); _entitiesOperations.Clear(); _transientEntitiesOperations.Clear(); _groupedEntityToAdd.Dispose(); _entityStreams.Dispose(); scheduler.Dispose(); } GC.SuppressFinalize(this); }
///-------------------------------------------- /// void MoveEntity(IEntityBuilder[] entityBuilders, EGID fromEntityGID, Type originalDescriptorType, EGID?toEntityGID) { var profiler = new PlatformProfiler(); using (profiler.StartNewSession("Move Entity")) { //for each entity view generated by the entity descriptor if (_groupEntityDB.TryGetValue(fromEntityGID.groupID, out var fromGroup) == false) { throw new ECSException("from group not found eid: ".FastConcat(fromEntityGID.entityID) .FastConcat(" group: ").FastConcat(fromEntityGID.groupID)); } //Check if there is an EntityInfoView linked to this entity, if so it's a DynamicEntityDescriptor! bool correctEntityDescriptorFound = true; EntityInfoView entityInfoView = default; if (fromGroup.TryGetValue(ENTITY_INFO_VIEW_TYPE, out var entityInfoViewDic) && (entityInfoViewDic as TypeSafeDictionary <EntityInfoView>).TryGetValue( fromEntityGID.entityID, out entityInfoView) && (correctEntityDescriptorFound = entityInfoView.type == originalDescriptorType)) { var entitiesToMove = entityInfoView.entitiesToBuild; Dictionary <Type, ITypeSafeDictionary> toGroup = null; if (toEntityGID != null) { var toGroupID = toEntityGID.Value.groupID; if (_groupEntityDB.TryGetValue(toGroupID, out toGroup) == false) { toGroup = _groupEntityDB[toGroupID] = new Dictionary <Type, ITypeSafeDictionary>(); } } for (int i = 0; i < entitiesToMove.Length; i++) { MoveEntityView(fromEntityGID, toEntityGID, toGroup, ref fromGroup, entitiesToMove[i].GetEntityType(), profiler); } } //otherwise it's a normal static entity descriptor else { #if DEBUG && !PROFILER if (correctEntityDescriptorFound == false) { throw new ECSException(INVALID_DYNAMIC_DESCRIPTOR_ERROR.FastConcat(" ID ").FastConcat(fromEntityGID.entityID) .FastConcat(" group ID ").FastConcat(fromEntityGID.groupID).FastConcat( " descriptor found: ", entityInfoView.type.Name, " descriptor Excepted ", originalDescriptorType.Name)); } #endif Dictionary <Type, ITypeSafeDictionary> toGroup = null; if (toEntityGID != null) { var toGroupID = toEntityGID.Value.groupID; if (_groupEntityDB.TryGetValue(toGroupID, out toGroup) == false) { toGroup = _groupEntityDB[toGroupID] = new Dictionary <Type, ITypeSafeDictionary>(); } } for (var i = 0; i < entityBuilders.Length; i++) { MoveEntityView(fromEntityGID, toEntityGID, toGroup, ref fromGroup, entityBuilders[i].GetEntityType(), profiler); } } } }