I am trying to multithread the conversion of bytes to floats using unity’s new job system. Previously, I was using System.BitConverter.ToSingle on the main thread and that was working perfectly. However, since arrays are not supported in the job system, I must use a NativeArray instead. And System.BitConverter.ToSingle does not accept a NativeArray. Converting the NativeArray to a normal array works but slows things down way too much since accessing managed memory from within a job is very slow. So instead, I looked at the source code for BitConverter and tried to just copy what they were doing which seemed simple enough. So I have:
using UnityEngine;
using Unity.Collections;
using Unity.Jobs;
using Unity.Burst;
[BurstCompile]
struct GetVerticesJob : IJobParallelFor
{
[ReadOnly]
public NativeArray<byte> bufferData;
public NativeArray<Vector3> vertices;
public int offset;
public void Execute(int i)
{
int step = 4;
int localOffset = offset + (12 * i);
vertices[i] = new Vector3(
ConvertBytesToFloat(localOffset + (step * 0)),
ConvertBytesToFloat(localOffset + (step * 1)),
ConvertBytesToFloat(localOffset + (step * 2))
);
}
float ConvertBytesToFloat(int offset)
{
int value = bufferData[offset] | bufferData[offset + 1] << 8 | bufferData[offset + 2] << 16 | bufferData[offset + 3] << 24;
return (float)value;
}
}
However, this results in very wrong and enormous values for the floats. What am I doing wrong and how can I get this working? Thanks!
That said, I don’t think your (float) int conversion is going to work either.
Because you’re just converting an int directly into a float. It’s never going to have decimal places.
I doubt this is the expected result you want.
I tried for both littleEndian and bigEndian (your solution) and both give me the wrong values. But at least with littleEndian the sign is always correct.
And yes, I think you’re correct about the cast not working. Not sure what else to try though. Any ideas?
Anyway if you don’t need a copy of your data you can probably just reinterpret the array.
public NativeArray<byte> bufferData;
NativeArray<float> floats = bufferData.Reinterpret<float>(UnsafeUtility.SizeOf<float>());
// these point to same memory, modifying one array will modify the other
I think this will only work if it’s big-endian (haven’t tested this at all).
Nop, this will work for both (considering both data and platform are using the same). If the source is little endian too, your example with Reinterpret is the best solution IMHO. But using Reinterpret could be done without the job.
Ah, perfect. It works now. Thank you!!
This is without using jobs but I guess jobs aren’t actually needed here.
Do you know what the performance implications of Reinterpret are?
the documentation says this ‘creates a view into memory’. Does that mean the new NativeArray does not actually use any memory?
both arrays point to the same memory. making a change on 1 array will modify the other.
if you don’t want this behaviour just make a copy of the array after reinterpretting.
I don’t need to make any changes to the array at all so don’t need to copy. This is perfect.
Is it possible to reinterpret the bytes as an array of vector3s of floats?
i have error
Library\PackageCache\com.unity.2d.common@2.0.2\Runtime\InternalBridge\InternalEngineBridge.cs(17,48): error CS1503: Argument 2: cannot convert from ‘Unity.Collections.NativeArray’ to ‘Unity.Collections.NativeArray<UnityEngine.Vector3>’
while updating package manager.
Any help?
I’ve created a job with a NativeString4096 field, i’m passing a ref to one from outside the job, i’m setting the value inside the job, but after the job Completes the result is still empty.
Shouldn’t it write to it if i pass it by ref to the constructor of the job?
NativeString4096 results = new NativeString4096();
MyJob job = new MyJob(ref results);
JobHandle jobHandle = job.Schedule();
jobHandle.Complete();
Debug.Log(results);
public struct StringJob : IJob
{
public NativeString4096 result;
public StringJob(ref NativeString4096 nativeString)
{
result = nativeString;
}
public void Execute()
{
result = "Hello";
}
}
NativeString4096 is a value-type. If you set it in the job, it won’t be copied back to the original struct that you used to schedule the job. You have to allocate the string somewhere that’s independent of the job, e.g. in a NativeArray<NativeString4096> of length 1:
struct StringJob : IJob {
public NativeArray<NativeString4096> result;
public void Execute() {
result[0] = new NativeString4096("Hello");
}
}