Search Unity

  1. Unity 2019.1 beta is now available.
    Dismiss Notice
  2. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  3. We're looking for insight from anyone who has experience with game testing to help us better Unity. Take our survey here. If chosen to participate you'll be entered into a sweepstake to win an Amazon gift card.
    Dismiss Notice
  4. Want to provide direct feedback to the Unity team? Join the Unity Advisory Panel.
    Dismiss Notice
  5. Unity 2018.3 is now released.
    Dismiss Notice
  6. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice

Does SetFilterChanged work as intended?

Discussion in 'Entity Component System and C# Job system' started by Piefayth, Dec 5, 2018.

  1. Piefayth

    Piefayth

    Joined:
    Feb 7, 2017
    Posts:
    22
    I feel like I'm missing something here, and I swear this used to work as expected. Below code tested on preview 21.

    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Collections;
    3. using UnityEngine;
    4.  
    5. public class Bootstrap : ComponentSystem {
    6.     protected override void OnUpdate() { }
    7.  
    8.     protected override void OnCreateManager() {
    9.         EntityManager entityManager = World.GetOrCreateManager<EntityManager>();
    10.  
    11.         Entity es1 = entityManager.CreateEntity(typeof(Data));
    12.         Entity es2 = entityManager.CreateEntity(typeof(Data));
    13.  
    14.         entityManager.SetComponentData(es1, new Data { value = 1 });
    15.         entityManager.SetComponentData(es2, new Data { value = 2 });
    16.     }
    17. }
    18.  
    19. public class DataChangedSystem : ComponentSystem {
    20.     ComponentGroup group;
    21.  
    22.     protected override void OnUpdate() {
    23.         EntityManager entityManager = World.GetOrCreateManager<EntityManager>();
    24.         group = group ?? entityManager.CreateComponentGroup(typeof(Data));
    25.  
    26.         int numChangedChunks = TestUsingDidAddOrChange();
    27.         int numChangedComponents = TestUsingSetFilterChanged();
    28.  
    29.         Debug.Log("Changed Components: " + numChangedComponents); // always 2?
    30.         Debug.Log("Changed Chunks: " + numChangedChunks); // 1 once, then 0 until next write
    31.     }
    32.  
    33.     int TestUsingSetFilterChanged() {
    34.         group.ResetFilter();
    35.         group.SetFilterChanged(typeof(Data));
    36.  
    37.         ComponentDataArray<Data> datas = group.GetComponentDataArray<Data>();
    38.         return datas.Length;
    39.     }
    40.  
    41.     int TestUsingDidAddOrChange() {
    42.         ArchetypeChunkComponentType<Data> dataType = GetArchetypeChunkComponentType<Data>(true);
    43.         NativeArray<ArchetypeChunk> chunks = group.CreateArchetypeChunkArray(Allocator.TempJob);
    44.  
    45.         int changed = 0;
    46.         for (int i = 0; i < chunks.Length; i++) {
    47.             if (chunks[i].DidAddOrChange(dataType, LastSystemVersion)) {
    48.                 changed++;
    49.             }
    50.         }
    51.  
    52.         chunks.Dispose();
    53.  
    54.         return changed;
    55.     }
    56. }
    57.  
    58. public class DataRandomWriteSystem : ComponentSystem {
    59.     ComponentGroup group;
    60.  
    61.     protected override void OnUpdate() {
    62.         EntityManager entityManager = World.GetOrCreateManager<EntityManager>();
    63.         group = group ?? entityManager.CreateComponentGroup(typeof(Data));
    64.  
    65.         if (UnityEngine.Random.value > 0.5f) {
    66.             WriteToComponentDataArray();
    67.         }
    68.     }
    69.  
    70.     void WriteToComponentDataArray() {
    71.         ComponentDataArray<Data> datas = group.GetComponentDataArray<Data>();
    72.         Data d = datas[0];
    73.         d.value = 100009;
    74.         datas[0] = d;
    75.     }
    76. }
    77.  
    78. struct Data : IComponentData {
    79.    public int value;
    80. }


    Above code bootstraps two entities with "Data" components. It then contains two systems. "DataChangedSystem" checks for changed Data components via SetFilterChanged, then again using chunk iteration and DidAddOrChange. "DataRandomWriteSystem" has a 50/50 chance of modifying an arbitrary data component.

    The CDA filtered with SetFilterChanged *always* returns every component, despite the fact that DidAddOrChange is correctly reporting when a chunk was accessed with write access. What am I overlooking?
     
  2. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    937
    Your code has some problem, resetting a filter and create it again will put a reference version number 0 and it is considered a change to everything. This number updates before OnUpdate. If you do it every frame then it is a changed every frame. You have to set the filter once, then returned length can be 0.

    Also slightly related, I have this bug report https://fogbugz.unity3d.com/default.asp?1105842_sl281r74d97j2eug with this code. cg.CalculateLength() fluctuates between 0 and 1 continuously where I did a write every frame. Debugging the version number reveals that CDA in the job sometimes apply the current global and sometimes not. Chunk's version is trustable.

    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Collections;
    3. using Unity.Jobs;
    4.  
    5. public struct ST : IComponentData
    6. {
    7.     public int i;
    8. }
    9.  
    10. public class One : JobComponentSystem
    11. {
    12.     ComponentGroup cg;
    13.     protected override void OnCreateManager()
    14.     {
    15.         Entity e = EntityManager.CreateEntity();
    16.         EntityManager.AddComponentData(e, new ST { i = 5 });
    17.         cg = GetComponentGroup(typeof(ST));
    18.     }
    19.  
    20.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    21.     {
    22.         Debug.Log($"ONE UPDATE global {GlobalSystemVersion} sys {LastSystemVersion}");
    23.         return new A { cda = cg.GetComponentDataArray<ST>() }.Schedule(inputDeps);
    24.     }
    25.  
    26.     struct A : IJob
    27.     {
    28.         public ComponentDataArray<ST> cda;
    29.         public void Execute()
    30.         {
    31.             var st = cda[0];
    32.             st.i++;
    33.             cda[0] = st;
    34.  
    35.             Debug.Log($"ONE EXECUTE {cda[0].i} -> {cda[0].i + 1}");
    36.         }
    37.     }
    38. }
    39.  
    40. [UpdateAfter(typeof(One))]
    41. public class Two : JobComponentSystem
    42. {
    43.     ComponentGroup cg;
    44.     protected override void OnCreateManager()
    45.     {
    46.         cg = GetComponentGroup(typeof(ST));
    47.         cg.SetFilterChanged(typeof(ST));
    48.     }
    49.  
    50.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    51.     {
    52.         Debug.Log($"TWO UPDATE global {GlobalSystemVersion} sys {LastSystemVersion} len {cg.CalculateLength()}");
    53.         var acct= GetArchetypeChunkComponentType<ST>();
    54.         using ( var aca = cg.CreateArchetypeChunkArray(Allocator.TempJob))
    55.         {
    56.             if(aca[0].DidAddOrChange(acct, LastSystemVersion))
    57.             {
    58.                 Debug.Log($"YES CHANGED");
    59.             }
    60.         }
    61.         return new A(){}.Schedule(inputDeps);
    62.     }
    63.  
    64.     struct A : IJob
    65.     {
    66.         public void Execute()
    67.         {
    68.             Debug.Log($"TWO EXECUTE");
    69.         }
    70.     }
    71. }
     
    Last edited: Dec 6, 2018
  3. Piefayth

    Piefayth

    Joined:
    Feb 7, 2017
    Posts:
    22
    Thanks for taking a look. You are saying make my system like this?

    Code (CSharp):
    1.     protected override void OnUpdate() {
    2.         int numChangedChunks = TestUsingDidAddOrChange();
    3.         int numChangedComponents = TestUsingSetFilterChanged();
    4.         // log stuff
    5.     }
    6.  
    7.     protected override void OnCreateManager() {
    8.         entityManager = World.GetOrCreateManager<EntityManager>();
    9.         group = entityManager.CreateComponentGroup(typeof(Data));
    10.         group.SetFilterChanged(typeof(Data));
    11.     }
    That still gives me the same erroneous behavior. But interesting that you find chunk change detection to be more reliable anyway; I'm just using that for now and it's fine... working great, even. Just seemed weird to me I struggled so much to get SetFilterChanged to work with ComponentDataArray!