Search Unity

Possible GetComponentGroup/SetFilterChanged Bug

Discussion in 'Entity Component System' started by dt665m, Jun 13, 2018.

  1. dt665m

    dt665m

    Joined:
    Aug 15, 2015
    Posts:
    11
    I'm not sure if I'm running into an obscure bug or I'm just misunderstanding the system. I'm running the latest 2018.2.0b7 and tried with 0.0.12-preview.6(and 5).

    What I'm trying to do:
    • ReactiveSystemA has ComponentGroup with SetFilterChanged on MoveComponent changes
    • ReactiveSystemA updates the ComponentGroup's PositionComponent
    • ReactiveSystemB has same ComponentGroup with SetFilterChanged on PositionComponent changes
    • ReactiveSystemB does SystemBy things.

    The issue I'm running into is if SystemA updates the PositionComponent through the ComponentGroup's GetComponentDataArray<Position>, SystemB's filter doesn't pick up the change. If I inject the same components and modify the position, SystemB's filter will pick up the change.

    Code (CSharp):
    1. public class ReactiveSystemA : ComponentSystem
    2. {
    3.     public struct EntityGroup
    4.     {
    5.         public int Length;
    6.         public EntityArray Entities;
    7.         public ComponentDataArray<Position> Position;
    8.         public ComponentDataArray<Heading> Heading;
    9.         public ComponentDataArray<Move> Move;
    10.     }
    11.     [Inject] EntityGroup m_entitiesInjected;
    12.  
    13.     private ComponentGroup m_entities;
    14.     protected override void OnCreateManager(int capacity)
    15.     {
    16.         m_entities = GetComponentGroup(
    17.             typeof(Position),
    18.             typeof(Heading),
    19.             typeof(Move)
    20.         );
    21.     }
    22.  
    23.     protected override void OnUpdate()
    24.     {
    25.         m_entities.ResetFilter();
    26.         m_entities.SetFilterChanged(ComponentType.Create<Move>());
    27.  
    28.         var positions = m_entities.GetComponentDataArray<Position>();
    29.         var headings = m_entities.GetComponentDataArray<Heading>();
    30.         var moves = m_entities.GetComponentDataArray<Move>();
    31.  
    32.         for (int i = 0; i < positions.Length; i++)
    33.         {
    34.             var move = moves[i].Value;
    35.             positions[i] = new Position { Value = new int2(move.x, move.y) }; //<--- this does not trigger SystemB's filter
    36.             m_entitiesInjected.Position[i] = new Position { Value = new int2(move.x, move.y) };  //<--- this triggers SystemB's filter
    37.         }
    38.     }
    39. }
    40.  
    41. public class ReactiveSystemB : ComponentSystem
    42. {
    43.     private ComponentGroup m_entities;
    44.     protected override void OnCreateManager(int capacity)
    45.     {
    46.         m_entities = GetComponentGroup(
    47.             typeof(Position),
    48.             typeof(Heading),
    49.             typeof(Move)
    50.         );
    51.     }
    52.  
    53.     protected override void OnUpdate()
    54.     {
    55.         m_entities.ResetFilter();
    56.         m_entities.SetFilterChanged(ComponentType.Create<Position>());
    57.  
    58.         var positions = m_entities.GetComponentDataArray<Position>();
    59.         for (int i = 0; i < positions.Length; i++)
    60.         {
    61.             Debug.Log(positions[i]);
    62.         }
    63.     }
    64. }

    Anyone have any clue what's going on here? SystemA's filter on MoveComponent works and the code is literally replicated to SystemB.
     
  2. dt665m

    dt665m

    Joined:
    Aug 15, 2015
    Posts:
    11
    I've further narrowed it down to the "write" back to the ComponentDataArray that is causing this. I don't think this has anything to do specifically with ComponentGroup as I'm seeing this behavior with injected groups as well. Reproduction Steps:

    • SystemB uses ComponentGroup.SetFilterChanged on a specific IComponentData
    • SystemA uses Injected Group, takes a temp variable on Group.ComponentDataArray, writes to the temp variable.

    Code (CSharp):
    1. public class ReactiveSystemA : ComponentSystem
    2. {
    3.     public struct EntityGroup
    4.     {
    5.         public int Length;
    6.         public EntityArray Entities;
    7.         public ComponentDataArray<Position> Position;
    8.         public ComponentDataArray<Heading> Heading;
    9.         public ComponentDataArray<Move> Move;
    10.     }
    11.     [Inject] EntityGroup m_entitiesInjected;
    12.  
    13.     protected override void OnUpdate()
    14.     {
    15.         var positions = m_entitiesInjected.Position;
    16.         var headings = m_entitiesInjected.Heading;
    17.         var moves = m_entitiesInjected.Move;
    18.         for (int i = 0; i < positions.Length; i++)
    19.         {
    20.             var move = moves[i].Value;
    21.            
    22.            //this does NOT trigger SystemB's filter
    23.            positions[i] = new Position { Value = new int2(move.x, move.y) };
    24.  
    25.             //this does trigger SystemB's filter
    26.             m_entitiesInjected.Position[i] = new Position { Value = new int2(move.x, move.y) };
    27.         }
    28.     }
    29. }
     
  3. capyvara

    capyvara

    Joined:
    Mar 11, 2010
    Posts:
    80
    I think this kind of filter is a WIP, there's a lot of comments on the test code:

    Code (CSharp):
    1.         // * TODO: using out of date version cached ComponentDataArray should give exception... (We store the System order version in it...)
    2.         // * TODO: Using monobehaviour as delta inputs?
    3.         // * TODO: Self-delta-mutation doesn't trigger update (ComponentDataFromEntity.cs)
    4.             // /////@TODO: GlobalSystemVersion can't be pulled from m_Entities... indeterministic
    5.         // * TODO: Chained delta works
    6.             // How can this work? Need to use specialized job type because the number of entities to be
    7.             // processed isn't known until running the job... Need some kind of late binding of parallel for length etc...
    8.             // How do we prevent incorrect usage / default...
    9.  
     
  4. dt665m

    dt665m

    Joined:
    Aug 15, 2015
    Posts:
    11

    Yep, no doubt about that. I figured documenting specific behaviour patterns could help track things down.