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()、メモリ内の以前のすべての変更が表示されるはずだからです。値 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 のコメントで指摘されています。あなたのコードでは、1 つのワークグループがヒットする可能性があります

clusters[0].size = 23;

別のワークグループがアトミックインクリメントを喜んで行っている間。ブロックに入るのは最初のワークグループの最初のスレッドだけでif(id==0)あり、ほとんどの GPU はワークグループを順番にディスパッチするため、値は一度書き込まれ、その後他の (ほとんどの) ワークグループによって何度もインクリメントされます。

于 2014-07-01T14:03:08.693 に答える