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

I have this :

public struct TestTagCo : IComponentData, IEnableableComponent
{
}
------------
(EntityManager.SetComponentEnabled<TestTagCo>(entity, false);)
------------
[RequireMatchingQueriesForUpdate]
public partial class TestSystem : SystemBase
{
    protected override void OnUpdate()
    {
        Entities.WithStructuralChanges().ForEach((Entity entity, in TestTagCo testTagCo) =>
        {

            Debug.Log("TestSystem is running");

           Debug.Log( EntityManager.IsComponentEnabled<TestTagCo>(entity) );

        }).Run();
    }
}

Even after disabling the component (TestTagCo), the system continues to work continuously.
Is there something that I am missing?

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:

Entities.WithoutBurst().ForEach((Entity entity, in TestTagCo testTagCo) =>
{
    Debug.Log("TestSystem is running");
    Debug.Log( EntityManager.IsComponentEnabled<TestTagCo>(entity));
}).Run();

I replaced WithStructuralChanges with WithoutBurst to try no structural changes version first. We got in result of the codegeneration is:

struct TestSystem_DBD3F09_LambdaJob_0_Job : Unity.Entities.IJobChunk
{
[global::System.Runtime.CompilerServices.CompilerGenerated]
public void Execute(in ArchetypeChunk chunk, int batchIndex, bool useEnabledMask, in Unity.Burst.Intrinsics.v128 chunkEnabledMask)
{
...
    if (!useEnabledMask)
    {
...
    }
    else
    {
...
    }
}
}

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:

Entities.WithStructuralChanges().ForEach((Entity entity, in TestTagCo testTagCo) =>
{
    Debug.Log("TestSystem is running");
    Debug.Log( EntityManager.IsComponentEnabled<TestTagCo>(entity));
}).Run();

And codegen:

struct TestSystem_DBD3F09_LambdaJob_0_Job
{
    public void RunWithStructuralChange(Unity.Entities.EntityQuery query)
    {
...
        var mask = query.GetEntityQueryMask();
        Unity.Entities.InternalCompilerInterface.UnsafeCreateGatherEntitiesResult(ref query, out var gatherEntitiesResult);
...
        try
        {
            int entityCount = gatherEntitiesResult.EntityCount;
            for (int entityIndex = 0; entityIndex != entityCount; entityIndex++)
            {
                var entity = Unity.Entities.InternalCompilerInterface.UnsafeGetEntityFromGatheredEntities(ref gatherEntitiesResult, entityIndex);
                if (mask.MatchesIgnoreFilter(entity))
                {
...
                }
            }
        }
        finally
        {
            Unity.Entities.InternalCompilerInterface.UnsafeReleaseGatheredEntities(ref query, ref gatherEntitiesResult);
        }
    }
}

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)](Method MatchesIgnoreFilter | Entities | 1.0.16)
".

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.

4 Likes

Thank you for shedding light on this, it’s much clearer now.

1 Like

Actually I think this is an incorrect behavior, but this is what we get now. Maybe it will be fixed in future releases.

1 Like

It is fixed in Entites 1.0.0-pre.65:

  • Entities.WithStructuralChanges().ForEach() now correctly handles enableable components.
1 Like

That’s great!