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.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Feedback Rename/improve the API for EntityCommandBuffer.SetBuffer

Discussion in 'Entity Component System' started by Sarkahn, Mar 21, 2020.

  1. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    440
    So I only recently found out we can directly add references to entities created via an EntityCommandBuffer to Dynamic Buffers. When I first tried it I would do this:

    Code (CSharp):
    1.            Entities
    2.                 .ForEach((int entityInQueryIndex, Entity e, ref DynamicBuffer<LinkedEntityGroup> group) =>
    3.             {
    4.                 var newA = ecb.CreateEntity(entityInQueryIndex, arch);
    5.                 group.Add(newA);
    6.             }).ScheduleParallel();
    This just stores an invalid reference in the dynamic buffer. So I created a weird workaround where I had a separate system add the reference on the next frame, and it was just a mess. I only today learned from helpful folks on the discord that what you actually have to do is this:

    Code (CSharp):
    1.  
    2.             Entities
    3.                 .ForEach((int entityInQueryIndex, Entity e) =>
    4.             {
    5.                 var newA = ecb.CreateEntity(entityInQueryIndex, arch);
    6.                 var buffer = ecb.SetBuffer<LinkedEntityGroup>(entityInQueryIndex, e);
    7.                 buffer.Add(newA);
    8.             }).ScheduleParallel();
    So you can't actually use the dynamic buffer that you pass into the lambda if you're adding a newly created entity reference to it. Not intuitive at all. I understand there's a good reason for this but it's just a bit confusing. The API naming is strange too - you're calling "SetBuffer" but you're getting back a buffer to operate on.

    I'm not sure exactly how to improve it - I think just renaming "SetBuffer" to something like "GetPlaybackBuffer" or something would help a bit.
     
    NotaNaN and Abbrew like this.
  2. fnuecke

    fnuecke

    Joined:
    Jul 1, 2015
    Posts:
    8
    It is worth noting however, that SetBuffer will cause the existing buffer to be reset (at least from my testing). So if you either keep long-term stuff in a buffer (LinkedEntityGroup anyone?) or have multiple systems writing to the same buffer using the same *EntityCommandBufferSystem (they'll overwrite each other) this will not suffice. Some way of adding to an existing buffer using an entity command buffer would be lovely indeed.
     
    Sarkahn and NotaNaN like this.
  3. BrendonSmuts

    BrendonSmuts

    Joined:
    Jun 12, 2017
    Posts:
    86
    SetBuffer works exactly the same way as SetComponent. If you set a component on a command buffer it will replace the existing version in its entirety. When working SetComponent or SetBuffer if you want to preserve any of the existing data you have to manually copy that data across. When working with a buffer the flow looks like so:

    Call SetBuffer on the command buffer to return a fresh buffer that will override the current one sitting on the entity. Copy the entities from the original buffer to the new one. Add the additional entities that have been newly created through the command buffer. Once the command buffer resolves the new entity buffer will contain all the original entities as well as the remapped versions of the ones you just created.
     
    Last edited: Mar 21, 2020
    Sarkahn likes this.
  4. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    It sounds like the sensible solution is to create the buffer during conversion. So everything is ready to go and the runtime system simply expects the buffer to already exist. Then you can add / remove elements freely at runtime. This is significantly faster.
     
  5. Sarkahn

    Sarkahn

    Joined:
    Jan 9, 2013
    Posts:
    440
    Hey, I appreciate the reply but I think you might have misunderstood the original post. Whether the LinkedEntityGroup buffer is attached to an entity during conversion or at any time else, this code doesn't work:

    Code (CSharp):
    1.            Entities
    2.                 .ForEach((int entityInQueryIndex, Entity e, ref DynamicBuffer<LinkedEntityGroup> group) =>
    3.             {
    4.                 var newA = ecb.CreateEntity(entityInQueryIndex, arch);
    5.                 group.Add(newA);
    6.             }).ScheduleParallel();
    The reference added to the LinkedEntityGroup is invalid, it will point to index -1. What @Brendon_Smuts described will do what I want, but it still seems pretty awkward compared to the snippet above which one would intuitively expect to "just work".
     
  6. fnuecke

    fnuecke

    Joined:
    Jul 1, 2015
    Posts:
    8
    Hmm, that's a cool workaround! But it does involve extra copying around of data. Would be nice if that could be avoided, given that buffers can, depending on the use case, hold a lot of data.
     
    Flipps likes this.
  7. iamarugin

    iamarugin

    Joined:
    Dec 17, 2014
    Posts:
    863
    Had the same problem today
    Unfortinately this workaround would not work if there are two systems, which want to add Entity to dynamic buffer using ecb. We definetely need some way of adding to an existing buffer using an entity command buffer.
     
  8. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,993
    I don't understand the failure case you are describing but this API exists in the latest Entities. ECB has an AppendToBuffer method now.
     
    Sarkahn and Flipps like this.
  9. iamarugin

    iamarugin

    Joined:
    Dec 17, 2014
    Posts:
    863
    I didn't know about this, I am still on 0.9 because 0.10 is too unstable. But this is fantastic news.
     
  10. zhxhome

    zhxhome

    Joined:
    Feb 1, 2019
    Posts:
    5
    As Brendon_Smuts said, If you want to adding new LinkedEntityGroup elems to an existing buffer using an entity command buffer. This is feasible:

    Entities.ForEach((int entityInQueryIndex, Entity entity, in DynamicBuffer<LinkedEntityGroup> linkedEntityGroups) =>
    {
    var newEntity = commandBuffer.CreateEntity(entityInQueryIndex);
    var buffer = commandBuffer.SetBuffer<LinkedEntityGroup>(entityInQueryIndex,entity);
    var curLEG = linkedEntityGroups.ToNativeArray(Allocator.Temp);
    buffer.AddRange(curLEG);
    buffer.Add(newEntity);
    curLEG.Dispose();
    }).ScheduleParallel(Dependency);