Search Unity

How do I access ComponentDataFromEntity inside an IJobParallelFor

Discussion in 'Entity Component System' started by davenirline, Jul 4, 2018.

  1. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    987
    So far, I've made my systems using inject and manually looping them in OnUpdate(). I want to optimize some systems by "jobifying" them. If I understand correctly, when your making a job that implements from IJobParallelFor, the NativeArrays in it should have the same length. However, ComponentDataFromEntity does not have the same length as the injected data. How is it fed in an IJobParallelFor struct then? Do I temporarily create an array for it and assign it to a NativeArray?

    Code (csharp):
    1.  
    2. private struct Data {
    3.     public int Length;
    4.     public ComponentDataArray<Target> Targets;
    5. }
    6.  
    7. [Inject]
    8. private ComponentDataFromEntity<Position> allPositions;
    9.  
    10. private struct Job : IJobParallelFor {
    11.     ...
    12.     public NativeArray<Position> Positions;
    13.     ...
    14. }
    15.  
    16. // Note sure if this is even correct
    17. protected override JobHandle OnUpdate(JobHandle inputDeps) {
    18.     // Prepare input positions
    19.     Position[] alignedPositions = new Position[this.data.Length];
    20.     for(int i = 0; i < this.data.Length; ++i) {
    21.         alignedPositions = this.allPositions[this.data.Targets[i].targetEntity];
    22.     }
    23.  
    24.     Job job = new Job();
    25.     job.Positions = new NativeArray(alignedPositions, Allocator.TempJob);
    26.  
    27.     return job.Schedule(this.data.Length, 64, inputDeps);
    28. }
    29.  
     
  2. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    if you are only reading from CDFE, add the [ReadOnly] attribute.

    if you must write to it and you know what you are doing, you can add [NativeDisableContainerSafetyRestriction], but that will disable the safety system and error checking, potentially crashing Unity.
     
  3. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    987
    Do you mean that if I have that attribute, I can assign it directly in the Job struct?
     
  4. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    yes but beware that if you write to the same entity from multiple threads, you will get race conditions (your results will be different depending on the timing of the threads, and your data can be corrupted, potentially causing crashes or undefined behavior later).
    also the cache will be invalidated from multiple threads, slowing down things.

    try to avoid that if you can (there is EntityCommandBuffer.Concurrent or NativeQueue<T>.Concurrent if you need them. you can write to them from multiple threads but not read)

    for your example, you can do
    Code (csharp):
    1.  
    2. struct Job : IJobParallelFor {
    3.     [ReadOnly] public ComponentDataFromEntity<Position> allPositions;
    4.     public ComponentDataArray<Target> Targets;
    5.  
    6.     void Execute(int index) {
    7.         var position = allPositions[Targets[index].entity];
    8.         // do your stuff
    9.         // assign your result to provided index of output array, or use a concurrent ECB or native queue to schedule commands to be executed later in the main thread
    10.     }
    11. }
    12.  
     
  5. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    987
    I do need to write to ComponentDataFromEntity. How do I use EntityCommandBuffer.Concurrent or NativeQueue.Concurrent? An EntityCommandBuffer can only be ReadOnly inside a job.
     
  6. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    Code (csharp):
    1. struct Job {
    2.     public EntityCommandBuffer.Concurrent ecb;
    3.     public NativeQueue<int>.Concurrent nq;
    4. }
    5.  
    6. OnUpdate() {
    7.     new Job {
    8.          ecb = aBarrierSystem.CreateCommandBuffer(),
    9.          nq = aNativeQueueYouHave
    10.     }.Schedule(deps);
    11. }
     
  7. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    987
    I see. Will try it.
     
  8. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    987
    I couldn't find the Concurrent type. Is there a namespace to be added first?
     
  9. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    did you pull the latest version? (com.unity.entities@0.0.12-preview.8 & com.unity.collections@0.0.9-preview.2)
     
  10. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    987
  11. mike_acton

    mike_acton

    Unity Technologies

    Joined:
    Nov 21, 2017
    Posts:
    110
    > I do need to write to ComponentDataFromEntity.

    To be clear, the only issue is if you need to write to components referenced by the *same* entity. If your logic makes this impossible (i.e. you've divided up the data in to exclusive groups based on some other types or values), you can write to the components directly without any issue wrt data consistency.
     
  12. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    987
    Yes. For example, I want to be able to do this:

    Code (csharp):
    1.  
    2. struct Job : IJobParallelFor {
    3.     public ComponentDataFromEntity<Position> allPositions;
    4.     public ComponentDataArray<Target> Targets;
    5.  
    6.     void Execute(int index) {
    7.         Position position = allPositions[Targets[index].entity];
    8.         // Compute new position
    9.  
    10.         allPositions[Targets[index].entity] = position; // Modify the position      
    11.     }
    12. }
    13.  
    14.  
     
    andywatts likes this.