Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

NativeArray.CopyTo to larger managed array

Discussion in 'Entity Component System' started by LennartJohansen, Aug 20, 2018.

  1. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Hi.

    When using the CopyTo function to copy data from a NativeSlice or a NativeArray to a managed array there is internal validation to make sure the managed array has the same size.

    Would it be possible to allow copying if the managed array is larger than the native array.
    I have a use case where I have a pre allocated managed array that in some cases are larger than the nativearray produced by jobs and I do not want to allocate this again and produce GC

    I do this now with a custom CopyTo function and MemCopy but I would like to get the unsafe code out of the project.

    Lennart
     
    FM-Productions and 5argon like this.
  2. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    You can use NativeSlice and Slice function to get a subsection of the array.
     
    Kuptsevych-Yuriy and Nyanpas like this.
  3. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Yes. I use slices. But if I have a slice of 900 items and want to copy this to a pre allocated array of 1023 (for instanced render api) it will not do that since the size does not match. I would like the CopyTo to just write to the first 900 items in the array.
     
  4. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    157
    Can you just use list.AddRange(nativeArray)?
     
  5. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    That would be much slower than a single memory copy.

    What I would like is basically this. Where it allows for copying the slice to a array that is larger than the slice. The CopyTo needs the size to be the same.

    This code works good but requires the unsafe code checkbox to be enabled. Would be nice if the built-in copyTo could do the same.

    Code (csharp):
    1.  
    2.   public static unsafe void CopyToFast<T>(
    3.             this NativeSlice<T> nativeSlice,
    4.             T[] array)
    5.             where T : struct
    6.         {        
    7.             if (array == null)
    8.             {
    9.                 throw new NullReferenceException(nameof(array) + " is null");
    10.             }          
    11.             int nativeArrayLength = nativeSlice.Length;
    12.             if (array.Length < nativeArrayLength)
    13.             {
    14.                 throw new IndexOutOfRangeException(
    15.                     nameof(array) + " is shorter than " + nameof(nativeSlice));
    16.             }
    17.             int byteLength = nativeSlice.Length * UnsafeUtility.SizeOf<T>();
    18.             void* managedBuffer = UnsafeUtility.AddressOf(ref array[0]);
    19.             void* nativeBuffer = nativeSlice.GetUnsafePtr();
    20.             UnsafeUtility.MemCpy(managedBuffer, nativeBuffer, byteLength);
    21.         }
    22.  
     
    Thomas-Mountainborn and Razmot like this.
  6. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    157
    The current NativeArray.CopyTo(T[]) is not a single memory copy, it copies each element to the destination individually. Even if they adjusted the method to allow copying to an array of a larger size, your method would still be faster (in most cases) since it is a single memory copy. That said, it would definitely be nice to see something like this in the Unity library.
     
  7. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Joachim Ante confirmed that in 2018.3 all the copy functions use the memory copy internally.
     
    Nyanpas, Antypodish and dartriminis like this.
  8. Manufacture43

    Manufacture43

    Joined:
    Apr 21, 2017
    Posts:
    140
    I would also be interested in a method to copy the content of a NativeArray to part of a managed List. So far I came up with this that crashes unity...
    Code (CSharp):
    1.     static Dictionary<Type, FieldInfo> fieldCache = new Dictionary<Type, FieldInfo>();
    2.     public unsafe static void Copy<T>(this List<T> list, int listStart, NativeArray<T> array, int arrayStart, int count) where T : struct
    3.     {
    4.         void* source = NativeArrayUnsafeUtility.GetUnsafePtr(array);
    5.  
    6.         FieldInfo field;
    7.         if (!fieldCache.TryGetValue(typeof(List<T>), out field))
    8.         {
    9.             field = typeof(List<T>).GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic);
    10.             fieldCache.Add(typeof(List<T>), field);
    11.         }
    12.  
    13.         object listItems = field.GetValue(list);
    14.  
    15.         ulong gcHandle;
    16.         void* destination = UnsafeUtility.PinGCObjectAndGetAddress(listItems, out gcHandle);
    17.  
    18.         UnsafeUtility.MemCpy(destination, source, count * UnsafeUtility.SizeOf(typeof(T)));
    19.  
    20.         UnsafeUtility.ReleaseGCObject(gcHandle);
    21.     }
    EDIT:
    Code (csharp):
    1. void* destination = UnsafeUtility.PinGCObjectAndGetAddress(listItems, out gcHandle);
    This is probably wrong. I'm probably getting the address of the array and not of its data.
     
    Last edited: Sep 19, 2018
  9. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    Use my function from the post above and change the index of the managed stay you get the pointer from.
     
  10. Manufacture43

    Manufacture43

    Joined:
    Apr 21, 2017
    Posts:
    140
    Ah, thanks for making me re-read it a third time. I finally got the bit I was missing :)
    That ref array[0], so simple...
     
  11. LennartJohansen

    LennartJohansen

    Joined:
    Dec 1, 2014
    Posts:
    2,394
    You can do a single copy like that to multi dimension arrays also
     
  12. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,005
    Just hit upon this problem. Another unity3d YAGNI.
    I DEMAND ;) functionality that let me copy NativeArray data to another NativeArray of different size.
    It is just a common sense.
     
  13. Baggers_

    Baggers_

    Joined:
    Sep 10, 2017
    Posts:
    97
  14. koirat

    koirat

    Joined:
    Jul 7, 2012
    Posts:
    2,005
    thx. looks like it is a static method.
     
  15. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    529
    Alternatively you can use slices, eg:
    Code (CSharp):
    1. target.Slice([start], [length]).CopyFrom(source.Slice([start], [length]))
     
    FM-Productions and nyanpath like this.
  16. Baggers_

    Baggers_

    Joined:
    Sep 10, 2017
    Posts:
    97
    In that case using GetSubArray would probably be faster unless you need it to handle stride.
     
    julian-moschuering likes this.
  17. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    529
    I'm pretty sure MemCpyStride simply forwards to MemCpy when sourceStride==destStride==elementSize but better be safe.
     
  18. Joachim_Ante

    Joachim_Ante

    Unity Technologies

    Joined:
    Mar 16, 2005
    Posts:
    5,203
    The reason GetSubArray is better is because NativeArray is faster than NativeSlice since the compiler can assume there is not a dynamic offset between elements. NativeSlice should only be used if it is actually necessary to have dynamic strides.