Always thankful for your support, Lotte💖
Created
August 8, 2018 00:30
-
-
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!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} |
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
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