Search Unity

[Bug] RequireSubtractiveComponent isn't considered for job dependency error checking

Discussion in 'Entity Component System' started by Ryetoast, Sep 17, 2018.

  1. Ryetoast

    Ryetoast

    Joined:
    Mar 8, 2015
    Posts:
    48
    The following script produces runtime errors, but given the subtractive component on one, and it being required in the other, these jobs can never act on the same entities.

    Script:
    Code (CSharp):
    1. using Unity.Burst;
    2. using Unity.Collections;
    3. using Unity.Entities;
    4. using Unity.Jobs;
    5. using Unity.Mathematics;
    6.  
    7. namespace Gamespace {
    8.     [UpdateAfter(typeof(SHealthChange))]
    9.     class SHealthClamp : JobComponentSystem {
    10.  
    11.         [BurstCompile]
    12.         [RequireSubtractiveComponent(typeof(CHealth.Max))]
    13.         struct NoMaxJob : IJobProcessComponentData<CHealth.Current> {
    14.             public void Execute (ref CHealth.Current current) {
    15.                 current.Value = math.max(current.Value, 0);
    16.             }
    17.         }
    18.  
    19.         [BurstCompile]
    20.         struct MaxJob : IJobProcessComponentData<CHealth.Current, CHealth.Max> {
    21.             public void Execute (ref CHealth.Current current, [ReadOnly] ref CHealth.Max max) {
    22.                 current.Value = math.clamp(current.Value, 0, max.Value);
    23.             }
    24.         }
    25.  
    26.         protected override JobHandle OnUpdate (JobHandle inputDeps) {
    27.             var noMaxJob = new NoMaxJob().Schedule(this, inputDeps);
    28.             var maxJob = new MaxJob().Schedule(this, inputDeps);
    29.             return JobHandle.CombineDependencies(noMaxJob, maxJob);
    30.         }
    31.     }
    32. }
    Error:
    Code (CSharp):
    1. InvalidOperationException: The previously scheduled job SHealthClamp:NoMaxJob writes to the NativeArray NoMaxJob.Iterator. You are trying to schedule a new job SHealthClamp:MaxJob, which writes to the same NativeArray (via MaxJob.Iterator). To guarantee safety, you must include SHealthClamp:NoMaxJob as a dependency of the newly scheduled job.
    2. Unity.Jobs.LowLevel.Unsafe.JobsUtility.ScheduleParallelFor (Unity.Jobs.LowLevel.Unsafe.JobsUtility+JobScheduleParameters& parameters, System.Int32 arrayLength, System.Int32 innerloopBatchCount) <0x34b4ca60 + 0x0007a> in <f718fbf7147e402282fdacf37bdd8584>:0
    3. Unity.Entities.JobProcessComponentDataExtensions.Schedule (System.Void* fullData, System.Int32 length, System.Int32 innerloopBatchCount, System.Boolean isParallelFor, Unity.Entities.JobProcessComponentDataCache& cache, Unity.Jobs.JobHandle dependsOn, Unity.Jobs.LowLevel.Unsafe.ScheduleMode mode) (at C:/Users/sourd/AppData/Local/Unity/cache/packages/packages.unity.com/com.unity.entities@0.0.12-preview.13/Unity.Entities/IJobProcessComponentData.cs:503)
    4. Unity.Entities.JobProcessComponentDataExtensions.ScheduleInternal_2[T] (T& jobData, Unity.Entities.ComponentSystemBase system, System.Int32 innerloopBatchCount, Unity.Jobs.JobHandle dependsOn, Unity.Jobs.LowLevel.Unsafe.ScheduleMode mode) (at C:/Users/sourd/AppData/Local/Unity/cache/packages/packages.unity.com/com.unity.entities@0.0.12-preview.13/Unity.Entities/IJobProcessComponentData.cs:554)
    5. Unity.Entities.JobProcessComponentDataExtensions.Schedule[T] (T jobData, Unity.Entities.ComponentSystemBase system, Unity.Jobs.JobHandle dependsOn) (at C:/Users/sourd/AppData/Local/Unity/cache/packages/packages.unity.com/com.unity.entities@0.0.12-preview.13/Unity.Entities/IJobProcessComponentData.cs:423)
    6. Gamespace.SHealthClamp.OnUpdate (Unity.Jobs.JobHandle inputDeps) (at Assets/Scripts/System/SHealthClamp.cs:28)
    7. Unity.Entities.JobComponentSystem.InternalUpdate () (at C:/Users/sourd/AppData/Local/Unity/cache/packages/packages.unity.com/com.unity.entities@0.0.12-preview.13/Unity.Entities/ComponentSystem.cs:445)
    8. Unity.Entities.ScriptBehaviourManager.Update () (at C:/Users/sourd/AppData/Local/Unity/cache/packages/packages.unity.com/com.unity.entities@0.0.12-preview.13/Unity.Entities/ScriptBehaviourManager.cs:77)
    9. Unity.Entities.ScriptBehaviourUpdateOrder+DummyDelagateWrapper.TriggerUpdate () (at C:/Users/sourd/AppData/Local/Unity/cache/packages/packages.unity.com/com.unity.entities@0.0.12-preview.13/Unity.Entities/ScriptBehaviourUpdateOrder.cs:703)
    10.  
     
  2. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    529
    Dependencies are only tracked by type not by actual chunks. This is currently by design and it's not always that easy to see that there is no sharing violation.

    Some Solutions:
    1. As your task is easily distributable over all threads there is no benefit in combining the dependencies. Running the jobs serially is just as fast.
    2. Using ArchetypeChunks you can split the chunks into the two groups and launch a job for each group. These can run in parallel. This is basically what you expected.
    3. Having two jobs, potentially with RO access, that do the calculations and write to intermediate buffers and write the result in a third job.

    In your case (1) makes the most sense.
     
  3. Ryetoast

    Ryetoast

    Joined:
    Mar 8, 2015
    Posts:
    48
    Yeah, (1) is definitely what I went with, I was just expecting what I posted to work. It's possible for the compiler to figure it out in this case, but I'm sure there's more important things to prioritize at this point in ECS development.