Search Unity

  1. The Unity Pro & Visual Studio Professional Bundle gives you the tools you need to develop faster & collaborate more efficiently. Learn more.
    Dismiss Notice
  2. Improved Prefab workflow (includes Nested Prefabs!), 2D isometric Tilemap and more! Get the 2018.3 Beta now.
    Dismiss Notice
  3. Want more efficiency in your development work? Sign up to receive weekly tech and creative know-how from Unity experts.
    Dismiss Notice
  4. Participate with students all over the world and build projects to teach people. Join now!
    Dismiss Notice
  5. Build games and experiences that can load instantly and without install. Explore the Project Tiny Preview today!
    Dismiss Notice
  6. Improve your Unity skills with a certified instructor in a private, interactive classroom. Watch the overview now.
    Dismiss Notice
  7. Want to see the most recent patch releases? Take a peek at the patch release page.
    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:
    12
    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:
    785
    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 at 5:14 AM
  3. Piefayth

    Piefayth

    Joined:
    Feb 7, 2017
    Posts:
    12
    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!