У меня есть два буфера, src
и target
. src
- это случайные значения с дубликатами (возможно, что-то вроде {1,3,1,3,2,3,...}
). target
изначально полностью равен нулю.
Что я хочу сделать, это (в синтаксисе c ++):
for (int i : src) {
target[i]++;
}
Но я хочу сделать это в HLSL-вычислительных шейдерах. Из-за дубликатов в src
могут быть гонки данных. Поэтому я реализовал AtomicAdd
операцию:
#define AtomicAdd(BUFFER, IDX, VALUE) \
keepWaiting = true;\
do {\
uint ov;\
InterlockedCompareExchange(BUFFER##_Mutex[IDX], 0, 1, ov);\
if (ov == 0) {\
BUFFER[IDX] += VALUE;\
BUFFER##_Mutex[IDX] = 0;\
keepWaiting = false;\
}\
} while (keepWaiting)
В вычислительном шейдере только с одной группой, например:
// called by group number (1, 1, 1)
[numthreads(512, 1, 1)]
void kernel(uint3 id : SV_DispatchThreadID) {
for (uint i = 0; i <= SrcSize / 512; i++) {
uint idx = i * 512 + id.x;
if (idx >= SrcSize) {
return;
}
bool keepWaiting;
uint srcValue = src[idx];
AtomicAdd(Target, srcValue, 1);
}
}
Это работает хорошо (по крайней мере, на моем устройстве). Но в вычислительном шейдере со многими группами, такими как:
// called by group numbers (X, 1, 1), X is a large number and X * 512 > SrcSize.
[numthreads(512, 1, 1)]
void kernel(uint3 id : SV_DispatchThreadID) {
if (id.x >= SrcSize) {
return;
}
bool keepWaiting;
uint srcValue = src[idx];
AtomicAdd(Target, srcValue, 1);
}
Это не дает правильных результатов.
Итак, мой вопрос: почему это происходит? В чем разница между этими двумя ядрами?