我正在尝试改进Henry Thasler的GLSL实现的双-单算法(来自他的GLSL Mandelbrot演示),以在Linux上的NVIDIA图形上可靠地工作。我最近了解到,从OpenGL 4.0(the spec中的§4.7精确限定符)或扩展名为GL_ARB_gpu_shader5
(spec)开始,我们可以使用{{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
答案 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()代码只是在编译时没有任何可以合并的操作。通常加和乘/除合并。但是您的代码仅具有添加操作。
更新:
在计算过程中,所有变量都存储在GPU寄存器中。寄存器的操作顺序与代码或编译器无关。它甚至不仅仅依赖于硬件。这取决于GPU中当前正在运行的操作。
寄存器之间的浮点运算精度不是严格的32位。然后通常更高。 GPU的实际精度是商业秘密。尽管变量存储在32位内存中,但x86 FPU的实际精度为80位或128位。
但是,GPU并非设计用于非常精确的计算。该算法的作者知道这一点,并实现了对32位浮点数的双重考虑。如果需要提高精度,则必须使用带32位浮点数的长双精度数。简单的“精确”无济于事。