【问题标题】:Are there DirectX guidelines for binding and unbinding resources between draw calls?是否有用于在绘图调用之间绑定和取消绑定资源的 DirectX 指南?
【发布时间】:2013-12-16 12:52:56
【问题描述】:

所有 DirectX 书籍和教程都强烈建议将绘图调用之间的资源分配降至最低——但我找不到任何更详细的指南。回顾了在网上找到的大量示例代码,我得出结论,程序员对这个主题有完全不同的编码原则。有些甚至设置和取消设置

VS/PS 
VS/PS ResourceViews
RasterizerStage 
DepthStencilState
PrimitiveTopology
... 

每个绘制调用之前和之后(尽管设置保持不变),而其他的则不然。

我想这有点过头了……

从我自己的实验中,我发现我必须在每个绘图调用上绑定的唯一资源是 ShaderResourceViews(在我的例子中是 VSPS)。此要求可能是由使用计算着色器引起的,因为我稍后将 UAVs 绑定/取消绑定到绑定到 VS / PS 的缓冲区。

在我发现有必要重新绑定之前,我已经浪费了很多小时的工作。而且我猜许多编码人员也不确定,他们更愿意解除绑定和重新绑定“有点太多”,而不是陷入类似的陷阱。

问题 1:关于这个问题至少有一些经验法则吗?

问题 2: 绑定到 VS/PSShaderResourceViews 是否可能不受驱动程序/DirectX 核心的绑定,因为我在 CS 调度调用之前将 UAVs 绑定到相同的缓冲区(我自己没有解绑SRVs)?

问题 3: 在使用计算着色器之前,我什至没有将 VS/PS 设置为 null。工作没有问题,但我一直不确定我是否正在使用这种“懒惰”的方法来挖掘我的下一个陷阱。

【问题讨论】:

    标签: directx-11 vertex-shader pixel-shader compute-shader


    【解决方案1】:

    您希望减少开销,同时避免无效的管道状态。这就是为什么有些人解除一切绑定(尽量防止),这取决于用例,当然你可以平衡一下。

    为了平衡这一点,您可以根据资源类型预先将特定资源分配给插槽,因为您有不同数量的插槽,可以应用不同的规则

    1/采样器和状态

    您有 16 个插槽,通常 90% 的时间使用 4-5 个采样器(线性/点/各向异性/阴影)。

    所以在应用程序启动时创建这些状态并将它们绑定到您需要的每个着色器阶段(尽量不要从零槽开始,因为它们很容易被错误地覆盖)。 创建一个映射 SamplerState -> 槽的着色器头文件,并在着色器中使用它,因此任何槽更新都会自动反映。

    尽可能重用这个,并且只绑定自定义采样器。

    对于标准状态(Blend/Depth/Rasterizer),在应用程序启动时构建一小部分常见状态并根据需要进行绑定是常见做法。

    一种以低成本最小化渲染状态绑定的简单方法,您可以构建一个堆栈,因此您可以设置一个默认状态,如果着色器需要更具体的状态,它可以在完成后将新状态推送到堆栈,弹出最后一个状态并再次将其应用于管道。

    2/常量缓冲区

    您有 14 个插槽,这相当多,很少使用所有插槽(至少在我的用例中),特别是现在您也可以使用缓冲区/结构化缓冲区。

    一个简单的常见情况是为相机设置保留插槽(包含您需要的所有数据,视图/投影/视图投影,以及它们的倒数,因为您可能也需要它。

    将它绑定到(如果需要的话)着色器阶段插槽,您只需每帧更新您的 cbuffer,它就可以在任何地方使用。

    3/着色器阶段

    您几乎不需要取消绑定 Compute Shader,因为它与管道完全分离。

    另一方面,对于流水线阶段,一个相当好的做法是设置您需要的所有内容,而不是取消绑定,并将不需要的内容设置为 null。

    如果您不按照此示例并渲染阴影贴图(仅限深度缓冲区),则可能仍会绑定像素着色器。

    如果您忘记取消设置之前使用的几何着色器,您可能会得到无效的布局组合,并且您的对象将无法呈现(错误只会在运行时调试模式下显示)。

    因此,设置完整的着色器阶段几乎不会增加开销,但安全性的权衡却远非可以忽略不计。

    在您的用例中(仅使用 VS/PS 和 CS 构建),您可以放心地忽略它。

    4/Uavs-RenderTargets-DepthStencil

    对于写入资源,当您完成工作单元时始终未设置。在同一个例程中,您可以在内部进行优化,但在渲染/计算着色器函数结束时,将输出设置回 null,因为管道在输出时不允许将任何内容作为 ShaderResource 反弹。

    在函数结束时不取消设置写入资源会导致灾难。

    5/ShaderResourceView

    这是非常情景化的,但想法是尽量减少,同时避免运行时警告(这可能是无害的,但会隐藏重要消息)。

    最终的一件事是在帧开始时将所有着色器资源输入重置为空,以避免仍然绑定在 VS 中的缓冲区在 CS 中设置为 UAV,这会花费您每帧 6 次管道调用,但是这通常是值得的。

    如果你有足够的空闲寄存器和一些恒定的资源,你当然也可以将它们设置在一些保留的插槽中,并一劳永逸地绑定它们。

    6/IA 相关资源

    对于这个,您需要设置正确的数据来绘制几何图形,因此每次绑定它时设置 InputLayout/Topology 是非常合理的。您当然可以组织您的绘图调用以最小化开关。

    我发现正确设置拓扑非常关键,因为无效的拓扑(例如,使用包含曲面细分的管道的三角形列表)不会绘制任何内容并给您一个运行时警告,但在 AMD 卡上很常见它只会让你的驱动程序崩溃,所以最好避免这种情况,因为它变得相当难以调试。

    通常从不真正解除绑定顶点/索引缓冲区(因为只是覆盖它们并且输入布局告诉如何获取无论如何)。 只有在计算/流输出中生成这些缓冲区的情况下,此规则才有例外,以避免上述运行时警告。

    【讨论】:

    • 我是否需要“取消设置”SetVertexBuffers() VertexBufferBindings?完成渲染后。
    • 我个人从不这样做,只是在绑定新几何体时覆盖。我有一些罕见的情况需要这样做(计算着色器生成的几何)
    【解决方案2】:

    答案 1:越少越好。

    答案 2:正好相反,在将资源绑定到不同类型的视图之前,您必须取消绑定视图。你应该启用调试层来捕捉这样的错误。

    答案 3:没关系。

    【讨论】:

    • 我创建了设置了调试标志的交换链,但我从未收到来自 DirectX 的任何调试消息。我正在使用 VS Express for the Web 2012(包括 F#),此版本不允许本机代码和图形调试。
    • 该标志用于D3D11CreateDevice 并命名为D3D11_CREATE_DEVICE_DEBUG。从可视化调试菜单中,您还可以强制它、使某些消息静音甚至中断它们。
    • 首先我必须补充一点,我使用 SharpDX 作为包装器,并且我已经在使用调试标志。由于我的 VS 版本功能有限,“调试”选项卡上除了“启用 VS 托管进程”之外没有其他选项。我尝试过 PIX(64 位),但似乎存在与 SharpDX 不兼容的问题(尽管我已将 SharpDX.Direct3D.PixHelper.AllowProfiling 设置为 true)——当创建 RenderTargetView 时程序崩溃(没有充分的理由)。真是一团糟。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-15
    相关资源
    最近更新 更多