Skip to content

Instantly share code, notes, and snippets.

@LotteMakesStuff
Created August 8, 2018 00:30
Show Gist options
  • Save LotteMakesStuff/c2f9b764b15f74d14c00ceb4214356b4 to your computer and use it in GitHub Desktop.
Save LotteMakesStuff/c2f9b764b15f74d14c00ceb4214356b4 to your computer and use it in GitHub Desktop.
[NativeCollections] How to copy a regular .C# array into a NativeArray suuuuuper quick using memcpy. its about as fast as its ever gunna get!
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine;
public class NativeMeshTest : MonoBehaviour
{
private NativeArray<float3> vertexBuffer;
private Vector3[] vertexArray;
// Use this for initialization
void Start ()
{
var mesh = GetComponent<MeshFilter>().mesh;
vertexArray = mesh.vertices; // note: mesh.vertices allocates an Vector3[] - not garbage free! save it for reuse
vertexBuffer = GetNativeVertexArrays(vertexArray); // copy the array data into a NativeArray. This lets us pass it into Jobs
// some simple work to prove were modifying the arrays
var copyBuffer = new NativeArray<float3>(vertexBuffer, Allocator.Temp); // Lets make a copy of the native array. this is hella fast!
int vertexCount = copyBuffer.Length; // for large meshes during deep profiling this could show up in the profiler (a whole 0.04ms on a 1000 vert mesh!), which is weird cos its tiny! figured i might as well cache it, if only to help in editor deep profiling
for (int i = 0; i < vertexCount; i++)
{
copyBuffer[i] *= 3; // simple modification
}
SetNativeVertexArray(vertexArray, copyBuffer); // now we have changed the vertex positions stored in our NativeArray, lets apply it back on to the actual mesh. we should see our 3d object grow in size!
mesh.vertices = vertexArray;
copyBuffer.Dispose(); // clean up the copy now were done with it
}
unsafe NativeArray<float3> GetNativeVertexArrays(Vector3[] vertexArray)
{
// create a destination NativeArray to hold the vertices
NativeArray<float3> verts = new NativeArray<float3>(vertexArray.Length, Allocator.Persistent,
NativeArrayOptions.UninitializedMemory);
// pin the mesh's vertex buffer in place...
fixed (void* vertexBufferPointer = vertexArray)
{
// ...and use memcpy to copy the Vector3[] into a NativeArray<floar3> without casting. whould be fast!
UnsafeUtility.MemCpy(NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(verts),
vertexBufferPointer, vertexArray.Length * (long) UnsafeUtility.SizeOf<float3>());
}
// we only hve to fix the .net array in place, the NativeArray is allocated in the C++ side of the engine and
// wont move arround unexpectedly. We have a pointer to it not a reference! thats basically what fixed does,
// we create a scope where its 'safe' to get a pointer and directly manipulate the array
return verts;
}
unsafe void SetNativeVertexArray(Vector3[] vertexArray, NativeArray<float3> vertexBuffer)
{
// pin the target vertex array and get a pointer to it
fixed (void* vertexArrayPointer = vertexArray)
{
// memcopy the native array over the top
UnsafeUtility.MemCpy(vertexArrayPointer, NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(vertexBuffer), vertexArray.Length * (long) UnsafeUtility.SizeOf<float3>());
}
}
void Update ()
{
// We could modify our cached version of our vertex buffer here, or totally regenerate it procedurally or whatever, skys the limit!
// instead lets just prove its still valid by drawing it out (badly! really we should also get the triangle index buffer the same way
// and use that to index the actual triangles in the mesh for drawing. still, shows us the general shape!)
for (int i = 1; i < vertexBuffer.Length; i++)
{
Debug.DrawLine(vertexBuffer[i-1], vertexBuffer[i], Color.red);
}
}
private void OnDestroy()
{
// All NativeCollections, including NativeArrays are unmanaged memory - we have to free memory when were done with
// it ouselves! We used a presistant allocater when we created vertextBuffer so we could keep it around for as
// long as we want to. We just have to make sure to call dispose at some point, so just before getting destroyed is as
// good a place as any!
vertexBuffer.Dispose();
}
}

Always thankful for your support, Lotte💖

Buy Me a Coffee at ko-fi.com Become a Patron!

@GeorgeAdamon
Copy link

Hello Lotte and thank you very much for this!

Could this copy operation become even faster by using mesh.GetNativeVertexBufferPtr() instead of mesh.vertices ?
https://docs.unity3d.com/ScriptReference/Mesh.GetNativeVertexBufferPtr.html

@Lunar2kPS
Copy link

Lunar2kPS commented Jul 6, 2020

Hello Lotte and thank you very much for this!

Could this copy operation become even faster by using mesh.GetNativeVertexBufferPtr() instead of mesh.vertices ?
https://docs.unity3d.com/ScriptReference/Mesh.GetNativeVertexBufferPtr.html

I'm wondering the same.

Using mesh.GetNativeVertexBufferPtr(...), I haven't quite figured out how to match their layout on the C# side with a struct -- I'm getting a NullReferenceException when I try to access elements in my NativeArray.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment