GLSL 中统一浮点行为和常量浮点行为的不同

Lou*_*uen 5 opengl floating-point precision shader glsl

我正在尝试在 GLSL 中实现模拟双精度,并且观察到一种奇怪的行为差异,导致 GLSL 中出现细微的浮点错误。

考虑以下片段着色器,写入 4 浮点纹理以打印输出。

layout (location = 0) out vec4 Output
uniform float s;
void main()
{
  float a = 0.1f;
  float b = s;

  const float split = 8193.0; // = 2^13 + 1

  float ca = split * a;
  float cb = split * b;

  float v1a = ca - (ca - a);
  float v1b = cb - (cb - b);

  Output = vec4(a,b,v1a,v1b);
}
Run Code Online (Sandbox Code Playgroud)

这是我观察到的输出

统一的 GLSL 输出:

a = 0.1            0x3dcccccd
b = 2.86129e-06    0x36400497
v1a = 0.0999756    0x3dccc000
v1b = 2.86129e-06  0x36400497
Run Code Online (Sandbox Code Playgroud)

b1现在,以和 的给定值b2作为输入, 的值v2b没有预期的结果。或者至少它没有与 CPU 上相同的结果(如此处所示

C++ 输出:

a = 0.100000     0x3dcccccd
b = 0.000003     0x36400497
v1a = 0.099976   0x3dccc000
v1b = 0.000003   0x36400000
Run Code Online (Sandbox Code Playgroud)

v1b请注意( 0x36400497vs )值的差异0x36400000

因此,为了弄清楚发生了什么(以及谁是对的),我尝试在 GLSL 中重做计算,用常量替换统一值,使用稍微修改过的着色器,其中我用它的值替换统一值。

layout (location = 0) out vec4 Output
void main()
{
  float a = 0.1f;
  float b = uintBitsToFloat(0x36400497u);

  const float split = 8193.0; // = 2^13 + 1

  float ca = split * a;
  float cb = split * b;

  float v1a = ca - (ca - a);
  float v1b = cb - (cb - b);

  Output = vec4(a,b,v1a,v1b);
}
Run Code Online (Sandbox Code Playgroud)

这次,我得到了与相同计算的 C++ 版本相同的输出。

带有常量的 GLSL 输出:

a = 0.1            0x3dcccccd
b = 2.86129e-06    0x36400497
v1a = 0.0999756    0x3dccc000
v1b = 2.86102e-06  0x36400000
Run Code Online (Sandbox Code Playgroud)

我的问题是,是什么使浮点计算在统一变量和常量之间表现不同?这是某种幕后编译器优化吗?

以下是来自笔记本电脑英特尔 GPU 的 OpenGL 供应商字符串,但我也在 nVidia 卡上观察到了相同的行为。

Renderer : Intel(R) HD Graphics 520
Vendor   : Intel
OpenGL   : 4.5.0 - Build 23.20.16.4973
GLSL     : 4.50 - Build 23.20.16.4973
Run Code Online (Sandbox Code Playgroud)

Lou*_*uen 4

因此,正如@njuffa在评论中提到的,通过对依赖于严格 IEEE754 操作的值使用precise修饰符来解决问题:

layout (location = 0) out vec4 Output
uniform float s;
void main()
{
  float a = 0.1f;
  float b = s;

  const float split = 8193.0; // = 2^13 + 1

  precise float ca = split * a;
  precise float cb = split * b;

  precise float v1a = ca - (ca - a);
  precise float v1b = cb - (cb - b);

  Output = vec4(a,b,v1a,v1b);
}
Run Code Online (Sandbox Code Playgroud)

输出 :

a = 0.1            0x3dcccccd
b = 2.86129e-06    0x36400497
v1a = 0.0999756    0x3dccc000
v1b = 2.86102e-06  0x36400000
Run Code Online (Sandbox Code Playgroud)

编辑:很可能只precise需要最后一个来限制导致其计算的操作,以避免不必要的优化。