I am currently writing a compute shader that requires a simple float multiplication of a float and a uint3, and I am getting really inaccurate results.
for example, uint3(1, 2, 3) * 0.25
expected result: uint3(0.25, 0.5, 0.75)
actual result: uint3(0.3, 0.5, 0.8)
Any idea how to increase the precision somehow?
The compute shader:
[numthreads(X_THREADS, Y_THREADS, Z_THREADS)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
// Fill the array
uint tid = id.x;
while (tid < octree_length)
{
// Normalize tid
uint depth = OctreeDepth(tid);
uint mid = tid - depth_offsets[depth];
// Compute node data
float node_size = bounds_size / pow(2, depth);
float3 node_min = MortonToXYZ(mid) * node_size;
// Build node
octree[tid].anchor = node_min;
octree[tid].size = node_size;
// Move tid
tid += total_num_threads;
}
}
The inaccurate multiplication:
float3 node_min = MortonToXYZ(mid) * node_size;
- mid: a Morton code for this thread
- MortonToXYZ: returns uint3 with elements between [0, infinity], this is a decoded Morton code
- node_size: a float between [0, infinity]
The code that launches the shader and reads the data back:
ComputeBuffer BuildOctree(Vector3 boundsMin, float boundsSize, int octreeDepth, Vector3Int numThreadGroups)
{
// Compute constants
int totalNumThreads = numThreadGroups.x * numThreadGroups.y * numThreadGroups.z * THREADS_PER_GROUPS;
// Compute depth offsets and octree length
int[] depthOffsets = new int[octreeDepth];
int length = 0;
for (int i = 0; i < octreeDepth; i++)
{
depthOffsets[i] = length;
length += (int) Mathf.Pow(8, i);
}
// Prepare buffers
ComputeBuffer octree = new ComputeBuffer(length, 4 * sizeof(float), ComputeBufferType.Structured);
ComputeBuffer depthOffsetsGPU = new ComputeBuffer(octreeDepth, sizeof(int), ComputeBufferType.Structured);
depthOffsetsGPU.SetData(depthOffsets);
octree.SetData(new OctreeNode[length]);
// Load data into shader
OCTREE_BUILDER.SetBuffer(0, "octree", octree);
OCTREE_BUILDER.SetBuffer(0, "depth_offsets", depthOffsetsGPU);
OCTREE_BUILDER.SetInt("total_num_threads", totalNumThreads);
OCTREE_BUILDER.SetInt("octree_length", length);
OCTREE_BUILDER.SetFloat("bounds_size", boundsSize);
// Launch kernal
OCTREE_BUILDER.Dispatch(0, numThreadGroups.x, numThreadGroups.y, numThreadGroups.z);
OctreeNode[] output = new OctreeNode[length];
octree.GetData(output);
for (int i = 0; i < output.Length; i++)
Debug.Log("cell[" + i + "]: " + output[i].anchor + ", " + output[i].size);
// Return octree buffer
return octree;
}
Note:\
- I have tried a minimal example of just computing uint3(1, 1, 1) * 0.25
expected result: uint3(0.25, 0.25, 0.25)
actual result: uint3(0.3, 0.3, 0.3)\ - I am using a RTX 2070
As user @bart pointed out, the issue was in the print, unity prints a 2 digit format which rounded my values down, by using
ToString("F5")
in my print it shows the correct values