Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

ArgumentException on native collectioons of elements containing a NativeSlice

Discussion in '2019.3 Beta' started by FWCorey, Nov 13, 2019.

  1. FWCorey

    FWCorey

    Joined:
    Feb 2, 2011
    Posts:
    42
    Our project currently can't be upgraded to Unity 2019.3 because of a change in CollectionHelper that impacts restrictions on native collections.

    Previously our collection elements could contain NativeSlice<unmanged type> fields and worked fine up until Unity 2019.2 and we use it extensively in our project.

    In Unity 2019.3 however instead of checking
    Code (CSharp):
    1. if (!UnsafeUtility.IsUnmanaged<T>())
    it now checks
    Code (CSharp):
    1. if (!UnsafeUtility.IsValidNativeContainerElementType<T>())
    . This invalidates NativeSlices from being used.

    If this is intended behaviour, what is the recommended workaround or migration path away from using them? We could use pointers or a new dummy native slice replacement that holds pointers in the same way, but I don't want to work around the safety systems if there is a valid reason for the change that we may not be aware of.
     
  2. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    if they are immutable, you can use blobs.

    if they are mutable, store the indices and pass the original NativeArray as a separate job field
    (then you can create the slice inside the job when needed)
     
  3. FWCorey

    FWCorey

    Joined:
    Feb 2, 2011
    Posts:
    42
    They are the mutable output from several jobs and systems. They are a collection of the slices of delta changes to the array for network transmission.
     
  4. M_R

    M_R

    Joined:
    Apr 15, 2015
    Posts:
    559
    then store the indices. you can always create a NativeSlice from that
     
  5. FWCorey

    FWCorey

    Joined:
    Feb 2, 2011
    Posts:
    42
    That's what we're currently planning to do. There are a lot of other scattered similar usages in the project though, so we were hoping it was a bug. Refactoring all that code will be expensive. :(
     
  6. FWCorey

    FWCorey

    Joined:
    Feb 2, 2011
    Posts:
    42
    Here is one of our jobs as an example that is impacted by this. We can't see any way to refactor this besides creating a new clone of the NativeSlice struct that doesn't have the NativeCollection attribute or taking a huge performance hit.

    does anyone else see a better path?


    Code (CSharp):
    1. [BurstCompile]
    2.         private unsafe struct GenerateSubchunkSlices : IJob {
    3.             [ReadOnly] public NativeArray<ushort> SubchunkCounts; // The number of subchunks in each chunk, indexed per chunk.
    4.             [ReadOnly] public NativeMultiHashMap<int, SubchunkInfo> Subchunks;
    5.             public NativeArray<byte> Bytes; // The underlying data (allocated from totalLength in CountChunks)
    6.             public NativeArray<NativeSlice<byte>> SubChunkSlices; // The backing array for the per-subchunk slice slices.
    7.             [WriteOnly] public NativeArray<NativeSlice<NativeSlice<byte>>> SubChunkSlicesPerChunk; // The chunk-indexed array of slices into the 'chunkSlices' array.
    8.             // For this I need to know the number of subchunks in each chunk, as well as the total number of subchunks overall.
    9.  
    10.             [WriteOnly] public NativeMultiHashMap<ushort, NativeSlice<byte>> DataPerSector; // This is the thing that the node repacker copies out of...
    11.             // I can use DataPerSector to calculate the total length for each sector...
    12.          
    13.             public int ChunkCount;
    14.             public void Execute() {
    15.                 // I can count the total number of entities per sector here as well...
    16.                 uint totalLength = 0;
    17.                 uint totalSubchunks = 0;
    18.                 for (int i = 0; i < ChunkCount; ++i) {
    19.                     //Debug.Log($"Subchunk Slices for chunk {i}: {totalSubchunks} - {totalSubchunks + SubchunkCounts[i]}");
    20.                     ushort currentSubchunkIndex = SubchunkCounts[i];
    21.                     int start = (int)totalSubchunks;
    22.                     SubChunkSlicesPerChunk[i] = new NativeSlice<NativeSlice<byte>>(SubChunkSlices, start, currentSubchunkIndex);
    23.                     // This implicitly sorts the data by chunk. Is this helpful?
    24.                     // I think it might be because it means the order is stable!
    25.                  
    26.                     if (Subchunks.TryGetFirstValue(i, out SubchunkInfo info, out var it)) { // MultiHashMaps are First In Last Out!
    27.                         do {
    28.                             currentSubchunkIndex--; // The last index is one less than the length...
    29.                             //Debug.Log($"Subchunk order when Counting... {i}: {info.Sector}");
    30.                             ushort sector = info.Sector;
    31.                             uint subLength = info.SubchunkLength;
    32.                             var subchunkSliceWithHeader = new NativeSlice<byte>(Bytes, (int) totalLength, (int) subLength);
    33.                             UnsafeUtility.CopyStructureToPtr(ref info.Header, subchunkSliceWithHeader.GetUnsafePtr());
    34.                             DataPerSector.Add(sector, subchunkSliceWithHeader); // For some reason this isn't in a stable order?
    35.                             var subchunkSlice = new NativeSlice<byte>(subchunkSliceWithHeader, sizeof(SubChunkHeader));
    36.                             // SubChunkSlices need to be in reversed order!
    37.                             SubChunkSlices[start + currentSubchunkIndex] = subchunkSlice; // This means we can write directly into the slice!
    38.                             totalSubchunks++;
    39.                             totalLength += subLength;
    40.                         } while (Subchunks.TryGetNextValue(out info, ref it));
    41.                     }
    42.                 }
    43.             }
    44.         }
    Code (csharp):
    1. [StructLayout(LayoutKind.Sequential)]
    2. private struct SubChunkHeader {
    3.    public ushort ArchetypeIndex, Stride, Count;
    4. }
    Code (csharp):
    1. private struct SubchunkInfo {
    2.    public ushort Sector;
    3.    public uint SubchunkLength; // The byte length of the subchunk (the total of all of these will be the total byte length of everything)
    4.    public SubChunkHeader Header;
    5. }