Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more..
    Dismiss Notice
  3. Dismiss Notice

Reading Component Data from within Job

Discussion in 'Entity Component System' started by wedrk, May 6, 2020.

  1. wedrk

    wedrk

    Joined:
    Jul 15, 2018
    Posts:
    9
    I'm trying to create a follow-leader system so that followers of a leader will move their position to the leader's position. I've seen that you can use ComponentDataFromEntity to read component data while inside a job, but because I'm reading the leader's Translation components and I want to write to the follower's Translation components, it throws an exception. Even if I use [NativeDisableParallelForRestriction] it still doesn't bypass the race condition detection. Is this possible within a job?

    One idea I had was to write another component system that just writes to the follower's translation component at the end of the frame and write to the new component in my follow-leader system, but that seems like bad ECS design.

    Another idea was to get a list of Translation components in one job, and then pass that in a native array to a second job which reads from it to write to the translation component of the follow entity. Is there a better way to do this?

    Code:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Entities;
    5. using Unity.Jobs;
    6. using Unity.Mathematics;
    7. using Unity.Transforms;
    8. using Unity.Collections;
    9. using Unity.Burst;
    10. public class FollowLeaderSystem : JobComponentSystem
    11. {
    12.  
    13.     struct FollowLeaderJob : IJobChunk
    14.     {
    15.         public ArchetypeChunkComponentType<Translation> TranslationType;
    16.         [ReadOnly] public ArchetypeChunkComponentType<FollowLeader> FollowLeaderType;
    17.         [NativeDisableParallelForRestriction] [ReadOnly] public ComponentDataFromEntity<Translation> LeaderTranslation;
    18.  
    19.         public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    20.         {
    21.             NativeArray<Translation> Translations = chunk.GetNativeArray(TranslationType);
    22.             NativeArray<FollowLeader> FollowLeaders = chunk.GetNativeArray(FollowLeaderType);
    23.            
    24.             for(int i = 0; i < chunk.Count; i++)
    25.             {
    26.                 Translation translation = Translations[i];
    27.                 FollowLeader followLeader = FollowLeaders[i];
    28.  
    29.                 if (!LeaderTranslation.Exists(followLeader.Leader))
    30.                     return;
    31.                
    32.                 Translation leaderTranslation = LeaderTranslation[followLeader.Leader];
    33.                 translation.Value = leaderTranslation.Value;
    34.  
    35.                 Translations[i] = translation;
    36.  
    37.             }
    38.  
    39.         }
    40.     }
    41.  
    42.     EntityQuery m_Query;
    43.  
    44.     protected override void OnCreate()
    45.     {
    46.         m_Query = GetEntityQuery(typeof(Translation), ComponentType.ReadOnly<FollowLeader>());
    47.     }
    48.  
    49.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    50.     {
    51.  
    52.         var job = new FollowLeaderJob()
    53.         {
    54.             TranslationType = GetArchetypeChunkComponentType<Translation>(false),
    55.             FollowLeaderType = GetArchetypeChunkComponentType<FollowLeader>(true),
    56.             LeaderTranslation = GetComponentDataFromEntity<Translation>(true)
    57.         };
    58.  
    59.         return job.Schedule(m_Query, inputDeps);
    60.     }
    61. }
    62.  
     
  2. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,653
    Code (CSharp):
    1. [NativeDisableParallelForRestriction] [ReadOnly] public ComponentDataFromEntity<Translation> LeaderTranslation;
    Remove
    [ReadOnly]
    if you using
    [NativeDisableParallelForRestriction]
    it's doesn't make sense :)
     
  3. wedrk

    wedrk

    Joined:
    Jul 15, 2018
    Posts:
    9
    This still doesn't stop the exception.
     
  4. Kmsxkuse

    Kmsxkuse

    Joined:
    Feb 15, 2019
    Posts:
    297
    What is the exception? Copy paste the entire exception dump if you need to.

    Try setting
    TranslationType
    the
    [NativeDisableParallelForRestriction]
    .

    LeaderTranslation is only being read from
    Translation leaderTranslation = LeaderTranslation[followLeader.Leader];
    but
    NativeArray<Translation> Translations = chunk.GetNativeArray(TranslationType);
    is the one being read and set by your job at
    Translations[i] = translation;
    .
     
  5. wedrk

    wedrk

    Joined:
    Jul 15, 2018
    Posts:
    9
    I've tried setting each field to [NativeDisableParallelForRestriction] as well as setting all of the to it but the exception doesn't go away. Here is the full exception:
    Code (CSharp):
    1. InvalidOperationException: The writable NativeArray FollowLeaderJob.JobData.TranslationType is the same NativeArray as FollowLeaderJob.JobData.LeaderTranslation, two NativeArrays may not be the same (aliasing).
    2. 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.10.0-preview.6/Unity.Entities/IJobChunk.cs:216)
    3. Unity.Entities.JobChunkExtensions.Schedule[T] (T jobData, Unity.Entities.EntityQuery query, Unity.Jobs.JobHandle dependsOn) (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/IJobChunk.cs:94)
    4. FollowLeaderSystem.OnUpdate (Unity.Jobs.JobHandle inputDeps) (at Assets/Systems/FollowLeaderSystem.cs:59)
    5. Unity.Entities.JobComponentSystem.Update () (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/JobComponentSystem.cs:141)
    6. Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/ComponentSystemGroup.cs:240)
    7. UnityEngine.Debug:LogException(Exception)
    8. Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/Stubs/Unity/Debug.cs:19)
    9. Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/ComponentSystemGroup.cs:244)
    10. Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/ComponentSystemGroup.cs:219)
    11. Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/ComponentSystem.cs:109)
    12. Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.10.0-preview.6/Unity.Entities/ScriptBehaviourUpdateOrder.cs:196)
    13.  
     
  6. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,653
    And it shouldn't, it's just note about clear code:)

    And error pretty clear. For preventing memory aliasing you can't have both - ComponentDataFromEntity<Translation> and ArchetypeChunkComponentType<Translation>. You should choose one. You maybe know that they not collide, but machine - no. You can replace ArchetypeChunkComponentType<Translation> to ArchetypeChunkEntityType and use that entity for ComponentDataFromEntity to get translation.
     
  7. wedrk

    wedrk

    Joined:
    Jul 15, 2018
    Posts:
    9
    The exception doesn't let me play the game still. Wouldn't feeding the job the list of all entities be inefficient since the job then needs to run on all entities, or will the entitiy query only run the job on the entities that match the query? If so how would I get the reference to the entity from within an IJobChunk?
     
  8. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,653
    And it wouldn't until you fix that.
    CDFE it's not list of entities. It reference to "dictionary" of entities with O(1) access. Only performance hit here is random memory access.
    That row don't hint at anything? :)
    job.Schedule(m_Query, inputDeps);

    I already told you, same as for types for chunk but for entities
    And your top part will be like that
    Code (CSharp):
    1.  
    2.             [ReadOnly] public ArchetypeChunkEntityType EntitiesType;
    3.  
    4.             //....other fields definitions
    5.  
    6.             public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    7.             {
    8.                 NativeArray<Entity>       entities      = chunk.GetEntityArray(EntitiesType);
    9.                 NativeArray<FollowLeader> FollowLeaders = chunk.GetNativeArray(FollowLeaderType);
    10.  
    11.                 for (int i = 0; i < chunk.Count; i++)
    12.                 {
    13.                     Translation  translation  = LeaderTranslation[entities[i]];
    14.                     FollowLeader followLeader = FollowLeaders[i];
    15.  
    16.                 //....other things
    17.  
     
    Last edited: May 6, 2020
    nicolasgramlich and wedrk like this.
  9. wedrk

    wedrk

    Joined:
    Jul 15, 2018
    Posts:
    9
    Thanks for the example code. I'm confused on how I should write onto the Translation component then as I don't get a reference to the entity but only the value. Should I use the ComponentDataFromEntity to write or should I pass in an EntityCommandBuffer as well.
     
  10. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,653
    ComponentDataFromEntity has setter if you sure in safety that nothing will write-write write-read same memory at same time