Search Unity

Anyone got Burst working with EntityCommandBuffer ?

Discussion in 'Burst' started by 5argon, Aug 4, 2018.

  1. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    I have previously trying to burst compile a job which contains ecb.SetSharedComponentData but burst debugger error says that function contains the use of `null` and not allowed to burst compile.

    Then today I use only `SetComponentData` and `AddComponentData`. It then says either you are setting a non existent component data (haven't add yet but try to set) or you cannot add 2 of the same component. (use add again instead of set) The Burst Debugger did not output compilation error this time.

    I don't know how it turns out that way. The function is going haywire and I guess it order the command in the buffer randomly, because it works correctly without [BurstCompile]. (I can turn on and off "Enable Burst Compilation" to reliably produce error and make the error disappear)

    So is this a known limitation that Burst will not work with any kind of Entity Command Buffer? I have tried both IJob/IParallelForJob and EntityCommandBuffer/EntityCommandBuffer.Concurrent , same result.
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    It's a known limitation that burst does not work with EntityCommandBuffer at this stage.

    The standard approach I believe atm is to add your entities to a concurrent native queue then iterate that in a seperate job with the command buffer.

    -edit-

    source: https://forum.unity.com/threads/ent...nent-t-strange-behaviour.535424/#post-3529452

    -edit2-

    Also being sneaky does not seem to work either sadly

    Code (CSharp):
    1. // Does not work
    2. [BurstCompile]
    3. private struct TestJob : IJobParallelFor
    4. {
    5.     [ReadOnly] public EntityArray Entities;
    6.  
    7.     [BurstDiscard] public EntityCommandBuffer.Concurrent CommandBuffer { get; set; }
    8.  
    9.     public void Execute(int index)
    10.     {
    11.         SetComponent(Entities[index], new TestComponent());
    12.     }
    13.  
    14.     [BurstDiscard]
    15.     private void SetComponent<T>(Entity entity, T component) where T : struct, IComponentData
    16.     {
    17.         CommandBuffer.SetComponent(entity, component);
    18.     }
    19. }
     
    Last edited: Aug 5, 2018
    5argon likes this.
  3. recursive

    recursive

    Joined:
    Jul 12, 2012
    Posts:
    669
    The only thing I've found that works on EntityCommandBuffer Burst-wise is DestroyEntity(). It seems like anything that needs ComponentType information (which would cover all the component related calls, and likely tie into create/create with archetype as well) won't work.
     
    5argon likes this.
  4. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,555
    Necro the thread but this might means Create and Add (and by extension Set?) are now working with Burst?
    https://docs.unity3d.com/Packages/com.unity.burst@0.2/changelog/CHANGELOG.html

    Screen Shot 2018-08-12 at 17.37.22.png


    Edit : Never mind it is still not possible. The point of compilation error now changes to the use of TypeManager which is based on statics and so still cannot be Burst compiled.

    Code (CSharp):
    1. /Users/Sargon/Library/Unity/cache/packages/staging-packages.unity.com/com.unity.entities@0.0.12-preview.8/Unity.Entities/Types/TypeManager.cs(86,13): error: Loading from a static field `System.Int32 Unity.Entities.TypeManager/StaticTypeLookup`1<T>::typeIndex` is not supported by burst
    2.  
    3. While processing function `System.Int32 Unity.Entities.TypeManager::GetTypeIndex<T>()`
    4.  
    5. While compiling job: System.Void Unity.Jobs.IJobParallelForExtensions/ParallelForJobStruct`1<NonLineStagingSystem/ShouldDrawJob>::Execute(T&,System.IntPtr,System.IntPtr,Unity.Jobs.LowLevel.Unsafe.JobRanges&,System.Int32)
    6.  
    7. Compiler exception: System.NotSupportedException: Loading from a static field `System.Int32 Unity.Entities.TypeManager/StaticTypeLookup`1<T>::typeIndex` is not supported by burst
    8.   at Burst.Compiler.IL.ILVisitor.NotSupported (Burst.Compiler.IL.Syntax.ILInstruction inst) [0x0016b] in <a2ed9824c1aa48fdb9ed369837529c5f>:0
    9.   at Burst.Compiler.IL.ILVisitor.Ldsfld (Burst.Compiler.IL.Syntax.ILInstruction inst) [0x0006c] in <a2ed9824c1aa48fdb9ed369837529c5f>:0
    10.   at Burst.Compiler.IL.ILVisitor.CompileInternal (Burst.Compiler.IL.Syntax.ILInstruction inst) [0x00453] in <a2ed9824c1aa48fdb9ed369837529c5f>:0
    11.   at Burst.Compiler.IL.ILVerifier.CompileInternal (Burst.Compiler.IL.Syntax.ILInstruction inst) [0x00000] in <a2ed9824c1aa48fdb9ed369837529c5f>:0 /Users/Sargon/Library/Unity/cache/packages/staging-packages.unity.com/com.unity.entities@0.0.12-preview.8/Unity.Entities/Types/TypeManager.cs(85,9): error: The opcode instruction `IL_0013: ldtoken T` is not supported by burst
    12.  
    13. While processing function `System.Int32 Unity.Entities.TypeManager::GetTypeIndex<T>()`
    14.  
    15. ...
    16.  
     
    Last edited: Aug 12, 2018
    Opeth001 and Deleted User like this.
  5. NearAutomata

    NearAutomata

    Joined:
    May 23, 2018
    Posts:
    51
    I can confirm the same cryptic error message that @5argon posted and it took me a few hours to stumble upon this thread which confirmed that ECB isn't working with Burst yet.
     
  6. Tazadar66

    Tazadar66

    Joined:
    Aug 27, 2013
    Posts:
    57
    Do we have news about this ? :)
     
  7. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    Its not supported yet.
     
  8. nttLIVE

    nttLIVE

    Joined:
    Sep 13, 2018
    Posts:
    80
    I'm trying to understand the pattern for using EntityCommandBuffers in job systems, especially with jobs that run in parallel.

    The first problem is that ECBs can't be used in burst compiled jobs. Is it confirmed that it will be supported?

    If so, will concurrent ECBs come in different interfaces? For example if I use a IJobChunk and I pass the chunk index there's a chance that the index 1 will be used twice.

    So far what I'm seeing is the best pattern is to stash every Entity/Component that needs to be created from a job and use an ECB on the main thread to create all the Entities so that BurstCompile isn't blocked. Is this correct?
     
  9. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    must be a unique ID per job (such as the index passed to Execute() in an IJobParallelFor).

    Which mean you pass index of chunk, in your case for example, order form by job and by index per job
    No, in this case you loose performance gains (For Busted Jobs its right partially, for create you can use non bursted parallel job, but if you have small amount of entities\components it's not mater)
     
    Last edited: Oct 16, 2018
  10. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Both .AddComponent and .SetComponent point to the:
    Code (CSharp):
    1. internal void AddEntityComponentCommand<T>(EntityCommandBufferChain* chain, int jobIndex, ECBCommand op, Entity e, T component) where T : struct
    2.         {
    3.             var typeIndex = TypeManager.GetTypeIndex<T>();
    4.             // NOTE: This has to be sizeof not TypeManager.SizeInChunk since we use UnsafeUtility.CopyStructureToPtr
    5.             //       even on zero size components.
    6.             var typeSize = UnsafeUtility.SizeOf<T>();
    7.             var sizeNeeded = Align(sizeof(EntityComponentCommand) + typeSize, 8);
    8.  
    9.             var cmd = (EntityComponentCommand*)Reserve(chain, jobIndex, sizeNeeded);
    10.  
    11.             cmd->Header.Header.CommandType = (int)op;
    12.             cmd->Header.Header.TotalSize = sizeNeeded;
    13.             cmd->Header.Header.SortIndex = chain->m_LastSortIndex;
    14.             cmd->Header.Entity = e;
    15.             cmd->ComponentTypeIndex = typeIndex;
    16.             cmd->ComponentSize = typeSize;
    17.  
    18.             byte* data = (byte*) (cmd + 1);
    19.             UnsafeUtility.CopyStructureToPtr(ref component, data);
    20.             if (RequiresEntityFixUp(data, typeIndex))
    21.             {
    22.                 if (op == ECBCommand.AddComponent)
    23.                     cmd->Header.Header.CommandType = (int) ECBCommand.AddComponentWithEntityFixUp;
    24.                 else if (op == ECBCommand.SetComponent)
    25.                     cmd->Header.Header.CommandType = (int) ECBCommand.SetComponentWithEntityFixUp;
    26.             }
    27.         }
    Which got me wondering, if we could pass the typeIndex manually, will it burst compile in theory? (Cache the type index, pass it as an int inside IComponentData to the job)

    Type index should be persistent, isn't it? If so, please consider making a public version of the Set/AddComponent methods for the EntityCommandBuffer.

    .Instantiate works now with burst. It would be really great to modify components in the same buffer queue.
     
    Last edited: Jan 25, 2019
  11. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    a plain int would obviously not be safe (you can not check if index is correct for T without calling
    GetTypeIndex<T>()
    ).
    i can see some
    ComponentTypeInfo<T>
    struct with index and size that you initialize on the main thread (like ACCT) and you pass to ECB:
    SetComponent<T>(Entity e, T component, ComponentTypeInfo<T> info)
    . then validation would be
    if (info.typeIndex == 0) throw ...
     
  12. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    If the type indexes are assigned once and not changed ever, that would solve issue with checking / fetching types within bursted jobs. Less checks == less ops, less fetches.

    I don't see why they shouldn't be. Floating indexes for less memory consumption? Not worth it.

    So the persistent type indexes would definitely be a win.

    More than that, I'd rather prefer validation upon generation of the index itself, rather than each time entity is created / component data set.

    If the job is messing around with indexes - that's a jobs / programmers problem. I'd rather prefer being able to shoot myself in the foot, rather than being banned from using any kind of a weapon. (Including forks)

    Heck, we're already here in the unsafe code zone. wtf?
     
  13. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    they are assigned once, but dynamically at runtime (i.e. the first time you call GetTypeIndex<T>), this will cause writing to static variables, and it is why it's not burst compatible.

    validation will only be done in debug mode, like all other validations. also remember that you can always pass a defaulted struct of any type, so there is no way in the runtime to prevent invalid data to exist (until it is used)
     
  14. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    If it is pre-allocated, it should not change. Type index is defined in generic static struct as static int.
    I don't think they change once they're created at least once.
    Unless I'm missing something from the source.

    I'd rather manually initialize data that should be manipulated at the start of the application. Something like:
    int typeIndex = TypeManager.GetTypeIndex<T>();

    Then use typeIndex in burst jobs all runtime.

    Sure it's "unsafe", but it'll allow to chain buffer calls inside burst jobs.

    Now that I've thinked about it, they're initialized at least once, because of the access to the GetTypeIndex<T>, so the case when index doesn't exist should apply when using cached value passed via IComponentData to the job.

    Well, unless nothing is passed, but that can be tested easily by asserting on index == 0 || index == -1;


    Also, I haven't found a place where that typeIndex value is written elsewhere than .GetTypeIndex.
     
    Last edited: Jan 25, 2019
  15. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    they don't change but you can't ensure at compile time that the static int is not first accessed in a job. if it is, job will write to the static int. Burst does not support that (and I don't think it will)

    the "problem" right now is that ECB is calling GetTypeIndex<T> that is reading from a non-cons, non-readonly static variable. the compiler cannot deduce safety from only this information
     
  16. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Well, I've just bypassed that by simply feeding the GetTypeIndex from the main thread and modifying the source.
    Problem arises when RequiresEntityFixUp is called, as it is also referencing a static type lookup with the TypeInfo in it.

    This part is what causing it to fail:
    Code (CSharp):
    1. if (RequiresEntityFixUp(data, typeIndex))
    2.             {
    3.                 if (op == ECBCommand.AddComponent)
    4.                     cmd->Header.Header.CommandType = (int) ECBCommand.AddComponentWithEntityFixUp;
    5.                 else if (op == ECBCommand.SetComponent)
    6.                     cmd->Header.Header.CommandType = (int) ECBCommand.SetComponentWithEntityFixUp;
    7.             }
    I wonder what's it for.
     
  17. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Lol. Indeed it burst compiles and works without it. But I'm pretty sure removing it will break something.

    I was able to set the position data for the instantiated entity within burst job.

    Here's what I've came up with:
    EntityCommandBuffer:
    Code (CSharp):
    1. // MODIFIED
    2.         internal void AddEntityComponentCommandViaIndex<T>(EntityCommandBufferChain* chain,
    3.                                                            int jobIndex,
    4.                                                            ECBCommand op,
    5.                                                            Entity e,
    6.                                                            int typeIndex,
    7.                                                            T component) where T : struct
    8. {
    9.      // NOTE: This has to be sizeof not TypeManager.SizeInChunk since we use UnsafeUtility.CopyStructureToPtr
    10.      //       even on zero size components.
    11.      var typeSize = UnsafeUtility.SizeOf<T>();
    12.      var sizeNeeded = Align(sizeof(EntityComponentCommand) + typeSize, 8);
    13.  
    14.      var cmd = (EntityComponentCommand*)Reserve(chain, jobIndex, sizeNeeded);
    15.  
    16.      cmd->Header.Header.CommandType = (int)op;
    17.      cmd->Header.Header.TotalSize = sizeNeeded;
    18.      cmd->Header.Header.SortIndex = chain->m_LastSortIndex;
    19.      cmd->Header.Entity = e;
    20.      cmd->ComponentTypeIndex = typeIndex;
    21.      cmd->ComponentSize = typeSize;
    22.  
    23.      byte* data = (byte*) (cmd + 1);
    24.      UnsafeUtility.CopyStructureToPtr(ref component, data);
    25.      /*if (RequiresEntityFixUp(data, typeIndex))
    26.      {
    27.          if (op == ECBCommand.AddComponent)
    28.             cmd->Header.Header.CommandType = (int) ECBCommand.AddComponentWithEntityFixUp;
    29.            else if (op == ECBCommand.SetComponent)
    30.             cmd->Header.Header.CommandType = (int) ECBCommand.SetComponentWithEntityFixUp;
    31.      }*/
    32. }
    EntityCommandBuffer.Concurrent:
    Code (CSharp):
    1. public void AddComponentViaIndex<T>(int jobIndex, Entity e, int typeIndex, T component) where T : struct, IComponentData {
    2.      CheckWriteAccess();
    3.      var chain = ThreadChain;
    4.      m_Data->AddEntityComponentCommandViaIndex<T>(chain, jobIndex, ECBCommand.AddComponent, e, typeIndex, component);
    5. }
    Code (CSharp):
    1. public void SetComponentViaIndex<T>(int jobIndex, Entity e, int typeIndex, T component) where T : struct, IComponentData {
    2.      CheckWriteAccess();
    3.      var chain = ThreadChain;
    4.      m_Data->AddEntityComponentCommandViaIndex<T>(chain, jobIndex, ECBCommand.SetComponent, e, typeIndex, component);
    5. }
    Job receives the index in OnUpdate of the JobComponentSystem (TypeManager.GetTypeIndex<T>()), via struct prop, then it's possible to do the following:
    Code (CSharp):
    1. private void SetupEntity([ReadOnly] Entity prefab, [ReadOnly] ref SpawnRequestData data) {
    2.      Entity instance = Buffer.Instantiate(_jobIndex, prefab);
    3.      Buffer.SetComponentViaIndex(_jobIndex,
    4.          instance,
    5.          PositionTypeIndex,
    6.              new Position {
    7.                                Value = data.Position
    8.                           });
    9. }

    I guess, it's almost there.
     
    Last edited: Jan 25, 2019
  18. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    About RequiresEntityFixUp it seems it's used to re-align entities' offsets in memory.

    Summoning @Joachim_Ante. Maybe some light can be shed on why it's done this way. And also, ETA on burst Add/SetComponents would be awesome.
     
    Last edited: Jan 25, 2019
  19. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    (and when you accidentally pass (...PositionTypeIndex, new Rotation()), you get a hard crash if you are lucky...)

    that approach should be sanitized by using the struct I mentioned:
    Code (CSharp):
    1. public struct ComponentTypeInfo<T> { internal int typeIndex; internal TypeInfo typeInfo;}
    2.  
    3. // add to TypeManager
    4. public static ComponentTypeInfo<T> GetComponentTypeInfo<T>() => new ComponentTypeInfo<T>{typeIndex = GetTypeIndex<T>(), typeInfo GetTypeInfo<T>()};
    then ECB/Concurrent will be
    AddComponent<T>(int jobIndex, Entity entity, ComponentTypeInfo<T> info, T component)
    , ensuring that the component and it's type info (index + all internal stuff) matches at compile time. you can still pass
    default
    , but then can be checked inside without accessing statics
     
    xVergilx likes this.
  20. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Running code first, safety later. Although I like struct approach.

    Edit: Wait, does that mean we can use TypeInfo for the RequiresEntityFixUp?

    Does that mean it's possible to burst compile AddComponent/SetComponent without side effects?
     
  21. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    I included TypeInfo because you mentioned the lookup in RequiresEntityFixUp, so you can pass that as well (or a pointer to it)

    before I thought only the size was needed
     
    xVergilx likes this.
  22. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    And the answer is nope:
    TypeInfo is not blittable.
     
  23. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    it contains a managed array (EntityOffsets) that it actually needs to calculate RequiresEntityFixUp. maybe you can bypass that via an unsafe pointer (
    unsafe struct Info<T> { [NativeDisableUnsafePointerRestriction] void* ptrToThatArray
    ), assuming that stuff is really readonly after initialization. then you need to do all calculations with unsafe pointer math to get the current behaviour
     
    xVergilx likes this.
  24. Elaixzar2

    Elaixzar2

    Joined:
    Dec 27, 2015
    Posts:
    2
    I know this is an old thread, but is this planned for the future? Not having a way to add/modify components and entities in a burst job is very limiting. It would be invaluable to have a command buffer able to be burst compiled
     
    Last edited: Sep 8, 2019
  25. RoughSpaghetti3211

    RoughSpaghetti3211

    Joined:
    Aug 11, 2015
    Posts:
    1,709
    I’m excited for has to drop , for now it just a //ToDo: burst and wait
     
  26. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    2019.3 at some point supposedly, though it's out of alpha and we haven't heard anything in 2 months.
     
  27. tim_jones

    tim_jones

    Unity Technologies

    Joined:
    May 2, 2019
    Posts:
    287
    We are actively working on supporting `EntityCommandBuffer` in Burst-compiled code - we know it's important. We're aiming for the 2019.3 timeframe (and it will only be compatible with 2019.3+). Keep an eye on the release notes, we'll definitely mention it there when it's ready!
     
    Last edited: Sep 9, 2019
  28. Radu392

    Radu392

    Joined:
    Jan 6, 2016
    Posts:
    210
    ECB is now supported by Burst. I'm curious to hear from anyone who's tried it out. Is it now worth using it when dealing with massive amounts of entities in multiple systems? (20k+) Because it wasn't previously. I'm talking only about the SetComponent method, not add or destroy as I know those trigger structural changes.
     
    Last edited: Nov 27, 2019
    charleshendry likes this.
  29. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    Playback performance the same, cause Playback still not burstable. You just can use Set inside burst job which improve performance of this jobs itself (which wasn't bursted before)
     
  30. Radu392

    Radu392

    Joined:
    Jan 6, 2016
    Posts:
    210
    Ah, that's too bad.
     
  31. Rouddem

    Rouddem

    Joined:
    Dec 15, 2015
    Posts:
    14
    Well.. still not available in 2020.2.2f1 with Entities@0.17.0-preview41 & Burst 1.4.1:(
     
    Last edited: Feb 8, 2021
  32. PublicEnumE

    PublicEnumE

    Joined:
    Feb 3, 2019
    Posts:
    729
    This has been available for over a year now. What is it you’re not finding that you expect to find?
     
    Antypodish likes this.
  33. Rouddem

    Rouddem

    Joined:
    Dec 15, 2015
    Posts:
    14
    Hi PublicEnumE,

    Here is my code.
    It is filling some data, then, if entities are close, set an ID to the entity (having no ID yet) the same as the close entity one.

    Code (CSharp):
    1. using Unity.Collections;
    2. using Unity.Entities;
    3. using Unity.Jobs;
    4. using Unity.Mathematics;
    5. using Unity.Transforms;
    6.  
    7. [UpdateAfter(typeof(MoveSystem))]
    8. public class EatSystem : SystemBase
    9. {
    10.     private EntityQuery m_botPlayerQuery;
    11.     private EndSimulationEntityCommandBufferSystem m_endSimulationEntityCommandBufferSystem;
    12.  
    13.     // Initialize EndSimulationEntityCommandBuffer
    14.     protected override void OnCreate()
    15.     {
    16.         m_endSimulationEntityCommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    17.     }
    18.  
    19.     protected override void OnUpdate()
    20.     {
    21.         int botPlayerCount = m_botPlayerQuery.CalculateEntityCount();
    22.  
    23.         // Create the command buffer, using AsParallelWriter because we use it in a ForEach
    24.         EntityCommandBuffer.ParallelWriter entityCommandBuffer = m_endSimulationEntityCommandBufferSystem.CreateCommandBuffer().AsParallelWriter();
    25.  
    26.         // Initialize arrays for few frames
    27.         NativeArray<Entity> entityArray = new NativeArray<Entity>(botPlayerCount, Allocator.TempJob);
    28.         NativeArray<float3> positionArray = new NativeArray<float3>(botPlayerCount, Allocator.TempJob);
    29.         NativeArray<int> playerIdArray = new NativeArray<int>(botPlayerCount, Allocator.TempJob);
    30.  
    31.         // Fill arrays
    32.         JobHandle dataJobHandle = Entities
    33.             .WithStoreEntityQueryInField(ref m_botPlayerQuery)
    34.             .ForEach((Entity entity, int entityInQueryIndex, in Translation _translation, in BotPlayerData _botPlayerData) =>
    35.             {
    36.                 entityArray[entityInQueryIndex] = entity;
    37.                 positionArray[entityInQueryIndex] = _translation.Value;
    38.                 playerIdArray[entityInQueryIndex] = _botPlayerData.m_playerId;
    39.             })
    40.             .ScheduleParallel(Dependency);
    41.  
    42.         // Iterate on each entities with BotPlayerData (perhaps there is a better way to do that, because we have already fill m_botPlayerQuery ?
    43.         JobHandle eatJobHandle = Entities
    44.             .WithReadOnly(entityArray)
    45.             .WithReadOnly(positionArray)
    46.             .WithReadOnly(playerIdArray)
    47.             .WithAll<BotPlayerData>()
    48.             .ForEach((int entityInQueryIndex) =>
    49.             {
    50.                 // Current index must have a playerId
    51.                 if (playerIdArray[entityInQueryIndex] == -1)
    52.                 {
    53.                     return;
    54.                 }
    55.  
    56.                 // Get current playerId and position
    57.                 int playerId = playerIdArray[entityInQueryIndex];
    58.                 float3 position = positionArray[entityInQueryIndex];
    59.  
    60.                 // Iterate on each entity
    61.                 for (int i = 0; i < botPlayerCount; ++i)
    62.                 {
    63.                     // If the entity has a playerId, skip it
    64.                     if (playerIdArray[i] != -1)
    65.                     {
    66.                         return;
    67.                     }
    68.                    
    69.                     // If the entity is close to the current position
    70.                     if (math.distance(position, positionArray[i]) < 1.5f)
    71.                     {
    72.                         // Change the playerId of this entity
    73.                         Entity entity = entityArray[i];
    74.                         entityCommandBuffer.SetComponent(entityInQueryIndex, entity, new BotPlayerData { m_playerId = playerId });
    75.                         entityCommandBuffer.SetSharedComponent(entityInQueryIndex, entity, new SharedBotPlayerData { m_playerId = playerId });
    76.                     }
    77.                 }
    78.             })
    79.             .ScheduleParallel(dataJobHandle);
    80.         m_endSimulationEntityCommandBufferSystem.AddJobHandleForProducer(eatJobHandle);
    81.  
    82.         Dependency = eatJobHandle;
    83.        
    84.         // Dispose
    85.         JobHandle disposeJobHandle = entityArray.Dispose(Dependency);
    86.         disposeJobHandle = JobHandle.CombineDependencies(disposeJobHandle, positionArray.Dispose(Dependency));
    87.         disposeJobHandle = JobHandle.CombineDependencies(disposeJobHandle, playerIdArray.Dispose(Dependency));
    88.         Dependency = disposeJobHandle;
    89.     }
    90. }
    However, Unity throws this exception:

    Code (CSharp):
    1. {Path_to_my_project}\Library\PackageCache\com.unity.entities@0.17.0-preview.41\Unity.Entities\Types\FastEquality.cs(215,13): Burst error BC1051: Invalid managed type found for the field `EqualFn` of the struct `Unity.Entities.FastEquality.TypeInfo`.: the type `System.Delegate` is a managed type and  is not supported
    2.  
    3.  at Unity.Entities.FastEquality.GetHashCode(void* dataPtr, Unity.Entities.FastEquality.TypeInfo typeInfo) (at {Path_to_my_project}\Library\PackageCache\com.unity.entities@0.17.0-preview.41\Unity.Entities\Types\FastEquality.cs:215)
    4.  at Unity.Entities.FastEquality.GetHashCode(SharedBotPlayerData lhs, Unity.Entities.FastEquality.TypeInfo typeInfo) (at {Path_to_my_project}\Library\PackageCache\com.unity.entities@0.17.0-preview.41\Unity.Entities\Types\FastEquality.cs:205)
    5.  at Unity.Entities.TypeManager.GetHashCode(ref SharedBotPlayerData val) (at {Path_to_my_project}\Library\PackageCache\com.unity.entities@0.17.0-preview.41\Unity.Entities\Types\TypeManager.cs:733)
    6.  at Unity.Entities.EntityCommandBuffer.IsDefaultObject(ref SharedBotPlayerData component, ref int hashCode) (at {Path_to_my_project}\Library\PackageCache\com.unity.entities@0.17.0-preview.41\Unity.Entities\EntityCommandBuffer.cs:1645)
    7.  at Unity.Entities.EntityCommandBuffer.ParallelWriter.SetSharedComponent(Unity.Entities.EntityCommandBuffer.ParallelWriter* this, int sortKey, Unity.Entities.Entity e, SharedBotPlayerData component) (at {Path_to_my_project}\Library\PackageCache\com.unity.entities@0.17.0-preview.41\Unity.Entities\EntityCommandBuffer.cs:2911)
    8.  at EatSystem.<>c__DisplayClass_OnUpdate_LambdaJob1.OriginalLambdaBody(EatSystem.<>c__DisplayClass_OnUpdate_LambdaJob1* this, int entityInQueryIndex) (at {Path_to_my_project}\Assets\Scripts\Systems\EatSystem.cs:75)
    9.  at EatSystem.<>c__DisplayClass_OnUpdate_LambdaJob1.IterateEntities(EatSystem.<>c__DisplayClass_OnUpdate_LambdaJob1* this, ref Unity.Entities.ArchetypeChunk chunk, ref EatSystem.<>c__DisplayClass_OnUpdate_LambdaJob1.LambdaParameterValueProviders.Runtimes runtimes)
    10.  at EatSystem.<>c__DisplayClass_OnUpdate_LambdaJob1.Execute(EatSystem.<>c__DisplayClass_OnUpdate_LambdaJob1* this, Unity.Entities.ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    11.  at Unity.Entities.JobChunkExtensions.JobChunkProducer`1<EatSystem.<>c__DisplayClass_OnUpdate_LambdaJob1>.ExecuteInternal(ref Unity.Entities.JobChunkExtensions.JobChunkWrapper`1<EatSystem.<>c__DisplayClass_OnUpdate_LambdaJob1> jobWrapper, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex) (at {Path_to_my_project}\Library\PackageCache\com.unity.entities@0.17.0-preview.41\Unity.Entities\IJobChunk.cs:386)
    12.  at Unity.Entities.JobChunkExtensions.JobChunkProducer`1<EatSystem.<>c__DisplayClass_OnUpdate_LambdaJob1>.Execute(ref Unity.Entities.JobChunkExtensions.JobChunkWrapper`1<EatSystem.<>c__DisplayClass_OnUpdate_LambdaJob1> jobWrapper, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, ref Unity.Jobs.LowLevel.Unsafe.JobRanges ranges, int jobIndex) (at {Path_to_my_project}\Library\PackageCache\com.unity.entities@0.17.0-preview.41\Unity.Entities\IJobChunk.cs:353)
    13.  
    14.  
    15. While compiling job: System.Void Unity.Entities.JobChunkExtensions/JobChunkProducer`1<EatSystem/<>c__DisplayClass_OnUpdate_LambdaJob1>::Execute(Unity.Entities.JobChunkExtensions/JobChunkWrapper`1<T>&,System.IntPtr,System.IntPtr,Unity.Jobs.LowLevel.Unsafe.JobRanges&,System.Int32)
    16. at {Path_to_my_project}\Library\PackageCache\com.unity.entities@0.17.0-preview.41\Unity.Entities\Types\FastEquality.cs:line 215
    I thought it was due to concurrency, the documentation https://docs.unity3d.com/Packages/com.unity.entities@0.17/manual/entity_command_buffer.html is still talking about a ToConcurrent method but I guess it's deprecated, now it's AsParallelWriter.

    After some researches, I was thinking that post was linked, but I'm probably wrong about it.
     
  34. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,685
    It's have nothing with ECB, you do things which are not allowed with Jobs and Burst. You can't use Shared components inside jobs, it's not supported in general.
    This row not allowed inside job:
    entityCommandBuffer.SetSharedComponent(entityInQueryIndex, entity, new SharedBotPlayerData { m_playerId = playerId });
     
    Rouddem likes this.
  35. Rouddem

    Rouddem

    Joined:
    Dec 15, 2015
    Posts:
    14
    Sorry for the delay and thank you for the reply :)

    Well, that's unfortunate we cannot do this, there is probably a good reason it has not been implemented.
    For now, I store ids into a map, then loop on it outside, like so :
    Code (CSharp):
    1. eatJobHandle.Complete();
    2.  
    3. // Change the playerId of entities
    4. foreach (var kv in botToPlayerMap)
    5. {
    6.    Entity botEntity = entityArray[kv.Key];
    7.    int playerId = kv.Value;
    8.  
    9.    EntityManager.SetComponentData(entityArray[kv.Key], new BotPlayerData { m_playerId = playerId });
    10.    EntityManager.SetSharedComponentData(entityArray[kv.Key], new SharedBotPlayerData { m_playerId = playerId });
    11.    EntityManager.SetSharedComponentData(entityArray[kv.Key], new SharedBotTypeData { m_botType = BotType.Follower });
    12. }
    I don't know if it's the best solution, but it's working for now ;)
     
  36. RoughSpaghetti3211

    RoughSpaghetti3211

    Joined:
    Aug 11, 2015
    Posts:
    1,709
    Did ecb not work for your use case ?
     
  37. Rouddem

    Rouddem

    Joined:
    Dec 15, 2015
    Posts:
    14
    Because I need to change a shared component, as eizenhorn mentionned, it's not possible to do it inside Jobs.
    Even if I have to do it outside a parallel (so in the OnUpdate() method), then I can simply use the EntityManager to do so, no need to create an EntityCommandBuffer.

    Does it answer your question ?
     
  38. Deleted User

    Deleted User

    Guest

    Hi, I just encountered the same exception and stumbled onto this post. The source seems to be that I setsharedcomponent from an ecb in my jobs too but the thing is... It works ?

    I get the exception but in my EntityDebugger, I can see the SharedComponents being set accordingly to how I want them to be (Entities@0.17, Unity2021.1.2)

    So uh, I guess my question is how bad is it to leave it this way ?

    Getting the exception is annoying but reworking the system to setsharedcomponent on the main thread would kind of be a headache (my entities to set are instantiated inside a job)

    What are the actual risks and is there any hope of this being supported in the coming year ?
     
  39. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,271
    The job is probably running without Burst.
     
    xVergilx likes this.
  40. Deleted User

    Deleted User

    Guest

    So Unity just ignores the [BurstCompile] rather than give the usual "Fix all compiler errors" ?
    Weird way to go about it but ok, thanks ^^
     
  41. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,364
    not ok, log a bug