Search Unity

BufferArray inside the job system - no docs or example. Help trying to do cellular automata with v10

Discussion in 'C# Job System' started by SocialSimulator, Feb 13, 2019.

  1. SocialSimulator

    SocialSimulator

    Joined:
    Sep 19, 2017
    Posts:
    26
    I am going mad trying to figure out how to implement a viral diffusion model where each entity has an array of neighbours that influence its state. Despite two solid days(!!), I cannot get it to work with v10, with buffer arrays. I also cannot find a single example anywhere on the internet that is not out of date, or proper documentation of BufferArrays in the job system.

    Inside the job system, I want to be able to get hold of that entity's buffer array and then look up various states in the neighbours components. Then recalculate the state.

    It should be quite a simple thing to do, so really appreciate any help, ideally a worked example )

    Here's my approach so far. Each entity is given a set of neighbors as follows:
    DynamicBuffer<NeighbourLink> neighborLinks = cellManager.GetBuffer<NeighbourLink>(cellEntity);
    where NeighbourLink is:
    public struct NeighbourLink : IBufferElementData {
    public int neighbourIndex;
    public float influencePower;
    }
    .
    Somehow, despite the different methods I've tried like IJobChunk, IJobProcessComponentDataWithEntity, I always fail to be able to access the buffer array correctly, and cannot access the other entities.Also, ComponentDataArray is getting deprecated, so prefer not to have that.

    Thanks.
     
  2. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Code (CSharp):
    1.         [BurstCompile]
    2.         [RequireComponentTag(typeof(Buffer1))]
    3.         private struct Job1 : IJobProcessComponentDataWithEntity<Data1>
    4.         {
    5.             [NativeDisableParallelForRestriction]
    6.             public BufferFromEntity<Buffer1> Buffer;
    7.  
    8.             public void Execute(Entity entity, int index, [ReadOnly] ref Data1 data)
    9.             {
    10.                 var array = this.Buffer[entity];
    11.             }
    12.         }
    Code (CSharp):
    1.         [BurstCompile]
    2.         private struct Job2 : IJobChunk
    3.         {
    4.             public ArchetypeChunkBufferType<Buffer1> BufferType;
    5.  
    6.             /// <inheritdoc />
    7.             public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    8.             {
    9.                 var buffers = chunk.GetBufferAccessor(BufferType);
    10.  
    11.                 for (var index = 0; index < chunk.Count; index++)
    12.                 {
    13.                     var array = buffers[index];
    14.                 }
    15.             }
    16.         }
     
    Last edited: Feb 14, 2019
    SocialSimulator likes this.
  3. SocialSimulator

    SocialSimulator

    Joined:
    Sep 19, 2017
    Posts:
    26
    Thank you, Tertle. I tried the code, but got this error:

    InvalidOperationException: Diffuser1.Data.Links is not declared [ReadOnly] in a IJobParallelFor job. The container does not support parallel writing. Please use a more suitable container type

    Am sure I am missing something obvious, but can't find it.

    This is my system code.

    public class DiffusionSystem : JobComponentSystem
    {
    // This is simulation loop
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
    var diffusionJob = new Diffuser1()
    {
    };
    return diffusionJob.Schedule(this, inputDeps);
    }

    // This is the job
    [BurstCompile]
    struct Diffuser1 : IJobProcessComponentDataWithEntity<VillageStateNow, VillageStateNext>
    {
    BufferFromEntity<NeighbourLinkBufferElement> Links;
    public void Execute(
    Entity entity,
    int index,
    [ReadOnly] ref VillageStateNow villageStateNow,
    ref VillageStateNext villageStateNext
    )
    {
    var array = this.Links[entity];
    for (int linkIndex = 0; linkIndex < array.Length; linkIndex++)
    {
    Debug.Log(array[linkIndex].neighbourIndex);
    }
    }
    }
    }

    Thanks.
     
  4. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Sorry, I forgot to add [NativeDisableParallelForRestriction]

    It's safe as long as you only use the Entity passed from IJobProcessComponentDataWithEntity

    You should also add a [RequireComponentTag(typeof(Buffer1))]

    Code updated.
     
  5. SocialSimulator

    SocialSimulator

    Joined:
    Sep 19, 2017
    Posts:
    26
    Thanks, Tertle.
    I get a new error on the var array = this.Buffer[entity]; line:
    NullReferenceException: Object reference not set to an instance of an object

    This might be because of the way I am setting up the buffer array in the first place. What do you think?


    Code (CSharp):
    1.             for (int i = 0; i < 400; i++)
    2.             {
    3.                 Entity villageEntity = villageManager.CreateEntity(villageArchetype);
    4.                 Vector3 pos = UnityEngine.Random.insideUnitSphere * 1000;
    5.                 villageManager.SetComponentData(villageEntity, new Position { Value = new float3(pos.x, 0, pos.z) });
    6.                 byte fmnrNow = (UnityEngine.Random.value < 0.1f) ? (byte)1 : (byte)0;
    7.                 villageManager.SetComponentData(villageEntity, new VillageStateNow { fmnrNow = fmnrNow });
    8.                 villageManager.SetComponentData(villageEntity, new VillageStateNext { fmnrNext = fmnrNow });
    9.  
    10.                 DynamicBuffer< NeighbourLinkBufferElement > neighbourLinks = villageManager.GetBuffer<NeighbourLinkBufferElement>(villageEntity);
    11.                 for (int j = 0; j < 5; j++) {
    12.                     var newLink = new NeighbourLinkBufferElement() { neighbourIndex = UnityEngine.Random.Range(0, 999), influenceFrequency = 1f };
    13.                     neighbourLinks.Add(newLink);
    14.                 }
    15. }
    16.  
     
  6. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I need to see more code, because from what I see what you're doing does not work in a job.

    What is villageManager
     
  7. SocialSimulator

    SocialSimulator

    Joined:
    Sep 19, 2017
    Posts:
    26
    Thanks for your continued (and legendary) help, Tertle! villageManager is the EntityManager.

    Code (CSharp):
    1.     public class VillageSpawner : MonoBehaviour
    2.     {
    3.         static EntityManager villageManager;
    4.         static SharedComponentDataArray<RenderMesh> villageRenderer;
    5.         static EntityArchetype villageArchetype;
    6.  
    7.         [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    8.         public static void Init()
    9.         {
    10.             villageManager = World.Active.GetOrCreateManager<EntityManager>();
    11.             villageArchetype = villageManager.CreateArchetype(
    12.                      typeof(Position),
    13.                      typeof(Rotation),
    14.                      typeof(VillageStateNow),
    15.                      typeof(VillageStateNext),
    16.                      typeof(NeighbourLinkBufferElement),
    17.                      typeof(RenderMesh));
    18.         }
    19.  
    20.         [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
    21.         public static void InitWithScene()
    22.         {
    23.             var settings = AntECS.PureFmnrSettings.Inst;
    24.             RenderMesh defaultRenderMesh = new RenderMesh
    25.             {
    26.                 mesh = settings.mesh,
    27.                 material = settings.noFmnrMaterial,
    28.                 subMesh = 0,
    29.                 castShadows = ShadowCastingMode.Off,
    30.                 receiveShadows = false
    31.             };
    32.  
    33.             // Create villages with some fake data
    34.             for (int i = 0; i < 400; i++)
    35.             {
    36.                 Entity villageEntity = villageManager.CreateEntity(villageArchetype);
    37.                 Vector3 pos = UnityEngine.Random.insideUnitSphere * 1000;
    38.                 villageManager.SetComponentData(villageEntity, new Position { Value = new float3(pos.x, 0, pos.z) });
    39.                 byte fmnrNow = (UnityEngine.Random.value < 0.1f) ? (byte)1 : (byte)0;
    40.                 villageManager.SetComponentData(villageEntity, new VillageStateNow { fmnrNow = fmnrNow });
    41.                 villageManager.SetComponentData(villageEntity, new VillageStateNext { fmnrNext = fmnrNow });
    42.  
    43.                 DynamicBuffer< NeighbourLinkBufferElement > neighbourLinks = villageManager.GetBuffer<NeighbourLinkBufferElement>(villageEntity);
    44.                 for (int j = 0; j < 5; j++) {
    45.                     var newLink = new NeighbourLinkBufferElement() { neighbourIndex = UnityEngine.Random.Range(0, 399), influenceFrequency = 1f };
    46.                     neighbourLinks.Add(newLink);
    47.                 }
    48.  
    49.                 villageManager.SetSharedComponentData<RenderMesh>(villageEntity, defaultRenderMesh);
    50.             }
    51.  
    52.         }
    53.        
    54.     }
    These are the components:
    Code (CSharp):
    1. InternalBufferCapacity(32)]
    2.     public struct NeighbourLinkBufferElement : IBufferElementData
    3.     {
    4.         public int neighbourIndex;
    5.         public float influenceFrequency;
    6.     }
    7.  
    8.     public struct VillageStateNext : IComponentData {
    9.         public byte fmnrNext;
    10.         public int villageIndex;   // Internal Index
    11.     }
    12.  
    13.    public struct VillageStateNow : IComponentData {
    14.         public byte fmnrNow;
    15.         public int villageIndex;   // Internal Index
    16.      }
    17. }
    It will represent the diffusion of a behavior between villages connected by roads (the neighbourLinks).
     
  8. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    What does your job creation (and job) look like? Are you assigning Buffer?

    I can't see anything obviously wrong with your initialization.
     
  9. GilCat

    GilCat

    Joined:
    Sep 21, 2013
    Posts:
    676
    You are not initializing Buffer and it should be public for initialization.
     
  10. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Whoops. Yeah needs to be public and initialized, this is what happens when you write code on a forum.
     
    GilCat likes this.
  11. SocialSimulator

    SocialSimulator

    Joined:
    Sep 19, 2017
    Posts:
    26
    Great. Thanks for this, tertle & GilCat )) For other people following, here is my revised code that goes inside the OnUpdate part:
    Code (CSharp):
    1.             var diffusionJob = new Diffuser1()
    2.             {
    3.                 Links = GetBufferFromEntity<NeighbourLinkBufferElement>()
    4.             };
    5.  
    where Links is defined in the DIffuser1 struct as:
    Code (CSharp):
    1.  [NativeDisableParallelForRestriction]
    2.             public BufferFromEntity<NeighbourLinkBufferElement> Links;
     
    MostHated likes this.