#if UNITY_ECS
using System.Collections;
using Svelto.Common;
using Svelto.DataStructures;
using Svelto.ECS.Native;
using Svelto.ECS.Schedulers;
using Unity.Entities;
using Unity.Jobs;
namespace Svelto.ECS.Extensions.Unity
{
///
/// Group of UECS/Svelto SystemBase engines that creates UECS entities.
/// Svelto entities are submitted
/// Svelto Add and remove callback are called
/// OnUpdate of the systems are called
/// finally the UECS command buffer is flushed
/// Note: I cannot use Unity ComponentSystemGroups nor I can rely on the SystemBase Dependency field to
/// solve external dependencies. External dependencies are tracked, but only linked to the UECS components operations
/// With Dependency I cannot guarantee that an external container is used before previous jobs working on it are completed
///
[DisableAutoCreation]
public sealed class SveltoUECSEntitiesSubmissionGroup : SystemBase, IQueryingEntitiesEngine , IReactOnAddAndRemove
, IReactOnSwap, ISveltoUECSSubmission
{
public SveltoUECSEntitiesSubmissionGroup(SimpleEntitiesSubmissionScheduler submissionScheduler)
{
_submissionScheduler = submissionScheduler;
_engines = new FasterList();
_afterSubmissionEngines = new FasterList();
_beforeSubmissionEngines = new FasterList();
}
protected override void OnCreate()
{
_ECBSystem = World.CreateSystem();
_entityQuery = GetEntityQuery(typeof(UpdateUECSEntityAfterSubmission));
}
public EntitiesDB entitiesDB { get; set; }
public void Ready() { }
public void Add(ref UECSEntityComponent entityComponent, EGID egid) { }
public void Remove(ref UECSEntityComponent entityComponent, EGID egid)
{
_ECB.DestroyEntity(entityComponent.uecsEntity);
}
public void MovedTo(ref UECSEntityComponent entityComponent, ExclusiveGroupStruct previousGroup, EGID egid)
{
_ECB.SetSharedComponent(entityComponent.uecsEntity, new UECSSveltoGroupID(egid.groupID));
}
public void Add(SubmissionEngine engine)
{
Svelto.Console.LogDebug($"Add Engine {engine} to the UECS world {_ECBSystem.World.Name}");
_ECBSystem.World.AddSystem(engine);
if (engine is IUpdateAfterSubmission afterSubmission)
_afterSubmissionEngines.Add(afterSubmission);
if (engine is IUpdateBeforeSubmission beforeSubmission)
_beforeSubmissionEngines.Add(beforeSubmission);
_engines.Add(engine);
}
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);
}
}
public IEnumerator SubmitEntitiesAsync(JobHandle jobHandle, uint maxEntities)
{
if (_submissionScheduler.paused)
yield break;
using (var profiler = new PlatformProfiler("SveltoUECSEntitiesSubmissionGroup - PreSubmissionPhase"))
{
PreSubmissionPhase(ref jobHandle, profiler);
var submitEntitiesAsync = _submissionScheduler.SubmitEntitiesAsync(maxEntities);
//Submit Svelto Entities, calls Add/Remove/MoveTo that can be used by the IUECSSubmissionEngines
while (true)
{
using (profiler.Sample("Submit svelto entities async"))
{
submitEntitiesAsync.MoveNext();
}
if (submitEntitiesAsync.Current == true)
{
using (profiler.Yield())
yield return null;
}
else
break;
}
AfterSubmissionPhase(profiler);
}
}
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;
}
using (profiler.Sample("Complete All Pending Jobs"))
{
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;
}
_ECB = entityCommandBuffer;
RemovePreviousMarkingComponents(entityCommandBuffer);
using (profiler.Sample("Before Submission Engines"))
{
BeforeECBFlushEngines().Complete();
}
}
void AfterSubmissionPhase(PlatformProfiler profiler)
{
JobHandle AfterECBFlushEngines()
{
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 < _afterSubmissionEngines.count; index++)
{
ref var engine = ref _afterSubmissionEngines[index];
using (profiler.Sample(engine.name))
{
jobHandle = JobHandle.CombineDependencies(jobHandle, engine.AfterSubmissionUpdate(jobHandle));
}
}
return jobHandle;
}
using (profiler.Sample("Flush Command Buffer"))
{
_ECBSystem.Update();
}
ConvertPendingEntities().Complete();
using (profiler.Sample("After Submission Engines"))
{
AfterECBFlushEngines().Complete();
}
}
void RemovePreviousMarkingComponents(EntityCommandBuffer ECB)
{
ECB.RemoveComponentForEntityQuery(_entityQuery);
}
JobHandle ConvertPendingEntities()
{
if (_entityQuery.IsEmpty == false)
{
NativeEGIDMultiMapper mapper =
entitiesDB.QueryNativeMappedEntities(
entitiesDB.FindGroups(), Allocator.TempJob);
Entities.ForEach((Entity id, ref UpdateUECSEntityAfterSubmission egidComponent) =>
{
mapper.Entity(egidComponent.egid).uecsEntity = id;
}).ScheduleParallel();
mapper.ScheduleDispose(Dependency);
}
return Dependency;
}
readonly SimpleEntitiesSubmissionScheduler _submissionScheduler;
SubmissionEntitiesCommandBufferSystem _ECBSystem;
readonly FasterList _engines;
readonly FasterList _beforeSubmissionEngines;
readonly FasterList _afterSubmissionEngines;
[DisableAutoCreation]
class SubmissionEntitiesCommandBufferSystem : EntityCommandBufferSystem { }
protected override void OnUpdate() { }
EntityQuery _entityQuery;
EntityCommandBuffer _ECB;
}
public interface ISveltoUECSSubmission
{
void Add(SubmissionEngine engine);
void SubmitEntities(JobHandle jobHandle);
IEnumerator SubmitEntitiesAsync(JobHandle jobHandle, uint maxEntities);
}
}
#endif