1

我的另一篇文章打算收集有关 GLSL 自旋锁种类的一般信息,但不幸的是,它没有任何结果,也没有解决我的问题。因此,一个具体的问题。我将我的问题简化为一个最小的示例,如下所示:

微不足道的问题使屏幕大小的锁纹理和颜色纹理。在第一阶段,颜色都设置为零(着色器 1)。在第二步中,绘制了两个三角形,几何着色器将其翻了四倍并略微偏移(着色器 2)。片段着色器以原子方式增加纹理的颜色。在第三阶段,颜色被可视化(着色器 3)。

着色器 1:

//Vertex
#version 440
uniform mat4 mat_P;
in vec4 _vec_vert_a;
void main(void) {
    gl_Position = mat_P*_vec_vert_a;
}

//Fragment
#version 440
layout(rgba32f) coherent uniform image2D img0;
void main(void) {
    imageStore(img0,ivec2(gl_FragCoord.xy),vec4(0.0,0.0,0.0,1.0));
    discard;
}

着色器 2:

//Vertex
#version 440
in vec4 _vec_vert_a;
out vec4 vert_vg;
void main(void) {
    vert_vg = _vec_vert_a;
}

//Geometry
#version 440
#define REPS 4
layout(triangles) in;
layout(triangle_strip,max_vertices=3*REPS) out;
uniform mat4 mat_P;
in vec4 vert_vg[3];
void main(void) {
    for (int rep=0;rep<REPS;++rep) {
        for (int i=0;i<3;++i) {
            vec4 vert = vert_vg[i];
            vert.xy += vec2(5.0*rep);
            gl_Position = mat_P*vert; EmitVertex();
        }
        EndPrimitive();
    }
}

//Fragment
#version 440
layout(rgba32f) coherent uniform image2D img0;
layout(r32ui) coherent uniform uimage2D img1;
void main(void) {
    ivec2 coord = ivec2(gl_FragCoord.xy);
    bool have_written = false;
    do {
        bool can_write = (imageAtomicExchange(img1,coord,1u)!=1u);
        if (can_write) {
            vec4 data = imageLoad(img0,coord);
            data.xyz += vec3(1.0,0.0,0.0);
            imageStore(img0,coord,data);
            memoryBarrier();
            imageAtomicExchange(img1,coord,0);
            have_written = true;
        }
    } while (!have_written);
    discard;
}

着色器 3:

//Vertex
#version 440
uniform mat4 mat_P;
in vec4 _vec_vert_a;
void main(void) {
    gl_Position = mat_P*_vec_vert_a;
}

#version 440
layout(rgba32f) coherent uniform image2D img0;
void main(void) {
    vec4 data = imageLoad(img0,ivec2(gl_FragCoord.xy));
    gl_FragData[0] = vec4(data.rgb/4.0, 1.0); //tonemap
}

主循环:

  1. 启用着色器 1
  2. 渲染全屏四边形
  3. glMemoryBarrier(GL_ALL_BARRIER_BITS);

  4. 启用着色器 2

  5. 渲染两个小三角形
  6. glMemoryBarrier(GL_ALL_BARRIER_BITS);

  7. 启用着色器 3

  8. 渲染全屏四边形

请注意,在第 3 步和第 6 步中,我 [认为我] 可以使用 GL_SHADER_IMAGE_ACCESS_BARRIER_BIT。以防万一,我是保守的。

可视化的颜色随时间抖动,并且大多相当小。这表明原子性没有发生。有人可以理智地检查这个程序吗?我错过了什么吗?

编辑:从这个页面,我发现使用discard可以使图像加载/存储在片段中未定义。我删除了丢弃物,但问题仍然存在。我还发现layout(early_fragment_tests) in;,它强制进行早期片段测试(它也没有帮助)。

4

1 回答 1

0

另一个相关链接:
https ://www.opengl.org/discussion_boards/showthread.php/182715-Image-load-store-mutex-problem?p=1255935#post1255935

上次我测试它的一些自旋锁代码(几年前授予):
http ://blog.icare3d.org/2010/07/opengl-40-abuffer-v20-linked-lists-of.html

同一应用程序的另一个实现:
https ://github.com/OpenGLInsights/OpenGLInsightsCode/blob/master/Chapter%2020%20Efficient%20Layered%20Fragment%20Buffer%20Techniques/lfbPages.glsl

在上面的链接中,使用了金丝雀,它绝对很重要,而且可能仍然很重要。coherent很重要,但你有。几年前memoryBarrier()根本没有实施,什么也没做。我希望不是这种情况,但是它可能是自旋锁工作得很好,并且写入img0不会按照后续读取的顺序发生。

GLSL 编译器有时会有点问题。这里有几个例子来说明 GLSL 编码有多么奇怪。关键是,尝试许多不同的方式来编写相同的代码会有所帮助。我在 while 循环中看到了函数作为条件的问题,只是失败了。据我所知,..while 的编译与典型的 while 大不相同。将尽可能多的条件组合到循环条件中有时也会有所帮助。有时else break行为不符合预期或允许编译器展开某些循环,您必须使用以下内容:

for (...)
{
    if (...)
    {
        ...
        continue;
    }
    break;
}
于 2014-02-08T02:01:00.087 回答