Search Unity

Bug Problem updating to 0.6: Raycast in System Entities.ForEach()

Discussion in 'Physics for ECS' started by jasons-novaleaf, Jan 24, 2021.

  1. jasons-novaleaf

    jasons-novaleaf

    Joined:
    Sep 13, 2012
    Posts:
    181
    EDIT: Please see #4 for a clean, no-dependency repro example.

    Original post below:


    Could I get some help, either pointing me to where a good "example" of doing system entity job raycasts would be, or helping me troubleshoot the following? Also if there are some good physics examples out there, I'd

    The following code "worked" in the 0.5 version. in 0.6. It's a simple raycast in an Entities.ForEach()



    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Mathematics;
    3. using Unity.Transforms;
    4. using Unity.Physics;
    5. using Unity.Physics.Systems;
    6. using Unity.Jobs;
    7. [UpdateAfter(typeof(EndFramePhysicsSystem))]
    8. [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
    9. public class EnemySystem : SystemBase
    10. {
    11.  
    12.     protected override void OnCreate()
    13.     {
    14.         base.OnCreate();
    15.      
    16.      
    17.     }
    18.  
    19.     protected override void OnUpdate()
    20.     {
    21.         var bpw = World.GetOrCreateSystem<BuildPhysicsWorld>();
    22.         var pw = bpw.PhysicsWorld;
    23.      
    24.      
    25.  
    26.         Entities
    27.             .WithReadOnly(pw)
    28.             .ForEach((int nativeThreadIndex, ref Movable mov, ref Enemy enemy, in Translation trans) =>
    29.         {
    30.             var targetDir = new float3(0, 0, -1);
    31.             var ray = new RaycastInput()
    32.             {
    33.                 Start = trans.Value,
    34.                 End = trans.Value + (targetDir * 0.9f),
    35.                 Filter = new CollisionFilter()
    36.                 {
    37.                     GroupIndex = 0,
    38.                     BelongsTo = 1u << 1, //bitmasks, so using bitshifts
    39.                     CollidesWith = 1u << 2
    40.                 }
    41.             };
    42.  
    43.             var hit = pw.CastRay(ray, out Unity.Physics.RaycastHit closestHit);
    44.  
    45.             if (hit)
    46.             {
    47.                 mov.direction = targetDir;
    48.             }
    49.             enemy.lastPos = trans.Value;
    50.         })
    51.             .ScheduleParallel();
    52.  
    53.  
    54.  
    55.     }
    56.  
    57.  
    58. }
    59.  
    60.  
    I'm getting a runtime error saying
    InvalidOperationException: The previously scheduled job Broadphase:prepareStaticBodyDataJob writes to the Unity.Collections.NativeArray`1[Unity.Physics.CollisionFilter] PrepareStaticBodyDataJob.FiltersOut. You are trying to schedule a new job EnemySystem:OnUpdate_LambdaJob0, which reads from the same Unity.Collections.NativeArray`1[Unity.Physics.CollisionFilter] (via OnUpdate_LambdaJob0.JobData.pw.CollisionWorld.Broadphase.m_StaticTree.BodyFilters). To guarantee safety, you must include Broadphase:prepareStaticBodyDataJob as a dependency of the newly scheduled job.
    Unity.Entities.JobChunkExtensions.FinalizeScheduleChecked (System.Boolean isParallel, System.Int32 unfilteredChunkCount, Unity.Jobs.JobHandle prefilterHandle, Unity.Collections.NativeArray`1[T] prefilterData, System.Void* deferredCountData, System.Boolean useFiltering, Unity.Jobs.LowLevel.Unsafe.JobsUtility+JobScheduleParameters& scheduleParams, System.Boolean& executed, Unity.Jobs.JobHandle& result) (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/IJobChunk.cs:270)
    Unity.Entities.JobChunkExtensions.ScheduleInternal[T] (T& jobData, Unity.Entities.EntityQuery query, Unity.Jobs.JobHandle dependsOn, Unity.Jobs.LowLevel.Unsafe.ScheduleMode mode, System.Boolean isParallel) (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/IJobChunk.cs:246)
    Unity.Entities.JobChunkExtensions.ScheduleParallel[T] (T jobData, Unity.Entities.EntityQuery query, Unity.Jobs.JobHandle dependsOn) (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/IJobChunk.cs:150)
    EnemySystem.OnUpdate () (at Assets/Scripts/Systems/EnemySystem.cs:361)
    Unity.Entities.SystemBase.Update () (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/SystemBase.cs:412)
    Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:472)
    UnityEngine.Debug:LogException(Exception)
    Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/Stubs/Unity/Debug.cs:19)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:477)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:423)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystem.cs:114)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:472)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:417)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystem.cs:114)
    Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ScriptBehaviourUpdateOrder.cs:333)

    That error basically says I need to call Job.Complete() because my job is still running.

    If I call this.Dependency.Complete() it works again. But.... I was told in a dots thread that I should try to use the AddInputDependency() and GetOutputDependency() from the PhysicsWorld.

    So I tried a bunch of threads and can't figure it out. Also there doesn't seem to be any documentation on StepPhysicsWorld, EndFramePhysicsSystem, etc... anywhere. Like what those systems do and what order they operate. I get the feeling that EndFramePhysicsSystem goes last and StepPhysicsWorld is first, but when I tried setting m_StepPhysicsWorld.AddInputDependency(myRayCastJobHandle) I get a bunch of other errors because other systems are accessing physics data at the same time as my job.

    If there's some details on these systems I would really love to see.
     
    Last edited: Jan 25, 2021
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Anytime you use PhysicsWorld in any system, you need to add something like this before you use it.

    Dependency = JobHandle.CombineDependencies(Dependency, _physicsWorld.GetOutputDependency());

    This ensure the physics world finishes updating before you try to use it.
     
  3. jasons-novaleaf

    jasons-novaleaf

    Joined:
    Sep 13, 2012
    Posts:
    181
    I did that (though I shouldn't have to should I? because I used the [UpdateAfter(typeof(EndFramePhysicsSystem))] attribute?) and now I get a different error, it looks like I'm stepping on some internal job: I am getting the same error as before.

    InvalidOperationException: The previously scheduled job TestRaycastSystem:OnUpdate_LambdaJob0 reads from the Unity.Collections.NativeHashMap`2[Unity.Entities.Entity,System.Int32] OnUpdate_LambdaJob0.JobData.pw.CollisionWorld.EntityBodyIndexMap. You must call JobHandle.Complete() on the job TestRaycastSystem:OnUpdate_LambdaJob0, before you can write to the Unity.Collections.NativeHashMap`2[Unity.Entities.Entity,System.Int32] safely.
    Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at <e414e10bfe5f45729ff122f3359de21b>:0)
    Unity.Collections.NativeHashMap`2[TKey,TValue].CheckWrite () (at Library/PackageCache/com.unity.collections@0.15.0-preview.21/Unity.Collections/NativeHashMap.cs:554)
    Unity.Collections.NativeHashMap`2[TKey,TValue].Clear () (at Library/PackageCache/com.unity.collections@0.15.0-preview.21/Unity.Collections/NativeHashMap.cs:194)
    Unity.Physics.CollisionWorld.Reset (System.Int32 numStaticBodies, System.Int32 numDynamicBodies) (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics/Collision/World/CollisionWorld.cs:53)
    Unity.Physics.PhysicsWorld.Reset (System.Int32 numStaticBodies, System.Int32 numDynamicBodies, System.Int32 numJoints) (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics/Dynamics/World/PhysicsWorld.cs:41)
    Unity.Physics.Systems.BuildPhysicsWorld.OnUpdate () (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics/ECS/Base/Systems/BuildPhysicsWorld.cs:122)
    Unity.Entities.SystemBase.Update () (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/SystemBase.cs:412)
    Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:472)
    UnityEngine.Debug:LogException(Exception)
    Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/Stubs/Unity/Debug.cs:19)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:477)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:423)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystem.cs:114)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:472)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:417)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystem.cs:114)
    Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ScriptBehaviourUpdateOrder.cs:333)

    ... assuming this is a step down the right path, how do I get around this?

    Here is the code: EDIT see the next post #4 for a repro code that you can run.

    I added bpw.AddInputDependency(this.Dependency); at the bottom to see if that helps, it does not.
     
    Last edited: Jan 25, 2021
  4. jasons-novaleaf

    jasons-novaleaf

    Joined:
    Sep 13, 2012
    Posts:
    181
    I also tried calling .Complete() on the last frame's jobHandle and same error. Maybe this is a bug.

    I distilled the repro down so there are no external code dependencies. feel free to run this to see the problem.

    Code (CSharp):
    1.  
    2.  
    3. [ExecuteAlways]
    4. [UpdateAfter(typeof(EndFramePhysicsSystem))]
    5. [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
    6. public class TestRaycastSystem : SystemBase
    7. {
    8.    protected override void OnUpdate()
    9.    {
    10.        Debug.LogWarning("In Update TestRaycastSystem");
    11.  
    12.        var bpw = World.GetOrCreateSystem<BuildPhysicsWorld>();
    13.        var pw = bpw.PhysicsWorld;
    14.        var handle = JobHandle.CombineDependencies(Dependency, bpw.GetOutputDependency());
    15.        var rayJob = new RayJob() { pw = pw };
    16.        var jobHandle = rayJob.Schedule(handle);
    17.        this.Dependency = jobHandle;
    18.        bpw.AddInputDependency(this.Dependency);
    19.    }
    20.  
    21.    struct RayJob : IJob
    22.    {
    23.        [ReadOnly]
    24.        public PhysicsWorld pw;
    25.        public void Execute()
    26.        {
    27.            var targetDir = new float3(0, 0, -1);
    28.            var ray = new RaycastInput()
    29.            {
    30.                Start = float3.zero,
    31.                End = targetDir,
    32.            };
    33.            var hit = pw.CastRay(ray, out Unity.Physics.RaycastHit closestHit);
    34.        }
    35.    }
    36. }
    37.  
    38.  
     
    Last edited: Jan 25, 2021
  5. jasons-novaleaf

    jasons-novaleaf

    Joined:
    Sep 13, 2012
    Posts:
    181
    Last edited: Jan 25, 2021
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I was just experimenting with something physics related and ran into a similar issue. I had a quick look at physics code and I think I see the issue.

    Try bpw.AddInputDependencyToComplete(this.Dependency) instead of bpw.AddInputDependency(this.Dependency);

    -edit-

    I think it's because your system is reading from the physics world, then build physics world runs again (fixed update) and calls

    PhysicsWorld.Reset(
    numStaticBodies + 1, // +1 for the default static body
    numDynamicBodies,
    numJoints);

    on the main thread but doesn't complete m_InputDependency, only m_InputDependencyToComplete

    Code (CSharp):
    1.             // Make sure last frame's physics jobs are complete before any new ones start
    2.             m_InputDependencyToComplete.Complete();
    3.  
    4.             // Combine implicit input dependency with the user one
    5.             Dependency = JobHandle.CombineDependencies(Dependency, m_InputDependency);
    6.  
    7.             int numDynamicBodies = DynamicEntityGroup.CalculateEntityCount();
    8.             int numStaticBodies = StaticEntityGroup.CalculateEntityCount();
    9.             int numJoints = JointEntityGroup.CalculateEntityCount();
    10.  
    11.             int previousStaticBodyCount = PhysicsWorld.NumStaticBodies;
    12.  
    13.             // Resize the world's native arrays
    14.             PhysicsWorld.Reset(
    15.                 numStaticBodies + 1, // +1 for the default static body
    16.                 numDynamicBodies,
    17.                 numJoints);
    Honestly I'm not a fan of how this dependency handling is done, people on our team constantly forget handles and errors pop up all the time. What I was working on was trying to remove the need for filling your systems with this and make it all play nice with ISystemBase.

    -edit- posted what i was working on here: https://forum.unity.com/threads/automatic-dependency-management-and-isystembase-support.1045105/
     
    Last edited: Jan 25, 2021
  7. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    @tertle, thank you, you're right about the AddInputDependencyToComplete()!

    The order of key physics systems is: BuildPhysicsWorld, StepPhysicsWorld, ExportPhysicsWorld, EndFramePhysicsSystem. If you just want to query, your system could update after BuildPhysicsWorld with current code to hook up jobs (note that both onUpdate and jobs need to be hooked up properly)
    var handle = JobHandle.CombineDependencies(Dependency, bpw.GetOutputDependency());
    . However, if you'd like to do more than queries, be careful with your system order - this thread gives some nice hints.

    @jasons-novaleaf , please let us know if applying these advices made it work.
     
    jasons-novaleaf likes this.
  8. jasons-novaleaf

    jasons-novaleaf

    Joined:
    Sep 13, 2012
    Posts:
    181
    ..... changing to
    bpw.AddInputDependencyToComplete(this.Dependency);
    worked.

    This is so frustrating. There is no documentation on these methods anywhere. What is the difference between these two functions? I don't understand why you would ever have an input dependency if you are not waiting for it to complete.


    Edit: by the way, with that "solution" I can see why there is an error: my job's raycast is still querying the CollisionWorld after the next frame's BuildPhysicsWorld kicks off. So I can understand getting an error. Just that the error message is wrong. It says my Job is still running when my next Job starts. What it means is that my Job is still running when my next Job's dependency starts.
     
    Last edited: Jan 25, 2021
    Egad_McDad, toomasio and milos85miki like this.
  9. jasons-novaleaf

    jasons-novaleaf

    Joined:
    Sep 13, 2012
    Posts:
    181
    milos85miki likes this.
  10. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    Hi @jasons-novaleaf , I totally agree and apologize for the shortcomings in our documentation. It hasn't been of highest priority since we're still in preview, but we ack people's frustration and will be working on it. Note that we're also investing time in forum support, so we have the motivation to prevent same problems from bothering people again and again (in slightly different forms). :)

    AddInputDependencyToComplete() only exists on BuildPhysicsWorld, because it resets the PhysicsWorld immediately in its OnUpdate() method (not via the scheduled job). So, any job that relies on the PhysicsWorld needs to be completed at the start of BuildPhysicsWorld.OnUpdate() and we implemented "subscribing to" that via AddInputDependencyToComplete().
     
    jasons-novaleaf likes this.
  11. jasons-novaleaf

    jasons-novaleaf

    Joined:
    Sep 13, 2012
    Posts:
    181
    Realizing that, I went back and stared at the stack trace I posted earlier. If anyone else runs into similar dependency problems, the stack trace does give some hints that could help in debugging. Remember that the RayCast job sets [ReadOnly] for the PhysicsWorld dependency. See bolded text below.

     
    Last edited: Jan 25, 2021
  12. toomasio

    toomasio

    Joined:
    Nov 19, 2013
    Posts:
    199
    Just ran into this issue today as well. Thanks for the fix.

    Would it be possible to just have a ComponentSystemGroup that handles this automatically? So we just have to do [UpdateInGroup] and that's it?
     
  13. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    toomasio likes this.
  14. Resshin27

    Resshin27

    Joined:
    Apr 21, 2018
    Posts:
    31
    For anyone coming hear in future, in addition to @tertle's perfect reply, I would like to add this snippet from EndFramePhysicsSystem.cs of UnityPhysics source code which highlights the use of AddInputDependencyToComplete(), for transferring dependencies to next frame run of BuildPhysicsWorldSystem.
    Code (CSharp):
    1.        protected override void OnUpdate()
    2.         {
    3.             // Combine implicit input dependency with the user one
    4.             Dependency = JobHandle.CombineDependencies(Dependency, m_InputDependency);
    5.  
    6.             m_OutputDependency = Dependency;
    7.  
    8.            // Inform the BuildPhysicsWorld of the dependency to complete before scheduling new jobs
    9.             m_BuildPhysicsWorldSystem.AddInputDependencyToComplete(m_OutputDependency);
    10.  
    11.             // Invalidate input dependency since it's been used now
    12.             m_InputDependency = default;
    13.         }
     
    Last edited: Mar 19, 2021
    petarmHavok and jasons-novaleaf like this.