我正在尝试DirectX12的新功能。到目前为止,我真的很喜欢其中的一些更改,例如,管道状态。同时,其他一些更改也让人有些困惑,例如描述符堆。
让我们从快速的背景开始,以便您更好地理解我的要求。
在DirectX11中,我们创建了具有不同着色器的对象,然后在设置绘制调用时必须在实际运行时分别绑定每个对象。这是一个伪示例:
deviceContext->VSSetShader(...);
deviceContext->HSSetShader(...);
deviceContext->DSSetShader(...);
deviceContext->PSSetShader(...);
在DirectX12中,他们已经实现了这么聪明,因为现在我们可以改为在初始化期间配置管道状态,然后通过一个API调用来设置所有上述内容:
commandList->SetPipelineState(...);
非常简单,优雅和快捷。最重要的是,这非常合乎逻辑。现在让我们来看一下描述符堆。我有点希望它遵循相同的优雅模式,这基本上就是我的问题。
在DirectX11中,我们创建了具有不同描述符(视图)的对象,然后在设置绘制调用时必须在实际运行时分别为每个着色器绑定它们。再次是一个伪示例:
deviceContext->PSSetConstantBuffers(0, n, ...);
deviceContext->PSSetShaderResources(0, n, ...);
deviceContext->PSSetSamplers(0, n, ...);
在DirectX12中,他们实现了称为描述符堆的东西。基本上,它们是包含我们要绑定的所有描述符的内存块,我们也可以在初始化期间进行设置。到目前为止,它看起来和管道状态一样优雅,因为我们可以通过一个API调用来设置所有内容:
commandList->SetDescriptorHeaps(n, ...);
还是可以吗?这是引起混乱的地方,因为经过搜索,我发现this question指出:
交换描述符堆是您不惜一切代价避免的昂贵操作。
与此同时,SetDesciptorHeaps
的{{3}}并没有说明这种方法特别昂贵。
考虑到他们设计流水线状态的优雅程度,我有点希望能够做到这一点:
commandList->SetPipelineState(...);
commandList->SetDescriptorHeaps(n, ...);
commandList->DrawInstanced(...);
commandList->SetPipelineState(...);
commandList->SetDescriptorHeaps(n, ...);
commandList->DrawInstanced(...);
commandList->SetPipelineState(...);
commandList->SetDescriptorHeaps(n, ...);
commandList->DrawInstanced(...);
但是,如果SetDescriptorHeaps
实际上那么贵,那很可能会带来非常糟糕的性能。还是会?如前所述,我在MSDN上找不到任何关于这实际上是个坏主意的陈述。
所以我的问题是:
SetDescriptorHeaps
?基本上,我想为每个管道状态设置两个描述符堆(CBV / SRV / UAV +采样器)。从更改管道状态的便宜程度来看,更改描述符堆同样便宜是合乎逻辑的。流水线状态和描述符堆是非常紧密相关的,即更改流水线状态很可能需要一组不同的描述符。
我知道为每种类型的描述符使用一个庞大的描述符堆的策略。但是考虑到跟踪每个单独的描述符索引所需的所有工作,这种方法感觉太过复杂了。最重要的是,描述符表中的描述符在堆中必须是连续的。
答案 0 :(得分:0)
描述符堆独立于管道;他们不必每次抽奖/派遣都绑定。您也可以只拥有一个大的描述符堆,然后绑定它。然后应通过根签名对此进行更正。它应该指向此描述符堆中的正确偏移量。这意味着您可以在一个堆中拥有唯一的纹理,并将根签名指向正确的描述符。您还可以将当前堆再分配为一个巨型堆。
答案 1 :(得分:0)
MSDN 文档现已解决了切换堆的性能问题:
<块引用>在某些硬件上,这可能是一项代价高昂的操作,需要 GPU 停顿来刷新依赖于当前绑定描述符堆的所有工作。
来源:Descriptor Heaps Overview - Switching Heaps
<块引用>可能发生这种情况的原因是,对于某些硬件,在执行期间在硬件描述符堆之间切换需要 GPU 等待空闲(以确保 GPU 对先前描述符堆的引用已完成)。
<块引用>为了避免受到描述符堆切换上这种可能的等待空闲的影响,应用程序可以利用渲染中断,这会导致 GPU 由于其他原因空闲,如进行描述符堆切换的时间,因为等待无论如何,空闲正在发生。