【问题标题】:Atomic moving average on image图像上的原子移动平均线
【发布时间】:2017-03-29 04:37:17
【问题描述】:

我正在尝试计算图像上的原子移动平均值,但平均值是错误的,当我多次使用相同的数据运行它时,平均值每次都不同。 我有一个 RGBA8 纹理,但我将其绑定为 R32UI,因为我需要原子操作和纹理过滤。

我为求平均值制作了一个测试应用程序,因为实际应用程序非常复杂。在测试应用程序中,我在单个纹素上的片段着色器中进行平均,所以我有一个 1x1 RGBA8 纹理,绑定为 R32UI。我正在渲染一个大小为 500x500 的四边形,并且每个片段着色器调用都会在纹素的平均值上添加一个蓝色或红色值,具体取决于片段坐标。

片段着色器:

#version 450 core

layout (binding = 0, r32ui) volatile uniform uimage2D img;

layout (location = 0) uniform int width;

vec4 convRGBA8ToVec4( uint val)
{
    return vec4(float((val & 0x000000FF)), float((val & 0x0000FF00) >> 8U),
                float((val & 0x00FF0000) >> 16U), float((val & 0xFF000000) >> 24U));
}

uint convVec4ToRGBA8( vec4 val)
{
    return (uint(val.w) & 0x000000FF) << 24U | (uint(val.z) & 0x000000FF) << 16U |
           (uint(val.y) & 0x000000FF) << 8U  | (uint(val.x) & 0x000000FF);
}

void imageAtomicRGBA8Avg(layout (r32ui) volatile uimage2D imgUI, ivec2 coords , vec4 val)
{
    val.rgb *= 255.0f;
    uint newVal = convVec4ToRGBA8(val);
    uint prevStoredVal = 0;
    uint curStoredVal;

    // Loop as long as destination value gets changed by other threads
    while((curStoredVal = imageAtomicCompSwap(imgUI, coords, prevStoredVal, newVal))
          != prevStoredVal)
    {
        prevStoredVal = curStoredVal;
        vec4 unpacked = convRGBA8ToVec4(curStoredVal);
        vec3 x = unpacked.xyz;
        float n = unpacked.w;
        vec4 curValF = vec4(vec3(((n * x) + val.xyz) / n), n + 1);
        newVal = convVec4ToRGBA8(curValF);
    }
}

void main()
{
    int idx = int(gl_FragCoord.y * width) + int(gl_FragCoord.x);

    if(idx % 2 == 0)
    {
        // Add red
        imageAtomicRGBA8Avg(img, ivec2(0, 0), vec4(1.0f, 0.0f, 0.0f, 1.0f));
    }
    else
    {
        // Add blue
        imageAtomicRGBA8Avg(img, ivec2(0, 0), vec4(0.0f, 0.0f, 1.0f, 1.0f));
    }
}

平均颜色介于红色和蓝色之间,有时甚至是纯红色或蓝色。我想知道为什么会这样,我在一篇论文中发现了平均函数,所以它实际上应该可以工作。

这是初始化代码:

glCreateTextures(GL_TEXTURE_2D, 1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);

glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);

glBindTextureUnit(0, texture);
glBindImageTexture(0, texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI);

和渲染代码:

glUseProgram(atomic_avg_prog);
glUniform1i(0, window_width);
glDrawArrays(GL_TRIANGLES, 0, 6);

glUseProgram(read_prog);
glDrawArrays(GL_TRIANGLES, 0, 6);

读取程序只是在四边形上绘制纹素

【问题讨论】:

  • "只要目标值被其他线程更改就循环"你到底想在这里做什么?
  • 我正在计算下一个平均值,但是如果其他线程更快并且在那段时间写了一个新的平均值,我需要丢弃平均值,选择写入的新平均值并计算它再次
  • 你没有看到这可能会导致无限循环吗?
  • 不会,在最坏的情况下一个线程会等待所有其他线程完成,但在实际应用中最多会发生 5 次冲突。性能不是问题。如果一个线程需要等待,那是因为其他一些线程完成了,所以所有线程迟早都会完成
  • "在最坏的情况下,一个线程会等待所有其他线程完成" 现在向我展示 GLSL 规范中允许一个线程“等待所有其他线程”的部分线程完成”。或者更具体地说,确保独立线程独立运行并因此允许一个线程等待其他线程完成的部分。因为我很确定标准中不存在。或者在 GPU 硬件中。如果这能产生结果,那你就走运了。

标签: c++ opengl glsl


【解决方案1】:

我通过用packUnorm4x8unpackUnorm4x8替换convRGBA8ToVec4convVec4ToRGBA8解决了这个问题,感谢这篇文章:https://rauwendaal.net/2013/02/07/glslrunningaverage/

该解决方案不允许平均超过 255 个值,因为计数器仅以 8 位存储在 alpha 中,但在我的情况下就足够了

void imageAtomicAvgRGBA8(layout (r32ui) volatile uimage2D imgUI, ivec2 coords , vec4 val)
{
    uint newVal = packUnorm4x8(vec4(val.xyz, 1.0f / 255.0f));
    uint prevStoredVal = 0;
    uint curStoredVal;

    // Loop as long as destination value gets changed by other threads
    while((curStoredVal = imageAtomicCompSwap(imgUI, coords, prevStoredVal, newVal))
          != prevStoredVal)
    {
        prevStoredVal = curStoredVal;
        vec4 unpacked = unpackUnorm4x8(curStoredVal);
        vec3 x = unpacked.xyz;

        float n = unpacked.w * 255.0f;

        x = (x * n + val.xyz) / (n + 1);
        newVal = packUnorm4x8(vec4(x, (n + 1) / 255.0f));
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-01
    • 2013-12-22
    • 2023-04-03
    • 2017-12-25
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    • 2011-06-29
    相关资源
    最近更新 更多