Weird behavior with CopyFromComponentDataArray: data doesn't update in debugger & some systems

UPDATE: In my case, this is caused by CopyFromComponentDataArray not updating the chunk’s component type change versions.

Editing CopyComponentArrayToChunks does the trick for me.

[BurstCompile]
unsafe struct CopyComponentArrayToChunks : IJobChunk
{
    [NativeDisableUnsafePtrRestriction] public byte* ComponentData;
    public int TypeIndex;

    public void Execute(ArchetypeChunk chunk, int chunkIndex, int entityOffset)
    {
        var archetype = chunk.Archetype.Archetype;
        var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex);
        var typeOffset = archetype->Offsets[indexInTypeArray];
        var typeSize = archetype->SizeOfs[indexInTypeArray];

        var dst = chunk.m_Chunk->Buffer + typeOffset;
        var src = ComponentData + (entityOffset * typeSize);
        var copySize = typeSize * chunk.Count;

        UnsafeUtility.MemCpy(dst, src, copySize);

        var chunkPtr = chunk.m_Chunk;

        var entityComponentStore = chunkPtr->Archetype->EntityComponentStore;
        var globalSystemVersion = entityComponentStore->GlobalSystemVersion;

        chunkPtr->SetChangeVersion(indexInTypeArray, globalSystemVersion);
    }
}

It does not feel right to modify the package source. But I do think this modification is the correct behavior though. Since CopyFromComponentDataArray is obviously a write operation, it has to update the chunk versions after copying.

===== original post =====

I don’t know if this is a bug or I did something wrong. Consider the following test system:

public struct TestDataComponent : IComponentData
{
    public int Value;
}

public class TestQuerySystem : SystemBase
{
    protected override void OnCreate()
    {
        base.OnCreate();
        EntityManager.CreateEntity(EntityManager.CreateArchetype(typeof(TestDataComponent)));
    }

    protected override void OnUpdate()
    {
        var query = EntityManager.CreateEntityQuery(typeof(TestDataComponent));

        var entities = query.ToEntityArray(Allocator.TempJob);
        var testDataComponents = query.ToComponentDataArray<TestDataComponent>(Allocator.TempJob);

        for (int i = 0; i < testDataComponents.Length; i++)
        {
            testDataComponents[i] = new TestDataComponent { Value = testDataComponents[i].Value + 1 };
            //EntityManager.SetComponentData(entities[i], new TestDataComponent { Value = testDataComponents[i].Value + 1 }); // "traditional" method, works correctly
        }

        query.CopyFromComponentDataArray(testDataComponents);

        // Below is the code to check wether the component values has been updated
        var newQuery = EntityManager.CreateEntityQuery(typeof(TestDataComponent));
        var newComponents = newQuery.ToComponentDataArray<TestDataComponent>(Allocator.TempJob);

        Debug.Log("Component value in array: " + testDataComponents[0].Value);
        Debug.Log("Component value in entity: " + EntityManager.GetComponentData<TestDataComponent>(entities[0]).Value);
        Debug.Log("Component value in new query: " + newComponents[0].Value);

        // All debug output shows the correct updated value

        entities.Dispose();
        newComponents.Dispose();
        testDataComponents.Dispose();
    }
}

It’s a simple system that the increments the value inside TestDataComponent by one every frame. All debug output from my test code shows that the values have been updated correctly.

However the values in Entity Debugger is weird. It’s basically not updating unless I hover my mouse over it.

oldpotablebuffalo

And if I use CopyFromComponentDataArray for Translations, not only the values in the debugger not updating unless I’m hovering my mouse, LocalToWorld does not update unless a structural change happen.

validrepentantcougar

(In the end of the video I did a structural change over the entity)

As you can see WorldRenderBounds also did not update. However all of my systems that read the Translations values directly through query (not through LocalToWorld) got the correct values. Also I want to add that I tried rearranging the execution order of the system that alter Translations using CopyFromComponentDataArray before and after TransformSystemGroup but the issue still persists.

If I use SetComponentData and a loop to set the values of each entities, everything works fine though.

My environment:
Unity 2019.4.3f1
Entities 0.11.1 preview.4
DOTS Editor 0.8.0 preview.1
Burst turned off
JobsDebugger On
LeakDetection Full

Well I think I know why this behavior is happening. It seems that CopyFromComponentDataArray does not change the chunk’s component change version even though the entity query uses ReadWrite for the component types?

How to make a batched set operation that updates the change version then?

I managed to make CopyFromComponentDataArray updates the chunk’s component type change version by modifying
CopyComponentArrayToChunks in Unity.Entities/Iterators/ChunkDataGatherJobs.

[BurstCompile]
unsafe struct CopyComponentArrayToChunks : IJobChunk
{
    [NativeDisableUnsafePtrRestriction] public byte* ComponentData;
    public int TypeIndex;

    public void Execute(ArchetypeChunk chunk, int chunkIndex, int entityOffset)
    {
        var archetype = chunk.Archetype.Archetype;
        var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex);
        var typeOffset = archetype->Offsets[indexInTypeArray];
        var typeSize = archetype->SizeOfs[indexInTypeArray];

        var dst = chunk.m_Chunk->Buffer + typeOffset;
        var src = ComponentData + (entityOffset * typeSize);
        var copySize = typeSize * chunk.Count;

        UnsafeUtility.MemCpy(dst, src, copySize);

        var chunkPtr = chunk.m_Chunk;

        var entityComponentStore = chunkPtr->Archetype->EntityComponentStore;
        var globalSystemVersion = entityComponentStore->GlobalSystemVersion;

        chunkPtr->SetChangeVersion(indexInTypeArray, globalSystemVersion);
    }
}

This modification fixes all of my issues above. I think this is how CopyFromComponentDataArray should behave since it’s obviously a write operation.