Search Unity

NativeArray and Mesh

Discussion in 'Entity Component System' started by DaDonik, Mar 21, 2018.

  1. DaDonik

    DaDonik

    Joined:
    Jun 17, 2013
    Posts:
    258
    It would be great if we could pass NativeArray's to the Mesh API.

    Methods like Mesh.SetVertices(), Mesh.SetNormals(), etc. currently require a list and can therefore not be jobified properly.

    Are there any plans to support this in the forseeable future?
     
  2. RootCauseProductions

    RootCauseProductions

    Joined:
    Feb 1, 2018
    Posts:
    31
    More then that, Mesh wants Vector3s and can only be used from the main thread. I do hope we get a way to create and manage meshes from inside the Jobs system (and thus inside ECS as well).
     
    Opeth001, MNNoxMortem, JesOb and 3 others like this.
  3. DaDonik

    DaDonik

    Joined:
    Jun 17, 2013
    Posts:
    258
    It would be great to alter meshes directly from a job, but for the time being i would be satisfied when i could just pass the data i have calculate in the job system to the mesh on the main thread.
    Let's not get too greedy ;)
     
    MNNoxMortem likes this.
  4. RootCauseProductions

    RootCauseProductions

    Joined:
    Feb 1, 2018
    Posts:
    31
    :D "The point is, ladies and gentleman, that greed, for lack of a better word, is good." - Gordon Gekko in Wall Street

    I'm not sure there would be huge amount of difference between modify Mesh and creating a new way to create a mesh then manage getting it to the main thread (when needed, DirectX 12, Vulkan and Metal don't have this limit).
     
  5. xoofx

    xoofx

    Unity Technologies

    Joined:
    Nov 5, 2016
    Posts:
    418
    Can't tell exactly when it is planned, but this is definitely part of the plan. There is an ultimate goal to make the whole rendering pipeline leveraging the C# ECS/job infrastructure, it will take time but once we will be there, it will be quite amazing.
     
    MNNoxMortem, Orimay, GilCat and 9 others like this.
  6. DaDonik

    DaDonik

    Joined:
    Jun 17, 2013
    Posts:
    258
    Thanks for confirming that.
    Not that i'm in a hurry, but i think i speak for all users here when i say "We want it NOW" :)
     
    davidfrk likes this.
  7. alexzzzz

    alexzzzz

    Joined:
    Nov 20, 2010
    Posts:
    1,447
    In the future we will also want to have overloads that would accept the types from the new math library, e.g. NativeArray<float3> etc
     
    Last edited: Mar 22, 2018
  8. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    better to not be outdated when that time comes :p
     
  9. GabrieleUnity

    GabrieleUnity

    Unity Technologies

    Joined:
    Sep 4, 2012
    Posts:
    116
    Absolutely. When the new math library will be out of experimental, we will make sure the new types can be used on any Unity API where it makes sense.
     
    MNNoxMortem, Orimay, JesOb and 4 others like this.
  10. FROS7

    FROS7

    Joined:
    Apr 8, 2015
    Posts:
    26
    This makes me gitty. I know good things take time, but i cant WAIT for all of this and will be watching the progress on all of this very closely.
     
  11. ecurtz

    ecurtz

    Joined:
    May 13, 2009
    Posts:
    640
    Any update on this or is it another case where we'll be waiting years for a few lines of code that everyone agrees are needed?
     
  12. davenirline

    davenirline

    Joined:
    Jul 7, 2010
    Posts:
    987
    I'm glad I'm not the only one. I have requested this in an older thread. :)
     
  13. ecurtz

    ecurtz

    Joined:
    May 13, 2009
    Posts:
    640
  14. slim_trunks

    slim_trunks

    Joined:
    Dec 31, 2017
    Posts:
    41
    My RAM would appreciate this feature.
    This is one of the only (and biggest) sources of garbage I am forced to deal with as of now.

    But it's probably more likely that Unity is focusing on reworking the Mesh Component from the ground up for ECS.
    Just a guess.
     
  15. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    How I convert DynamicBuffers to Mesh, no garbage, super quick Single memcpy per buffer.

    I actually had an even faster version that I've talked about a bit before where I could manipulate a List<T> within a job so it didn't even require a memcopy but I feel the very minor performance cost of a memcopy is worth the safety of using buffers instead. Also means I don't need SharedComponentData.

    Code (CSharp):
    1.  
    2.     public class MeshSystem : ComponentSystem
    3.     {
    4.         private readonly List<Vector3> verticesList = new List<Vector3>();
    5.         private readonly List<Vector3> normalsList = new List<Vector3>();
    6.         private readonly List<Vector3> uvsList = new List<Vector3>();
    7.         private readonly List<int> trianglesList = new List<int>();
    8.  
    9.         // ...
    10.  
    11.         private void SetMesh(
    12.             Mesh mesh,
    13.             DynamicBuffer<Vector3> vertices,
    14.             DynamicBuffer<Vector3> uvs,
    15.             DynamicBuffer<Vector3> normals,
    16.             DynamicBuffer<int> triangles)
    17.         {
    18.             mesh.Clear();
    19.  
    20.             if (vertices.Length == 0)
    21.             {
    22.                 return;
    23.             }
    24.  
    25.             this.verticesList.NativeAddRange(vertices);
    26.             this.uvsList.NativeAddRange(uvs);
    27.             this.normalsList.NativeAddRange(normals);
    28.             this.trianglesList.NativeAddRange(triangles);
    29.  
    30.             mesh.SetVertices(this.verticesList);
    31.             mesh.SetNormals(this.normalsList);
    32.             mesh.SetUVs(0, this.uvsList);
    33.             mesh.SetTriangles(this.trianglesList, 0);
    34.  
    35.             this.verticesList.Clear();
    36.             this.normalsList.Clear();
    37.             this.uvsList.Clear();
    38.             this.trianglesList.Clear();
    39.         }
    Code (CSharp):
    1. public static unsafe void NativeAddRange<T>(this List<T> list, DynamicBuffer<T> dynamicBuffer)
    2.             where T : struct
    3.         {
    4.             NativeAddRange(list, dynamicBuffer.GetBasePointer(), dynamicBuffer.Length);
    5.         }
    6.  
    7.         private static unsafe void NativeAddRange<T>(List<T> list, void* arrayBuffer, int length)
    8.             where T : struct
    9.         {
    10.             var index = list.Count;
    11.             var newLength = index + length;
    12.  
    13.             // Resize our list if we require
    14.             if (list.Capacity < newLength)
    15.             {
    16.                 list.Capacity = newLength;
    17.             }
    18.  
    19.             var items = NoAllocHelpers.ExtractArrayFromListT(list);
    20.             var size = UnsafeUtility.SizeOf<T>();
    21.  
    22.             // Get the pointer to the end of the list
    23.             var bufferStart = (IntPtr) UnsafeUtility.AddressOf(ref items[0]);
    24.             var buffer = (byte*)(bufferStart + (size * index));
    25.  
    26.             UnsafeUtility.MemCpy(buffer, arrayBuffer, length * (long) size);
    27.  
    28.             NoAllocHelpers.ResizeList(list, newLength);
    29.         }
     
  16. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    157
    How did you get access to the internal NoAllocHelpers class? It doesn't appear that you're using reflection.
     
  17. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Originally I wrote methods to mimic the behaviour but it required System.Reflection.Emit which is a no go so I just ended up calling it with reflection. The reflection is cached so except for the first time, it's nearly just as fast as calling it normally.

    Code (CSharp):
    1.     using System;
    2.     using System.Collections.Generic;
    3.     using System.Reflection;
    4.  
    5.     using UnityEngine;
    6.  
    7.     /// <summary>
    8.     /// Provides access to the internal UnityEngine.NoAllocHelpers methods.
    9.     /// </summary>
    10.     public static class NoAllocHelpers
    11.     {
    12.         private static readonly Dictionary<Type, Delegate> ExtractArrayFromListTDelegates = new Dictionary<Type, Delegate>();
    13.         private static readonly Dictionary<Type, Delegate> ResizeListDelegates = new Dictionary<Type, Delegate>();
    14.  
    15.         /// <summary>
    16.         /// Extract the internal array from a list.
    17.         /// </summary>
    18.         /// <typeparam name="T"><see cref="List{T}"/>.</typeparam>
    19.         /// <param name="list">The <see cref="List{T}"/> to extract from.</param>
    20.         /// <returns>The internal array of the list.</returns>
    21.         public static T[] ExtractArrayFromListT<T>(List<T> list)
    22.         {
    23.             if (!ExtractArrayFromListTDelegates.TryGetValue(typeof(T), out var obj))
    24.             {
    25.                 var ass = Assembly.GetAssembly(typeof(Mesh)); // any class in UnityEngine
    26.                 var type = ass.GetType("UnityEngine.NoAllocHelpers");
    27.                 var methodInfo = type.GetMethod("ExtractArrayFromListT", BindingFlags.Static | BindingFlags.Public)
    28.                     .MakeGenericMethod(typeof(T));
    29.  
    30.                 obj = ExtractArrayFromListTDelegates[typeof(T)] = Delegate.CreateDelegate(typeof(Func<List<T>, T[]>), methodInfo);
    31.             }
    32.  
    33.             var func = (Func<List<T>, T[]>)obj;
    34.             return func.Invoke(list);
    35.         }
    36.  
    37.         /// <summary>
    38.         /// Resize a list.
    39.         /// </summary>
    40.         /// <typeparam name="T"><see cref="List{T}"/>.</typeparam>
    41.         /// <param name="list">The <see cref="List{T}"/> to resize.</param>
    42.         /// <param name="size">The new length of the <see cref="List{T}"/>.</param>
    43.         public static void ResizeList<T>(List<T> list, int size)
    44.         {
    45.             if (!ResizeListDelegates.TryGetValue(typeof(T), out var obj))
    46.             {
    47.                 var ass = Assembly.GetAssembly(typeof(Mesh)); // any class in UnityEngine
    48.                 var type = ass.GetType("UnityEngine.NoAllocHelpers");
    49.                 var methodInfo = type.GetMethod("ResizeList", BindingFlags.Static | BindingFlags.Public)
    50.                     .MakeGenericMethod(typeof(T));
    51.                 obj = ResizeListDelegates[typeof(T)] =
    52.                     Delegate.CreateDelegate(typeof(Action<List<T>, int>), methodInfo);
    53.             }
    54.  
    55.             var action = (Action<List<T>, int>)obj;
    56.             action.Invoke(list, size);
    57.         }
    58.     }
     
    Egad_McDad, MNNoxMortem, The5 and 4 others like this.
  18. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    157
    Sweet. I was previously using reflection to manually adjust my managed list's array and size (using FieldInfo.Get/SetValue), but this is a bit faster and way easier to work with. Also doesn't have any recurring GC allocations. Thanks! :D
     
  19. TJHeuvel-net

    TJHeuvel-net

    Joined:
    Jul 31, 2012
    Posts:
    838
    @GabrieleUnity Ideally we could use it on anything that is enumerable and of type Vector3 or float3. That way we dont need overloads for Lists, and all other types work too.
     
  20. owen_proto

    owen_proto

    Joined:
    Mar 18, 2018
    Posts:
    120
    The mathematics library has been out of preview for a few months. Is there a timeline now for supporting it throughout the other libraries (but like OP I'm particularly interested in meshes)?
     
  21. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    529
  22. owen_proto

    owen_proto

    Joined:
    Mar 18, 2018
    Posts:
    120
    @julian-moschuering Thank you! This looks even better than I was expecting. It's unclear whether the mathematics library types are accepted though. I'll have to test it out when I get a moment unless someone knows offhand.
     
    Last edited: Aug 29, 2019
  23. owen_proto

    owen_proto

    Joined:
    Mar 18, 2018
    Posts:
    120
    @julian-moschuering It looks like you may be able to set a mesh's VertexAttributeFormat, for example, with SetVertexBufferParams allowing manual specification of the data type (aka float3). I cannot find the use of these in the documentation, though.
     
  24. julian-moschuering

    julian-moschuering

    Joined:
    Apr 15, 2014
    Posts:
    529
    Last edited: Aug 29, 2019
    Dinamytes and owen_proto like this.
  25. Dinamytes

    Dinamytes

    Joined:
    Nov 3, 2016
    Posts:
    51
    Is it possible to use Mesh.SetVertexBufferParams with Position - float16 (mathemetics.half)?
    I'm getting a format error on Mesh.SetVertexBufferData
     
  26. owen_proto

    owen_proto

    Joined:
    Mar 18, 2018
    Posts:
    120
    Are there equivalent methods for setting UVs, normals and tangents manually?

    Edit: jeeze, I just needed to read a little more carefully. To answer my own question in case someone else is feeling as lazy as I was, its done by creating a struct for a custom vertex layout that contains values for vertex position, normal, tangent, and uv. Then by specifying the proper data characteristics in the second argument of Mesh.SetVertexBufferParams, you can pass in an array of your custom vertex struct to get all of that in there. The VertexAttribute that corresponds with default UVs is TexCoord0. This is all in the example for Mesh.SetVertexBufferData.
     
    Last edited: Sep 3, 2019
  27. TheJavierD

    TheJavierD

    Joined:
    Jan 6, 2017
    Posts:
    51
    Would it be possible to use NativeList instead of DynamicBuffer?, I'm not using Entities and i don't think i can use DynamicBuffer.

    I'm thinking something like:
    NativeAddRange<T>(this List<T> list, NativeList<T> aNativeList)
     
  28. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    This thread is a bit dated as Unity added support for using NativeArray with SetVertices, SetUVs in 2019.3

    Just use AsArray on your NativeList and pass that in.
     
  29. coidevoid

    coidevoid

    Joined:
    Oct 26, 2017
    Posts:
    55
    I'm wondering, how would you do that with the triangles ? The mesh won't take a NativeArray in the triangles so it makes it tricky to avoid GC for me
     
    bb8_1 likes this.
  30. dartriminis

    dartriminis

    Joined:
    Feb 3, 2017
    Posts:
    157
    Instead of using SetTrianges(), consider using SetIndices(). Not sure why SetTrianges() doesn't have a NativeArray equivalent.
     
    bb8_1 likes this.
  31. cvoigt

    cvoigt

    Joined:
    Dec 29, 2013
    Posts:
    9
    As far as I can see, there is still no support for using NativeArrays with MaterialPropertyBlocks. Are there any plans to add this, as it basically causes the same problems?
     
  32. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    6,005
    Mesh "simple" API entirely supports the native arrays. Well, not entirely... One small setter still holds out against the natives ... why was that omitted? It makes no sense. :(

    upload_2020-6-6_13-8-2.png

    That's in Unity 2020.1.0b10 ...
     
  33. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    SirDorius9 and Nothke like this.
  34. chadfranklin47

    chadfranklin47

    Joined:
    Aug 11, 2015
    Posts:
    229
    This is interesting stuff, thanks. I would expect to see a fixed block or a GCHandle.Alloc() on the backing array though, or am I missing something?