Search Unity

Can There be Entities.ForEach override for Optional Component

Discussion in 'Entity Component System' started by Lieene-Guo, Sep 20, 2020.

  1. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Aside from [Entities.ForEach]'s existing .WithAny and
    .WithEntityQueryOptions(EntityQueryOptions.FilterWriteGroup).
    Can there be an Optional input type for Entities.ForEach lambda function's signature
    For example:
    Code (CSharp):
    1.  
    2. public struct Optional<T>
    3. where T : IComponentData
    4. {
    5.     public T Value;
    6.     public bool Exists;
    7. }
    8. public struct OptionalBuffer<T>
    9.     where T : struct, IBufferElementData
    10. {
    11.     public DynamicBuffer<T> Value;
    12.     public bool Exists;
    13. }
    14.  
    So we can do
    Code (CSharp):
    1.  
    2. Entities.ForEach((ref DamageData damage,in Optional<Booster> booster) =>
    3. {
    4.     damage.Value *= booster.Exists ? (float)(booster.Value.Rate) : 1f;
    5. }).Schedule();
    Without using two Entities.ForEach or IJobChunks.
    Optional Components can be added to the Query's Any group by Entities CodeGen.
    It should work well with WriteGroup by default.
     
    Last edited: Sep 20, 2020
    Enzi and florianhanke like this.
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Does HasComponent GetComponent not solve this for most use cases?

    I know this is not the same as doing a check on [Component|Buffer]TypeHandle but for the majority of use cases it's going to perform fine.
     
  3. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Absolutely NO.
    HasComponent and GetComponent access remote pointer twice. They get TypeID from TypeManager twice. It accesses EntityDataStore Twice. They are only for Entity that is not Expected in the query, not for chunk iteration. I would rather use Two separate Entities.ForEach.

    Optional<T> will take the if brach up stack back to IJobChunk. Per Chunk "if" is better than per entity. when the component data is passed to lambda It is a simple unmanaged value. burst and operator? will do fine. Also, it makes the code cleaner and easier to read.
    Please, Let CodeGen do the nasty branch generation;
    This is my first ESC system. Almost half a year ago. I was struggling with all suggestions like
    "if branch is slow", "% operator is slow" and so.

    Code (CSharp):
    1.         [BurstCompile]
    2.         struct TimeChunkJob : IJobChunk
    3.         {
    4.             [ReadOnly] public ComponentTypeHandle<TimeScale> tsTp;
    5.             public ComponentTypeHandle<DeltaTime> dtTp;
    6.             public ComponentTypeHandle<ElapsedTime> etTp;
    7.             public ComponentTypeHandle<FixedTimeStep> ftsTp;
    8.             public ComponentTypeHandle<StepCounter> scTp;
    9.             [ReadOnly] public float deltaTime;
    10.             public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    11.             {
    12.                 //DeltaTime dt;
    13.                 var count = chunk.Count;
    14.                 var chunkDeltaTime = chunk.Has(etTp) ? chunk.GetNativeArray(dtTp) : default;
    15.                 var chunkTimeScale = chunk.Has(tsTp) ? chunk.GetNativeArray(tsTp) : default;
    16.                 var chunkElapsedTime = chunk.Has(etTp) ? chunk.GetNativeArray(etTp) : default;
    17.                 var chunkFts = chunk.Has(ftsTp) ? chunk.GetNativeArray(ftsTp) : default;
    18.                 var chunkSc = chunk.Has(scTp) ? chunk.GetNativeArray(scTp) : default;
    19.                 if (chunkTimeScale != default)//has time scale
    20.                 {
    21.                     if (chunkElapsedTime != default)//has ElapsedTime
    22.                     {
    23.                         if (chunkDeltaTime != default)//has DeltaTime and ElapsedTime
    24.                         {
    25.                             if (chunkFts != default)//has fixed time step
    26.                             {
    27.                                 if (chunkSc != default)//has step counter
    28.                                 {
    29.                                     for (int i = 0; i < count; i++)
    30.                                     {
    31.                                         var dt = chunkTimeScale[i].Scale(deltaTime);
    32.                                         chunkElapsedTime[i] = chunkElapsedTime[i].Tick(dt);
    33.                                         chunkDeltaTime[i] = dt;
    34.                                         var fst = chunkFts[i] = chunkFts[i].Tick(dt);
    35.                                         chunkSc[i] = chunkSc[i].Tick(fst.AggSteps);
    36.                                     }
    37.                                 }
    38.                                 else//has fixed time step no step counter
    39.                                 {
    40.                                     for (int i = 0; i < count; i++)
    41.                                     {
    42.                                         var dt = chunkTimeScale[i].Scale(deltaTime);
    43.                                         chunkElapsedTime[i] = chunkElapsedTime[i].Tick(dt);
    44.                                         chunkDeltaTime[i] = dt;
    45.                                         chunkFts[i] = chunkFts[i].Tick(dt);
    46.                                     }
    47.                                 }
    48.                             }
    49.                             else//no fixed time step
    50.                             {
    51.                                 for (int i = 0; i < count; i++)
    52.                                 {
    53.                                     var dt = chunkTimeScale[i].Scale(deltaTime);
    54.                                     chunkElapsedTime[i] = chunkElapsedTime[i].Tick(dt);
    55.                                     chunkDeltaTime[i] = dt;
    56.                                 }
    57.                             }
    58.                         }
    59.                         else//has ElapsedTime no DeltaTime
    60.                         {
    61.                             if (chunkFts != default)//has fixed time step
    62.                             {
    63.                                 if (chunkSc != default)//has step counter
    64.                                 {
    65.                                     for (int i = 0; i < count; i++)
    66.                                     {
    67.                                         var dt = chunkTimeScale[i].Scale(deltaTime);
    68.                                         chunkElapsedTime[i] = chunkElapsedTime[i].Tick(dt);
    69.                                         var fst = chunkFts[i] = chunkFts[i].Tick(dt);
    70.                                         chunkSc[i] = chunkSc[i].Tick(fst.AggSteps);
    71.                                     }
    72.                                 }
    73.                                 else//has fixed time step no step counter
    74.                                 {
    75.                                     for (int i = 0; i < count; i++)
    76.                                     {
    77.                                         var dt = chunkTimeScale[i].Scale(deltaTime);
    78.                                         chunkElapsedTime[i] = chunkElapsedTime[i].Tick(dt);
    79.                                         chunkFts[i] = chunkFts[i].Tick(dt);
    80.                                     }
    81.                                 }
    82.                             }
    83.                             else//no fixed time step
    84.                             {
    85.                                 for (int i = 0; i < count; i++)
    86.                                 {
    87.                                     var dt = chunkTimeScale[i].Scale(deltaTime);
    88.                                     chunkElapsedTime[i] = chunkElapsedTime[i].Tick(dt);
    89.                                 }
    90.                             }
    91.                         }
    92.                     }
    93.                     else//no ElapsedTime
    94.                     {
    95.                         //has DeltaTime no ElapsedTime
    96.                         if (chunkDeltaTime != default)
    97.                         {
    98.                             if (chunkFts != default)//has fixed time step
    99.                             {
    100.                                 if (chunkSc != default)//has step counter
    101.                                 {
    102.                                     for (int i = 0; i < count; i++)
    103.                                     {
    104.                                         var dt = chunkTimeScale[i].Scale(deltaTime);
    105.                                         chunkDeltaTime[i] = chunkTimeScale[i].Scale(deltaTime);
    106.                                         var fst = chunkFts[i] = chunkFts[i].Tick(dt);
    107.                                         chunkSc[i] = chunkSc[i].Tick(fst.AggSteps);
    108.                                     }
    109.                                 }
    110.                                 else//has fixed time step no step counter
    111.                                 {
    112.                                     for (int i = 0; i < count; i++)
    113.                                     {
    114.                                         var dt = chunkTimeScale[i].Scale(deltaTime);
    115.                                         chunkDeltaTime[i] = chunkTimeScale[i].Scale(deltaTime);
    116.                                         chunkFts[i] = chunkFts[i].Tick(dt);
    117.                                     }
    118.                                 }
    119.                             }
    120.                             //no fixed time step
    121.                             else for (int i = 0; i < count; i++) chunkDeltaTime[i] = chunkTimeScale[i].Scale(deltaTime);
    122.                         }
    123.                         else//no DeltaTime no ElapsedTime
    124.                         {
    125.                             if (chunkFts != default)//has fixed time step
    126.                             {
    127.                                 if (chunkSc != default)//has step counter
    128.                                 {
    129.                                     for (int i = 0; i < count; i++)
    130.                                     {
    131.                                         var dt = chunkTimeScale[i].Scale(deltaTime);
    132.                                         var fst = chunkFts[i] = chunkFts[i].Tick(dt);
    133.                                         chunkSc[i] = chunkSc[i].Tick(fst.AggSteps);
    134.                                     }
    135.                                 }
    136.                                 //has fixed time step no step counter
    137.                                 else for (int i = 0; i < count; i++) chunkFts[i] = chunkFts[i].Tick(chunkTimeScale[i].Scale(deltaTime));
    138.                             }//no fixed time step
    139.                         }
    140.                         //else no DeltaTime no ElapsedTime no fixedtime step no update
    141.                     }
    142.                 }
    143.                 else//no time scale
    144.                 {
    145.                     if (chunkElapsedTime != default)//has ElapsedTime
    146.                     {
    147.                         if (chunkDeltaTime != default)//has DeltaTime and ElapsedTime
    148.                         {
    149.                             if (chunkFts != default)//has fixed time step
    150.                             {
    151.                                 if (chunkSc != default)//has step counter
    152.                                 {
    153.                                     for (int i = 0; i < count; i++)
    154.                                     {
    155.                                         chunkElapsedTime[i] = chunkElapsedTime[i].Tick(deltaTime);
    156.                                         chunkDeltaTime[i] = deltaTime;
    157.                                         var fst = chunkFts[i] = chunkFts[i].Tick(deltaTime);
    158.                                         chunkSc[i] = chunkSc[i].Tick(fst.AggSteps);
    159.                                     }
    160.                                 }
    161.                                 else//has fixed time step no step counter
    162.                                 {
    163.                                     for (int i = 0; i < count; i++)
    164.                                     {
    165.                                         chunkElapsedTime[i] = chunkElapsedTime[i].Tick(deltaTime);
    166.                                         chunkDeltaTime[i] = deltaTime;
    167.                                         chunkFts[i] = chunkFts[i].Tick(deltaTime);
    168.                                     }
    169.                                 }
    170.                             }
    171.                             else//no fixed time step
    172.                             {
    173.                                 for (int i = 0; i < count; i++)
    174.                                 {
    175.                                     chunkElapsedTime[i] = chunkElapsedTime[i].Tick(deltaTime);
    176.                                     chunkDeltaTime[i] = deltaTime;
    177.                                 }
    178.                             }
    179.                         }
    180.                         else//has ElapsedTime no DeltaTime
    181.                         {
    182.                             if (chunkFts != default)//has fixed time step
    183.                             {
    184.                                 if (chunkSc != default)//has step counter
    185.                                 {
    186.                                     for (int i = 0; i < count; i++)
    187.                                     {
    188.                                         chunkElapsedTime[i] = chunkElapsedTime[i].Tick(deltaTime);
    189.                                         var fst = chunkFts[i] = chunkFts[i].Tick(deltaTime);
    190.                                         chunkSc[i] = chunkSc[i].Tick(fst.AggSteps);
    191.                                     }
    192.                                 }
    193.                                 else//has fixed time step no step counter
    194.                                 {
    195.                                     for (int i = 0; i < count; i++)
    196.                                     {
    197.                                         chunkElapsedTime[i] = chunkElapsedTime[i].Tick(deltaTime);
    198.                                         chunkFts[i] = chunkFts[i].Tick(deltaTime);
    199.                                     }
    200.                                 }
    201.                             }
    202.                             //no fixed time step
    203.                             else for (int i = 0; i < count; i++) chunkElapsedTime[i] = chunkElapsedTime[i].Tick(deltaTime);
    204.                         }
    205.                     }
    206.                     else//no ElapsedTime
    207.                     {
    208.                         //has DeltaTime no ElapsedTime
    209.                         if (chunkDeltaTime != default)
    210.                         {
    211.                             if (chunkFts != default)//has fixed time step
    212.                             {
    213.                                 if (chunkSc != default)//has step counter
    214.                                 {
    215.                                     for (int i = 0; i < count; i++)
    216.                                     {
    217.                                         chunkDeltaTime[i] = deltaTime;
    218.                                         var fst = chunkFts[i] = chunkFts[i].Tick(deltaTime);
    219.                                         chunkSc[i] = chunkSc[i].Tick(fst.AggSteps);
    220.                                     }
    221.                                 }
    222.                                 else//has fixed time step no step counter
    223.                                 {
    224.                                     for (int i = 0; i < count; i++)
    225.                                     {
    226.                                         chunkDeltaTime[i] = deltaTime;
    227.                                         chunkFts[i] = chunkFts[i].Tick(deltaTime);
    228.                                     }
    229.                                 }
    230.                             }
    231.                             //no fixed time step
    232.                             else for (int i = 0; i < count; i++) chunkDeltaTime[i] = deltaTime;
    233.                         }
    234.                         else//no DeltaTime no ElapsedTime
    235.                         {
    236.                             if (chunkFts != default)//has fixed time step
    237.                             {
    238.                                 if (chunkSc != default)//has step counter
    239.                                 {
    240.                                     for (int i = 0; i < count; i++)
    241.                                     {
    242.                                         var fst = chunkFts[i] = chunkFts[i].Tick(deltaTime);
    243.                                         chunkSc[i] = chunkSc[i].Tick(fst.AggSteps);
    244.                                     }
    245.                                 }
    246.                                 else//has fixed time step no step counter
    247.                                 { for (int i = 0; i < count; i++) chunkFts[i] = chunkFts[i].Tick(deltaTime); }
    248.                             }
    249.                         }
    250.                         //else no DeltaTime no ElapsedTime no fixedtime step no update
    251.                     }
    252.                 }
    253.             }
    254.         }
    255.  
     
    Last edited: Sep 20, 2020
  4. apkdev

    apkdev

    Joined:
    Dec 12, 2015
    Posts:
    284
    I like this idea. Any reason why we can't just use Nullable<T> for this? I'm pretty sure it is already supported by Burst.
    Code (CSharp):
    1. Entities.ForEach((ref DamageData damage, in Booster? booster)
    2.   => damage.Value *= booster?.Rate ?? 1f
    3. ).Schedule();
    Wouldn't the branch predictor do an excellent job here? Or we could just emit a separate job for every possible component combo.
     
    Neonage and Lieene-Guo like this.
  5. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    I think DOTS team can do that. It's just If they want to put it in the plan.
     
  6. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
  7. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    either that or an TryGet/TrySet-like api (which could be added to the XXXTypeHandle apis too).
     
  8. Enzi

    Enzi

    Joined:
    Jan 28, 2013
    Posts:
    966
    Yeah, hope this gets implemented in some way as long as Has/GetComponent isn't performant enough.Some of my systems could see a huge improvement because I'm way too lazy to write a ForEach for every possible combination of optional components that may or may not be on an entity.

    (Just thinking out loud)
    Are the pre-processing costs to get to the foreach included? There is some overhead to create the Optional data so if that's on par with get/set we might just have moved the costs to someplace else.
     
    Lieene-Guo and Tony_Max like this.
  9. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Maybe add a new special Parameter is Good enough.
    Somthing like
    Code (CSharp):
    1. public struct EntityInChunk
    2. {
    3.     ArchetypeChunk Chunk;
    4.     int IndexInChunk;
    5.     EntityTypeHandle EntityType;
    6. }
    So we can test if some TypeIndex exist on that specific chunk directlly. and read/write to them directlly. instead of go back to EntityComponentStore and seek back down.
    But still Optinal<T> is the best solution. As The Type is known at compile time.
     
  10. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547

    Attached Files:

    Lukas_Kastern likes this.