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. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Issues writing absorption Jobified system

Discussion in 'Entity Component System' started by Soaryn, Sep 6, 2018.

  1. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    325
    My goal was to have cubes that, when within a radius of each other, the 'Absorber' would take the values for the 'Absorbable'

    I can do this linearly; however, my problem resides when I try to Jobify it.

    My approach was to have two groups, Absorbables and Absorber
    Code (CSharp):
    1. public struct GroupAbsorbable {
    2.     public EntityArray Entities;
    3.  
    4.     [ReadOnly]
    5.     public ComponentDataArray<Growth> Growths;
    6.  
    7.     [ReadOnly]
    8.     public ComponentDataArray<LocalToWorld> Positions;
    9.  
    10.     [ReadOnly]
    11.     public ComponentDataArray<Absorbable> Match;
    12.  
    13.     public SubtractiveComponent<Absorbed> IgnoreAbsorbed;
    14.  
    15.     public readonly int Length;
    16. }
    17.  
    18. public struct GroupAbsorber {
    19.     public EntityArray Entities;
    20.     public ComponentDataArray<Growth> Growths;
    21.     public ComponentDataArray<LocalToWorld> Positions;
    22.  
    23.     public ComponentDataArray<Absorber> Match;
    24.     public SubtractiveComponent<Absorbed> IgnoreAbsorbed;
    25.  
    26.     public readonly int Length;
    27. }

    Both notably with a "Growth" component that would indicate how much the object has grown. The reason I want it in its own tag is to simplify the relation ship between entities. An absorber should be able to be an Absorbable as well, and in the future the larger value would 'win'.

    This works in a non-jobified system, but when you try to do a parallel for loop with it, the system errors out "
    InvalidOperationException: The writable NativeArray FindJob.Absorber.Growths is the same NativeArray as FindJob.Absorbables.Growths, two NativeArrays may not be the same (aliasing)."

    What I find interesting, is that the two are impossible to be the same in this particular test as the Growths array are of different entities.
    I then approached the problem with a IProcessJob, which made the code a lot more clean and readable, but resulted in the same issue. Every sample I found was in direct correlation to Something emits damage, something has health, but I have yet to find a sample, where both entities have a similar component with the 'winner' taking the value for them self.


    Any thoughts on how to parallelize this?

    This was the attempted IJobProcess
    Code (CSharp):
    1. private struct Job : IJobProcessComponentData<Growth, LocalToWorld, Absorber> {
    2.     [ReadOnly]
    3.     [NativeDisableParallelForRestriction]
    4.     public GroupAbsorbable Absorbables;
    5.  
    6.     [ReadOnly]
    7.     public EntityCommandBuffer Buffer;
    8.  
    9.     public void Execute(ref Growth growth, [ReadOnly] ref LocalToWorld position, [ReadOnly] ref Absorber absorber) {
    10.         for (var absorbable = 0; absorbable < Absorbables.Entities.Length; absorbable++) {
    11.             var bPos = Absorbables.Positions[absorbable].Value;
    12.             if (math.distance(position.Value.c3, bPos.c3) > absorber.Radius) return;
    13.             growth.Value += Absorbables.Growths[absorbable].Value;
    14.             Buffer.DestroyEntity(Absorbables.Entities[absorbable]);
    15.         }
    16.     }
    17. }
     
  2. dzamani

    dzamani

    Joined:
    Feb 25, 2014
    Posts:
    122
  3. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    325
    I need this to be on a rather massive scale, so multiple small IJobs may not be ideal consider 1k absorbers and on each potentially say 100 absorbables.
     
  4. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    529
    You mean, there are no entities having both? I guess it doesn't check if the actual entities collide.

    I see two solutions:
    1. Quickfix: mark growth in absorbers as ReadOnly and update the value using the EntityComponentBuffer. Slower.
    2. Use ArchetypeQuery to get all chunks with writable growth and split chunks manually into absorber/absorbable. This would allow entities having both at the same time and should give you the best possible performance.
     
    Last edited: Sep 6, 2018
  5. dzamani

    dzamani

    Joined:
    Feb 25, 2014
    Posts:
    122
    You could do it with one job iterating over all Absorbers, the main issue here is that your groups have 2 references pointing to the same array (at compile time).
    Just like julian-moschuering said, you should use ArchetypeQuery and chunks to separate absorber/absorbable and then start a job with them.
     
  6. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    325
    The quick fix worked for now. Until I can sit down and look at the query the ProcessJobWithEntity will do.
     
  7. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    529
    Also: instead of using EntityComponentBuffer try writing the entities to delete to a NativeQuery and do the actual EntityCommandBuffer update in a separate IJob. Then activate Burst on the first job... it makes a huge difference.
    Hopefully EntityCommandBuffer will get Burst support for some of it's functions.
     
  8. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    325
    It would be really handy if NativeQueue.Concurrent had a dequeue :(
     
  9. Soaryn

    Soaryn

    Joined:
    Apr 17, 2015
    Posts:
    325
    Seemingly new problem. When doing the queued destroy with the Buffer, if the entity is a parent to children, then it errors out.
    First consideration, was to loop through the children and handle them accordingly; however, I see no way to determine that association from within a job. Ideally, what I'd like to do from within a job, is if the parent is 'absorbed', absorb the children as well.
     
  10. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    529
    Not required. Filling ECB from NativeQuery wouldn't benefit from multiple threads. Just use a IJob without concurrent for this.
     
    dzamani likes this.
  11. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    529
    Maybe look at the TransformSystem source for inspiration. It creates some intermediate data structures sorted by hierarchy depth.
    Of course you can just jump through the hierarchy using ComponentDataFromEntity.
     
    dzamani likes this.