Я пытаюсь реализовать спин-блокировку в вычислительном шейдере. Но моя реализация, похоже, ничего не блокирует.
Вот как я реализую блокировку вращения:
void LockAcquire()
{
uint Value = 1;
[allow_uav_condition]
while (Value) {
InterlockedCompareExchange(DataOutBuffer[0].Lock, 0, 1, Value);
};
}
void LockRelease()
{
uint Value;
InterlockedExchange(DataOutBuffer[0].Lock, 0, Value);
}
Предыстория: мне нужна блокировка спина, потому что мне нужно вычислить сумму данных в большом двухмерном массиве. Сумма двойная. Вычисление суммы с помощью одного потока и двойного цикла дает правильный результат. Вычисление суммы с помощью многопоточности дает неверный результат, даже если вводится спин-блокировка, чтобы избежать конфликта при вычислении суммы.
Я не могу использовать InterLockedAdd, потому что сумма не умещается в 32-битное целое число, а я использую шейдерную модель 5 (компилятор 47).
Вот однопоточная версия, дающая правильный результат:
[numthreads(1, 1, 1)]
void CSGrayAutoComputeSumSqr(
uint3 Gid : SV_GroupID,
uint3 DTid : SV_DispatchThreadID, // Coordinates in RawImage window
uint3 GTid : SV_GroupThreadID,
uint GI : SV_GroupIndex)
{
if ((DTid.x == 0) && (DTid.y == 0)) {
uint2 XY;
int Mean = (int)round(DataOutBuffer[0].GrayAutoResultMean);
for (XY.x = 0; XY.x < (uint)RawImageSize.x; XY.x++) {
for (XY.y = 0; XY.y < (uint)RawImageSize.y; XY.y++) {
int Value = GetPixel16BitGrayFromRawImage(RawImage, rawImageSize, XY);
uint UValue = (Mean - Value) * (Mean - Value);
DataOutBuffer[0].GrayAutoResultSumSqr += UValue;
}
}
}
}
а ниже - многопоточная версия. Эта версия дает аналогичные, но разные результаты при каждом выполнении, которое IMO вызвано неработающей блокировкой.
[numthreads(1, 1, 1)]
void CSGrayAutoComputeSumSqr(
uint3 Gid : SV_GroupID,
uint3 DTid : SV_DispatchThreadID, // Coordinates in RawImage window
uint3 GTid : SV_GroupThreadID,
uint GI : SV_GroupIndex)
{
int Value = GetPixel16BitGrayFromRawImage(RawImage, RawImageSize, DTid.xy);
int Mean = (int)round(DataOutBuffer[0].GrayAutoResultMean);
uint UValue = (Mean - Value) * (Mean - Value);
LockAcquire();
DataOutBuffer[0].GrayAutoResultSumSqr += UValue;
LockRelease();
}
Использованные данные:
cbuffer TImageParams : register(b0)
{
int2 RawImageSize; // Actual image size in RawImage
}
struct TDataOutBuffer
{
uint Lock; // Use for SpinLock
double GrayAutoResultMean;
double GrayAutoResultSumSqr;
};
ByteAddressBuffer RawImage : register(t0);
RWStructuredBuffer<TDataOutBuffer> DataOutBuffer : register(u4);
Код отправки:
FImmediateContext->CSSetShader(FComputeShaderGrayAutoComputeSumSqr, NULL, 0);
FImmediateContext->Dispatch(FImageParams.RawImageSize.X, FImageParams.RawImageSize.Y, 1);
Функция GetPixel16BitGrayFromRawImage обращается к буферу байтового адреса RawImage для извлечения 16-битного значения пикселя из изображения в градациях серого. Это дает ожидаемый результат.
Любая помощь приветствуется.