3

I've seen a lot of GLSL code that looks like this:

vec2 x = y * 0.00390625;

Normally, for clarity, I'd write this as:

vec2 x = y * (1.0 / 256.0);

Or as:

vec2 x = y / 256.0;

Is it justified to use the top version? It seems to me that your compiler would have to be pretty pathetic indeed if it didn't support constant folding for numeric literals, or simple strength reduction.

So my question could be rephrased: Are you likely to actually encounter GLSL compilers without support for constant folding or strength reduction? I'm just thinking about the GLSL compilers in your OpenGL implementation, not GLSL optimizers.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415

1 Answers1

5

I'm not a compiler expert, but I would be very surprised if a GLSL compiler did not do simple constant folding. So I think it should be safe to assume that the division of the constant in the following case gets evaluated at compile time:

vec2 x = y * (1.0 / 256.0);

The second case is actually much more interesting:

vec2 x = y / 256.0;

If this were C or C++ code, my understanding is that a compiler would not replace this by a multiplication by default. The reason is that it could not be guaranteed that the result would be identical to the last bit, due to different rounding. For reference, see the answers in Optimizing a floating point division and conversion operation, where one of the answers confirms that for example gcc does not do the replacement unless the -freciprocal-math compiler option is used.

However, GLSL is a different case. The OpenGL ES GLSL spec specifically allows this type of transformation:

The C++ standard requires that expressions must be evaluated in the order specified by the precedence of operations and may only be regrouped if the result is the same or where the result is undefined. No other transforms may be applied that affect the result of an operation. GLSL ES relaxes these requirements in the following ways:

...

Floating point division may be replaced by reciprocal and multiplication.

Interestingly, this is not part of the full (i.e. non-ES) GLSL spec. It does have precision requirements, which say that the error of a division can be at most 2.5 ULP (units in the last place). The way I interpret this, if the replacement of a division by a multiplication meets that precision requirement, the substitution would be legal. But to be sure that your code is as efficient as it can be, it still seems safer to use multiplications instead of divisions where possible.

Community
  • 1
  • 1
Reto Koradi
  • 53,228
  • 8
  • 93
  • 133
  • 1
    I specifically chose 256 because rounding would be identical in either case. – Dietrich Epp Jan 27 '16 at 04:52
  • 1
    This answer is well written, but unfortunately it doesn't really answer the question, which is whether there is reason to believe that your GLSL compiler might not support strength reduction or constant folding. – Dietrich Epp Jan 27 '16 at 04:54
  • 1
    @DietrichEpp that depends on the driver and most devs will err on the side of caution anyway. – ratchet freak Jan 27 '16 at 09:42
  • I was hoping to dig up some real world experience with the GLSL compilers out there in the wild, because it seems like we're stuck with a bunch of compilers of varying quality, and it's hard to examine their output. If you're curious about the case with GCC, you'll find that floating point division strength reduction without `-freciprocal-math`, as long as optimizations are enabled. Funny enough, integer division will get reduced to multiplication even at `-O0`. – Dietrich Epp Jan 27 '16 at 09:52