Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GLSL atan on iPhone 6 Plus gives wrong result

Tags:

ios

iphone

glsl

Consider the following fragment shader. When I run it on the Simulator or iPhone 5 (8.3) it shows the expected color (red). If I run it on an iPhone 6 Plus (8.2) it goes to the second if clause (green) that is clearly wrong as the correct result of the calculation should be around 1.22. If I supply the atan parameters directly as compile time constants then the calculation works.

I have tried both on the newest Xcode 6.2 and the newest 6.3 Beta.

void main()
{
    highp float y = 0.57;
    highp float x = 0.21;

    highp float a = atan(y, x); 
    // works if changed to atan(0.57, 0.21);
    // does not work if changed to atan(y / x);

    if (a > 1.2 && a < 1.25)
    {
        // Should always come here
        // Works on Simulator, iPhone 5
        gl_FragColor = vec4(1, 0, 0, 1);
    }
    else if (a > 1.5 && a < 1.55)
    {
        // Comes here on iPhone 6 Plus
        gl_FragColor = vec4(0, 1, 0, 1);
    }

    return;
}

UPDATE: Plot thickens.

If I change the shader in the following way the error goes away and atan returns the right value. It seems that whatever I put to x and y must be something else than compile time constants. This fixed the problem in my production code as well where I have no compile time constants at all. BTW the same problem affect acos and asin as well.

highp float y = texCoord.x;
highp float x = texCoord.x;

if (x == x) // Removing this line makes error return
{
    x = 0.21;
}

if (y == y) // Removing this line makes error return
{
    y = 0.57;
}

Another example. This is how I'm actually using this.

If I do the calculation like this, it fails on iPhone 6:

float y = sin(lon2 - lon1) * cos(lat2);
float x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2 - lon1);

float bearing = atan(y, x) + 2.0 * pi;

return mod(bearing, 2.0 * pi);

If I do it like this, it works:

float y = texCoord.y;
float x = texCoord.x;

if (y == y)
{
    y = sin(lon2 - lon1) * cos(lat2);
}

if (x == x)
{
    x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2 - lon1);
}

float bearing = atan(y, x) + 2.0 * pi;

return mod(bearing, 2.0 * pi);
like image 447
Seppo Avatar asked Oct 31 '22 07:10

Seppo


1 Answers

This looks like a compile-time constant propagation error.

Your if (x == x) clause looks like it could be eliminated by the compiler, but since it is a fp value, it is really a test for NaN.

I.e. comparing anything for equality with a NaN is always false, even when you compre the value against itself.

It looks like this test is stopping the compiler from propagating your constant values, this should be obvious if you ask for assembler output from your compiles.

like image 188
Terje Mathisen Avatar answered Nov 11 '22 03:11

Terje Mathisen