Почему операция atomi c работает только в группе потоков в HLSL-вычислительном шейдере? - PullRequest
1 голос
/ 18 апреля 2020

У меня есть два буфера, 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);
}

Это не дает правильных результатов.

Итак, мой вопрос: почему это происходит? В чем разница между этими двумя ядрами?

...