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. Dismiss Notice

Can't use EntityCommandBuffer inside Entity.ForEach

Discussion in 'Entity Component System' started by herkip, Mar 27, 2022.

  1. herkip

    herkip

    Joined:
    Dec 21, 2013
    Posts:
    30
    Hi

    When trying to create an entity or instantiate inside ForEach. It will give me the error.

    Code (CSharp):
    1. Assets\Scripts\Systems\Render.cs(140,13): error DC0004: Entities.ForEach Lambda expression captures a non-value type 'this'. This is only allowed with .WithoutBurst() and .Run()
    2.  
    Pseudocode below.

    Code (CSharp):
    1. public partial class Render : SystemBase
    2.     {
    3.         EndSimulationEntityCommandBufferSystem m_EndSimulationEcbSystem;
    4.        
    5.         protected override void OnCreate()
    6.         {      
    7.             m_EndSimulationEcbSystem = World
    8.                 .GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    9.         }
    10.  
    11.         protected override void OnUpdate()
    12.         {
    13.             CameraTransform tempCameraTransform = default;
    14.             EntityCommandBuffer ecb = m_EndSimulationEcbSystem.CreateCommandBuffer();
    15.            
    16.             Entities
    17.                 .ForEach((Entity entity,
    18.                     ref Translation translation,
    19.                     ref Scale scale,
    20.                     in LinkWithBackend linkWithBackend,
    21.                     in Parent parent) =>
    22.                 {
    23.                     ecb.CreateEntity(); // no allowed anymore
    24.                 }).Run();
    25.            
    26.         }
    27.     }
    Thank you
     
  2. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,574
    You doing thing in job in parallel. You need pass job index into ecb parallel writer.

    Type ecb. in visual studio (with dot) and see intellisense suggestion for parallel writer. Your job for each also need index. Look forum and repos for parallel writing into ecb.
     
  3. herkip

    herkip

    Joined:
    Dec 21, 2013
    Posts:
    30
    I thought that when I use .Run() it will run in the main thread so it should work without parraler.

    And when I try to run it Schedule or ScheduleParraler I still run into this issue. Both these codes were working before.

    Code (CSharp):
    1. EntityCommandBuffer.ParallelWriter ecb = m_EndSimulationEcbSystem.CreateCommandBuffer().AsParallelWriter();
    2.  
    3. Entities
    4.         .WithAll<ZoomLevelOne>()
    5.         .ForEach((int entityInQueryIndex,
    6.             Entity entity,
    7.             ref Translation translation,
    8.             ref Scale scale,
    9.             in LinkWithBackend linkWithBackend) =>
    10.         {
    11.             var frontEndEntity = ecb.Instantiate(entityInQueryIndex, _ring);
    12.  
    13.         }).ScheduleParallel();
    14.  
    15.         or
    16.  
    17.         .Schedule()
     
  4. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,574
    Sorry, yes you are right. I missed you used Run.

    I'm that case, your problem are refs and ins. You can not have them "floating". You need read o write values from/to them inside a foreah job. Or remove them from for each query, if not needed.
    Or use. WithAll,. WithAny,. WithNone. And use corresponding components, if you require them in a query.
     
    herkip likes this.
  5. herkip

    herkip

    Joined:
    Dec 21, 2013
    Posts:
    30
    Sry this was minified code. I will add the full code here. Those components are used inside ForEach.

    And Methods that are called inside ForEach are static. If I remove ecb code it will work fine or if I add WithoutBurst().




    Code (CSharp):
    1. CameraTransform tempCameraTransform = default;
    2.             EntityCommandBuffer.ParallelWriter ecb = m_EndSimulationEcbSystem.CreateCommandBuffer().AsParallelWriter();
    3.          
    4.             Entities
    5.                 .ForEach((Entity entity, in CameraTransform cameraTransform) =>
    6.                 {
    7.                     tempCameraTransform = cameraTransform;
    8.                 }).Run();
    9.          
    10.             var backendEntities = _backendEntities;
    11.             Entities
    12.                 .WithAll<ZoomLevelOne>()
    13.                 .ForEach((int entityInQueryIndex,
    14.                     Entity entity,
    15.                     ref Translation translation,
    16.                     ref Scale scale,
    17.                     in LinkWithBackend linkWithBackend) =>
    18.                 {
    19.                     if(backendEntities.ContainsKey(linkWithBackend.BackendEntity))
    20.                     {
    21.                         var gameTranslation = backendEntities[linkWithBackend.BackendEntity].GameTranslation;
    22.                         var gameScale = backendEntities[linkWithBackend.BackendEntity].GameScale;
    23.                         var innerTranslation = InnerTranslation(gameTranslation, tempCameraTransform);
    24.                      
    25.                         var computedScale = ComputeScale(gameScale, tempCameraTransform);
    26.  
    27.                         if (computedScale < 1f)
    28.                         {
    29.                             // Destroy Entity
    30.                             ecb.DestroyEntity(entityInQueryIndex, entity);
    31.  
    32.                             var frontEndEntity = ecb.Instantiate(entityInQueryIndex, _ring);
    33.                             ecb.AddComponent<ZoomLevelTwo>(entityInQueryIndex, frontEndEntity);
    34.                             ecb.SetComponent(entityInQueryIndex, frontEndEntity, new Translation()
    35.                             {
    36.                                 Value = InnerTranslation(backendEntities[linkWithBackend.BackendEntity].GameTranslation, tempCameraTransform)
    37.                             });
    38.                             ecb.AddComponent(entityInQueryIndex, frontEndEntity, typeof(LocalToWorld));
    39.                          
    40.                             ecb.AddComponent(entityInQueryIndex, frontEndEntity, new Scale
    41.                             {
    42.                                 Value = 1
    43.                             });
    44.                          
    45.                             ecb.AddComponent(entityInQueryIndex, frontEndEntity, linkWithBackend);
    46.                         }
    47.                      
    48.                         if(computedScale > 1)
    49.                         {
    50.                             scale.Value = gameScale.Scale / tempCameraTransform.OneUnitInMeters;
    51.                         }
    52.                      
    53.                         translation.Value = innerTranslation;
    54.                     }
    55.                 }).ScheduleParallel();
     
  6. CookieStealer2

    CookieStealer2

    Joined:
    Jun 25, 2018
    Posts:
    119
    Maybe its a bug? Only weird thing I noticed is on line 38
    Not sure burst can handle that since its a class. Try this instead:
    Code (CSharp):
    1. ecb.AddComponent<LocalToWorld>(entityInQueryIndex, frontEndEntity)
     
    herkip likes this.
  7. herkip

    herkip

    Joined:
    Dec 21, 2013
    Posts:
    30
    No dice with that change. Thanks for the suggestion.
     
  8. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,574
    Try assign ecb.CreateEntity() to variable from your first example.
    Like for example
    Entity entity = ecb.CreateEntity();
    I Don think that will help, but worth a shot.
     
    herkip likes this.
  9. herkip

    herkip

    Joined:
    Dec 21, 2013
    Posts:
    30
    Thanks, for helping guys. I did figure it out.
    There were two issues. Somehow I messed up the unity package and had to restore it and another issue was (_ring) variable that was not assigned to the local scope.

    The error message is quite bad for finding those issues.
     
    Antypodish likes this.
  10. kibbles-n-crits

    kibbles-n-crits

    Joined:
    Apr 22, 2016
    Posts:
    59
    Hello,
    Sorry to piggyback of this thread, but I'm having the same issue. And as noted the error message leaves you out to dry.

    I always run into this error:
    Assets\Scripts\Atmosphere\AtmosphereSystem.cs(23,9): error DC0004: Entities.ForEach Lambda expression captures a non-value type 'this'. This is only allowed with .WithoutBurst() and .Run()

    And I have no idea what is causing it and what to do about it. Would you be so kind as to help? Thank you!

    Code (CSharp):
    1. [BurstCompile]
    2. public partial class AtmosphereSystem : SystemBase
    3. {
    4.     EntityQuery atmoQuery;
    5.     [ReadOnly] NativeArray<Entity> allVoxelEntities;
    6.     [ReadOnly] ComponentDataFromEntity<AtmosphereVoxel> allAtmoVoxel;
    7.     [ReadOnly] BufferFromEntity<IntElement> allElementBuffer;
    8.  
    9.     protected override void OnUpdate() {
    10.         NativeList<AtmoNeeds> needs = new NativeList<AtmoNeeds>(Allocator.TempJob);
    11.  
    12.         allVoxelEntities = atmoQuery.ToEntityArray(Allocator.TempJob);
    13.         allAtmoVoxel = GetComponentDataFromEntity<AtmosphereVoxel>();
    14.         allElementBuffer = GetBufferFromEntity<IntElement>();
    15.  
    16.         Entities.ForEach((AtmosphereVoxel atmo, DynamicBuffer<IntElement> dynamicBuffer) => {
    17.             for (int i = 0; i < dynamicBuffer.Length; i++ ) {
    18.         //find matching voxel
    19.                 for(int x = 0; x < allVoxelEntities.Length; x++) {
    20.                     Entity curEntity = allVoxelEntities[x];
    21.                     AtmosphereVoxel n_atmo = allAtmoVoxel[curEntity];
    22.                     DynamicBuffer<IntElement> n_dynamicBuffer = allElementBuffer[curEntity];
    23.                     //do stuff here...
    24.                 }
    25.                
    26.                 Debug.Log($"need {needs.Length}");
    27.             }
    28.         }).Schedule();
    29.  
    30.         needs.Dispose();
    31.     }
     
  11. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    3,983
    Lambdas can only capture local variables to work with Burst, not member variables.
     
  12. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,574
    As DreamingLation mentioned, define
    allVoxelEntities, allAtmoVoxel, allElementBuffer inside OnUpdate.

    Also,
    Entities.ForEach((AtmosphereVoxel atmo, DynamicBuffer<IntElement> dynamicBuffer)
    Dynamic Buffer should be first.
    And use IN and REF keywords, for read, read / write.
     
  13. kibbles-n-crits

    kibbles-n-crits

    Joined:
    Apr 22, 2016
    Posts:
    59
    Thank you for the prompt reply!

    How do I know the order in which Entities.Foreach arguments should appear in?

    I am getting a two new error concerning the disposal of native arrays:
    A Native Collection has not been disposed, resulting in a memory leak. Allocated from:
    AtmosphereSystem:OnUpdate() (at Assets\Scripts\Atmosphere\AtmosphereSystem.cs:25)


    Code (CSharp):
    1. protected override void OnUpdate() {
    2.         NativeList<AtmoNeeds> needs = new NativeList<AtmoNeeds>(Allocator.Temp);
    3.  
    4.         NativeArray<Entity> allVoxelEntities = atmoQuery.ToEntityArray(Allocator.TempJob);
    5.         ComponentDataFromEntity<AtmosphereVoxel> allAtmoVoxel = GetComponentDataFromEntity<AtmosphereVoxel>();
    6.         BufferFromEntity<IntElement> allElementBuffer =  GetBufferFromEntity<IntElement>();
    7.  
    8.         //setup neighbor index
    9.         NativeArray<Entity> neighbors = new NativeList<Entity>(6, Allocator.Temp);      
    10.  
    11.         Entities.ForEach((in DynamicBuffer<IntElement> dynamicBuffer, in AtmosphereVoxel atmo) => {
    12.             //do stuff
    13.         }).Schedule();
    14.  
    15.         allVoxelEntities.Dispose();
    16.         neighbors.Dispose();
    17.         needs.Dispose();
    18.     }
    I call .dispose() at the end of the OnUpdate, where should I be putting them instead? I tried inside the foreach before .schedule but no difference.
    Is using Allocator.TempJob correct? It complained if I used Allocator.Temp in a diffrent error.

    The second error is:
    InvalidOperationException: The writeable ComponentDataFromEntity<AtmosphereVoxel> AtmosphereSystem_LambdaJob_0_Job.JobData.allAtmoVoxel is the same ComponentTypeHandle<AtmosphereVoxel> as AtmosphereSystem_LambdaJob_0_Job.JobData.__atmoTypeHandle, two containers may not be the same (aliasing).
    AtmosphereSystem.OnUpdate () (at Assets/Scripts/Atmosphere/AtmosphereSystem.cs:28)

    I think the error says I maybe accessing the same set of data? But not sure what it actually means and what to do about it.

    Thank you for your help!
     
    Last edited: May 5, 2022
  14. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,574
    Lime 5 and 11 conflicts wih each other. You can not have two same components in for each, in such form.

    You can also avoid using, by using Get component inside for each. It is special method for for each lambda.

    Also, if you use outside get components, mark them read only, or read write, with false, or true bool.

    Native colectioms created outside entities for each, need to be disposed. Unless are persistent. Then you dispose them at game end/close.

    You better look into unity samples on git. You ot tons of issues here with a code. Follow samples and create your systems.