Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Question Is the (Entities.ForEach) inside the (SystemBase) capable of handling IEnableableComponent's

Discussion in 'Entity Component System' started by FriedrichNietzsche, Mar 18, 2023.

  1. FriedrichNietzsche

    FriedrichNietzsche

    Joined:
    Mar 18, 2023
    Posts:
    6
    I have this :

    Code (CSharp):
    1. public struct TestTagCo : IComponentData, IEnableableComponent
    2. {
    3. }
    4. ------------
    5. (EntityManager.SetComponentEnabled<TestTagCo>(entity, false);)
    6. ------------
    7. [RequireMatchingQueriesForUpdate]
    8. public partial class TestSystem : SystemBase
    9. {
    10.     protected override void OnUpdate()
    11.     {
    12.         Entities.WithStructuralChanges().ForEach((Entity entity, in TestTagCo testTagCo) =>
    13.         {
    14.  
    15.             Debug.Log("TestSystem is running");
    16.  
    17.            Debug.Log( EntityManager.IsComponentEnabled<TestTagCo>(entity) );
    18.  
    19.         }).Run();
    20.     }
    21. }
    Even after disabling the component (TestTagCo), the system continues to work continuously.
    Is there something that I am missing?
     
  2. Rukhanka

    Rukhanka

    Joined:
    Dec 14, 2022
    Posts:
    89
    Indeed, documentation says that disabled components shoud be excluded from all entity queries, but your example proves the opposite. So, to solve this puzzle we need to go deeper (c).
    Look into code that is generated for our OnUpdate() function. Firstly modify the body a little:
    Code (CSharp):
    1.  
    2. Entities.WithoutBurst().ForEach((Entity entity, in TestTagCo testTagCo) =>
    3. {
    4.     Debug.Log("TestSystem is running");
    5.     Debug.Log( EntityManager.IsComponentEnabled<TestTagCo>(entity));
    6. }).Run();
    7.  
    I replaced WithStructuralChanges with WithoutBurst to try no structural changes version first. We got in result of the codegeneration is:
    Code (CSharp):
    1.  
    2. struct TestSystem_DBD3F09_LambdaJob_0_Job : Unity.Entities.IJobChunk
    3. {
    4. [global::System.Runtime.CompilerServices.CompilerGenerated]
    5. public void Execute(in ArchetypeChunk chunk, int batchIndex, bool useEnabledMask, in Unity.Burst.Intrinsics.v128 chunkEnabledMask)
    6. {
    7. ...
    8.     if (!useEnabledMask)
    9.     {
    10. ...
    11.     }
    12.     else
    13.     {
    14. ...
    15.     }
    16. }
    17. }
    18.  
    I have skipped most of the code to highlight essential parts. We can see several important things here:
    • Entites.ForEach is transformed into IJobChunk job
    • Enableable components correctly handled by IJobChunk.Execute function (useEnabledMask, chunkEnabledMask variables usage)
    Now we will modify our code back as in your example:
    Code (CSharp):
    1.  
    2. Entities.WithStructuralChanges().ForEach((Entity entity, in TestTagCo testTagCo) =>
    3. {
    4.     Debug.Log("TestSystem is running");
    5.     Debug.Log( EntityManager.IsComponentEnabled<TestTagCo>(entity));
    6. }).Run();
    7.  
    And codegen:
    Code (CSharp):
    1.  
    2. struct TestSystem_DBD3F09_LambdaJob_0_Job
    3. {
    4.     public void RunWithStructuralChange(Unity.Entities.EntityQuery query)
    5.     {
    6. ...
    7.         var mask = query.GetEntityQueryMask();
    8.         Unity.Entities.InternalCompilerInterface.UnsafeCreateGatherEntitiesResult(ref query, out var gatherEntitiesResult);
    9. ...
    10.         try
    11.         {
    12.             int entityCount = gatherEntitiesResult.EntityCount;
    13.             for (int entityIndex = 0; entityIndex != entityCount; entityIndex++)
    14.             {
    15.                 var entity = Unity.Entities.InternalCompilerInterface.UnsafeGetEntityFromGatheredEntities(ref gatherEntitiesResult, entityIndex);
    16.                 if (mask.MatchesIgnoreFilter(entity))
    17.                 {
    18. ...
    19.                 }
    20.             }
    21.         }
    22.         finally
    23.         {
    24.             Unity.Entities.InternalCompilerInterface.UnsafeReleaseGatheredEntities(ref query, ref gatherEntitiesResult);
    25.         }
    26.     }
    27. }
    28.  
    Now we have completely different code:
    • It is ordinary struct and not a job
    • Entities loop is very different than what we saw in previous case
    And to solve our puzzle we should look into entites query processing code. Here we have a mask from query.GetEntityQueryMask() and entity filtering by if (mask.MatchesIgnoreFilter(entity)). Documentation for this method says: "
    Returns: True if the entity would be returned by the EntityQuery (ignoring any filtering or enableable components)
    ".

    Mystery solved: the codegeneration with WithStructuralChanges attribute usage will generate a code that does not respect enableable flag in their entity queries and internal loops.
     
  3. FriedrichNietzsche

    FriedrichNietzsche

    Joined:
    Mar 18, 2023
    Posts:
    6
    Thank you for shedding light on this, it's much clearer now.
     
    Rukhanka likes this.
  4. Rukhanka

    Rukhanka

    Joined:
    Dec 14, 2022
    Posts:
    89
    Actually I think this is an incorrect behavior, but this is what we get now. Maybe it will be fixed in future releases.
     
    FriedrichNietzsche likes this.
  5. Rukhanka

    Rukhanka

    Joined:
    Dec 14, 2022
    Posts:
    89
    It is fixed in Entites 1.0.0-pre.65:
    • Entities.WithStructuralChanges().ForEach() now correctly handles enableable components.
     
    FriedrichNietzsche likes this.
  6. FriedrichNietzsche

    FriedrichNietzsche

    Joined:
    Mar 18, 2023
    Posts:
    6
    That's great!