Search Unity

CommandBuffer, instantiate and destroy entities in job

Discussion in 'Entity Component System' started by Init33, Mar 19, 2019.

  1. Init33

    Init33

    Joined:
    Aug 30, 2017
    Posts:
    67
    Hi there,

    Once again I am stuck with another problem concerning instantiating and destroying entities from jobs.

    I have the following system:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Entities;
    5. using Unity.Transforms;
    6. using Unity.Mathematics;
    7. using Unity.Rendering;
    8. using Unity.Collections;
    9. using Unity.Jobs;
    10.  
    11. [UpdateAfter(typeof(TileSelectionSystem))]
    12. [UpdateAfter(typeof(MapUpdaterSystem))]
    13. public class GridDisplaySystem : JobComponentSystem
    14. {
    15.     EndSimulationEntityCommandBufferSystem m_EntityCommandBufferSystem;
    16.  
    17.     ComponentGroup m_selectedTile;
    18.     ComponentGroup m_tileMapGroup;
    19.     ComponentGroup m_highlightSpawner;
    20.  
    21.     public struct GridHighlightSpawn : IJobProcessComponentData<Tile>
    22.     {
    23.         [ReadOnly] public EntityCommandBuffer CommandBuffer;
    24.         [ReadOnly] public float3 selectedCoord;
    25.         [ReadOnly] public float gridHighlightDisplayDistance;
    26.         [ReadOnly, DeallocateOnJobCompletion] public NativeArray<GridHighlightSpawner> tilePreviewSpawner;
    27.  
    28.         public void Execute(ref Tile t)
    29.         {
    30.             if ((math.distance(t.tileCoord, selectedCoord) < gridHighlightDisplayDistance) && (t.isValid == 1))
    31.             {
    32.                 if (t.highlighted == 0)
    33.                 {
    34.                     Entity instance = CommandBuffer.Instantiate(tilePreviewSpawner[0].Prefab);
    35.                     Translation tr = new Translation { Value = new float3(t.tileCoord) };
    36.                     CommandBuffer.SetComponent(instance, tr);
    37.                     t.highlighted = 1;
    38.                 }    
    39.             } else
    40.             {
    41.                 t.highlighted = 0;
    42.             }
    43.  
    44.         }
    45.     }
    46.  
    47.     public struct GridHighlightDestroy : IJobProcessComponentDataWithEntity<GridTileHighlight, Translation>
    48.     {
    49.         [ReadOnly] public EntityCommandBuffer CommandBuffer;
    50.         [ReadOnly] public float3 selectedCoord;
    51.         [ReadOnly] public float gridHighlightDisplayDistance;
    52.  
    53.         public void Execute(Entity entity, int Index, ref GridTileHighlight g, ref Translation t)
    54.         {
    55.             if (math.distance(t.Value, selectedCoord) >= gridHighlightDisplayDistance)
    56.             {
    57.                 CommandBuffer.DestroyEntity(entity);
    58.             }
    59.         }
    60.     }
    61.  
    62.  
    63.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    64.     {      
    65.         var input = m_selectedTile.ToComponentDataArray<SelectedTile>(Allocator.TempJob);  
    66.         var tiles = m_tileMapGroup.ToComponentDataArray<Tile>(Allocator.TempJob);
    67.         var spawner = m_highlightSpawner.ToComponentDataArray<GridHighlightSpawner>(Allocator.TempJob);
    68.         int indx = input[0].selectedIndex;
    69.         float3 coord = tiles[indx].tileCoord;
    70.  
    71.         input.Dispose();
    72.         tiles.Dispose();
    73.  
    74.         var gridSpawnJob = new GridHighlightSpawn
    75.         {
    76.             CommandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer(),
    77.             selectedCoord = coord,
    78.             gridHighlightDisplayDistance = Settings.Instance.gridHighlightDisplayDistance,
    79.             tilePreviewSpawner = spawner,
    80.  
    81.         }.Schedule(this, inputDeps);
    82.  
    83.         var gridDestroyJob = new GridHighlightDestroy
    84.         {
    85.             CommandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer(),
    86.             selectedCoord = coord,
    87.             gridHighlightDisplayDistance = Settings.Instance.gridHighlightDisplayDistance,
    88.         }.Schedule(this, gridSpawnJob);
    89.  
    90.  
    91.         m_EntityCommandBufferSystem.AddJobHandleForProducer(gridSpawnJob);
    92.         m_EntityCommandBufferSystem.AddJobHandleForProducer(gridDestroyJob);
    93.  
    94.         return gridDestroyJob;
    95.     }
    96.  
    97.  
    98.     protected override void OnCreateManager()
    99.     {
    100.         m_selectedTile = GetComponentGroup(typeof(SelectedTile));
    101.         m_tileMapGroup = GetComponentGroup(typeof(Tile));
    102.         m_highlightSpawner = GetComponentGroup(typeof(GridHighlightSpawner));
    103.         m_EntityCommandBufferSystem = World.GetOrCreateManager<EndSimulationEntityCommandBufferSystem>();
    104.     }
    105. }
    106.  

    It has two jobs:
    1. check distance from point and spawn highlight entities in the desired locations
    2. get existing highlight entities, do a distance check and destroy any that fail

    Something which in my head should be simple is continuously proving to be difficult for me to do properly.

    I get numerous errors such as:
    ArgumentException: All entities passed to EntityManager must exist. One of the entities has already been destroyed or was never created.
    EntityCommandBuffer was recorded in GridDisplaySystem and played back in Unity.Entities.EndSimulationEntityCommandBufferSystem.
    at Unity.Entities.EntityDataManager.AssertEntitiesExist (Unity.Entities.Entity* entities, System.Int32 count) [0x00081] in D:\Unity Projects\RTS_SciFi\Library\PackageCache\com.unity.entities@0.0.12-preview.26\Unity.Entities\EntityDataManager.cs:341

    at Unity.Entities.EntityManager.DestroyEntityInternal (Unity.Entities.Entity* entities, System.Int32 count) [0x00008] in D:\Unity Projects\RTS_SciFi\Library\PackageCache\com.unity.entities@0.0.12-preview.26\Unity.Entities\EntityManager.cs:419

    at Unity.Entities.EntityManager.DestroyEntity (Unity.Entities.Entity entity) [0x00001] in D:\Unity Projects\RTS_SciFi\Library\PackageCache\com.unity.entities@0.0.12-preview.26\Unity.Entities\EntityManager.cs:413

    at Unity.Entities.EntityCommandBuffer.PlaybackChain (Unity.Entities.EntityManager mgr, Unity.Entities.ECBSharedPlaybackState& playbackState, Unity.Collections.NativeArray`1[T] chainStates, System.Int32 currentChain, System.Int32 nextChain) [0x00121] in D:\Unity Projects\RTS_SciFi\Library\PackageCache\com.unity.entities@0.0.12-preview.26\Unity.Entities\EntityCommandBuffer.cs:1041

    at Unity.Entities.EntityCommandBuffer.Playback (Unity.Entities.EntityManager mgr) [0x0025d] in D:\Unity Projects\RTS_SciFi\Library\PackageCache\com.unity.entities@0.0.12-preview.26\Unity.Entities\EntityCommandBuffer.cs:943

    at Unity.Entities.EntityCommandBufferSystem.FlushBuffers (System.Boolean playBack) [0x00048] in D:\Unity Projects\RTS_SciFi\Library\PackageCache\com.unity.entities@0.0.12-preview.26\Unity.Entities\ComponentSystem.cs:799
    Unity.Entities.EntityCommandBufferSystem.FlushBuffers (System.Boolean playBack) (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystem.cs:851)
    Unity.Entities.EntityCommandBufferSystem.OnUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystem.cs:773)
    Unity.Entities.ComponentSystem.InternalUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystem.cs:479)
    Unity.Entities.ScriptBehaviourManager.Update () (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ScriptBehaviourManager.cs:97)
    Unity.Entities.ComponentSystemGroup.OnUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystemGroup.cs:264)
    Unity.Entities.ComponentSystem.InternalUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystem.cs:479)
    Unity.Entities.ScriptBehaviourManager.Update () (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ScriptBehaviourManager.cs:97)
    Unity.Entities.ScriptBehaviourUpdateOrder+DummyDelegateWrapper.TriggerUpdate () (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ScriptBehaviourUpdateOrder.cs:132)

    NullReferenceException: Object reference not set to an instance of an object
    Unity.Entities.EntityCommandBufferData.AddEntityCommand (Unity.Entities.EntityCommandBufferChain* chain, System.Int32 jobIndex, Unity.Entities.ECBCommand op, System.Int32 index, Unity.Entities.Entity e, System.Boolean batchable) (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/EntityCommandBuffer.cs:330)
    Unity.Entities.EntityCommandBuffer.Instantiate (Unity.Entities.Entity e) (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/EntityCommandBuffer.cs:728)
    GridDisplaySystem+GridHighlightSpawn.Execute (Tile& t) (at Assets/Scripts/ECS/Systems/GridDisplaySystem.cs:34)
    Unity.Entities.JobProcessComponentDataExtensions+JobStruct_Process_D`2[T,U0].ExecuteChunk (Unity.Entities.JobProcessComponentDataExtensions+JobStruct_Process_D`2[T,U0]& jobData, System.IntPtr bufferRangePatchData, System.Int32 begin, System.Int32 end) (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/IJobProcessComponentData.generated.cs:408)
    Unity.Entities.JobProcessComponentDataExtensions+JobStruct_Process_D`2[T,U0].Execute (Unity.Entities.JobProcessComponentDataExtensions+JobStruct_Process_D`2[T,U0]& jobData, System.IntPtr additionalPtr, System.IntPtr bufferRangePatchData, Unity.Jobs.LowLevel.Unsafe.JobRanges& ranges, System.Int32 jobIndex) (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/IJobProcessComponentData.generated.cs:374)

    Assertion failure. Values are not equal.

    Expected: 1 == 2
    UnityEngine.Assertions.Assert:AreEqual(Int32, Int32)
    Unity.Assertions.Assert:AreEqual(Int32, Int32) (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/Stubs/Unity.Assertions/Assert.cs:66)
    Unity.Entities.EntityCommandBuffer:playback(EntityManager) (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/EntityCommandBuffer.cs:915)
    Unity.Entities.EntityCommandBufferSystem:FlushBuffers(Boolean) (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystem.cs:799)
    Unity.Entities.EntityCommandBufferSystem:OnUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystem.cs:773)
    Unity.Entities.ComponentSystem:InternalUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystem.cs:479)
    Unity.Entities.ScriptBehaviourManager:Update() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ScriptBehaviourManager.cs:97)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystemGroup.cs:264)
    Unity.Entities.ComponentSystem:InternalUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystem.cs:479)
    Unity.Entities.ScriptBehaviourManager:Update() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ScriptBehaviourManager.cs:97)
    Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ScriptBehaviourUpdateOrder.cs:132)

    Assertion failure. Value was False

    Expected: True
    UnityEngine.Assertions.Assert:IsTrue(Boolean)
    Unity.Assertions.Assert:IsTrue(Boolean) (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/Stubs/Unity.Assertions/Assert.cs:15)
    Unity.Entities.EntityCommandBuffer:playbackChain(EntityManager, ECBSharedPlaybackState&, NativeArray`1, Int32, Int32) (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/EntityCommandBuffer.cs:1014)
    Unity.Entities.EntityCommandBuffer:playback(EntityManager) (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/EntityCommandBuffer.cs:943)
    Unity.Entities.EntityCommandBufferSystem:FlushBuffers(Boolean) (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystem.cs:799)
    Unity.Entities.EntityCommandBufferSystem:OnUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystem.cs:773)
    Unity.Entities.ComponentSystem:InternalUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystem.cs:479)
    Unity.Entities.ScriptBehaviourManager:Update() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ScriptBehaviourManager.cs:97)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystemGroup.cs:264)
    Unity.Entities.ComponentSystem:InternalUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ComponentSystem.cs:479)
    Unity.Entities.ScriptBehaviourManager:Update() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ScriptBehaviourManager.cs:97)
    Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.0.12-preview.26/Unity.Entities/ScriptBehaviourUpdateOrder.cs:132)

    I cannot for the life of me seem to figure out how to properly/cleanly instantiate and destroy entities from jobs. I would really appreciate an example piece of code that shows how to do this.
    What is wrong with my code?

    A few open ended questions:
    1. When are the command buffers run in relation to other parts of the system?
    2. What if two systems both use command buffers, which command buffer executes first?
    3. Do command buffers in a job wait until the job completes then execute the backlog consecutively?
    4. Do command buffers execute completely before the next update cycle?
     
  2. Init33

    Init33

    Joined:
    Aug 30, 2017
    Posts:
    67
    I believe I have solved this issue.

    I changed the EntityCommandBuffer to EntityCommandBuffer.Concurrent and I don't see any errors plus the system works as intended.
     
    trojanfoe_ likes this.
  3. CharlesWard

    CharlesWard

    Unity Technologies

    Joined:
    Apr 19, 2017
    Posts:
    23
    Yes, IJobProcessComponentData uses a parallel Job, so a Concurrent Entity Command Buffer is required. (You can use ScheduleSingle() to schedule the job on a single thread instead.)