Search Unity

Run code after Entities.ForEach() ??

Discussion in 'Entity Component System' started by jasons-novaleaf, Jan 2, 2021.

  1. jasons-novaleaf

    jasons-novaleaf

    Joined:
    Sep 13, 2012
    Posts:
    181
    I would like to run code after my Entities.ForEach() completes.

    Here is an example, where I want to preserve the rand state (a struct, so needs to be coppied back to the class property)

    Say I have code like:

    Code (CSharp):
    1. public class MySystem : SystemBase{
    2. private Unity.Mathematics.Random rand = new Unity.Mathematics.Random(0);
    3. protected override void OnUpdate(){
    4. //burst doesn't let me reference the class property from the ForEach(), so need to copy to a stack reference
    5. var rand = this.rand;
    6. Entities.ForEach((ref Movable mov, ref Enemy enemy, in Translation trans) =>
    7. {
    8.   var i = rand.NextInt();
    9.   //do something with i
    10. }).Schedule()
    11. }
    12. }

    I would like to run code after the ForEach completes, like:
    Code (CSharp):
    1. //update our class property with the current rand state
    2. .Then(()=>{ this.rand = rand; })
    Is there a way to do so? I looked in the docs: https://docs.unity3d.com/Packages/com.unity.entities@0.2/manual/entities_job_foreach.html but don't see anything about running after.


    also PS: I'm learning Unity and DOTS so any helpful resources links would be greatly appreciated. I'm currently going through Oliver Carson's great Udemy course on the subject but don't really have other resources (than the official docs)
     
    Last edited: Jan 2, 2021
  2. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    There is – use
    Job.WithCode
    (see https://docs.unity3d.com/Packages/com.unity.entities@0.16/manual/ecs_job_withcode.html). In general, the entities package is quite helpful re the entities "world".

    The
    Schedule
    methods that you call on the ForEach and Job.WithCode use the
    Dependency
    Property, which you can also explicitly pass in. This Dependency property tracks dependencies. So if you call
    Schedule
    on a ForEach, followed by a
    Schedule
    on a WithCode, as an example, the lambda from WithCode will be run after the one from ForEach.

    P.S: I'm not sure if you'll be able to access this.rand directly from the lambda, but for the "run code after the ForEach completes" the above should help.
    P.P.S: Just noticed from your updated entry you're using Entities 0.2 – iirc, you will have to update to a later version since Job.WithCode was introduced later.
     
    Last edited: Jan 2, 2021
    jasons-novaleaf likes this.
  3. jasons-novaleaf

    jasons-novaleaf

    Joined:
    Sep 13, 2012
    Posts:
    181
    Thank you @florianhanke I skimmed through Jobs and Dependencies, does this look correct? any other suggestions?

    Code (CSharp):
    1.  
    2. var rand = this.rand;
    3. var jobHandle = Entities.ForEach((ref Movable mov, ref Enemy enemy, in Translation trans) =>{
    4.   var i = rand.NextInt();
    5.   //do something with i
    6. }).Schedule(new Unity.Jobs.JobHandle());  //use this .Schedule() overload to obtain a jobHandle
    7.  
    8. Job.WithCode(() => {
    9.   //copy back to class property
    10.   this.rand = rand;
    11. }).Schedule(jobHandle); //pass ForEach jobHandle to specify dependency
    12.  
     
  4. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    I'd use it without the explicit job handle (the implicit
    Dependency
    property will be used)
    Code (CSharp):
    1. var rand = this.rand;
    2. Entities.ForEach((ref Movable mov, ref Enemy enemy, in Translation trans) =>{
    3.   var i = rand.NextInt();
    4.   //do something with i
    5. }).Schedule();
    6. Job.WithCode(() => {
    7.   //copy back to class property
    8.   this.rand = rand;
    9. }).Schedule();
    Also: Random numbers in ForEach lambdas need to be treated with care (when using ScheduleParallel) – here I think it could be ok, since it's only running one job (can't check currently – does it run like you want it to run?).
     
    jasons-novaleaf likes this.
  5. jasons-novaleaf

    jasons-novaleaf

    Joined:
    Sep 13, 2012
    Posts:
    181
    Thanks again, am following a Udemy course so it's a bit too simple to really test right now, but I will give it a try.

    I am surprised about implicit
    Dependency 
    though. I would assume that means the
    Job.WithCode()
    and
    Entities.ForEach() 
    could have race conditions. When I am able to test I will try both ways and write a reply with what I find out.
     
  6. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    440
    You can't access a member variable in a job - if you want to get data out of a job you need to pass a native container into the job and populate that. I've seen a few solutions for handling RNG in jobs, this one seems pretty reasonable.
     
    jasons-novaleaf and florianhanke like this.
  7. florianhanke

    florianhanke

    Joined:
    Jun 8, 2018
    Posts:
    426
    I should have been clearer. Effectively if you omit the parameter, what it does is:
    Code (CSharp):
    1. Dependency = Entities.ForEach(...).Schedule(Dependency);
    2. Dependency = Job.WithCode(...).Schedule(Dependency);
    Effectively tracking the dependencies, ensuring that the
    Job.WithCode
    job runs after the
    Entities.ForEach
    job.
     
  8. jasons-novaleaf

    jasons-novaleaf

    Joined:
    Sep 13, 2012
    Posts:
    181
    Got it. That makes sense to limit footguns!

    Oh thank you you are right. Skimming over that link it looks like a fantastic resource. Thank you so much.
     
    florianhanke likes this.