【发布时间】: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