编辑:我重新措辞了问题,使其更加通用,并简化了代码。
我可能在计算着色器的线程同步方面遗漏了一些东西。我有一个简单的计算着色器,可以对一些数字进行并行归约,然后我需要修改最终的总和:
#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系统。
coherent
,才能让内存屏障对更新的可见性产生任何影响。我会考虑将整个destBuffer
的定义声明为coherent。这将确保对ob.clusters []
的写入被屏障所遵循。否则,此计算着色器的另一个调用可能会轻松地破坏你在if(id == 0)
分支末尾写入的值。 - Andon M. Coleman| GL_BUFFER_UPDATE_BARRIER_BIT
,是否会有任何变化?我认为你现在拥有的位更多是用于调度访问SSB的绘制调用,而不是确保着色器在使用glGetBufferSubData(...)
读取缓冲区之前完成。 - Andon M. ColemanGL_ALL_BARRIER_BITS
进行调试,但结果仍然相同。如果我使用glGetBufferSubData
从缩减中读取原始数据,我总是能得到正确的结果。 - Jaa-cbarrier
的描述仅声称同步单个工作组内的执行。我猜id
不等于0的组的线程可以覆盖clusters [0]
。 - GuyRTif(id == 0)
,因为任何非0 id组都可以在clusters[
0和1]
中写入任何它想要的内容,只有id为0的组才会触发你想要写入那里的值。如果你希望23和13在所有情况下都存在,无论哪个组在写入,请尝试将if(id == 0)
更改为if(id == id)
(仅用于代码保留测试),或使if
子句内的2行代码无条件执行。 - GMasucci