When the Fractional Part of a Float Fixes Your Shader

A developer's Voronoi shader stuttered on one Windows RTX 4070 machine but ran fine on five other devices. After LLMs failed to reproduce the bug, manual debugging revealed the culprit: the fract() function in a noise hash. Replacing it with x - floor(x) fixed the issue, exposing a likely GPU driver miscompilation.
A compiler that computes the same equation differently depending on which instructions it emits is, by definition, a miscompile.
- earth-tattoo
Nice article. What a joy to read. Reminds me of what hacker news was until a few years ago!
I haven't worked with opengl/shaders in a while, but was thinking about some algorithm I wrote a few years ago, while in shower yesterday. It brought back the memories of how difficult it was for me to first understand the whole concept of shaders. There's basically no main function, no for loops etc. Your shader is called for each pixel, for every frame. I was wondering how easy it would have been in today's world. I spent like 2 months on something that is just a prompt now.
- Sharlin
Should be noted that `fract(x)` is importantly only equivalent to `x - floor(x)` when x >= 0. Which it was in this case, but often negative values are also possible/expected and the latter should be used instead. This is an easy mistake to make.
- flohofwoe
It turned out to be something different, but another much more common source of shader output differences is when a float value is clamped to integer (with the value being very close to the closest integer), and then use the clamped value as index (or tex coord with an unfiltered sampler). This may result in an off-by-one error on some gpu/driver combos but not others. Completely understandable why it happens, but hard to catch unless testing on a wide range of GPUs and drivers.
TL;DR: don't expect that floating point operations on GPUs are strictly IEEE-754 compatible