Skip to content

Instantly share code, notes, and snippets.

@LotteMakesStuff
Created August 8, 2018 00:30
Show Gist options
  • Select an option

  • Save LotteMakesStuff/c2f9b764b15f74d14c00ceb4214356b4 to your computer and use it in GitHub Desktop.

Select an option

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!

@g-adamopoulos
Copy link
Copy Markdown

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
Copy Markdown

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