Search Unity

Feedback API For Depress Version Change

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

  1. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Can there be a Depress Version Change API in Entities.ForEach();

    For example:
    In the first system.
    Code (CSharp):
    1. Dependency = Entities.WithChangeFilter<Damage>().ForEach((ref HealthPoint hp, in Damage dmg) =>
    2. {
    3.     if (dmg.Value > 0) hp.Damage(dmg.Value, out var overflow);
    4.     else DepressVersionChange<HealthPoint>();
    5. }).ScheduleParallel(Dependency);
    In the next system
    Code (CSharp):
    1. Dependency = Entities.WithChangeFilter<HealthPoint>().ForEach((in HealthPoint hp) =>
    2. {
    3.     //react to hp change
    4. }).ScheduleParallel(Dependency);
    I am aware that version is pre type pre chunk instead of per type per entity.
    By DepressVersionChange, we can filter out (not all entities but all chunks) Entities.ForEach in the 2nd system when Hp value is not changed. which will filter out many redundant procedural.

    This might not be a good example, as Damage=0 is rare.
    But in my game when data write dependency grows. WithChangeFilter will become useless because there always some deep source component accessed with readwrite access. And not matter if the value is actually changed it's version grows.
    DepressVersionChange() would help keep old version if the target type of component in whole chunk is not changed.

    I am capable of doing this In IJobChunk(code attached). but Entities.ForEach needs this function too.
     

    Attached Files:

    Last edited: Sep 29, 2020
  2. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    If you use ComponentDataFromEntity you can avoid the version change when nothing has changed. Obviously this makes writing significantly slower.
     
  3. LazyGameDevZA

    LazyGameDevZA

    Joined:
    Nov 10, 2016
    Posts:
    143
    If I remember correctly the change filter isn't actually tracked per entity, but per chunk. Meaning if you suppress for one entity you'll also do that for the entire chunk. I might be wrong, but that is my understanding based on the blogpost @5argon wrote.
     
  4. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    That is correct.
     
  5. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    As I mention "DepressVersionChange() would help keep old version if the target type of component in whole chunk is not changed."
    ForEach can collect all Depress info and see if all entity is depressing and keep/change version accordingly.

    Still, it will save tons of redundant computation.
     
  6. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    My team currently working on a full DOTS game, Pre Component Disable / Version Change control / Optional Component support are the three main problems we are facing every day. I have made some progress with my own solution and tested them in my game. And they are really helpful in terms of coding/reviewing efficiency and run-time efficiency.
    I have got a positive reply on pre component disable/enable.
    And how about the other two?
     
    deus0 and JesOb like this.
  7. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Optional component suppport in Entities.ForEach using nullable / ? makes sense imo.

    Version change control sounds reasonable, but i'd love to see some real world profiling data from a game that shows what the perf impact of this is.
     
  8. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,258
    Entities.ForEach works at the "for each entity" scope. IJobChunk works at the "for each chunk" scope. Change filtering is a chunk-scope concept. Therefore, I disagree with your statement.

    Really what you want is more flexibility to customize the IJobChunk lambda jobs generate. Hopefully with the move to source generators, this might become accessible.
     
  9. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    That why I can not do it the "Clean Way"(not changing the unity package).
    And as I can do it pretty well with IJobChunk, CodeGen will be able to aggregate entity version change Depress requests in the chunk and decide if version needs to be kept.
     
  10. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    FYI. You can use Entities.WithStructuralChange(). It will compare output & input before applying. It is significantly slower...
     
    Lieene-Guo likes this.
  11. MartijnGG

    MartijnGG

    Joined:
    May 3, 2018
    Posts:
    74
    Could the ForEach not hash the array per writable component type in the chunk it is handling before and after iterating over it? If the hash doesnt match after iteration, the chunk version for that type can be updated.
    This should be relatively fast seeing the memory layout of the components in chunks.
     
  12. Lieene-Guo

    Lieene-Guo

    Joined:
    Aug 20, 2013
    Posts:
    547
    Never know that! Thanks!
    For now, I'm switching to IJobChunk and see if version change control is really that helpful.
    Also, I just found that most of the time this redundancy happens with data aggregating.

    For example, in a roguelike game there could be 50 types of power-up who can increase player damage. and they can be pickup and dropped from time to time. I'm using component data from a database as power-up and they have different levels (which means different damage value that changes). I just don't want to add them all up every frame or say on any deep-dependency change.
    For my use-case, I'm using a redundant dynamic buffer that each power-up could check it's TypeIndex for existence and Set/Add/Remove value from the buffer. And I need to depress versions change of this dynamic buffer. And the aggregated value could be any unmanaged type(not just damage value). Redundant aggregation should happen as less frequently as possible.
    WithChangeFilter<T>() helps a lot in this case. but whenever one of the power-ups needs to be updated every frame, say apply damage after moved 5 meters (but damage does not change), then everything breaks.

    Also, I just checked DataFlowGraph looks like Simulation Node's Message can somehow be used for that purpose.
    Just don't know if it fits into the case.
     
    Last edited: Sep 30, 2020
    deus0 likes this.