我一直在尝试实现基于计算着色器的粒子系统。
我有一个计算着色器,它使用带有D3D11_BUFFER_UAV_FLAG_COUNTER标志的UAV构建粒子的结构化缓冲区。
当我添加到此缓冲区时,我会检查此粒子是否有任何复杂的行为,我想要过滤掉并在单独的计算着色器中执行。例如,如果粒子想要执行碰撞检测,我将其索引添加到另一个结构化缓冲区,同时使用D3D11_BUFFER_UAV_FLAG_COUNTER标志。
然后我运行第二个计算着色器,它处理所有索引,并对这些粒子应用碰撞检测。
然而,在第二个计算着色器中,我估计大约5%的索引是错误的 - 它们属于其他不支持碰撞检测的粒子。
这是用于构建列表构建的计算着色器代码:
// append to destination buffer
uint dstIndex = g_dstParticles.IncrementCounter();
g_dstParticles[ dstIndex ] = particle;
// add to behaviour lists
if ( params.flags & EMITTER_FLAG_COLLISION )
{
uint behaviourIndex = g_behaviourCollisionIndices.IncrementCounter();
g_behaviourCollisionIndices[ behaviourIndex ] = dstIndex;
}
如果我将“添加到行为列表”位拆分为单独的计算着色器,并在构建粒子列表后运行它,那么一切都很完美。但是我认为我不应该这样做 - 这会浪费带宽再次通过所有粒子。
我怀疑IncrementCounter
实际上并不能保证将唯一索引返回到UAV中,并且有一些聪明的优化正在进行,这意味着索引仅在其使用的计算着色器内有效。因此,我将其传递给第二个计算着色器的尝试无效。
任何人都能对这里发生的事情给出具体的答案吗?如果我有办法将过滤保留在与我的核心更新相同的计算着色器中?
谢谢!
答案 0 :(得分:1)
IncrementCounter是一个原子操作,因此(尽管有驱动程序/硬件错误)会为每个调用它的线程返回一个唯一值。
你有没有想过为它使用Append / Consume缓冲区,因为它是为它们设计的?第一个传递只是将复杂的碰撞粒子附加到AppendStructuredBuffer,第二个传递消耗来自相同的缓冲区,而是使用ConsumeStructuredBuffer视图。第二轮计算将需要使用DispatchIndirect,因此您只需要为列表中的数字运行尽可能多的线程组(CPU不会知道)。
通常的建议适用,您是否尝试过D3D11调试层并在参考设备上运行它以确保它不是驱动程序问题?