ComputeShader in Unity : Why are the values all wrong?

Here’s how the object is defined in C#, for Unity to be able to work with it :

(please note: I left all fields — even the ones not used in this question — in case the issue was in an seemingly-unrelated place than where I’m searching)

[StructLayout(LayoutKind.Explicit)]
public struct CSOrbitFunction
{
    [FieldOffset(0)]
    public OrbitTypes type; // sizeof(enum-int) == 4

    #region IOffsetOrbitFunction Members
    [FieldOffset(4)]
    public float offsetX;
    [FieldOffset(8)] // sizeof(float) == 4
    public float offsetY;
    [FieldOffset(12)]
    public float offsetZ;
    #endregion

    #region output
    [FieldOffset(16)]
    public float outX;
    [FieldOffset(20)] // sizeof(float) == 4
    public float outY;
    [FieldOffset(24)]
    public float outZ;
    #endregion

    #region ILagueKeplerOrbitFunction Members
    [FieldOffset(28)]
    public double periapsis;


    [FieldOffset(36)] // sizeof(double) == 8
    public double apoapsis;

    [FieldOffset(44)]
    public int durationSeconds;
    #endregion

    [FieldOffset(48)]
    public int previous; // linked list

    [FieldOffset(52)]
    public int id;

    public static int SizeInBytes = 56;
}

The values are passed to the shader in a completely standard way :

CSOrbitFunction[] functions = new CSOrbitFunction[1];
var f = new CSOrbitFunction();
f.periapsis = 10.0;
f.apoapsis = 40.0;
f.durationSeconds = 10;
functions[0] = f;

int sizeInBytes = CSOrbitFunction.SizeInBytes;
ComputeBuffer b = new ComputeBuffer(functions.Length, sizeInBytes);
b.SetData(functions);

ComputeShader shader = new ComputeShader();
shader.SetBuffer(0, "functions", computeBuffer);

The shader is “called” like this (still pretty standard)

    var timeMs_ID = Shader.PropertyToID("timeMs");
    shader.SetInt(timeMs_ID, (int)timeMs);

    // Run the shader
    shader.Dispatch(0, functions.Length, 1, 1);

    // Read result (overwrite input)
    computeBuffer.GetData(functions);

This is the shader itself :

// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain


// Must match exactly CSOrbitFunction.cs
struct CSOrbitFunction
{
    int type; // from enum OrbitTypes
    
    // shared by all types
    float offsetX;
    float offsetY;
    float offsetZ;
    
    // store result
    float outX;
    float outY;
    float outZ;    
    
    // if the type is LAGUE_KEPLER
    double periapsis;
    double apoapsis;
    int durationSeconds;
    
    // linked list
    int previous;
    
    int id;
};


RWStructuredBuffer<CSOrbitFunction> functions;
int timeMs;

void foobar(uint3 id : SV_DispatchThreadID);

[numthreads(1024,1,1)] // dimensions
void CSMain (uint3 id : SV_DispatchThreadID)
{
    foobar(id);
}


void foobar(uint3 id : SV_DispatchThreadID)
{
    functions[id.x].outX = (float)functions[id.x].periapsis;
    functions[id.x].outY = (float)functions[id.x].apoapsis;
    functions[id.x].outZ = (float)functions[id.x].durationSeconds;
}

=====================

Now, the problem.

Immediately after GetData, the values are as follows :

enter image description here

outX, outY and outZ are all wrong. And yet, something does happen as the values are changed. Plus, periapsis and apoapsis are correct it seems.

As a debug test, I tried to just do just this (below) inside foobar and it is indeed called and returns that value in outX.

functions[id.x].outX = 666.666;

So why does it fail only with peripasis and apoapsis? Is it because there’s a conversion issue between float and double?

There were two fundamental issues.

  1. for some reason, “double” is allowed syntactically but doesn’t work. I’m not sure what I’m doing wrong (the FieldOffsets seem to be correct) but then casting to float fails miserably. So I changed all the doubles to float. Functions such as cos, sin and sqrt take floats anyways.

  2. PI didn’t work when declared as a “global” variable like I did. It orks only if it’s declared inside the function. I’m probably too new to HLSL shaders to know about those quirks.

After that, everything seemed to work properly.

Leave a Comment