EntityManager operations not allowed in ForEach when looping DynamicBuffer

The 0.1.0 patch notes mentioned:

However, when EntityManager is used inside a DynamicBuffer foreach, I get the following error:

This is the code to reproduce:

using Unity.Entities;

public struct Tag : IComponentData
{
}

[InternalBufferCapacity(4)]
public struct BufferData : IBufferElementData
{
    public float Value;
}

public class TestSystem : ComponentSystem
{
    protected override void OnUpdate()
    {
        Entities.ForEach((Entity entity, DynamicBuffer<BufferData> buffer) =>
        {
            foreach (BufferData bufferData in buffer)
            {
                if (!EntityManager.HasComponent<Tag>(entity))
                {
                    EntityManager.AddComponentData(entity, new Tag());
                }
            }
        });
    }
}

The DynamicBuffer is filled with 4 values at entity conversion time. Is this error by design or a bug?

Tested your code and can confirm. After the EntityManager.AddComponentData, buffer will be deallocated. Should be a bug.

For the time being, you’ll have to keep doing EntityManager.GetBuffer<BufferData>(entity) after every structural change.

Can you update with bug report if you file one. I would love to track this

I sent a bug report a couple hours ago: https://fogbugz.unity3d.com/default.asp?1185212_umgejlaka8ub07ua

Pretty sure this is by design.

DynamicBuffer is a pointer to an array (usually stored inline in the chunk). When you add the Tag component you move the entity and its components (including the array) to another archetype/chunk, but the pointer still points to the old location, so it is correctly recognized as invalid.
Could use a better error message I guess.

1 Like

But isn’t it the same with all EntityManager operations inside a ForEach? To me it’s weird that the new version handles only some of the chunk-altering methods.

The new (post-0.1.0) ForEach is more or less:

var query = PrepareQuery();
var entities = query.ToEntityArray();
foreach (var entity in entities) {
    var copy1 = GetComponent<SecondLambdaArg>(entity);
    lambda(entity, ref copy1);
    if (HasComponent<SecondLambdaArg>(entity)){
        SetComponent(entity, copy1);
    }
}

Entities are stable across structural changes and all their components are copied locally in the ForEach to make the ref stable.

1 Like

Okay that makes sense. For perf reasons, probably not a good idea to also copy buffers around.

I had the same issue and got that error today.

JPrzemieniecki is right, it’s an error by design.

You can copy the buffer to a NativeArray using buffer.ToNativeArray and keep your code design using the EntityManager inside the loop without errors.

Thanks everyone, this makes sense!