- Home /
Cast Vector3[] to float3[]
Hey there,
I'm developing a critical part of an application that must iterate through an array of 1.000.000+ Vector3 objects. Through some testing I discovered that the speed of the existing algorithm improves 30% just by iterating through a float3 array instead.
After manipulation, the array must feed a mesh vertices array, so must be converted to Vector3[].
As the distribution in memory of a Vector3 and a float3 is the same, I know there must be a way to quickly cast from one to the other (in C it would be through pointers, but doesn't seem as easy in C+).
I also tried to copy from Vector3[] to float3[], but obviously this method makes me have a duplication in memory. The way I tried to do this is with the following:
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
private static unsafe extern void CopyMemory(void* dest, void* src, int count);
private static unsafe void Serialize(Vector3[] src, float3[] dst)
{
fixed (void* d = &dst[0])
{
fixed (void* s = &src[0])
{
CopyMemory(d, s, src.Length * sizeof(float) * 3);
}
}
}
Anybody has an idea on how to achieve this?
So iterating through float3 is faster than vector3, but you want to cast each one of them twice in the process. I suppose the function calls alone will eventually negate the benefit.
I don't want to cast each of them. I want to cast the array as a whole. As a Vector3[1000] and a float3[1000] occupy exactly the same amount of memory and their components are in the same place, I want to "reinterpret" the bunch of memory.
I'm 90% sure they don't occupy the same space in memory, just because they consist of three float members each, the Compiler can reorder data fields or implicitly pad different bits between fields, you should remember that for a pointer swap not only the types need to be the same, they need to sit at the same spot inside their respective memory layout AND sizeof() needs to be the same for both structs, which I don't think is guaranteed for Vector3 and float3
Answer by andrew-lukasik · Mar 03, 2021 at 09:19 AM
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Assert = UnityEngine.Assertions.Assert;
public unsafe static void MemCpy <SRC,DST> ( SRC[] src , DST[] dst )
where SRC : struct
where DST : struct
{
int srcSize = src.Length * UnsafeUtility.SizeOf<SRC>();
int dstSize = dst.Length * UnsafeUtility.SizeOf<DST>();
Assert.AreEqual( srcSize , dstSize , $"{nameof(srcSize)}:{srcSize} and {nameof(dstSize)}:{dstSize} must be equal." );
void* srcPtr = UnsafeUtility.PinGCArrayAndGetDataAddress( src , out ulong srcHandle );
void* dstPtr = UnsafeUtility.PinGCArrayAndGetDataAddress( dst , out ulong dstHandle );
UnsafeUtility.MemCpy( destination:dstPtr , source:srcPtr , size:srcSize );
UnsafeUtility.ReleaseGCObject( srcHandle );
UnsafeUtility.ReleaseGCObject( dstHandle );
}
public unsafe static void MemCpy <SRC,DST> ( NativeArray<SRC> src , DST[] dst )
where SRC : struct
where DST : struct
{
int srcSize = src.Length * UnsafeUtility.SizeOf<SRC>();
int dstSize = dst.Length * UnsafeUtility.SizeOf<DST>();
Assert.AreEqual( srcSize , dstSize , $"{nameof(srcSize)}:{srcSize} and {nameof(dstSize)}:{dstSize} must be equal." );
void* srcPtr = NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr( src );
void* dstPtr = UnsafeUtility.PinGCArrayAndGetDataAddress( dst , out ulong handle );
UnsafeUtility.MemCpy( destination:dstPtr , source:srcPtr , size:srcSize );
UnsafeUtility.ReleaseGCObject( handle );
}
< credits roll >
https://forum.unity.com/threads/terraindata-api-nativearray.662620/#post-4442287
https://gist.github.com/LotteMakesStuff/6198f966e414a88d1337b0360cb891f5
< credits roll >
Thank you for your answer! I'm sure that it can be helpful in some situations, but it copies memory, so we still have a duplication, right? The good point of this over the method I use, is that this one is not Windows-dependent.
Yes, you need two allocations of matching lengths but this copy operation is one of the fastest possible. Definitely faster that what Linq's Cast() bs can do.
You can cast pointers tho
float3 f3 = new float3{};
float3* f3Ptr = &f3;
Vector3* v3ptr = (Vector3*) f3Ptr;
I don't think you can cast struct array references in c# right now.
Btw. try this instead: Cast every value before/after processing and ignore all that array type casting drama:
Vector3[] vectors = ...;
for( int i=0 ; i<vectors.Length ; i++ )
{
float3 f3 = (float3) vectors[i];// cast #1
{
// all relevent data transformations
f3 = math.pow( f3 , 10 );
}
vectors[i] = (Vector3) f3;// cast #2
}
Your answer
![](https://koobas.hobune.stream/wayback/20220613030118im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
Get forward direction of face hitted by Raycast. 1 Answer
Modify mesh problems 1 Answer
Failed setting triangles in my mesh 1 Answer
Mesh creation by code not working? 0 Answers
Array index is out of range 2 Answers