我试图在计算着色器中实现自旋锁。但是我的实现似乎并没有锁住任何东西。
这是我实现自旋锁的方式:
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;
}
}
}
}
以下是多线程版本。每次执行此版本会产生类似但不同的结果,我认为这是由于锁未正常工作引起的。
[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位像素值。它会产生预期的结果。
非常感谢任何帮助。