当我尝试按降序进行索引排序时,我遇到了一个奇怪的问题。我很确定我错过了关于 GPU 如何工作的一些基本概念,因为我希望这个程序能够正常执行:
layout(binding = 0, std430) buffer _6_3
{
uint _m0[];
} _3;
layout(binding = 1, std430) buffer _8_4
{
uint _m0[];
} _4;
uint _51(uint _63, uint _64)
{
uint _66 = 0u;
uint _67 = 1u;
for (;;)
{
uint _72 = _66;
if (_72 < 150u)
{
memoryBarrierBuffer();
barrier();
uint _76 = atomicCounter(_3._m0[_72]);
memoryBarrierBuffer();
barrier();
_67 += uint(((_76 == _63) && (_72 > _64)) || (_76 < _63));
_66++;
continue;
}
else
{
break;
}
}
uint _87 = _67;
uint _90 = _64 + 1u;
memoryBarrierBuffer();
barrier();
_3._m0[150u - _87] = _90;
memoryBarrierBuffer();
barrier();
return _90;
}
void main()
{
uint _49 = _3._m0[gl_GlobalInvocationID.x];
uint _50 = _51(_49, gl_GlobalInvocationID.x);
}
这个程序适用于小的输入——让我们假设一个从 1 到 500 的自然数的有序列表。如果我们用 50 或 75 替换 150u,结果如预期:前 75 个数字从 75 下降到 1,之后他们从 76 到 500 开始。
问题是输入超过 100 时,一些索引会被打乱。结果完全是结构的乱码,但索引当然保持在范围内。奇怪的是,如果 while 不变量递增到 5000 表示 500 个元素的循环,则排序再次开始工作。
知道问题可能是什么吗?我很确定我无法理解一些基本的同步。据我了解,除了在函数末尾交换索引之前,我的代码中不需要任何其他障碍。
值得一提的是,这是对以下 SPIR-V 函数的翻译:
%rankDown = OpFunction %1 None %rankdown_uint_signature
%self = OpFunctionParameter %1
%sindex = OpFunctionParameter %1
%rd = OpLabel
%iter = OpVariable %u32ptr Function %uint_0
%rank = OpVariable %u32ptr Function %uint_1
OpBranch %while_start
%while_start = OpLabel
OpLoopMerge %endloop %continueloop None
OpBranch %rankloop
%rankloop = OpLabel
%rloopload = OpLoad %1 %iter
%loopgate = OpULessThan %bool %rloopload %uint_500
OpBranchConditional %loopgate %start %endloop
%start = OpLabel
%_thrdValue = OpAccessChain %_ptr_Uniform_uint %_ %uint_0 %rloopload
OpControlBarrier %uint_1 %uint_1 %UniformMemory
%thrdValue = OpAtomicLoad %1 %_thrdValue %uint_1 %UniformMemory
OpControlBarrier %uint_1 %uint_1 %UniformMemory
%smaller = OpULessThan %bool %thrdValue %self
%same = OpIEqual %bool %thrdValue %self
%_is_after = OpUGreaterThan %bool %rloopload %sindex
%_if_gate = OpLogicalAnd %bool %same %_is_after
%if_gate2 = OpLogicalOr %bool %_if_gate %smaller
%sel = OpSelect %1 %if_gate2 %uint_1 %uint_0
%rankLoad = OpLoad %1 %rank
%rank_adder = OpIAdd %1 %rankLoad %sel
OpStore %rank %rank_adder
OpBranch %continueloop
%continueloop = OpLabel
%iterLoad = OpLoad %1 %iter
%iterAdd = OpIAdd %1 %iterLoad %uint_1
OpStore %iter %iterAdd
OpBranch %while_start
%endloop = OpLabel
%rankll = OpLoad %1 %rank
%ow_index = OpISub %1 %uint_500 %rankll
%_overwrite = OpAccessChain %_ptr_Uniform_uint %_ %uint_0 %ow_index
%ow_index2 = OpIAdd %1 %sindex %uint_1
OpControlBarrier %uint_1 %uint_1 %UniformMemory
OpStore %_overwrite %ow_index2
OpControlBarrier %uint_1 %uint_1 %UniformMemory
OpReturnValue %ow_index2
OpFunctionEnd
例如,如果我们将 150u 更改为 250u 并可视化结果,我们会得到以下结果:
在这里,我们可以看到排序表明,在某些情况下,while 循环似乎没有运行足够的次数——结果中存在重复值,并且一些值消失了。
任何输入表示赞赏!