Вычислить шейдеры — как глобально синхронизировать потоки?

РЕДАКТИРОВАТЬ: я перефразировал вопрос, чтобы сделать его более общим и упростил код.

Я, вероятно, что-то упустил с синхронизацией потоков в вычислительных шейдерах. У меня есть простой вычислительный шейдер, который выполняет параллельное сокращение некоторых чисел, а затем мне нужно изменить итоговую сумму:

#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 и Linux с проприетарным драйвером nvidia (331.49).

3

Решение

Вы не можете синхронизировать потоки глобально, то есть через рабочие группы. Об этом говорится в комментариях GuyRT. В вашем коде одна рабочая группа может ударить

clusters[0].size = 23;

в то время как другая рабочая группа счастливо делает атомарные приращения. Поскольку это только первый поток первой рабочей группы, который входит в if(id==0) блокировать и поскольку большинство графических процессоров распределяют рабочие группы по порядку, тогда значение будет записано один раз, а затем увеличено много раз другими (большинством) рабочих групп.

2

Другие решения


По вопросам рекламы ammmcru@yandex.ru
Adblock
detector