#if GRAPH_DESIGNER /// --------------------------------------------- /// Behavior Designer /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.BehaviorDesigner.Runtime.Tasks.Composites { using Opsive.BehaviorDesigner.Runtime.Components; using Opsive.BehaviorDesigner.Runtime.Utility; using Opsive.GraphDesigner.Runtime; using Unity.Burst; using Unity.Collections; using Unity.Entities; using Unity.Jobs; using UnityEngine; /// /// A node representation of the parallel task. /// [NodeIcon("f612c025389b22640b1b6df88f4502e7", "8a4a401bcfb527a48a08351efaf92e14")] [Opsive.Shared.Utility.Description("Similar to the sequence task, the parallel task will run each child task until a child task returns failure. " + "The parallel task will run all of its children tasks simultaneously versus running each task one at a time. " + "Like the sequence class, the parallel task will return success once all of its children tasks have return success. " + "If one tasks returns failure the parallel task will end all of the child tasks and return failure.")] public class Parallel : ECSCompositeTask, IParentNode, IParallelNode { public override ComponentType Flag { get => typeof(ParallelFlag); } /// /// Adds the IBufferElementData to the entity. /// /// The world that the entity exists in. /// The entity that the IBufferElementData should be assigned to. /// The GameObject that the entity is attached to. /// The index of the element within the buffer. public override int AddBufferElement(World world, Entity entity, GameObject gameObject) { var index = base.AddBufferElement(world, entity, gameObject); ComponentUtility.AddInterruptComponents(world.EntityManager, entity); return index; } /// /// Returns a new TBufferElement for use by the system. /// /// A new TBufferElement for use by the system. public override ParallelComponent GetBufferElement() { return new ParallelComponent() { Index = RuntimeIndex }; } } /// /// The DOTS data structure for the Parallel class. /// public struct ParallelComponent : IBufferElementData { [Tooltip("The index of the node.")] [SerializeField] ushort m_Index; public ushort Index { get => m_Index; set => m_Index = value; } } /// /// A DOTS tag indicating when a Parallel node is active. /// public struct ParallelFlag : IComponentData, IEnableableComponent { } /// /// Runs the Parallel logic. /// [DisableAutoCreation] public partial struct ParallelTaskSystem : ISystem { private EntityQuery m_Query; private JobHandle m_Dependency; /// /// Builds the query. /// /// THe current SystemState. private void OnCreate(ref SystemState state) { m_Query = SystemAPI.QueryBuilder().WithAllRW().WithAllRW().WithAllRW().WithAll().Build(); } /// /// Creates the job. /// /// The current state of the system. [BurstCompile] private void OnUpdate(ref SystemState state) { m_Dependency.Complete(); var ecb = SystemAPI.GetSingleton().CreateCommandBuffer(state.WorldUnmanaged); state.Dependency = new ParallelJob() { EntityCommandBuffer = ecb.AsParallelWriter() }.ScheduleParallel(m_Query, state.Dependency); m_Dependency = state.Dependency; } /// /// Job which executes the task logic. /// [BurstCompile] private partial struct ParallelJob : IJobEntity { [Tooltip("CommandBuffer which sets the component data.")] public EntityCommandBuffer.ParallelWriter EntityCommandBuffer; /// /// Executes the parallel logic. /// /// The entity that is being acted upon. /// The index of the entity. /// An array of ParallelComponents. /// An array of TaskComponents. /// An array of BranchComponents. [BurstCompile] public void Execute(Entity entity, [EntityIndexInQuery] int entityIndex, ref DynamicBuffer parallelComponents, ref DynamicBuffer taskComponents, ref DynamicBuffer branchComponents) { for (int i = 0; i < parallelComponents.Length; ++i) { var parallelComponent = parallelComponents[i]; var taskComponent = taskComponents[parallelComponent.Index]; var branchComponent = branchComponents[taskComponent.BranchIndex]; // Do not continue if there will be an interrupt or the branch cannot execute. if (branchComponent.InterruptType != InterruptType.None || !branchComponent.CanExecute) { continue; } ushort childIndex; TaskComponent childTaskComponent; if (taskComponent.Status == TaskStatus.Queued) { taskComponent.Status = TaskStatus.Running; taskComponents[taskComponent.Index] = taskComponent; childIndex = (ushort)(parallelComponent.Index + 1); while (childIndex != ushort.MaxValue) { childTaskComponent = taskComponents[childIndex]; childTaskComponent.Status = TaskStatus.Queued; taskComponents[childIndex] = childTaskComponent; var childBranchComponent = branchComponents[childTaskComponent.BranchIndex]; childBranchComponent.NextIndex = childTaskComponent.Index; branchComponents[childTaskComponent.BranchIndex] = childBranchComponent; childIndex = taskComponents[childIndex].SiblingIndex; } } else if (taskComponent.Status != TaskStatus.Running) { continue; } var childrenFailure = false; var childrenRunning = false; childIndex = (ushort)(parallelComponent.Index + 1); while (childIndex != ushort.MaxValue) { childTaskComponent = taskComponents[childIndex]; if (childTaskComponent.Status == TaskStatus.Queued || childTaskComponent.Status == TaskStatus.Running) { childrenRunning = true; } else if (childTaskComponent.Status == TaskStatus.Failure) { childrenFailure = true; var childBranchComponent = branchComponents[childTaskComponent.BranchIndex]; childBranchComponent.NextIndex = ushort.MaxValue; branchComponents[childTaskComponent.BranchIndex] = childBranchComponent; break; } else if (childTaskComponent.Status == TaskStatus.Success) { var childBranchComponent = branchComponents[childTaskComponent.BranchIndex]; if (childBranchComponent.ActiveIndex != ushort.MaxValue) { childBranchComponent.NextIndex = ushort.MaxValue; branchComponents[childTaskComponent.BranchIndex] = childBranchComponent; } } childIndex = taskComponents[childIndex].SiblingIndex; } // If a single child fails then all tasks should be stopped. if (childrenFailure) { var maxChildIndex = taskComponent.Index + TraversalUtility.GetChildCount(taskComponent.Index, ref taskComponents); for (ushort j = (ushort)(taskComponent.Index + 1); j <= maxChildIndex; ++j) { childTaskComponent = taskComponents[j]; if (childTaskComponent.Status == TaskStatus.Running || childTaskComponent.Status == TaskStatus.Queued) { childTaskComponent.Status = TaskStatus.Failure; taskComponents[j] = childTaskComponent; branchComponent = branchComponents[childTaskComponent.BranchIndex]; EntityCommandBuffer.SetComponentEnabled(entityIndex, entity, true); if (branchComponent.ActiveIndex == childTaskComponent.Index) { branchComponent.NextIndex = ushort.MaxValue; branchComponents[childTaskComponent.BranchIndex] = branchComponent; } } } branchComponent.NextIndex = taskComponent.ParentIndex; branchComponents[taskComponent.BranchIndex] = branchComponent; taskComponent.Status = TaskStatus.Failure; taskComponents[taskComponent.Index] = taskComponent; continue; } if (childrenRunning) { continue; } // No more children are running. Resume the parent task. taskComponent.Status = TaskStatus.Success; taskComponents[taskComponent.Index] = taskComponent; branchComponent.NextIndex = taskComponent.ParentIndex; branchComponents[taskComponent.BranchIndex] = branchComponent; } } } } } #endif