Search Unity

  1. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice
  2. Enter the 2020.2 Beta Sweepstakes for a chance to win an Oculus Quest 2.
    Dismiss Notice

Getting Entity from a NativeList<ArchetypeChunk>?

Discussion in 'Data Oriented Technology Stack' started by S_Darkwell, Nov 25, 2018.

  1. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
    Good evening!

    I have not worked with Unity's Entity-Component-System architecture since it was announced that [Inject] is to be depreciated. As such, I am just now learning about chunk iteration.

    One challenge I've faced is how to obtain an entity (to destroy it) from a NativeList<ArchetypeChunk>.

    Below is a simplified version of a test system.
    How would I destroy entities containing PlayerInput components in this example?

    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using Unity.Entities;
    4. using Unity.Collections;
    5.  
    6. public struct PlayerInput : IComponentData {
    7.     public int Key;
    8. }
    9.  
    10. public sealed class TextInputToOutputSystem : ComponentSystem {
    11.  
    12.     private readonly EntityArchetypeQuery query = new EntityArchetypeQuery {
    13.         All = new ComponentType[] {typeof(PlayerInput)},
    14.         Any = Array.Empty<ComponentType>(),
    15.         None = Array.Empty<ComponentType>()
    16.     };
    17.  
    18.     protected override void OnUpdate() {
    19.         var componentType = EntityManager.GetArchetypeChunkComponentType<PlayerInput>(true);
    20.         var chunks = EntityManager.CreateArchetypeChunkArray(query, Allocator.Temp);
    21.  
    22.         foreach (var chunk in chunks) {
    23.             var components = chunk.GetNativeArray(componentType);
    24.             foreach (var component in components) {
    25.                 Debug.Log("Player pressed: " + (char)component.Key);
    26.             }
    27.         }
    28.  
    29.         chunks.Dispose();
    30.     }
    31. }
    Any assistance is much appreciated!

    Thank you and be well!
    - S.
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    2,542
    Code (CSharp):
    1. var entityType = this.GetArchetypeChunkEntityType();
    2. // ...
    3. var entities = chunk.GetNativeArray(entityType);
    Also you shouldn't use

    EntityManager.GetArchetypeChunkComponentType
    EntityManager.CreateArchetypeChunkArray

    You should use

    this.GetArchetypeChunkComponentType
    this.CreateArchetypeChunkArray

    ('this' is optional)

    Where 'this' is ComponentSystem or JobComponentSystem
     
    Last edited: Nov 25, 2018
    S_Darkwell likes this.
  3. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
    Hello.

    Thank you so much for the response, @tertle!

    I wasn't aware that 'GetArchetypeChunkComponentType' was implemented by ComponentSystemBase. Nifty!

    'this.CreateArchetypeChunkArray', however, is not found. I also don't see anything similarly-named. Am I missing something?

    I'm currently using Entities 'v0.0.12.preview.21' in 'Unity v2019.1.0a10.'

    To clarify for my example, I would first iterate through each component to output their values, then secondly iterate through each entity to destroy them? Or is there a more efficient way?

    Thank you in advance and be well!
    - S.
     
  4. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    Minimal samle of chunk iteration without Jobs:
    Code (CSharp):
    1. public class ChunkIterationWithoutJobs : ComponentSystem{
    2.  
    3.   private EntityArchetypeQuery _query;
    4.  
    5.   protected override void OnCreateManager(){
    6.     _query = new EntityArchetypeQuery(){
    7.       All = new ComponentType[]{ComponentType.Create<A>(),},
    8.       None = Array.Empty<ComponentType>(),
    9.       Any = Array.Empty<ComponentType>(),
    10.     };
    11.   }
    12.            
    13.   protected override void OnUpdate(){
    14.     var chunks = EntityManager.CreateArchetypeChunkArray (_query, Allocator.TempJob); // get chunks
    15.  
    16.     var aChunkTypeRW = GetArchetypeChunkComponentType<A>(false); // -rw
    17.     var entityChunkType = GetArchetypeChunkEntityType ();
    18.  
    19.     foreach( var c in chunks ){ // for each chunk  
    20.       var entities = c.GetNativeArray (entityChunkType);
    21.       var aArr = c.GetNativeArray( aChunkTypeRW );  
    22.       for( int i = 0; i < entities.Length; i++ ){ // iterate on data
    23.         //....
    24.       }
    25.     }
    26.     chunks.Dispose();
    27.   }
    28. }
    However, with IJobChunk you don't need to care about creating ChunkArray

    Code (CSharp):
    1.  
    2. public class ChunkIterationWithIJobChunk : JobComponentSystem{
    3.   private ComponentGroup _g;
    4.  
    5.   protected override void OnCreateManager(){
    6.     _g = GetComponentGroup(ComponentType.Create<A>());
    7.   }
    8.  
    9.   protected override JobHandle OnUpdate( JobHandle deps )=>new Job(){
    10.     aChunkType = GetArchetypeChunkComponentType<A>(), // -RW
    11.     entityType = GetArchetypeChunkEntityType(),
    12.   }.Schedule( _g, deps );
    13.  
    14.   [BurstCompile]
    15.   private struct Job : IJobChunk{
    16.     public ArchetypeChunkComponentType<A> aChunkType;
    17.     public ArchetypeChunkEntityType entityType;
    18.     public void Execute( ArchetypeChunk chunk, int chunkIndex ){
    19.       var entities  = chunk.GetNativeArray(entityType);
    20.       var aArr = chunk.GetNativeArray(aChunkType);
    21.       for( int i = 0; i < entities.Length; i++ ){
    22.         //...
    23.       }
    24.     }
    25.   }
    26. }
    27.  
    But also you should know that the recommended approach is to use
    IJobProcessComponentData unless you hit its limits

    Code (CSharp):
    1. public class IterationWithIJobProcessComponentData : JobComponentSystem{
    2.  
    3.  protected JobHandle OnUpdate( JobHandle deps ) => new Job().Schedule( this, deps );
    4.  
    5.   [BurstCompile]
    6.   private struct Job : IJobProcessComponentDataWithEntity<A>{
    7.     public void Execute(Entity e, int index, ref A a){    
    8.       // ...
    9.     }
    10.   }
    11. }
     
    Last edited: Nov 26, 2018
    rigidbuddy and S_Darkwell like this.
  5. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
    Hello @SubPixelPerfect,

    This is perfect. Thank you!

    My one curiosity now is why using 'Allocator.Temp' in my ComponentSystem (NOT JobComponentSystem) results in the error "InvalidOperationException: The NativeContainer GatherChunks.Chunks is allocated with Temp memory. Temp memory containers cannot be used when scheduling a job, use TempJob instead."

    Changing it to "Allocator.TempJob" does in fact resolve it.

    Any idea why?

    Be well!
    - S.
     
  6. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    S_Darkwell likes this.
  7. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
  8. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    You can do that using EntityCommandBuffer
    Code (CSharp):
    1. public class CommandBufferInIJobProcessComponent : JobComponentSystem{
    2.   [Inject] EndFrameBarrier _barrier;  // inject barrier
    3.   protected override JobHandle OnUpdate(JobHandle deps) => new Job(){
    4.       CommandBuffer = _barrier.CreateCommandBuffer().ToConcurrent() // create new command buffer
    5.   }.Schedule( this, deps );
    6.  
    7.   [RequireSubtractiveComponent(typeof(B))]
    8.   private struct Job : IJobProcessComponentDataWithEntity<A>{
    9.     public EntityCommandBuffer.Concurrent CommandBuffer;
    10.     public void Execute( Entity entity, int index, [ReadOnly] ref A a ){
    11.       //schedule changes
    12.       CommandBuffer.AddComponent( index, entity, new B() ); //index must a unique ID per job
    13.     }
    14.   }
    15. }
    *NOTE: Burst does not work with EntityCommandBuffer at this stage. see https://forum.unity.com/threads/any...with-entitycommandbuffer.543789/#post-3747409
     
    S_Darkwell likes this.
  9. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
    @SubPixelPerfect,

    Amazing!

    I cannot thank you enough. Thank you!

    I've successfully run your examples and am tweaking them to include in my personal example library.

    These should enable me to progress in my efforts and hopefully make some progress in my project tomorrow.

    Thank you again! Be well! :)
    - S.
     
  10. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    2,542
    SubPixelPerfect likes this.
  11. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    2,542
    Another thing, you should use ComponentGroups for chunk iteration now and not directly call EntityManager.

    Updated the above example.

    Code (CSharp):
    1. public class ChunkIterationWithoutJobs : ComponentSystem{
    2.   private ComponentGroup _query;
    3.   protected override void OnCreateManager(){
    4.     _query = GetComponentGroup(new EntityArchetypeQuery(){
    5.       All = new ComponentType[]{ComponentType.Create<A>(),},
    6.       None = Array.Empty<ComponentType>(),
    7.       Any = Array.Empty<ComponentType>(),
    8.     });
    9.   }
    10.      
    11.   protected override void OnUpdate()
    12.  {
    13.      var chunks = _query.CreateArchetypeChunkArray(Allocator.TempJob);
    14.     var aChunkTypeRW = GetArchetypeChunkComponentType<A>(false); // -rw
    15.     var entityChunkType = GetArchetypeChunkEntityType ();
    16.  
    17.   }
    18. }
    You are therefore left with no EntityManager calls.

    If you can't use IJobProcessComponentData (there are a lot of cases it's too restrictive) you can use IJobChunk instead.

    Code (CSharp):
    1. private struct Job : IJobChunk
    2.   {
    3.  
    4.     public EntityCommandBuffer.Concurrent CommandBuffer;
    5.  
    6.     public ArchetypeChunkComponentType<A> AChunkTypeRW;
    7.  
    8.     [ReadOnly]
    9.     public ArchetypeChunkEntityType EntityChunkType;
    10.  
    11.     public void Execute(ArchetypeChunk chunk, int chunkIndex)
    12.     {
    13.         var entities = c.GetNativeArray (entityChunkType);
    14.         var aArr = c.GetNativeArray( aChunkTypeRW );
    15.         for( int i = 0; i < entities.Length; i++ )
    16.         {
    17.             CommandBuffer.AddComponent( index, entity, new B() ); //index must a unique ID per job
    18.         }
    19.     }
    20.   }
    Which can be schedule liked

    Code (CSharp):
    1.             return new Job
    2.             {
    3.                 EntityChunkType = GetArchetypeChunkEntityType(),
    4.                 AChunkTypeRW = GetArchetypeChunkComponentType<A>(false),
    5.                 CommandBuffer = _barrier.CreateCommandBuffer().ToConcurrent(),
    6.             }.Schedule(_query, inputDeps);
    (wrote these without an IDE, might have errors but should give you an idea.
     
    Last edited: Nov 26, 2018
    S_Darkwell and SubPixelPerfect like this.
  12. SubPixelPerfect

    SubPixelPerfect

    Joined:
    Oct 14, 2015
    Posts:
    224
    is it necessary to mark EntityChunkType with ReadOnly attribute? - it is always read-only.

    Code (CSharp):
    1.     [ReadOnly]
    2.     public ArchetypeChunkEntityType EntityChunkType;
     
    S_Darkwell likes this.
  13. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    2,542
    Not actually sure, probably not. If it's not throwing an error without it then it's probably fine. Kind of just habit.
     
    S_Darkwell likes this.
  14. 5argon

    5argon

    Joined:
    Jun 10, 2013
    Posts:
    1,510
    The reason is Entities lib start and complete a small job to get you the ACA faster so you need that allocator to be compatible. From certain versions ago the lib uses job internally even for getting return values of methods.

    The docs is quite assertive about having to include matching read only https://github.com/Unity-Technologi..._iteration.md#accessing-entity-data-in-chunks

     
    SubPixelPerfect and S_Darkwell like this.
  15. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    2,542
    S_Darkwell likes this.
  16. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
    Good morning!

    This is all incredibly helpful. Thank you all!

    A few questions:
    • Is there a specific reason for using 'ComponentType.Create<A>()' versus 'typeof(A)' when providing values to 'EntityArchetypeQuery', or is it simply personal preference?
    • I notice '[BurstCompile]' is only placed before Jobs. Is this the only place where '[BurstCompile]' is useful?
    • Why are ComponentGroups preferred over raw EntityArchetypeQuerys?
    Thank you again. Be well!
    - S.
     
    Last edited: Nov 26, 2018
  17. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
    Hello @5argon,

    That makes perfect sense.

    Thank you and be well!

    - S.
     
  18. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    2,542
    Is there a specific reason for using 'ComponentType.Create<A>()' versus 'typeof(A)' when providing values to 'EntityArchetypeQuery', or is it simply personal preference?

    typeof(A) gets converted to ComponentType.Create(Type) so it's the same.
    I used Create<A> just to be consistent appearance for when I have ComponentType.ReadOnly<A>() but it's just personal preference.

    I notice '[BurstCompile]' is only placed before Jobs. Is this the only place where '[BurstCompile]' is useful?
    I'm unaware of anything else that can be burst compiled. Burst compiler is designed to optimized jobs.

    Why are ComponentGroups preferred over raw EntityArchetypeQueues?
    I assume you mean EntityArchetypeQuerys?
    ComponentGroup takes an entity archetype query so you're still using query's but it means you can avoid EntityManager calls.
    If you want to use your chunks in a job, IJobChunk requires an ComponentGroup to be passed to it.

    Under the hood EntityManager.CreateArchetypeChunkArray and ComponentGroup.CreateArchetypeChunkArray seem to work quite different. For instance ComponentGroup.CreateArchetypeChunkArray actually does it's work in a burst compiled job. I'm not sure what the affect of these differences are though, they should produce the same results.
     
    SubPixelPerfect and S_Darkwell like this.
  19. S_Darkwell

    S_Darkwell

    Joined:
    Oct 20, 2013
    Posts:
    318
    Hello @tertle,

    Yes, 'EntityArchetypeQuerys', thank you. I've corrected my original post.

    Thank you for taking the time to write and post your response. It answers my questions perfectly.

    Thank you again and be well!
    - S.
     
unityunity