【问题标题】:GLSL compiler optimizations lead to incorrect behavior with floating point operationsGLSL 编译器优化导致浮点运算的错误行为
【发布时间】:2016-05-31 13:05:24
【问题描述】:

我是一个使用 OpenGL 编写 Android 应用程序的团队的一员。我们有很多使用浮点数模拟双精度数学的着色器代码。 (具体来说,我们在 Andrew Thall 的 Extended-Precision Floating-Point Numbers for GPU Computation 中实现了算法。)它在应用程序的 DirectX 版本中运行良好,但我发现在 Android 上,GLSL 编译器正在以这样一种方式优化一些代码,代数, 行为应该被保留,但实际上它会改变行为,因为优化正在丢弃浮点错误。例如,在下面:

vec2 add(float a, float b) {
    float sum = a + b;
    float err = b - (sum - a);
    return vec2(sum, err);
}

编译器将错误值 e 简化为 0,因为这在代数上是正确的,但当然,当考虑到浮点错误时,情况并非总是如此。

我试过“#pragma optimize (off)”,但它不是标准的,没有效果。我发现唯一可行的方法是创建一个保持设置为 0 的“零”统一浮点数,并将其添加到战略位置的违规值,因此上述函数的工作版本将是:

vec2 add(float a, float b) {
    float sum = a + b;
    sum += zero;
    float err = b - (sum - a);
    return vec2(sum, err);
}

这显然不理想。 1)这是一个 PITA 来追踪哪里是必要的,并且 2)它依赖于编译器。另一个编译器可能不需要它,而另一个编译器可以将 e 值优化到 zero。有没有“正确”的方法来解决这个问题并确保 GLSL 编译器不会优化掉实际行为?

编辑:

虽然技术上的答案看起来仍然是“否”,但我找到了更好的解决方法并想在此处记录它。 “零”统一方法确实开始因更复杂的表达式/链接操作而失败。我发现的解决方法是为加法和减法创建两个函数:

float plus_frc(float a, float b) {
    return mix(a, a + b, b != 0);
}

float minus_frc(float a, float b) {
    return mix(0, a - b, a != b);
}

(“frc”代表“force”和“farce”,因为你在强制操作,但必要性是愚蠢的。)这些复制了 (a + b) 和 (a - b) 的功能,但在某种程度上编译器不应该能够优化掉,不使用分支并使用fast builtin 来完成工作。于是上面的保错“add”函数就变成了:

vec2 add(float a, float b) {
    float sum = plus_frc(a, b);
    float err = b - (sum - a);
    return vec2(sum, err);
}

请注意,我们并不总是需要使用我们的“frc”函数(例如查找错误的方程式),但仅在编译器可以完成破坏优化的地方使用。

【问题讨论】:

    标签: android opengl-es glsl glsles


    【解决方案1】:

    没有。 GLSL 中没有控制优化的绑定方法。如果编译器认为假设您的错误项为零是合理的,那么它将为零。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-19
      • 1970-01-01
      • 1970-01-01
      • 2019-04-06
      • 2012-03-04
      • 2012-10-05
      • 2020-06-07
      • 2016-09-19
      相关资源
      最近更新 更多