Search Unity

About custom native container error

Discussion in 'Entity Component System' started by eterlan, Jul 3, 2019.

  1. eterlan

    eterlan

    Joined:
    Sep 29, 2018
    Posts:
    177
    I use @tertle 's MinHeap for path finding, it works well in main thread, but has some problems in job. After figure out this problem is caused by case sensitive.(I use m_minIndex because IDE suggest that.)
    InvalidOperationException: PathFindingJob.Data.OpenSet uses the [NativeContainer] but has no AtomicSafetyHandle embedded, this is an internal error in the container type.
    Then I found a new problem. How to use [NativeContainerSupportsDeallocateOnJobCompletion] attribute? I do exactly what document suggests, but it still throw InvalidOperationException:
    InvalidOperationException: PathFindingJob.CloseSet uses [DeallocateOnJobCompletion] but the native container does not support deallocation of the memory from a job.
    https://docs.unity3d.com/Packages/c...NativeContainerSupportsMinMaxWriteRestriction

    Here is the NativeMinHeap generously provided by @tertle. I try to get it works by copy some code from other Native Container, with no luck.
    Code (CSharp):
    1. using System;
    2. using System.Diagnostics;
    3. using Unity.Burst;
    4. using Unity.Collections;
    5. using Unity.Collections.LowLevel.Unsafe;
    6.  
    7. namespace ProjectZ.AI.PathFinding.Container
    8. {
    9.     [NativeContainer]
    10.     [NativeContainerSupportsMinMaxWriteRestriction]
    11.     [NativeContainerSupportsDeallocateOnJobCompletion]
    12.  
    13.     public unsafe struct NativeMinHeap : IDisposable
    14.     {
    15.         [NativeDisableUnsafePtrRestriction]
    16.         private void* m_Buffer;
    17.         private AtomicSafetyHandle m_Safety;
    18.         [NativeSetClassTypeToNullOnSchedule]
    19.         private DisposeSentinel m_DisposeSentinel;
    20.         private Allocator m_AllocatorLabel;
    21.         private int m_Head;
    22.         private int m_Length;
    23.         private int m_MinIndex;
    24.         private int m_MaxIndex;
    25.         private int m_Capacity;
    26.  
    27.         public NativeMinHeap
    28.             (int capacity, Allocator allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory)
    29.         {
    30.             Allocate(capacity,allocator,out this);
    31.             if ((options & NativeArrayOptions.ClearMemory) != NativeArrayOptions.ClearMemory)
    32.                 return;
    33.             UnsafeUtility.MemClear(m_Buffer, (long) this.Length * (long) UnsafeUtility.SizeOf<MinHeapNode>());
    34.         }
    35.        
    36.         private static void Allocate
    37.         (int               capacity,
    38.          Allocator         allocator,
    39.          out NativeMinHeap nativeMinHeap)
    40.         {
    41.             var size = (long) UnsafeUtility.SizeOf<MinHeapNode>() * capacity;
    42. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    43.             // Native allocation is only valid for Temp, Job and Persistent.
    44.             if (allocator <= Allocator.None)
    45.                 throw new ArgumentException("Allocator must be Temp, TempJob or Persistent", nameof(allocator));
    46.  
    47.             if (capacity < 0)
    48.                 throw new ArgumentOutOfRangeException(nameof(capacity), "capacity must be >= 0");
    49.  
    50.             if (!UnsafeUtility.IsBlittable<MinHeapNode>())
    51.                 throw new ArgumentException(string.Format("{0} used in NativeCustomArray<{0}> must be blittable",
    52.                     typeof(MinHeapNode)));
    53.  
    54.             if (size > int.MaxValue)
    55.                 throw new ArgumentOutOfRangeException(nameof(capacity),
    56.                     $"Length * sizeof(element) cannot exceed {(object) int.MaxValue} bytes");
    57. #endif
    58.             nativeMinHeap.m_Buffer =
    59.                 UnsafeUtility.Malloc(size, UnsafeUtility.AlignOf<MinHeapNode>(), allocator);
    60.             //
    61.             //UnsafeUtility.MemClear(m_Buffer , size);
    62.             nativeMinHeap.m_Capacity       = capacity;
    63.             nativeMinHeap.m_AllocatorLabel = allocator;
    64.             nativeMinHeap.m_Head   = -1;
    65.             nativeMinHeap.m_Length = 0;
    66.  
    67. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    68.             nativeMinHeap.m_MinIndex = 0;
    69.             nativeMinHeap.m_MaxIndex = capacity - 1;
    70.             DisposeSentinel.Create(out nativeMinHeap.m_Safety, out nativeMinHeap.m_DisposeSentinel, 1, allocator);
    71. #endif
    72.             /*if ((options & NativeArrayOptions.ClearMemory) != NativeArrayOptions.ClearMemory)
    73.                 return;
    74.             UnsafeUtility.MemClear(m_Buffer, (long) m_capacity * UnsafeUtility.SizeOf<MinHeapNode>());*/
    75.         }
    76.  
    77.         public bool HasNext()
    78.         {
    79. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    80.             AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
    81. #endif
    82.             return m_Head >= 0;
    83.         }
    84.        
    85.         public int Length => m_Length;
    86.  
    87.         public void Push(MinHeapNode node)
    88.         {
    89. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    90.             if (m_Length == m_Capacity)
    91.                 throw new IndexOutOfRangeException("Capacity Reached");
    92.  
    93.             AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
    94. #endif
    95.  
    96.             UnsafeUtility.WriteArrayElement(m_Buffer, m_Length, node);
    97.             m_Length += 1;
    98.  
    99.             if (m_Head < 0) { m_Head = m_Length - 1; }
    100.             else if (node.ExpectedCost < this[m_Head].ExpectedCost)
    101.             {
    102.                 node.Next = m_Head;
    103.                 m_Head    = m_Length - 1;
    104.             }
    105.             else
    106.             {
    107.                 var currentPtr = m_Head;
    108.                 var current    = this[currentPtr];
    109.  
    110.                 while (current.Next >= 0 && this[current.Next].ExpectedCost <= node.ExpectedCost)
    111.                 {
    112.                     currentPtr = current.Next;
    113.                     current    = this[current.Next];
    114.                 }
    115.  
    116.                 node.Next    = current.Next;
    117.                 current.Next = m_Length - 1;
    118.             }
    119.         }
    120.  
    121.         public int Pop()
    122.         {
    123. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    124.             AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
    125. #endif
    126.             var result = m_Head;
    127.             m_Head = this[m_Head].Next;
    128.             return result;
    129.         }
    130.  
    131.         public MinHeapNode this[int index]
    132.         {
    133.             get
    134.             {
    135. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    136.                 if (index < m_MinIndex
    137.                     || index > m_MaxIndex)
    138.                     FailOutOfRangeError(index);
    139.  
    140.                 AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
    141. #endif
    142.  
    143.                 return UnsafeUtility.ReadArrayElement<MinHeapNode>(m_Buffer, index);
    144.             }
    145.         }
    146.         //
    147.         // public void Clear()
    148.         // {
    149.         //     m_head   = -1;
    150.         //     m_length = 0;
    151.         // }
    152.        
    153.         public bool IsCreated => (IntPtr)m_Buffer != IntPtr.Zero;
    154.         [WriteAccessRequired]
    155.         public void Dispose()
    156.         {
    157.             if (!UnsafeUtility.IsValidAllocator(m_AllocatorLabel))
    158.                 throw new InvalidOperationException(
    159.                     "The NativeArray can not be Disposed because it was not allocated with a valid allocator.");
    160. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    161.             DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
    162. #endif
    163.             UnsafeUtility.Free(m_Buffer, m_AllocatorLabel);
    164.             m_Buffer   = (void*) IntPtr.Zero;
    165.             m_Capacity = 0;
    166.         }
    167.  
    168. #if ENABLE_UNITY_COLLECTIONS_CHECKS
    169.         private void FailOutOfRangeError(int index)
    170.         {
    171.             if (index < m_Capacity
    172.                 && (m_MinIndex != 0 || m_MaxIndex != m_Capacity - 1))
    173.                 throw new IndexOutOfRangeException(
    174.                     $"Index {(object) index} is out of restricted IJobParallelFor range [{(object) m_MinIndex}...{(object) m_MaxIndex}] in ReadWriteBuffer.\nReadWriteBuffers are restricted to only read & write the element at the job index. You can use double buffering strategies to avoid race conditions due to reading & writing in parallel to the same elements from a job.");
    175.  
    176.             throw new IndexOutOfRangeException(
    177.                 $"Index {(object) index} is out of range of '{(object) m_Capacity}' Length.");
    178.         }
    179. #endif
    180.         [BurstDiscard]
    181.         private static void IsBlittableAndThrow()
    182.         {
    183.             if (!UnsafeUtility.IsBlittable<MinHeapNode>())
    184.             {
    185.                 var type = typeof(MinHeapNode);
    186.                 throw new ArgumentException($"{type} must be blittable");
    187.             }
    188.         }
    189.  
    190.     }
    191. }
    Hope anyone know the question.:p

    p.s. Actually I didn't notice the document above at the beginning, I supposed these informations should be here, but nothing found.
    https://docs.unity3d.com/ScriptRefe...rSupportsMinMaxWriteRestrictionAttribute.html