#How to tell when a IJobEntity is completed when using ScheduleParallel?

1 messages · Page 1 of 1 (latest)

cold forum
#

I'm new to ECS, so excuse the potentially silly question 😅

I'm making a boids sim, my boid system first constructs an array of all positions and rotations of the boids, before passing this array to the BoidSimulateJob, which takes a BoidAspect in its Execute method. The job looks at the array of positions/rotations to modify the transform of its own BoidAspect. This all works fine, but the problem is I then need to dispose of this position/rotation array in my system, but only once the BoidSimulateJob has completed... how can I track when the job is completed if I use .ScheduleParallel? I don't get any JobHandler like I would with .Schedule.

I tried turning my job into a regular IJob, and looping over all BoidAspects in my system and kicking off a job for each one, and passing the boidAspect (which contains the boid transform to be modified directly), but then I ran into all kinds of errors about unsafe pointers..

What's the proper way to do this? It feels like it should be simple, but I'm having trouble tracking down the right approach online

cursive nebulaBOT
cold forum
#
        public void OnUpdate(ref SystemState state)
        {
            //Need to dispose of this!!
            NativeList<BoidTransformData> boidTransformDataList = new NativeList<BoidTransformData>(Allocator.TempJob);

            float scaleMult = BoidControlPanel.instance.GetBoidScale();

            // Retrieve entities matching the entity query
            NativeArray<Entity> boidEntities = boidQuery.ToEntityArray(Allocator.TempJob);
            foreach (Entity boidEntity in boidEntities)
            {
                ForiegnBoidAspect foreignBoidAspect = state.EntityManager.GetAspect<ForiegnBoidAspect>(boidEntity);

                boidTransformDataList.Add(new BoidTransformData
                {
                    position = foreignBoidAspect.localTransform.ValueRO.Position,
                    direction = foreignBoidAspect.localTransform.ValueRO.Forward(),
                });
            }
            boidEntities.Dispose(); // Dispose the native array

            new SimulateBoidJob
            {
                returnDistance = 5, //TODO read in from frontend
                deltaTime = SystemAPI.Time.DeltaTime,
                moveSpeed = BoidControlPanel.instance.GetBoidSpeed(),
                rotateSpeed = BoidControlPanel.instance.GetBoidRotationSpeed(),
                boidTransforms = boidTransformDataList,
                maxBoidAvoidDistance = BoidControlPanel.instance.GetMaxBoidAvoidDistance() * scaleMult,
                maxBoidFollowDistance = BoidControlPanel.instance.GetMaxBoidFollowDistance() * scaleMult,
                colorDebug = BoidControlPanel.instance.colorDebug,
                eColorDebug = BoidControlPanel.instance.eColorDebug,
            }.ScheduleParallel();

            //How do I know when to dispose of my boidTransformDataList?
        }
fallen turtle
#

just schedule a job that disposes it

#

or use world update allocator which will get disposed automatically

cold forum
#

Isn't that the same problem though? I need to make sure that dispose job executes only after the BoidSimulateJobs have all finished?

fallen turtle
#
            NativeArray<Entity> boidEntities = boidQuery.ToEntityArray(Allocator.TempJob);
            foreach (Entity boidEntity in boidEntities)
            {
                ForiegnBoidAspect foreignBoidAspect = state.EntityManager.GetAspect<ForiegnBoidAspect>(boidEntity);

                boidTransformDataList.Add(new BoidTransformData
                {
                    position = foreignBoidAspect.localTransform.ValueRO.Position,
                    direction = foreignBoidAspect.localTransform.ValueRO.Forward(),
                });
            }
#

btw, this part needs to be moved to job as well

#

otherwise you are making a sync point here

cold forum
#

if that gets moved to the job, aren't I having to repeat the construction of that array? The job will run in parallel for each BoidAspect (around 10,000 different entities), so this that'll mean this array has to be constructed 10,000 times? Isn't it faster to just create that array once in the system, and then pass it to each job? Or am I misunderstanding something fundamental?

fallen turtle
cold forum
#

Right, but filling the array is still a big operation that should surely just be done once on the main thread? You have to iterate through all 10,000 boids and put their pos/rot into the array.

Is my entire approach here just janky? I need a job/instance of a job for each boid, that modifies the transform of that boid, based on the read-only pos/rots of all the other boids... So my thinking has been to create an array (that'll just be read) of all pos/rots, and then send them to a job that takes good aspect in its execute (and so will run once for each boid)

fallen turtle
#

you always want to have only one job per logic type

#

so whether it's 1 entity or 10k entities - it's still just one job

cold forum
#

Because all the boid sim jobs depend on that data, but they only need to read it, rather than write, so afaik nothing actually needs to be synced across jobs. The main thread does that initial process of creatiing that array, and then passes it to the job.

I could have that array construction being handled in its own job, and wait until that finishes before doing the sim job, but I still need to know when the parallel sim job finish so I know to dispose that array..

cold forum
#

Found the solution, the job handle gets stored in state.Depdendency automatically (even if I'm scheduling in parallel, where the call doesn't return the JH directly to my code), I can then just do state.Dependency.Complete()

fallen turtle
#

without dependency parameter - it's code gened as state.Dependency = job.Schedule(state.Dependency);