#if UNITY_ECS
using System;
using System.Collections.Generic;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Native;
using Svelto.ECS.Schedulers;
using Unity.Entities;
using Unity.Jobs;
using Allocator = Unity.Collections.Allocator;
namespace Svelto.ECS.SveltoOnDOTS
{
///
/// SveltoDOTS ECSEntitiesSubmissionGroup expand the _submissionScheduler responsibility to integrate the
/// submission of Svelto entities with the submission of DOTS ECS entities using EntityCommandBuffer.
/// As there is just one submissionScheduler per enginesRoot, there should be only one SveltoDOTS
/// ECSEntitiesSubmissionGroup
/// per engines group. It's expected use is showed in the class SveltoOnDOTS ECSEnginesGroup which should be used
/// instead of using this class directly.
/// Groups DOTS ECS/Svelto SystemBase engines that creates DOTS ECS entities.
/// Flow:
/// Complete all the jobs used as input dependencies (this is a sync point)
/// Create the new frame Command Buffer to use
/// Svelto entities are submitted
/// Svelto Add and remove callback are called
/// ECB is injected in all the registered engines
/// all the OnUpdate of the registered engines/systems are called
/// the DOTS ECS command buffer is flushed
/// all the DOTS ECS entities created that need Svelto information will be processed
///
[DisableAutoCreation]
public sealed partial class SveltoOnDOTSEntitiesSubmissionGroup : SystemBase, IQueryingEntitiesEngine,
ISveltoOnDOTSSubmission
{
public SveltoOnDOTSEntitiesSubmissionGroup(SimpleEntitiesSubmissionScheduler submissionScheduler,
EnginesRoot enginesRoot)
{
_submissionScheduler = submissionScheduler;
_submissionEngines = new FasterList();
_cachedList = new List();
_sveltoOnDotsHandleLifeTimeEngines = new FasterList();
var defaultSveltoOnDotsHandleLifeTimeEngine = new SveltoOnDOTSHandleLifeTimeEngine();
enginesRoot.AddEngine(defaultSveltoOnDotsHandleLifeTimeEngine);
_sveltoOnDotsHandleLifeTimeEngines.Add(defaultSveltoOnDotsHandleLifeTimeEngine);
}
public EntitiesDB entitiesDB { get; set; }
public void Ready() { }
//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 Add(SveltoOnDOTSHandleCreationEngine engine)
{
// Console.LogDebug($"Add Submission Engine {engine} to the DOTS world {_ECBSystem.World.Name}");
//this is temporary enabled because of engines that needs EntityManagers for the wrong reasons.
_submissionEngines.Add(engine);
engine.entityManager = EntityManager;
engine.OnCreate();
}
public void Add(ISveltoOnDOTSHandleLifeTimeEngine engine)
{
// Console.LogDebug($"Add Submission Engine {engine} to the DOTS world {_ECBSystem.World.Name}");
_sveltoOnDotsHandleLifeTimeEngines.Add(engine);
}
void PreSubmissionPhase(ref JobHandle jobHandle, PlatformProfiler profiler)
{
using (profiler.Sample("Complete All Pending Jobs")) jobHandle.Complete();
_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 AfterSubmissionPhase(PlatformProfiler profiler)
{
JobHandle combinedHandle = default;
for (var i = 0; i < _submissionEngines.count; i++)
{
try
{
combinedHandle = JobHandle.CombineDependencies(combinedHandle, _submissionEngines[i].OnUpdate());
}
catch (Exception e)
{
Console.LogException(e, _submissionEngines[i].name);
throw;
}
}
using (profiler.Sample("Playback Command Buffer"))
{
_entityCommandBuffer.Playback(EntityManager);
_entityCommandBuffer.Dispose();
}
using (profiler.Sample("ConvertPendingEntities"))
ConvertPendingEntities(combinedHandle);
}
//Note: when this is called, the CommandBuffer is flushed so the not temporary DOTS entity ID will be used
void ConvertPendingEntities(JobHandle combinedHandle)
{
var entityCommandBuffer = new EntityCommandBuffer((Allocator)Common.Allocator.TempJob);
var cmd = entityCommandBuffer.AsParallelWriter();
_cachedList.Clear();
//note with DOTS 0.17 unfortunately this allocates a lot :(
EntityManager.GetAllUniqueSharedComponentData(_cachedList);
Dependency = JobHandle.CombineDependencies(Dependency, combinedHandle);
for (int i = 0; i < _cachedList.Count; i++)
{
var dotsEntityToSetup = _cachedList[i];
if (dotsEntityToSetup.@group == ExclusiveGroupStruct.Invalid) continue;
var mapper = entitiesDB.QueryNativeMappedEntities(dotsEntityToSetup.@group);
//Note: for some reason GetAllUniqueSharedComponentData returns DOTSEntityToSetup with valid values
//that are not used anymore by any entity. Something to keep an eye on if fixed on future versions
//of DOTS
Entities.ForEach((Entity entity, int entityInQueryIndex, in DOTSSveltoEGID egid) =>
{
mapper.Entity(egid.egid.entityID).dotsEntity = entity;
cmd.RemoveComponent(entityInQueryIndex, entity);
}).WithSharedComponentFilter(dotsEntityToSetup).ScheduleParallel();
}
Dependency.Complete();
entityCommandBuffer.Playback(EntityManager);
entityCommandBuffer.Dispose();
}
protected override void OnCreate()
{
}
protected override void OnUpdate()
{
throw new NotSupportedException("if this is called something broke the original design");
}
readonly FasterList _submissionEngines;
readonly FasterList _sveltoOnDotsHandleLifeTimeEngines;
readonly SimpleEntitiesSubmissionScheduler _submissionScheduler;
readonly List _cachedList;
EntityCommandBuffer _entityCommandBuffer;
}
}
#endif