Search Unity

Help Wanted [Solved] Trying to use CalculateDistance inside of Job System -> InvalidOperationException

Discussion in 'DOTS Physics' started by ArnaudJopart, Apr 5, 2020.

  1. ArnaudJopart

    ArnaudJopart

    Joined:
    Oct 27, 2016
    Posts:
    8
    Hi everyone.

    Developping on 2019.3 and Physics Package 0.3.1, I'm currently trying to implement a job system using CollisionWorld.CalculateDistance but I find this error when starting the simulation.

    InvalidOperationException: The previously scheduled job Broadphase:prepareStaticBodyDataJob writes to the NativeArray PrepareStaticBodyDataJob.FiltersOut. You are trying to schedule a new job VortexJobSystem:VortexJob, which reads from the same NativeArray (via VortexJob.Data.m_collisionWorld.Broadphase.m_StaticTree.BodyFilters). To guarantee safety, you must include Broadphase:prepareStaticBodyDataJob as a dependency of the newly scheduled job.
    Unity.Entities.JobForEachExtensions.Schedule (System.Void* fullData, Unity.Collections.NativeArray`1[T] prefilterData, System.Int32 unfilteredLength, System.Int32 innerloopBatchCount, System.Boolean isParallelFor, System.Boolean isFiltered, Unity.Entities.JobForEachExtensions+JobForEachCache& cache, System.Void* deferredCountData, Unity.Jobs.JobHandle dependsOn, Unity.Jobs.LowLevel.Unsafe.ScheduleMode mode) (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/IJobForEach.cs:415)
    Unity.Entities.JobForEachExtensions.ScheduleInternal_EC[T] (T& jobData, Unity.Entities.ComponentSystemBase system, Unity.Entities.EntityQuery query, System.Int32 innerloopBatchCount, Unity.Jobs.JobHandle dependsOn, Unity.Jobs.LowLevel.Unsafe.ScheduleMode mode) (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/IJobForEach.gen.cs:3346)
    Unity.Entities.JobForEachExtensions.Schedule[T] (T jobData, Unity.Entities.ComponentSystemBase system, Unity.Jobs.JobHandle dependsOn) (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/IJobForEach.gen.cs:900)
    VortexJobSystem.OnUpdate (Unity.Jobs.JobHandle _inputDeps) (at Assets/VortexJobSystem.cs:51)
    Unity.Entities.JobComponentSystem.Update () (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/JobComponentSystem.cs:129)
    Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/ComponentSystemGroup.cs:182)
    UnityEngine.Debug:LogException(Exception)
    Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/Stubs/Unity/Debug.cs:19)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/ComponentSystemGroup.cs:186)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/ComponentSystemGroup.cs:169)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/ComponentSystem.cs:107)
    Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.6.0-preview.24/Unity.Entities/ScriptBehaviourUpdateOrder.cs:152)

    The suggestion form this error is :"To guarantee safety, you must include Broadphase : PrepareStaticBodyDataJob as a dependency of the newly scheduled job." But I don't know how to implement this.

    Here is the code I use.

    Code (CSharp):
    1. [UpdateAfter(typeof(BuildPhysicsWorld))]
    2. public class VortexJobSystem : JobComponentSystem
    3. {
    4.     private BuildPhysicsWorld m_buildPhysicsWorld;
    5.     private struct VortexJob : IJobForEachWithEntity<AttractData>
    6.     {
    7.         [ReadOnly]
    8.         public CollisionWorld m_collisionWorld;
    9.      
    10.         public void Execute(Entity _entity, int _index, [ReadOnly]ref AttractData _attractData)
    11.         {
    12.             var pointDistanceInput = new PointDistanceInput
    13.             {
    14.                 MaxDistance = _attractData.m_maxDistance,
    15.                 Position = _attractData.m_position
    16.             };
    17.          
    18.             var hits = new NativeList<DistanceHit>(Allocator.Temp);
    19.             if(m_collisionWorld.CalculateDistance(pointDistanceInput, ref hits))
    20.             {
    21.                 Debug.Log("Hit");
    22.                 // Do stuff...
    23.             }
    24.  
    25.             hits.Dispose();
    26.         }
    27.     }
    28.  
    29.     protected override void OnCreate()
    30.     {
    31.         m_buildPhysicsWorld = World.DefaultGameObjectInjectionWorld.GetExistingSystem<BuildPhysicsWorld>();
    32.     }
    33.  
    34.     protected override JobHandle OnUpdate(JobHandle _inputDeps)
    35.     {
    36.         //Already tried to CombineDependencies
    37.         //_inputDeps = JobHandle.CombineDependencies(_inputDeps, m_buildPhysicsWorld.FinalJobHandle);
    38.         var currentCollisionWorld = m_buildPhysicsWorld.PhysicsWorld.CollisionWorld;
    39.      
    40.         var job = new VortexJob
    41.         {
    42.             m_collisionWorld = currentCollisionWorld
    43.         };
    44.  
    45.         return job.Schedule(this,_inputDeps);
    46.     }
    47. }
    Can anyone tell me what I'm missing ? Thanks a lot.
     
  2. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Hey,

    I just tried this and managed to find out what's wrong. First, I suggest uncommenting the CombineDependencies() call, as it's a proper way to do things, although it doesn't fix your current issue.

    Second, what happens is that your VortexJob clashes with the BuildPhysicsWorld system of the next frame, not the current one. To avoid having this, you must make sure your job ends before EndFramePhysicsSystem. To do so, just add its handle to the EndFramePhysicsSystem.HandlesToWaitFor. Something like this:

    private EndFramePhysicsSystem m_endFramePhysicsSystem;

    m_endFramePhysicsSystem = World.DefaultGameObjectInjectionWorld.GetExistingSystem<EndFramePhysicsSystem>(); (in OnCreate())

    m_endFramePhysicsSystem.HandlesToWaitFor.Add(handle); (in OnUpdate(), right before returning the job handle)

    The error goes away for me with these 2 fixes. Let me know if it works for you!
     
  3. ArnaudJopart

    ArnaudJopart

    Joined:
    Oct 27, 2016
    Posts:
    8
    Hello.

    Thanks a lot for your help. It's working great now!

    For those passing by who might interested, here is the simple code of a VortexJobSystem attracting entities nearby, based on the example in the docs.

    Code (CSharp):
    1. [UpdateAfter(typeof(BuildPhysicsWorld))]
    2. public class VortexJobSystem : JobComponentSystem
    3. {
    4.     private BuildPhysicsWorld m_buildPhysicsWorld;
    5.     private EndFramePhysicsSystem m_endFramePhysicsSystem;
    6.     private EntityCommandBufferSystem m_entityCommandBufferSystem;
    7.     private struct VortexJob : IJobForEachWithEntity<AttractData>
    8.     {
    9.         [ReadOnly]
    10.         public CollisionWorld m_collisionWorld;
    11.  
    12.         public EntityCommandBuffer.Concurrent m_entityCommandBuffer;
    13.      
    14.         public void Execute(Entity _entity, int _index, [ReadOnly]ref AttractData _attractData)
    15.         {
    16.             var filter = new CollisionFilter
    17.             {
    18.                 BelongsTo = ~0u,
    19.                 CollidesWith = ~0u, // all 1s, so all layers, collide with everything
    20.                 GroupIndex = 0
    21.             };
    22.            
    23.             var pointDistanceInput = new PointDistanceInput
    24.             {
    25.                 MaxDistance = _attractData.m_maxDistance,
    26.                 Position = _attractData.m_position,
    27.                 Filter = filter
    28.             };
    29.  
    30.            
    31.             var hits = new NativeList<DistanceHit>(Allocator.Temp);
    32.             if(m_collisionWorld.CalculateDistance(pointDistanceInput, ref hits))
    33.             {
    34.                
    35.                 foreach (var VARIABLE in hits)
    36.                 {
    37.                    
    38.                     Debug.Log("Hit System: "+VARIABLE.Entity);
    39.                     var component = new AttractedTo
    40.                     {
    41.                         m_targetPosition = pointDistanceInput.Position,
    42.                         m_strength = _attractData.m_strength
    43.                     };
    44.                     m_entityCommandBuffer.AddComponent(_index,VARIABLE.Entity,component);
    45.                 }
    46.             }
    47.  
    48.             hits.Dispose();
    49.         }
    50.     }
    51.  
    52.     protected override void OnCreate()
    53.     {
    54.         m_buildPhysicsWorld = World.DefaultGameObjectInjectionWorld.GetExistingSystem<BuildPhysicsWorld>();
    55.         m_endFramePhysicsSystem = World.DefaultGameObjectInjectionWorld.GetExistingSystem<EndFramePhysicsSystem>();
    56.         m_entityCommandBufferSystem =
    57.             World.DefaultGameObjectInjectionWorld.GetExistingSystem<EndSimulationEntityCommandBufferSystem>();
    58.     }
    59.  
    60.     protected override JobHandle OnUpdate(JobHandle _inputDeps)
    61.     {
    62.         _inputDeps = JobHandle.CombineDependencies(_inputDeps, m_buildPhysicsWorld.FinalJobHandle);
    63.         var currentCollisionWorld = m_buildPhysicsWorld.PhysicsWorld.CollisionWorld;
    64.      
    65.         var job = new VortexJob
    66.         {
    67.             m_collisionWorld = currentCollisionWorld,
    68.             m_entityCommandBuffer = m_entityCommandBufferSystem.CreateCommandBuffer().ToConcurrent()
    69.         };
    70.  
    71.         var handle = job.Schedule(this, _inputDeps);
    72.         m_entityCommandBufferSystem.AddJobHandleForProducer(handle);
    73.         m_endFramePhysicsSystem.HandlesToWaitFor.Add(handle);
    74.        
    75.         return handle;
    76.     }
    77. }
    78.  
    79. public struct AttractedTo :IComponentData
    80. {
    81.     public float3 m_targetPosition;
    82.     public float m_strength;
    83. }
    84.  
    85.  
    86. public class AttractJobSystem : JobComponentSystem
    87. {
    88.    
    89.     protected override JobHandle OnUpdate(JobHandle _inputDeps)
    90.     {
    91.         return Entities.ForEach((Entity _entity,ref Translation _translation, ref AttractedTo _attractedTo, ref
    92.                 PhysicsVelocity _velocity,
    93.             ref Rotation _rotation) =>
    94.         {
    95.             float3 diff = _attractedTo.m_targetPosition - _translation.Value;
    96.             float distSqrd = math.lengthsq(diff);
    97.             _velocity.Linear += _attractedTo.m_strength * (diff / math.sqrt(distSqrd));
    98.         }).Schedule(_inputDeps);
    99.     }
    100. }
     
unityunity