Search Unity

Question Dependency problems when trying to use Raycasts from jobs

Discussion in 'Physics for ECS' started by trepan, Feb 19, 2023.

  1. trepan

    trepan

    Joined:
    Feb 11, 2011
    Posts:
    113
    I'm having a lot of trouble with dependencies when trying to call physics ray-casts from a scheduled job. I was hitting this issue with ECS/Physics 0.51 and it motivated me to upgrade to 1.0.0pre44 but sadly with no change...

    I have a system that runs several different jobs, from two of which I want to perform ray-casts. So as per the instructions for Physics 1.0.0 I use GetSingleton<PhysicsWorldSingleton>() to access the PhysicsWorldSingleton, passing this in to each of the jobs.

    The code below shows the relevant parts of my set-up.

    The jobs can use either IJobFor or IJobParallelFor based on the debug define. So I'm striving to make both multi-threaded but can easily test things single-threaded.

    Having tested things both ways, when single threaded I get no errors from the RefreshSpawners job but lots of errors from RefreshMotes. When multi-threaded, both jobs yield the same dependency errors.

    Specifically, the errors are variants of:
    • InvalidOperationException: The previously scheduled job RefreshMotesJob reads from the UNKNOWN_OBJECT_TYPE RefreshMotesJob.physicsWorldSingleton.PhysicsWorld.CollisionWorld.EntityBodyIndexMap. You must call JobHandle.Complete() on the job RefreshMotesJob, before you can write to the UNKNOWN_OBJECT_TYPE safely.
    • InvalidOperationException: The previously scheduled job RefreshMotesJob reads from the UNKNOWN_OBJECT_TYPE RefreshMotesJob.physicsWorldSingleton.PhysicsWorld.DynamicsWorld.m_MotionVelocities. You are trying to schedule a new job Solver:parallelApplyGravityAndCopyInputVelocitiesJob, which writes to the same UNKNOWN_OBJECT_TYPE (via ParallelApplyGravityAndCopyInputVelocitiesJob.MotionVelocities). To guarantee safety, you must include RefreshMotesJob as a dependency of the newly scheduled job.
    • InvalidOperationException: The previously scheduled job DispatchPairSequencer:CreateDispatchPairPhasesJob writes to the UNKNOWN_OBJECT_TYPE CreateDispatchPairPhasesJob.SolverSchedulerInfo.NumWorkItems. You are trying to schedule a new job NativeStream:ConstructJob, which reads from the same UNKNOWN_OBJECT_TYPE (via ConstructJob.Length). To guarantee safety, you must include DispatchPairSequencer:CreateDispatchPairPhasesJob as a dependency of the newly scheduled job.
    • InvalidOperationException: The Unity.Collections.NativeStream.Writer ParallelBuildJacobiansJob.JacobiansWriter has not been assigned or constructed. All containers must be valid when scheduling a job.
    • InvalidOperationException: The previously scheduled job DispatchPairSequencer:CreateDispatchPairPhasesJob writes to the UNKNOWN_OBJECT_TYPE CreateDispatchPairPhasesJob.SolverSchedulerInfo.NumWorkItems. You are trying to schedule a new job NativeStream:ConstructJob, which reads from the same UNKNOWN_OBJECT_TYPE (via ConstructJob.Length). To guarantee safety, you must include DispatchPairSequencer:CreateDispatchPairPhasesJob as a dependency of the newly scheduled job.
    So it looks like all of these are within the Physics system itself...

    My questions would be:
    1. You're supposed to be able to do raycasts from (non main thread) jobs, right?
    2. ...And they should be valid for multi-threaded jobs too, right?
    3. Am I missing something? Especially with the latest revisions the docs seem to suggest that user code shouldn't have to worry about dependencies like this, so what's going on here?!
    I would really expect there to be mention in the docs of non-main/multi-threaded limitations on ray-casts calls - I don't see that anywhere...

    Any advice greatly appreciated!

    Thanks,

    Chris


    Code (CSharp):
    1.  
    2. [UpdateInGroup(typeof(LateSimulationSystemGroup))]
    3. [BurstCompile]
    4. public unsafe partial class MoteSystem: SystemBase {
    5.  
    6.         protected override void OnUpdate()
    7.         {
    8.             PhysicsWorldSingleton physicsWorldSingleton = SystemAPI.GetSingleton<PhysicsWorldSingleton>();
    9.  
    10.             var refreshSpawnersHandle = new RefreshSpawnersJob() {
    11.                 // ...
    12.                 physicsWorldSingleton = physicsWorldSingleton,
    13.             }.Schedule(activeSpawners.Length,
    14.                        #if !_DEBUG_MOTES
    15.                        8,
    16.                        #endif
    17.                        Dependency);
    18.  
    19.             Dependency = new RefreshMotesJob() {
    20.                 // ...
    21.                 physicsWorldSingleton = physicsWorldSingleton,
    22.             }.Schedule(
    23.                      #if !_DEBUG_MOTES
    24.                      numActiveRenderObjectMotes.ValuePtr,
    25.                     64,
    26.                     #else
    27.                     numActiveRenderObjectMotes.Value,
    28.                     #endif
    29.                     refreshMotesHandle);
    30.         }
    31.  
    Code (CSharp):
    1.     #if _DEBUG_MOTES
    2.     public unsafe struct RefreshSpawnersJob: IJobFor
    3.     #else
    4.     [BurstCompile(CompileSynchronously = true)]
    5.     public struct RefreshSpawnersJob: IJobParallelFor
    6.     #endif
    7.     {
    8.         [ReadOnly] public PhysicsWorldSingleton physicsWorldSingleton;
    9.  
    10.         public unsafe void Execute(int index)
    11.         {
    12.                 // ...
    13.                 float3 res = physicsWorldSingleton.CastRay(raycastInput, out var hit0) ? hit0.Position: raycastInput.End;
    14.  
    15.         }
    16.     }
    17.     // (RefreshMotesJob is basically the same configuration as the above)
    18.  
     
  2. trepan

    trepan

    Joined:
    Feb 11, 2011
    Posts:
    113
    Another interesting aspect to this: I have other systems where I've been quite happily using Raycasts from within IJobEntity jobs. For those jobs I pass in a CollisionWorld parameter to the jobs (which I would be doing in my examples above only I already encountered the dependency issues with that approach and wanted to be following the up to date example to the letter).

    Is it possible IJobEntity has some kind of automatic dependency management that a IJobFor/IJobParallelFor does not?
     
  3. trepan

    trepan

    Joined:
    Feb 11, 2011
    Posts:
    113
    I think I figured it out... I believe the critical thing is that SystemBase's Dependency must include all jobs that reference the CollisionWorld (and although my 'paraphrased' example above suggested that it did, in actual fact this system is a lot more complicated and this was not the case!).

    I honestly wish the ECS documentation would explain a bit more about how some of the system's core mechanism worked... I'm a much more capable and confident programmer when I can see the reasons for doing a thing than I am when just told to do that thing! This seems to be a common problem with most technical documentation and maybe with how programming is taught these days...

    In this case, I wish I understood how a JobHandle was implemented, what operations occur when JobHandles are combined, and of course what special handling there might be for the 'Dependency' JobHandle. I can only assume that somewhere deep within the ECS codebase a SystemBase Dependency's reference to CollisionWorld allows Unity to recognize that this handle must become a dependency of the Physics system itself... It's a fairly disappointing outcome when you just have to conclude 'I guess something like that must be happening <shrug>'.