OpenCL粒子系统内存布局

OpenCL粒子系统内存布局,opencl,Opencl,我需要一些关于OpenCL代码的建议。我在OpenCL中编写一个粒子系统,直接从OpenCL绘制到gpu上,因此不需要复制到CPU。这一切都很好,但我在创建新粒子时遇到了问题 我已经在GPU上分配了一个大内存区域,其中包含所有粒子数据。粒子的一个参数称为isAlive,它决定粒子的活动天气。当我想要创建一个新粒子时,我会找到一个不活动的粒子,在将其设置为isAlive true之前将其位置修改为其起始位置。这个过程非常昂贵,因为我必须遍历所有粒子以找到不活动的粒子,同时我必须确保多个线程不会同时

我需要一些关于OpenCL代码的建议。我在OpenCL中编写一个粒子系统,直接从OpenCL绘制到gpu上,因此不需要复制到CPU。这一切都很好,但我在创建新粒子时遇到了问题

我已经在GPU上分配了一个大内存区域,其中包含所有粒子数据。粒子的一个参数称为isAlive,它决定粒子的活动天气。当我想要创建一个新粒子时,我会找到一个不活动的粒子,在将其设置为isAlive true之前将其位置修改为其起始位置。这个过程非常昂贵,因为我必须遍历所有粒子以找到不活动的粒子,同时我必须确保多个线程不会同时创建同一个粒子(因此我不会得到比我要求的更多的粒子)。
有什么好的考虑、算法或策略可以更优雅、更快地解决这个问题吗

我会尝试一种方法:将isAlive的标志与数据结构的其余部分分开。这似乎是一段经常读取但很少写入的数据。使用单个uint跟踪32个粒子的状态。使用0表示活动,1表示死亡——本质上就是创建一个isDead列表。我想你会有比死粒子多得多的活粒子

当您需要时,可以将这些值(每次32个)读入本地内存。这允许您创建一个内核,该内核可以快速遍历数据,查找非零值。在这里,密集数据带来了巨大的性能提升,从而减少了存储和加载标志的内存开销。这使得检查其中一个值成为一种更便宜的操作,允许您更快地迭代它们。更改32位值时需要小心,以免损坏共享同一uint的其他数据(隔行扫描可能有助于此)。当您需要缩小1位的确切位置时,clz和popcount指令将非常有用

可能的优化#1: 如果需要,可以尝试交错这些值,以便第一个uint跟踪索引0,32,64,96,…,992,第二个uint表示1,33,65,97,…,993,依此类推。这可能允许通常在特定粒子上工作的工作项读取32个连续的isDead状态。这可能比它值得付出的更多努力,但这取决于您的应用程序

可能的优化#2: 如果死粒子真的很稀疏,那么在更高级别上跟踪isDead列表可能是值得的。使用相同的技术,很容易将isDead bit/uint列表再次减少32倍。第二级上的每个位表示相应的uint状态。ie:如果设置了uint N中的任何位,也将设置此列表的位N。只有在数据中需要大量零时才有用,但这一额外步骤可以节省大量搜索数据中稀有“开”位的周期。包括原始isDead数据在内的总内存开销将达到:memBits=ceil(particleCount/32)+ceil(particleCount/32^2),或者每2^20个粒子大约128kb+4kb


使用上述方法,您可以编写一个内核,返回给定范围内的死粒子数,并快速找到下一个可用的死粒子。

因此,您没有将数据复制回主机,而是在每个时间步从主机调用不同的内核吗?另外,一旦一个粒子死了,除了被一个新的粒子替换,它还能再活吗?是的,我从不把数据复制回CPU。我重复一系列内核调用,以每一个时间步更新粒子。是的,我一次又一次地重复使用死粒子,但它作为物理世界中的一个新粒子,只是替换了旧粒子的记忆区域,所以它与旧的死粒子没有任何联系。你知道在给定时间内死粒子的数量吗?您是否有足够的内存来分配位数组来跟踪活动状态?你总共瞄准多少个粒子?您是同时替换所有的死粒子,还是可以等到以后的时间步?我可以很容易地找到死粒子的数量,我想是的。是的,我确实有更多的可用内存。迭代一个位数组然后在大数组中查找是否可以更快?粒子死亡和存活的方式各不相同。它是一些图形的交互式引擎。有时我需要用大量的粒子快速地填充屏幕。非常感谢所有这些信息!我将尝试这种方法。