編集:質問を言い換えて、より一般的なものにし、コードを簡素化しました。
コンピューティング シェーダーでのスレッド同期に関する何かが欠けている可能性があります。いくつかの数値で並列削減を行う単純な計算シェーダーがあり、最終的な合計を変更する必要があります。
#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 があります。