3

编辑:我重新表述了这个问题,使其更通用并简化了代码。

我可能在计算着色器中缺少线程同步的一些东西。我有一个简单的计算着色器,可以并行减少一些数字,然后我需要修改最终总和:

#version 430 core
#define SIZE 256
#define CLUSTERS 5

layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;

struct Cluster {
    vec3 cntr;
    uint size;
};
coherent restrict layout(std430, binding = 0) buffer destBuffer {
    Cluster clusters[CLUSTERS];
};
shared uint sizeCache[SIZE];

void main() {
    const ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
    const uint id = pos.y * (gl_WorkGroupSize.x + gl_NumWorkGroups.x) + pos.x;

    if(id < CLUSTERS) {
        clusters[id].size = 0;
    }

    memoryBarrierShared();
    barrier();
    sizeCache[gl_LocalInvocationIndex] = 1;
    int stepv = (SIZE >> 1); 
    while(stepv > 0) { //reduction over data in each working group
        if (gl_LocalInvocationIndex < stepv) {
            sizeCache[gl_LocalInvocationIndex] += sizeCache[gl_LocalInvocationIndex + stepv];
        }
        memoryBarrierShared();
        barrier();
        stepv = (stepv >> 1);
    }
    if (gl_LocalInvocationIndex == 0) {
        atomicAdd(clusters[0].size, sizeCache[0]);
    }

    memoryBarrier();
    barrier();

    if(id == 0) {
        clusters[0].size = 23; //this doesn't do what I would expect
        clusters[1].size = 13; //this works
    }
}

减少工作并产生正确的结果。如果我注释最后一个条件,那么里面的值clusters[0].size是262144,这是正确的(它是线程数)。如果我取消注释它,我希望得到值 23,因为据我了解,之后的线程barrier()应该是同步的,并且之前memoryBarrier()所有的内存更改都应该是可见的。但是它不起作用,它会产生类似 259095 的结果。我猜值 23 是由先前atomicAdd从另一个线程重写的,但我不明白为什么。

这就是我在 CPU 上读取结果的方式:

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, resultBuffer);

//currently it dispatches 262144 threads
glDispatchCompute(32, 32, 1);
glCheckError();

glMemoryBarrier(GL_ALL_BARRIER_BITS); //for debug

struct Cl {
    glm::vec3 cntr;
    uint size;
};

glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, resultBuffer);

std::vector<Cl> data(5);
glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeOfresult, &data[0]);

我有 NVIDIA GT630M 卡和带有 nvidia 专有驱动程序(331.49)的 linux。

4

1 回答 1

2

您不能全局同步线程,即跨工作组。GuyRT 在评论中指出了这一点。在您的代码中,一个工作组可以命中

clusters[0].size = 23;

而另一个工作组正在愉快地进行原子增量。因为它只是进入if(id==0)块的第一个工作组的第一个线程,并且由于大多数 GPU 按顺序分派工作组,因此该值将被写入一次,然后由其他(大多数)工作组递增多次。

于 2014-07-01T14:03:08.693 回答