1

我一直在尝试实现基于计算着色器的粒子系统。

我有一个计算着色器,它使用带有 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,并且正在进行一些巧妙的优化,这意味着该索引仅在使用它的计算着色器内有效。因此我尝试将它传递给第二个计算着色器无效。

任何人都可以对这里发生的事情给出任何具体的答案吗?如果有办法让我将过滤保持在与我的核心更新相同的计算着色器中?

谢谢!

4

1 回答 1

1

IncrementCounter 是一个原子操作,因此(尽管存在驱动程序/硬件错误)将向调用它的每个线程返回一个唯一值。

您是否考虑过为此使用追加/使用缓冲区,因为这是它们的设计目的?第一遍简单地将复杂的碰撞粒子附加到 AppendStructuredBuffer 中,第二遍从同一个缓冲区消耗,但使用 ConsumeStructuredBuffer 视图。第二次计算将需要使用 DispatchIndirect,因此您只需要为列表中的数字运行尽可能多的线程组(CPU 不会知道这一点)。

不过,通常的建议仍然适用,您是否尝试过 D3D11 调试层并在参考设备上运行它以确保它不是驱动程序问题?

于 2013-06-18T19:58:21.980 回答