4

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
niv shalom
  • 93
  • 8
  • Hi, have you tried creating a [minimal example](https://stackoverflow.com/help/minimal-reproducible-example) ? Without the computations around the Morton keys. Most of the time, it helps understand where the problem lies and how you can solve it. – limserhane Jun 10 '22 at 12:17
  • Also, how to you read the data back and print it ? – limserhane Jun 10 '22 at 12:19
  • 1
    @limserhane I have tried creating a minimal example and the problem still persists, and I will add the code for the data reading to the original post now (I am using compute buffers) – niv shalom Jun 10 '22 at 12:40
  • 1
    Can you post this minimal example please ? In general, it's a good practive to post the simplest (most minimal) code you can. It will be easier to help you ;) – limserhane Jun 10 '22 at 12:47
  • 1
    @limserhane yea I just it added to the post, I am making edits in real time XD – niv shalom Jun 10 '22 at 12:48
  • 1
    Side note : you can print expressions inside strings to avoid chaining concatenations : `$"cell[{i}]: {output[i].anchor}, {output[i].size}"` – limserhane Jun 10 '22 at 12:52
  • @limserhane you just blew my mind ty! I didn't know that :) – niv shalom Jun 10 '22 at 12:53
  • Does it happen if you try `int3(1, 1, 1) * 0.25` ? Also, for weird shader shenanigans it's a good idea to specify the model of GPU you use for development. – Soonts Jun 10 '22 at 12:58
  • @Soonts just tested int3(1, 1, 1) * 0.25, it still happens (the result is int3(0.3, 0.3, 0.3)), and I am using a RTX 2070 – niv shalom Jun 10 '22 at 13:11
  • I think uint3(0.25, 0.25, 0.25) should be zero. Did you mean float3(0.25,0.25,0.25) ? – Soonts Jun 10 '22 at 13:22
  • i cannot reproduce this behaviour with a dummy compute shader. – limserhane Jun 10 '22 at 13:29
  • @Soonts I did mean float3(0.25, 0.25, 0.25) – niv shalom Jun 10 '22 at 13:33
  • @limserhane maybe I setup something incorrectly? – niv shalom Jun 10 '22 at 13:34
  • 2
    I don’t know what’s the issue, but here’s an advice how to debug. Make the simplest compute shader you can think of (single thread, single SRV of constant buffer for input, single UAV for output), try to reproduce. If you will reproduce, use https://renderdoc.org/ debugger, it has integration with Unity and it can debug shaders. If you use debug build of the shaders you’ll even get source HLSL in the renderdoc debugger. – Soonts Jun 10 '22 at 13:38
  • 1
    Are you sure this is an issue with the actual values, and not just a printing precision issue? Could you try to format your output to print floating point values at a higher precision? By default Unity's ToString for Vector3 values uses a 2 digit format. Try explicitly using something like `ToString("F5")`. – Bart Jun 10 '22 at 13:52
  • 2
    oh my god, you are right!, my code worked all along XD, ty! – niv shalom Jun 10 '22 at 14:26
  • Yeah, easy mistake to make. I have fallen for that one as well in the past. Same deal if you use a debugger. By default it will show you values at the lower precision, which can be confusing. – Bart Jun 10 '22 at 15:01

1 Answers1

3

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

niv shalom
  • 93
  • 8