为什么“精确”限定词不生效?

时间:2019-05-25 20:40:42

标签: c++ opengl floating-point glsl precision

我正在尝试改进Henry Thasler的GLSL实现的双-单算法(来自他的GLSL Mandelbrot演示),以在Linux上的NVIDIA图形上可靠地工作。我最近了解到,从OpenGL 4.0(the spec中的§4.7精确限定符)或扩展名为GL_ARB_gpu_shader5spec)开始,我们可以使用{{1 }}限定词,使计算遵循GLSL源中指定的精确算术运算顺序。

但是以下尝试似乎没有任何改善:

precise

结果与未添加#version 330 #extension GL_ARB_gpu_shader5 : require vec2 ds_add(vec2 dsa, vec2 dsb) { precise float t1 = dsa.x + dsb.x; precise float e = t1 - dsa.x; precise float t2 = ((dsb.x - e) + (dsa.x - (t1 - e))) + dsa.y + dsb.y; precise vec2 dsc; dsc.x = t1 + t2; dsc.y = t2 - (dsc.x - t1); return dsc; } 相同。我已经检查过算法本身是否正确:它可以在Intel Core i7-4765T内置图形上按原样运行(即使没有precise),如果我隐藏了一些变量来禁止优化,那么NVidia也会给出正确的结果。这是我禁止优化的方法:

precise

因此,显然,我使用了#version 330 #define hide(x) ((x)*one) uniform float one=1; vec2 ds_add(vec2 dsa, vec2 dsb) { float t1 = dsa.x + dsb.x; float e = hide(t1) - dsa.x; float t2 = ((dsb.x - e) + (dsa.x - (t1 - e))) + dsa.y + dsb.y; vec2 dsc; dsc.x = t1 + t2; dsc.y = t2 - (hide(dsc.x) - t1); return dsc; } 限定词不正确。但是这里到底有什么问题呢?

作为参考,我使用带有二进制nvidia驱动程序390.116的NVidia GeForce GTX 750Ti。这是完整的C ++测试:

precise

2 个答案:

答案 0 :(得分:1)

尽管您可能会从这里学习OpenCL或CUDA受益,但我从未精打细算过。

无论如何,您的GLSL version is 3.30, which is tied with OpenGL 3.3。通过扩展可以使用精确的限定词,但如果可能的话,我将始终尝试使用OpenGL的内置功能。<​​/ p>

该扩展名可能无法以相同的方式实现,建议您至少使用GLSL 4.0版,最好是最新的OpenGL / GLSL版本。

有时候,如果没有人使用这些旧扩展,则可以在较新的GPU上进行回归。

GPU编译器在优化方面趋于更加自由。您可能会从查看已编译的着色器的输出中受益,也许可以通过某种方式查看来自带有GLSL的Nvidia编译器的PTX程序集输出。使用CUDA,您绝对可以预览程序集输出,以确保编译器不会对操作进行重新排序。

该规范提到MAD是限定符的主要原因-它将迫使编译器不使用MAD指令。也许用精确的限定词对加/减进行很少的测试。

如果hide为您解决了问题,最好将其命名为一天,我怀疑确切的限定词是否已在GLSL方面进行了彻底检查。我强烈建议为此使用CUDA或OpenCL,如果您还希望快速显示纹理,则可以使用CL-GL interop,这不会很痛苦。

精确的限定符可确保不会对操作进行重新排序,但不会提及不会影响排序的优化。看起来AMD只是在使用它时关闭了优化功能。 Nvidia仍可能会应用会影响您结果的优化,这些优化与操作顺序无关,而与执行添加操作的特定优化有关。

precise float t1 = dsa.x + dsb.x;
precise float e = t1 - dsa.x;

这可能将e计算为简单的dsb.x。编译器可能仍会添加不影响操作顺序的优化,因为这是规范所保证的全部。除了重新排序的操作会影响此结果,我想不出其他任何方法,但是我在这里不是专家。

要注意的另一件事是,根据我对规范的粗略阅读,可能还需要将ds_add的结果存储到精确变量中,以使计算精确。该函数可能仅在Nvidia上内联(至少在历史上它们具有更好的优化),因此我认为编译器可以执行内联,然后,如果将结果存储到非精确变量中,则所有现有的精确限定词都是忽略。

答案 1 :(得分:-2)

着色器没有问题。 ds_add()代码只是在编译时没有任何可以合并的操作。通常加和乘/除合并。但是您的代码仅具有添加操作。

更新:

  1. 在计算过程中,所有变量都存储在GPU寄存器中。寄存器的操作顺序与代码或编译器无关。它甚至不仅仅依赖于硬件。这取决于GPU中当前正在运行的操作。

  2. 寄存器之间的浮点运算精度不是严格的32位。然后通常更高。 GPU的实际精度是商业秘密。尽管变量存储在32位内存中,但x86 FPU的实际精度为80位或128位。

  3. 但是,GPU并非设计用于非常精确的计算。该算法的作者知道这一点,并实现了对32位浮点数的双重考虑。如果需要提高精度,则必须使用带32位浮点数的长双精度数。简单的“精确”无济于事。