Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Bug Job system not populating result value

Discussion in 'C# Job System' started by crener, Nov 15, 2020.

  1. crener

    crener

    Joined:
    Aug 29, 2015
    Posts:
    27
    So I've been messing with the job system again recently after not touching it for a year or two using the current latest preview version with 2020.1.4f1.

    The code I'm running works when not using the Job system (calling .Execute() ), I get a value I expect and everything is great but when I schedule the same job using the job system I can see that the value get's generated by stepping though the code but the job structure in C# doesn't have the value.

    The code does use ref inside the job code but it only uses that data as read only, so as far as I know it should be fine (helps improve speed when not using burst). Burst is perfectly happy to compile it and I can scroll through all the code in the Burst inspector so the Job system should also be able to run with it?

    Code (csharp):
    1. public class test : MonoBehaviour
    2. {
    3.     // Start is called before the first frame update
    4.     void Start()
    5.     {
    6.         const float progress = 0.3f;
    7.         ISpline3D spline3D = GetComponent<ISpline3D>();
    8.        
    9.         // calculate the point manually
    10.         Dynamic3DJob dynamic2 = new Dynamic3DJob(spline3D, progress);
    11.         dynamic2.Execute();
    12.         float3 manualResult = dynamic2.Result; // float3(12.19931f, 33.66863f, -61.08683f)
    13.  
    14.         // calculate the point using dynamic job
    15.         Dynamic3DJob dynamic = new Dynamic3DJob(spline3D, progress);
    16.         JobHandle handle = dynamic.Schedule();
    17.         handle.Complete();
    18.         float3 dynamicJobResult = dynamic.Result; // float3(0f, 0f, 0f)
    19.        
    20.         // calculate the point using specific job type
    21.         BezierSpline3DPointJob direct = new BezierSpline3DPointJob
    22.         {
    23.             Spline = spline3D.SplineEntityData3D.Value,
    24.             SplineProgress = new SplineProgress(progress)
    25.         };
    26.         JobHandle handle2 = direct.Schedule();
    27.         handle2.Complete();
    28.         float3 directJobResult = direct.Result; // float3(0f, 0f, 0f)
    29.  
    30.         if(handle.IsCompleted)
    31.         {
    32.             Assert.AreEqual(manualResult, directJobResult);
    33.             Assert.AreEqual(manualResult, dynamicJobResult);
    34.         }
    35.     }
    36. }
    The job code is a little too much to just paste here so you can grab a copy of the project here: https://github.com/crener/Dots-Spline/tree/16edb565de43b35a86a38b7235ff57fe3ba0e662 There is a scene in there called "3D sample" which will assert when the playmode is started

    As far as I can tell this should run fine.
     
  2. Baggers_

    Baggers_

    Joined:
    Sep 10, 2017
    Posts:
    98
    Jobs are structs and are copied when passed to the worker threads. This means you can't get the result from the job struct like that. Instead you will need to write the result into a native collections (e.g. NativeArray) and then read from that once the job has been completed.
     
    eizenhorn likes this.
  3. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,683
    Your problem is:
    upload_2020-11-16_16-39-10.png
    You can't do that, you should use native containers (or raw pointers) and write into them for getting results back from jobs. For example use NativeArray<float>(1, Allocator.TempJob) for m_result.

    Edit:
    Ah beaten in a second. @Baggers_ :)
     
    Baggers_ likes this.
  4. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    440
    We also have
    NativeReference<T>
    now. A little less verbose and makes the code a bit more clear when dealing with a single value.
     
  5. crener

    crener

    Joined:
    Aug 29, 2015
    Posts:
    27
    Hmm, I figured that since float3 only consists of floats and is a struct it would be a blittable type... Also if this is not valid why was there is error at all? I can understand Burst compiling cause there isn't necessarily an issue with the code itself but surely the Job system should be able to say that I'm an idiot if all the safety stuff is switched on

    Oh well. I guess I can't expect the system to stay the same, as long as it improves :)
    Thanks
     
    xshadowmintx likes this.
  6. Baggers_

    Baggers_

    Joined:
    Sep 10, 2017
    Posts:
    98
    It is a blittable type, however as mentioned the Job struct is copied when passed to the worker, that means any changes you make to it won't be made the copy you have on the main thread.

    You can store the result to a NativeReference<T> which will then let you read it from the main thread

    [EDIT] I just wanted to stress - this is not a missing feature, nor is it a bug. This isn't something that will be "fixed" as it's not broken. This is the intended behavior :)
     
    Last edited: Nov 16, 2020
  7. crener

    crener

    Joined:
    Aug 29, 2015
    Posts:
    27
    Yeah I know it's just me not even considering that the Job system would be changed in this way that means they don't support blittable type return values like they did when I used it last.

    I can't edit the title so.... :(
     
  8. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,683
    They never supported that, using native containers\pointers always was the only way for safe getting the result back from jobs, as it's not Unity thing but plain C# behaviour of value type copy.
     
    Baggers_ likes this.
  9. Baggers_

    Baggers_

    Joined:
    Sep 10, 2017
    Posts:
    98
    I'm just guessing here, but it's possible that is seemed to work if you were using 'Execute' rather than schedule, if the job was run in-place (or passed via ref internally). However that would be (in my opinion) poor behavior for Execute as it would mean different visible results than from using Schedule.

    Best of luck!
     
  10. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,683
    Calling Execute have nothing with job system you just calling a method on a regular struct, and it will change a field on this struct which you access through struct instance, simple plain C#. In case of schedule, the internals gathers job data to different struct through
    JobsUtility.JobScheduleParameters
    which creates
    JobsUtility.CreateJobReflectionData
    which results goes to internal C++ scheduler, at this point, it's absolutely different struct with data, and in case of native containers you can get the result back despite you copy native container it will copy just container utility fields and a pointer to real data of this container which you can access by this pointer from whatever copy of this struct, pointer just number which will point still to the same memory place :) this is not the same at all as calling just method on a struct :D
     
    Lukas_Kastern likes this.
  11. Baggers_

    Baggers_

    Joined:
    Sep 10, 2017
    Posts:
    98
    @eizenhorn Good to know. Thanks. That's what I get for random guesses :D