Search Unity

EntityManager operations not allowed in ForEach when looping DynamicBuffer

Discussion in 'Entity Component System' started by jeremies_unity, Sep 18, 2019.

  1. jeremies_unity

    jeremies_unity

    Joined:
    Oct 16, 2018
    Posts:
    26
    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:
    Code (CSharp):
    1. using Unity.Entities;
    2.  
    3. public struct Tag : IComponentData
    4. {
    5. }
    6.  
    7. [InternalBufferCapacity(4)]
    8. public struct BufferData : IBufferElementData
    9. {
    10.     public float Value;
    11. }
    12.  
    13. public class TestSystem : ComponentSystem
    14. {
    15.     protected override void OnUpdate()
    16.     {
    17.         Entities.ForEach((Entity entity, DynamicBuffer<BufferData> buffer) =>
    18.         {
    19.             foreach (BufferData bufferData in buffer)
    20.             {
    21.                 if (!EntityManager.HasComponent<Tag>(entity))
    22.                 {
    23.                     EntityManager.AddComponentData(entity, new Tag());
    24.                 }
    25.             }
    26.         });
    27.     }
    28. }
    The DynamicBuffer is filled with 4 values at entity conversion time. Is this error by design or a bug?
     
  2. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    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.
     
  3. RoughSpaghetti3211

    RoughSpaghetti3211

    Joined:
    Aug 11, 2015
    Posts:
    1,705
    Can you update with bug report if you file one. I would love to track this
     
  4. jeremies_unity

    jeremies_unity

    Joined:
    Oct 16, 2018
    Posts:
    26
  5. JPrzemieniecki

    JPrzemieniecki

    Joined:
    Feb 7, 2013
    Posts:
    33
    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.
     
    tarahugger likes this.
  6. jeremies_unity

    jeremies_unity

    Joined:
    Oct 16, 2018
    Posts:
    26
    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.
     
  7. JPrzemieniecki

    JPrzemieniecki

    Joined:
    Feb 7, 2013
    Posts:
    33
    The new (post-0.1.0) ForEach is more or less:

    Code (CSharp):
    1. var query = PrepareQuery();
    2. var entities = query.ToEntityArray();
    3. foreach (var entity in entities) {
    4.     var copy1 = GetComponent<SecondLambdaArg>(entity);
    5.     lambda(entity, ref copy1);
    6.     if (HasComponent<SecondLambdaArg>(entity)){
    7.         SetComponent(entity, copy1);
    8.     }
    9. }
    Entities are stable across structural changes and all their components are copied locally in the ForEach to make the ref stable.
     
    jeremies_unity likes this.
  8. Singtaa

    Singtaa

    Joined:
    Dec 14, 2010
    Posts:
    492
    Okay that makes sense. For perf reasons, probably not a good idea to also copy buffers around.
     
  9. farpini

    farpini

    Joined:
    Aug 9, 2018
    Posts:
    7
    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.
     
  10. jeremies_unity

    jeremies_unity

    Joined:
    Oct 16, 2018
    Posts:
    26
    Thanks everyone, this makes sense!