Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
[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

This comment has been minimized.

Copy link

commented Mar 16, 2019

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.