编辑:我已经改写了这个问题,使其更加通用并简化了代码。
我可能在计算着色器中遗漏了一些线程同步。我有一个简单的计算着色器,可以对某些数字进行并行缩减,然后我需要修改最终的总和:
#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.I我猜测值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专有驱动程序的linux(331.49)。
答案 0 :(得分:2)
您无法全局同步线程,即跨工作组。 GuyRT的评论指出了这一点。在您的代码中,一个工作组可以点击
clusters[0].size = 23;
而另一个工作组正在愉快地进行原子增量。由于它只是第一个工作组的第一个线程进入if(id==0)
块,并且大多数GPU按顺序调度工作组,因此该值将被写入一次,然后由其他值递增多次(大多数)工作组。