在着色器中除以0会发生什么?

时间:2011-04-27 10:02:38

标签: iphone opengl-es glsl shader

已知分支在OpenGL ES着色器中的计算成本特别高。在这样的着色器中,我在除以之前检查值是否为null,例如:

if(value == 0.0)
    other_value = 0.0;
else
    other_value = 1.0 / value;

为了加快速度,我想通过直接做法来避免if

other_value = 1.0 / value;

我想知道如果value碰巧等于0会发生什么,这在我的治疗中有点罕见,这就是为什么测试它并不容易。着色器是否会崩溃?应用程序崩溃了吗?

6 个答案:

答案 0 :(得分:14)

确实未定义。

在iPad模拟器上,将红色分量除以0.0得0(所有红色都从此图片中消失)。

enter image description here

在iPad硬件上,将r分量除以0.0会使其饱和为全​​红色(即+无限制钳位)。

enter image description here

所以不,你根本不能依赖未定义的行为来产生一致的结果,但除了0 in和of之外没什么不好的。

在我的着色器中,我将rgb值除以alpha。碰巧的是,如果alpha值为0,则无论如何都不会出现像素,所以我不会出现任何一个分区的问题(给0或+ inf)。

答案 1 :(得分:4)

这样的分支通常不贵。它们通常使用预测来实现。也就是说,GPU计算两个分支但仅实际存储预测条件为真的指令的结果。因此,不使用跳转指令。这是汇编代码的样子:

cmp_eq p0, r0, 0.0   // predicate = (value == 0.0)
(p0) mov r1, 0.0     // other_value = 0.0, if predicate true
(!p0) rcp r1, r0     // other_value = 1.0 / value, if predicate false

请注意,在这种情况下,实际上不必预测第二条指令。无论如何,正如其他人所指出的,当分母为零时,除法的结果(倒数)是不确定的。但正如你所看到的那样,你应该能够以几个便宜的指令为代价获得明确定义的行为(划分通常很慢)。据我所知,所有支持真实分支(跳转指令)的GPU也支持预测。着色器编译器将评估是使用预测还是跳转,并且通常会做正确的事情。

当然,如果你真的不关心除零的结果,那么你可以节省任何和所有预测成本。

答案 2 :(得分:2)

如果您想限制自己使用iPhone,为什么不尝试看看会发生什么?但是,无法保证将来运行iPhone应用程序的硬件会发生什么。它可能会崩溃。它无能为力。它可以显示奇怪的像素。它可以叫你婆婆。 (因为它是未定义的行为。)

答案 3 :(得分:2)

我从来没有见过这样的着色器。它很可能会导致垃圾值(如nan)并搞乱您对结果执行的任何其他计算。除此之外我不会担心它(并且绝对不会添加分支代码来防止它)。

答案 4 :(得分:2)

而不是

float invert_value(in float value)
{
if(value == 0.0)
    return 0.0;
else
    return 1.0 / value;
}

你可以写

float invert_value_ifless(in float value)
{
float sign_value = sign(value);
float sign_value_squared = sign_value*sign_value;
return sign_value_squared / ( value + sign_value_squared - 1.0);
}

这将完全返回您想要的内容

  1. no ifs
  2. 没有划分0
  3. 我不确定这在预测的近期硬件上是否真的更快。

答案 5 :(得分:1)

在旧的图形硬件上,除零会产生浮点无穷大的值,这实际上是正确的答案。然而,这可能不是你想要的,因为你可能只是将无穷大(或从它衍生的东西)存储在帧缓冲区中,这可能是像RGBA8这样的格式,无法区分无穷大和0或者1。

在较新的桌面硬件上,有可能有一个浮点帧缓冲区,在这种情况下无限是一个有效的东西存储在那里 - 但仍然不太可能是你想要的,除非你的浮点功夫相当先进。