Mesh API request: Provide vertices, triangles, colors, uvs properties that accepts NativeArray

This will save CPU on converting NativeArray to .NET array which becomes costly when the array gets bigger. We’re using this function for copying which is fast but it can totally be avoided if Mesh can accept NativeArray instead.

public static unsafe void CopyToFast<T>(this NativeArray<T> nativeArray, T[] array) where T : struct {
    int byteLength = nativeArray.Length * UnsafeUtility.SizeOf(typeof(T));
    void* managedBuffer = UnsafeUtility.AddressOf(ref array[0]);
    void* nativeBuffer = nativeArray.GetUnsafePtr();
    UnsafeUtility.MemCpy(managedBuffer, nativeBuffer, byteLength);
}
9 Likes

Edit: More future proof. Array is pinned. Keep GCHandle alive while jobs are using the native array.
As the function name makes clear, this is totally safe :roll_eyes:.
Disclaimer: don’t use in production, might break in the future:

    unsafe void DoTotallySafeStuff()
    {
        var someBuffer = new Vector3[1024 * 64];
      
        var handle = GCHandle.Alloc(someBuffer, GCHandleType.Pinned);
        var mesh = new Mesh();
        try
        {
            var managedBuffer = handle.AddrOfPinnedObject().ToPointer();

            var native = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<float3>(managedBuffer, someBuffer.Length, Allocator.Invalid);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
            NativeArrayUnsafeUtility.SetAtomicSafetyHandle( ref native, AtomicSafetyHandle.Create());
#endif
      
            for (var i = 0; i < 512; i++)
            {
                native[i] = new float3(1, 2, 3);
            }

            var header = GetHeader(managedBuffer);
            var originalLength = header->length;
            try
            {
                header->length = (UIntPtr)512;
                mesh.vertices = someBuffer;
            }
            finally
            {
                header->length = originalLength;
            }
        }
        finally
        {
            handle.Free();
            Destroy(mesh);
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    struct ArrayHeader
    {
        public UIntPtr type;
        public UIntPtr length;
    }

    unsafe static ArrayHeader* GetHeader(void* pBytes)
    {
        return (ArrayHeader*)pBytes - 1;
    }
1 Like

This is not safe and just plain bad.

This will crash if the GC moves the memory or collects it.
Use the fixed keyword to extract the pointer to the data.That is actually robust. Fixed keyword tells the GC that the array must be pinned (not moved by the GC)

Wasn’t serious but as far as I know unity’s gc doesn’t reallocate which is the reason fragmentation is a huge problem.

That is true today but won’t be true later next year.
Making those assumptions is what makes grown men cry when upgrading…

5 Likes

sGen GC coming in 3 months, Yay! :slight_smile:
Edit: :frowning:

For the record: I don’t use this hack and wouldn’t use it in production but it is useful for prototyping until NativeArray interfaces are here.
The array header hack is actually active in Dungeons 2 and fixed alot of performance problems back then.

Edit: Managed array is now properly pinned and should not break with future GC updates.