HLSL计算着色器中如何实现多数据位的原子交换?
多纹理同步的原地3D落沙算法实现问题
我正在实现原地3D落沙算法,逻辑是让3D纹理中非零元素在下方位置为空(值为0)时向下移动一位。使用单uint原子操作时能正常运行,代码如下:
// from 是坐标xyz,to 是 xyz + (0, -1, 0) uint toValueWas; InterlockedCompareExchange(_texture[to], 0, _texture[from], toValueWas); if (toValueWas == 0) { _texture[from] = toValueWas; }
但当需要扩展存储到另一张纹理_textureB时,两个纹理会出现不同步问题,问题代码如下:
uint toValueWas; InterlockedCompareExchange(_texture[to], 0, _texture[from], toValueWas); if (toValueWas == 0) { _texture[from] = toValueWas; // 此处操作会导致与_texture不同步 InterlockedExchange(_textureB[to], _textureB[from], _textureB[from]); }
核心问题:有没有办法有效实现超过32位数据的原子交换?
可行解决方案
1. 利用64位原子操作(API支持时优先)
如果你的图形API(如DirectX 12、Vulkan)支持64位原子操作,可以将两个uint数据打包成一个uint64_t,存储到单个纹理通道或专用的64位纹理中,通过InterlockedCompareExchange64这类原子操作一次性完成交换,从根源上保证数据同步。
示例代码思路:
// 将两个纹理的对应值打包为64位整数 uint64_t fromPacked = (uint64_t)_texture[from] << 32 | _textureB[from]; uint64_t toPackedWas; // 仅当目标位置的打包值为0(即两个纹理对应位置都为空)时,执行原子交换 InterlockedCompareExchange64(_packedTexture[to], 0, fromPacked, toPackedWas); if (toPackedWas == 0) { // 将原位置的两个纹理值都置为0 _texture[from] = 0; _textureB[from] = 0; // 目标位置已通过原子操作完成赋值,无需额外操作 }
2. 原子自旋锁实现同步
如果不支持64位原子操作,可以使用一张单独的锁纹理,每个位置对应一个自旋锁,确保对_texture和_textureB的操作是原子性的。
示例代码思路:
// 锁纹理:0表示未锁定,1表示锁定 while (InterlockedCompareExchange(_lockTexture[to], 1, 0) != 0) { // 自旋等待锁释放 } // 锁定后检查目标位置是否为空 uint toVal = _texture[to]; uint toValB = _textureB[to]; if (toVal == 0 && toValB == 0) { // 执行双纹理值交换 _texture[to] = _texture[from]; _textureB[to] = _textureB[from]; _texture[from] = 0; _textureB[from] = 0; } // 释放锁 InterlockedExchange(_lockTexture[to], 0);
注意:自旋锁在高并发场景下可能带来性能损耗,需根据实际运行环境调整优化。
3. 合并数据存储结构
将需要同步的多字段数据合并为一个结构体,使用支持结构体原子操作的GPU计算缓冲区(如结构化缓冲区),通过原子操作一次性交换整个结构体,避免多纹理的同步问题。
内容的提问来源于stack exchange,提问作者Jolly Theory
相关产品推荐
相关产品推荐

