Search Unity

(BUG) BufferArray safety breaks when used differently in multiple systems

Discussion in 'Entity Component System' started by tertle, Nov 14, 2018.

  1. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    System1 has a job with some BufferArrays accesed using ArchetypeChunkBufferTypes and are written to.

    Code (CSharp):
    1.         [BurstCompile]
    2.         private struct GenerateMeshJob : IJobChunk
    3.         {
    4.             // ...
    5.  
    6.             public ArchetypeChunkBufferType<VisibleBlockFaces> BlockFacesType;
    7.  
    8.             public ArchetypeChunkBufferType<Vertex> VerticesType;
    9.  
    10.             public ArchetypeChunkBufferType<Uv> UvsType;
    11.  
    12.             public ArchetypeChunkBufferType<Normal> NormalsType;
    13.  
    14.             public ArchetypeChunkBufferType<Triangle> TrianglesType;
    15.  
    16.             // ...
    17.  
    18.             public void Execute(ArchetypeChunk chunk, int chunkIndex)
    19.             {
    20.                 var blockFacesAccessor = chunk.GetBufferAccessor(this.BlockFacesType);
    21.                 var verticesAccessor = chunk.GetBufferAccessor(this.VerticesType);
    22.                 var uvsAccessor = chunk.GetBufferAccessor(this.UvsType);
    23.                 var normalsAccessor = chunk.GetBufferAccessor(this.NormalsType);
    24.                 var trianglesAccessor = chunk.GetBufferAccessor(this.TrianglesType);
    25.  
    26.                 for (var i = 0; i < chunk.Count; i++)
    27.                 {
    28.                     var blockFaces = blockFacesAccessor[i];
    29.                     var vertices = verticesAccessor[i].Reinterpret<float3>();
    30.                     var uvs = uvsAccessor[i].Reinterpret<float3>();
    31.                     var normals = normalsAccessor[i].Reinterpret<float3>();
    32.                     var triangles = trianglesAccessor[i].Reinterpret<int>();
    33.  
    34.             // ...
    35.  
    36.  
    Which is called by


    Code (CSharp):
    1. var blockFacesTypeRW = this.GetArchetypeChunkBufferType<VisibleBlockFaces>();
    2.             var verticesTypeRW = this.GetArchetypeChunkBufferType<Vertex>();
    3.             var uvsTypeRW = this.GetArchetypeChunkBufferType<Uv>();
    4.             var normalsTypeRW = this.GetArchetypeChunkBufferType<Normal>();
    5.             var trianglesTypeRW = this.GetArchetypeChunkBufferType<Triangle>();
    6.  
    7.             // ...
    8.  
    9.             var greedyMeshJob = new GenerateMeshJob
    10.                 {
    11.                     // ...
    12.                     BlockFacesType = blockFacesTypeRW,
    13.                     VerticesType = verticesTypeRW,
    14.                     UvsType = uvsTypeRW,
    15.                     NormalsType = normalsTypeRW,
    16.                     TrianglesType = trianglesTypeRW,
    17.                 }
    18.                 .Schedule(this.chunkGroup, getVisibleFaces);
    When run, this system and collection of jobs works great.

    The problem is when I add a secondary system, System2, that does reading from this using BufferFromEntity

    Code (CSharp):
    1.         [BurstCompile]
    2.         private struct RayCastJob : IJobProcessComponentData<CameraRay, Selection>
    3.         {
    4.             [ReadOnly]
    5.             public BufferFromEntity<Vertex> Vertices;
    6.  
    7.             [ReadOnly]
    8.             public BufferFromEntity<Normal> Normals;
    9.  
    10.             [ReadOnly]
    11.             public BufferFromEntity<Triangle> Triangles;
    setup using

    Code (CSharp):
    1.             var cameraRayJob = new RayCastJob
    2.             {
    3.                 Vertices = this.GetBufferFromEntity<Vertex>(true),
    4.                 Normals = this.GetBufferFromEntity<Normal>(true),
    5.                 Triangles = this.GetBufferFromEntity<Triangle>(true),
    6.                 Positions = this.GetComponentDataFromEntity<Position>(true),
    7.             };
    8.  
    9.             var cameraRayHandle = cameraRayJob.Schedule(this, handle);
    Soon as this new system is added, the original system now starts throwing an exception, InvalidOperationException: The native container has been declared as [ReadOnly] in the job, but you are writing to it.

    The exception is being thrown in System1 when it tries to write to the buffer array but System1 does not declare the container ReadOnly, only system2 does.

    -edit2- thought i solved it, but only if i force system order
     
    Last edited: Nov 14, 2018
    chadfranklin47 likes this.
  2. gebbiz

    gebbiz

    Joined:
    May 23, 2018
    Posts:
    14
    I have the exact same issue. I debugged it a bit and it seems that dependencies are correct and everything is run in the right order (second system job waits for the first one before starting), but somehow calling schedule in the second system breaks the safety system if the first job is still running at that point.

    As a workaround i call complete on the input dependencies in the second system before scheduling job.
     
    tertle likes this.
  3. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    Yeah I'm doing the same thing. I actually first noticed this bug a few months ago but never reported it. Was refactoring some stuff and it popped back up so thought I should get around to reporting it.


    I think without debugging too deeply it has to do with the fact that Buffer has a a readonly property and this might be copied/corrupted when doing a reinterpret... wild guess.
     
    MNNoxMortem likes this.
  4. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    Has this been resolved yet?
     
  5. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,759
    as far as i'm aware yes
     
  6. Abbrew

    Abbrew

    Joined:
    Jan 1, 2018
    Posts:
    417
    I've tried annotating the DynamicBuffers in IJobForEach_B* jobs with [ReadOnly], but it would cause these mysterious runtime exceptions. I guess I'll give it another go
     
  7. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    226
    Running into this issue in Unity 2021.3.13 LTS though my setup is a bit different as I am not using ECS. I have a struct that I would like to act as a "view" into a NativeArray's memory buffer like so (simplified example):

    Code (CSharp):
    1. public unsafe struct NativeFloatMap
    2. {
    3.     [NativeDisableUnsafePtrRestriction]
    4.     private float* m_Buffer;
    5.  
    6. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    7.     private AtomicSafetyHandle m_Safety;
    8. #endif
    9.  
    10.     public NativeFloatMap(NativeArray<float> array)
    11.     {
    12. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    13.         m_Safety = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(array);
    14.         AtomicSafetyHandle.UseSecondaryVersion(ref m_Safety);
    15. #endif
    16.         m_Buffer = (float*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array);
    17.     }
    18.  
    19.     public float this[int index]
    20.     {
    21.         get
    22.         {
    23. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    24.             AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
    25. #endif
    26.             return m_Buffer[index];
    27.         }
    28.  
    29.         set
    30.         {
    31. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    32.             AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
    33. #endif
    34.             m_Buffer[index] = value;
    35.         }
    36.     }
    37. }
    Scheduling job2 (that writes to NativeFloatMap) which depends on job1 (writes to the same buffer via [WriteOnly] NativeArray) causes job1? to throw that same exception:
    InvalidOperationException: The native container has been declared as [ReadOnly] in the job, but you are writing to it.

    Getting rid of the safety checks for NativeFloatMap avoids the issue, though I would like to have safety checks if possible. Could I be doing something incorrectly here?
     
    Last edited: Nov 11, 2022