Strange float overflow in fragment shader?

I am witnessing a strange float overflow (?) in fragment shader. I am trying to understand what’s exactly going in. OpenGL ES 3.1, Adreno 318.

Here’s the code:


precision lowp float;
(...)
float f1 = 255.0;
float f2 = 255.0;

uint rgb = (uint(f1)<<16u) + uint(f2);

if(  rgb == 0u ) displayRedPixel();   // for debugging this mystery only 
else             displayGreenPixel(); // so I can see what's the value of rgb...

The above displays Red pixels, i.e. rgb==0u. Now this:


precision lowp float;
(...)
float f2 = 255.0;

uint rgb = (uint(255.0)<<16u) + uint(f2);

if(  rgb == 0u ) displayRedPixel();   // for debugging this mystery only 
else             displayGreenPixel(); // so I can see what's the value of rgb...

displays GREEN pixels, i.e. rgb is now != 0u (actually further debugging proves that now rgb’s value is exactly what one would expect). What the hell?

If I up the precision ( ‘precision highp float’ ) then the overflow does not happen (that’s why I suspect it is some kind of float overflow) . So I could correct this by setting float precision to ‘highp’, but at this point I am just trying to understand why


precision lowp float;
float f1=255.0;
float f2=255.0;
uint rgb = (uint(f1)<<16u) + uint(f2);

results in rgb==0u but


precision lowp float;
float f2=255.0;
uint rgb = (uint(255.0)<<16u) + uint(f2);

or


precision lowp float;
float f1=255.0;
uint rgb = (uint(f1)<<16u) + uint(255.0);

or


precision lowp float;
float f1=255.0;
float f2=255.0;
uint rgb = (uint(f1)<<15u) + uint(f2);

do not?

[QUOTE=Utumno;1291130]I am witnessing a strange float overflow (?) in fragment shader. I am trying to understand what’s exactly going in. OpenGL ES 3.1, Adreno 318.

Here’s the code:


precision lowp float;
(...)
float f1 = 255.0;
float f2 = 255.0;

[/QUOTE]

A [var]lowp[/var] float is only required to support the range -2.0 to 2.0. Anything outside that is implementation-defined. Values which don’t work in general may sometimes work if the compiler can optimise out intermediate stages (e.g. uint(255.0) might get optimised to the equivalent of [var]255u[/var], while storing the value in a variable might not).

More generally, trying to determine what will actually happen for implementation-defined behaviour requires information which typically isn’t published (e.g. compiler internals).

ok, I can see that now in section 4.7.1 of the GLSL ES 3.1 spec (thanks!).

In the meantime though I figured out that this


precision mediump float;  // or lowp, but not highp. In general on this platform it seems that mediump == lowp
float f1 = 1.0;
float f2 = 1.0;
int rgb = (int(f1)<<16) + int(f2);

also results in rgb==0. The above is also the simplest code which reproduces this issue; for example


precision mediump float;
float f1 = 1.0;
float f2 = 1.0;
int i1   = int(f1);
int i2   = int(f2);
int rgb  = (i1<<16) + i2;

and rgb != 0…

This might be related to integer precision. The ES 3.1 GLSL specification says:

In cases where operands do not have a precision qualifier, the precision qualification will come from the other operands. If no operands have a precision qualifier, then the precision qualifications of the operands of the next consuming operation in the expression will be used. This rule can be applied recursively until a precision qualified operand is found. If necessary, it will also include the precision qualification of lvalues for assignments, of the declared variable for initializers, of formal parameters for function call arguments, or of function return types for function return values. If the precision cannot be determined by this method e.g. if an entire expression is composed only of operands with no precision qualifier, and the result is not assigned or passed as an argument, then it is evaluated at the default precision of the type or greater. When this occurs in the fragment shader, the default precision must be defined.

What isn’t clear from this is whether conversions from floats to integers can cause an integer expression to inherit the precision from float operands. If that’s the case, you’re explicitly setting the default float precision to [var]mediump[/var], and a [var]mediump[/var] integer is only required to have 16 bits. The default precision for integers is [var]highp[/var] in vertex and compute shaders and [var]mediump[/var] in fragment shaders. If you’re relying upon 32-bit integers, I’d suggest explicitly using [var]highp[/var]-qualified variables.

Thanks, I see. Now I am using highp.

I guess my problem is, this GLSL stuff looks deceptively like C but then seemingly simple stuff like

float f1 = 1.0;
float f2 = 1.0;
int rgb = (int(f1)<<16) + int(f2);

results in rgb==0 and boggles my mind…