【问题标题】:Extended floating point precision on mobile GPU移动 GPU 上的扩展浮点精度
【发布时间】:2015-05-01 03:10:18
【问题描述】:

我正在尝试使用 opengl-es 2.0 在 gpu 上计算图像的梯度矢量场。我为它找到了一个 cpu 实现,我用它来与我的 gpu 实现进行比较。这里的挑战是 cpu 实现依赖于 java 类型的 float(32 位),而我的 gpu 实现使用的是 lowp float(8 位)。我知道我可以使用 mediump 或 highp 来获得更好的结果,但我仍然想继续使用 lowp float 以确保我的代码能够在最差的硬件上运行。

计算梯度向量场的前几个步骤非常简单:

  1. 计算归一化灰度(红+绿+蓝)/3.0
  2. 计算边缘图(右像素-左像素)/2.0 和(上像素-下像素)/2.0
  3. 计算拉普拉斯算子(稍微复杂一点,但现在不需要详细说明)

目前,无需做任何花哨的事情,我就可以准确地模仿第 1 步,以使 cpu 实现的图像结果与 gpu 的图像结果相同。

不幸的是,我已经卡在第 2 步了,因为我的边缘图计算在 gpu 上不够准确。

所以我尝试实现扩展精度浮点,灵感来自http://andrewthall.org/papers/df64_qf128.pdf

我对 opengl-es 还很陌生,所以我什至不确定我在这里做的是否正确,但下面是我打算编写的操作,以解决我目前正在遭受的这种精度损失。

    vec2 split(float a)
{
    float   t   =   a * (2e-8+1.0);
    float   aHi =   t - (t -a);
    float   aLo =   a - aHi;

    return vec2(aHi,aLo);
}

vec2 twoProd(float a, float b)
{
    float   p   = a * b;
    vec2    aS  = split(a);
    vec2    bS  = split(b);
    float   err = ( ( (aS.x * bS.x) - p) + (aS.x * bS.y) + (aS.y * bS.x) ) + (aS.y * bS.y);

    return vec2(p,err);
}

vec2 FMAtwoProd(float a,float b)
{
    float   x   =   a * b;
    float   y   =   a * b - x;

    return vec2(x,y);
}

vec2 div(vec2 a, vec2 b)
{
    float   q   = a.x / b.x;
    vec2    res = twoProd( q , b.x );
    float   r   = ( a.x - res.x ) - res.y ;

    return vec2(q,r);
}

vec2 div(vec2 a, float b)
{
    return div(a,split(b));
}

vec2 quickTwoSum(float a,float b)
{
    float   s   =   a + b;
    float   e   =   b - (s-a);

    return vec2(s,e);
}

vec2 twoSum(float a,float b)
{
    float   s   =   a + b;
    float   v   =   s - a;
    float   e   =   ( a - (s - v)) + ( b - v );

    return vec2(s,e);
}

vec2 add(vec2 a, vec2 b)
{
    vec2    s   =   twoSum(a.x , b.x);
    vec2    t   =   twoSum(a.y , b.y);

    s.y     +=  t.x;
    s       =   quickTwoSum(s.x,s.y);
    s.y     +=  t.y;
    s       =   quickTwoSum(s.x,s.y);

    return s;
}

vec2 add(vec2 a,float b)
{
    return add(a,split(b));
}

vec2 mult2(vec2 a,vec2 b)
{
    vec2    p   =   twoProd(a.x,b.x);
    p.y     +=  a.x * b.y;
    p.y     +=  a.y * b.x;
    p       =   quickTwoSum(p.x,p.y);

    return p;
}

vec2 mult(vec2 a,float b)
{
    return mult2(a, split(b));
}

显然,我一定是在这里做错了什么,或者错过了一些非常基本的概念,因为无论我使用简单运算还是扩展浮点运算,我都会得到相同的结果......

【问题讨论】:

    标签: opengl-es opengl-es-2.0 gpu floating-point-precision post-processing


    【解决方案1】:

    这里的挑战是 cpu 实现依赖于 java 类型 float(32 位),而我的 gpu 实现使用的是 lowp float(8 位)。

    lowp 实际上并不暗示用于浮点运算的位数。它更多地与 必须是 可表达的值的范围和最小可区分值(精度)有关 - 您可以使用它来计算最小位数,但 GLSL 从不讨论它.

    目前,无需做任何花哨的事情,我就可以准确地模仿第 1 步,以使 cpu 实现的图像结果与 gpu 的图像结果相同。

    这很幸运,因为您的描述中的一个直接问题来自于 lowp 只能保证代表 [-2.0,2.0 范围内的值]。如果您尝试通过将低精度浮点值除以 3(如 步骤 1 中所示)来归一化低精度浮点值,这可能有效,也可能无效。在最坏的情况下,这将不起作用,因为浮点值永远不会达到 3.0。但是,在某些 GPU 上它可能会起作用,因为 lowpmediump 之间可能没有区别,或者 GPU 的 lowp 可能超过 GLSL ES 1.00 规范的 4.5.2 Precision Qualifiers 中列出的最低要求。

    ...我仍然想继续使用 lowp float 以确保我的代码能够在最差的硬件上运行。

    如果您的目标是尽可能低端的硬件,请记住 ES 2.0 在所有着色器阶段都需要 mediump 支持。 lowp 唯一能让你受益的是某些 GPU 的性能提高,但任何可以承载 ES 2.0 的 GPU 都是支持中等精度浮点的 GPU,而你的算法需要的范围大于 lowp 保证。

    【讨论】:

      猜你喜欢
      • 2012-04-26
      • 2012-11-08
      • 2015-11-13
      • 2015-10-06
      • 1970-01-01
      • 1970-01-01
      • 2011-02-27
      • 1970-01-01
      • 2013-09-04
      相关资源
      最近更新 更多