Search Unity

NativeArray Problem

Discussion in 'Entity Component System' started by Vaspra, Jul 16, 2019.

  1. Vaspra

    Vaspra

    Joined:
    Apr 15, 2018
    Posts:
    34
    Hi all,

    After dabbling a while ago with ECS I'm taking a new look at it to solve some practical problems, and I'm getting to grips with a few of the (welcome) changes to coding with ECS.

    In an attempt to recreate some old work from scratch to refresh my memory, I've been attempting to rewrite in my own overly described version of the AccelerationParallelFor example.

    Found Here:
    https://github.com/stella3d/job-sys...ter/Assets/Scripts/AccelerationParallelFor.cs

    I seem to be doing something wrong with my velocities NativeArray<Vector3>, as at appears new AccelerationJobs in Update are trying to access the same native array the previous AccelerationJob is using.
    From what I can tell the older AccelerationJobs should be concluding (from LateUpdate: m_MoveJobHandle.Complete() that depends on AccelerationJob completing).

    Why are the next AccelerationJobs causing this error?

    Thanks!

    InvalidOperationException: The previously scheduled job LiquidBodyParallelFor:AccelerationJob writes to the NativeArray AccelerationJob.velocity. You are trying to schedule a new job LiquidBodyParallelFor:AccelerationJob, which writes to the same NativeArray (via AccelerationJob.velocity). To guarantee safety, you must include LiquidBodyParallelFor:AccelerationJob as a dependency of the newly scheduled job.


    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using System;
    6.  
    7. using Unity.Entities;
    8. using Unity.Burst;
    9. using Unity.Mathematics;
    10. using Unity.Transforms;
    11. using Unity.Jobs;
    12. using Unity.Collections;
    13. using UnityEngine.Jobs;
    14.  
    15. public class LiquidBodyParallelFor : BaseJobObject
    16. {
    17.     public Vector3 m_Acceleration = Vector3.up;
    18.     public Vector3 m_AccelerationMod = Vector3.down * 0.001f;
    19.  
    20.     NativeArray<Vector3> m_Velocities;
    21.     TransformAccessArray m_TransformsAccessArray;
    22.  
    23.     // Defined in this class
    24.     MoveJob m_MoveJob;
    25.     AccelerationJob m_AccelJob;
    26.  
    27.     JobHandle m_MoveJobHandle;
    28.     JobHandle m_AccelJobHandle;
    29.  
    30.     /// <summary>
    31.     /// In Start, we are creating a bunch of bodies to work with, generating references to both their
    32.     /// Transforms (in a TransformAccessArray) and their renderers.
    33.     ///
    34.     /// We are also creating a NativeArray (persistent) to contain the velocities of these objects.
    35.     /// </summary>
    36.     protected void Start()
    37.     {
    38.         m_Velocities = new NativeArray<Vector3>(m_BodyCount, Allocator.Persistent);
    39.  
    40.         m_Objects = LiquidUtil.PlaceLiquidBodies(m_BodyCount, m_BodyPlacementRadius, new Vector3(0, 50, 0));
    41.  
    42.         for (int i = 0; i < m_BodyCount; i++)
    43.         {
    44.             var obj = m_Objects[i];
    45.             m_Transforms[i] = obj.transform;
    46.             m_Renderers[i] = obj.GetComponent<Renderer>();
    47.         }
    48.  
    49.         m_TransformsAccessArray = new TransformAccessArray(m_Transforms);
    50.     }
    51.  
    52.     /// <summary>
    53.     /// A parallel job - doesn't need any other object reference information than an index.
    54.     /// The values required for the actual execution must still be defined when the struct
    55.     /// is created (timescale, access to the velocity array, current accel, accel modification).
    56.     /// </summary>
    57.     struct AccelerationJob : IJobParallelFor
    58.     {
    59.         public NativeArray<Vector3> velocity;
    60.  
    61.         public Vector3 acceleration;
    62.         public Vector3 accelerationMod;
    63.  
    64.         public float deltaTime;
    65.  
    66.         /// <summary>
    67.         /// Executing this job will feed any desired acceleration behaviour into the transform's
    68.         /// velocity. For now, let's just add accelerationMod.
    69.         /// </summary>
    70.         public void Execute(int i)
    71.         {
    72.             velocity[i] += (acceleration + accelerationMod) * deltaTime;
    73.         }
    74.     }
    75.  
    76.     /// <summary>
    77.     /// A parallel job, specifically involving transforms - these must define the input data
    78.     /// required to execute, and the execution method itself:
    79.     /// Execute(int index, TransformAccess transform).
    80.     /// </summary>
    81.     struct MoveJob : IJobParallelForTransform
    82.     {
    83.         [ReadOnly]
    84.         public NativeArray<Vector3> velocity; // The velocities from AccelerationJob
    85.  
    86.         public float deltaTime;
    87.  
    88.         /// <summary>
    89.         /// Executing this job simply requires adding the velocity to the transform's position.
    90.         /// </summary>
    91.         public void Execute(int i, TransformAccess transform)
    92.         {
    93.             transform.position += velocity[i] * deltaTime;
    94.         }
    95.     }
    96.  
    97.     /// <summary>
    98.     /// In Update we prepare the movement and acceleration jobs, populating their JobHandles.
    99.     /// These are typically run to conclusion (ensured by jobHandle.Complete() in LateUpdate).
    100.     /// </summary>
    101.     public void Update()
    102.     {
    103.         // Prepare the jobs by defining the common data going into each parallel task
    104.         m_AccelJob = new AccelerationJob()
    105.         {
    106.             deltaTime = Time.deltaTime,
    107.             velocity = m_Velocities,
    108.             acceleration = m_Acceleration,
    109.             accelerationMod = m_AccelerationMod
    110.         };
    111.  
    112.         m_MoveJob = new MoveJob()
    113.         {
    114.             deltaTime = Time.deltaTime,
    115.             velocity = m_Velocities
    116.         };
    117.  
    118.         // Here we are scheduling the acceleration job, then the move job, and saying that the
    119.         // move job depends on the completion of the acceleration job
    120.         m_AccelJobHandle = m_AccelJob.Schedule(m_BodyCount, 64);
    121.         m_MoveJobHandle = m_MoveJob.Schedule(m_TransformsAccessArray, m_AccelJobHandle);
    122.     }
    123.  
    124.     /// <summary>
    125.     /// In LateUpdate, we make sure the movement job completes. Since this depends on the
    126.     /// completion of the acceleration job, both will be completed.
    127.     /// </summary>
    128.     public void LateUpdate()
    129.     {
    130.         m_MoveJobHandle.Complete();
    131.     }
    132.  
    133.     /// <summary>
    134.     /// It is important to dispose of component arrays after we're done with them.
    135.     /// </summary>
    136.     private void OnDestroy()
    137.     {
    138.         m_Velocities.Dispose();
    139.         m_TransformsAccessArray.Dispose();
    140.     }
    141. }
    142.  
     
  2. Vaspra

    Vaspra

    Joined:
    Apr 15, 2018
    Posts:
    34
    Solved. For anyone that runs into a similar problem:

    The logic in the updates is fine. I needed some more control to refresh and recreate the native arrays - I moved from creating things in Awake to custom 'Spawn' and 'Dispose' functions. I'm not certain but I think some array was being left undisposed somewhere messing with things.