您希望减少开销,同时避免无效的管道状态。这就是为什么有些人解除一切绑定(尽量防止),这取决于用例,当然你可以稍微平衡一下。
为了平衡这一点,您可以根据资源类型预先将特定资源分配给插槽,因为您有不同数量的插槽,可以应用不同的规则
1/采样器和状态
您有 16 个插槽,通常 90% 的时间使用 4-5 个采样器(线性/点/各向异性/阴影)。
所以在应用程序启动时创建这些状态并将它们绑定到您需要的每个着色器阶段(尽量不要从零槽开始,因为它们很容易被错误地覆盖)。创建一个映射 SamplerState -> slot 的着色器头文件,并在着色器中使用它,因此任何插槽更新都会自动反映。
尽可能重用这个,并且只绑定自定义采样器。
对于标准状态(Blend/Depth/Rasterizer),在应用程序启动时构建一小部分常见状态并根据需要进行绑定是常见的做法。
一种以低成本最小化渲染状态绑定的简单方法,您可以构建一个堆栈,因此您设置一个默认状态,如果着色器需要更具体的状态,它可以将新状态推送到堆栈,一旦完成,最后弹出状态并将其再次应用于管道。
2/恒定缓冲区
您有 14 个插槽,这相当多,很少使用它们(至少在我的用例中),特别是现在您也可以使用缓冲区/结构化缓冲区。
一个简单的常见情况是为相机设置保留插槽(包含您需要的所有数据,视图/投影/视图投影,以及它们的倒数,因为您可能也需要它。
将它绑定到(如果需要的话)着色器阶段插槽,您唯一需要做的就是每帧更新您的 cbuffer,它可以在任何地方使用。
3/着色器阶段
您几乎不需要取消绑定 Compute Shader,因为它与管道完全分离。
另一方面,对于管道阶段,而不是解除绑定,一个相当好的做法是设置所有你需要的并将你不需要的设置为空。
如果您不按照此示例并渲染阴影贴图(仅限深度缓冲区),则可能仍会绑定像素着色器。
如果您忘记取消设置之前使用的几何着色器,您可能会得到无效的布局组合,并且您的对象将不会渲染(错误只会在运行时调试模式下显示)。
因此,设置完整的着色器阶段几乎不会增加开销,但安全性的权衡远非可以忽略不计。
在您的用例中(仅使用 VS/PS 和 CS 构建),您可以放心地忽略它。
4/Uavs-RenderTargets-DepthStencil
对于写入资源,在完成工作单元时始终未设置。在同一个例程中,您可以在内部进行优化,但在渲染/计算着色器函数结束时,将输出设置回 null,因为管道在输出时不允许将任何内容作为 ShaderResource 反弹。
在函数结束时不取消设置写入资源会导致灾难。
5/ShaderResourceView
这是非常有情境的,但想法是尽量减少,同时避免运行时警告(这可能是无害的,但会隐藏重要消息)。
最终的一件事是在帧开始时将所有着色器资源输入重置为空,以避免仍然绑定在 VS 中的缓冲区在 CS 中设置为 UAV,这会花费您每帧 6 次管道调用,但通常值得它。
如果您有足够的备用寄存器和一些恒定资源,您当然也可以将它们设置在一些保留的插槽中并一劳永逸地绑定它们。
6/IA 相关资源
对于这个,您需要设置正确的数据来绘制几何图形,因此每次绑定它时设置 InputLayout/Topology 是非常合理的。您当然可以组织您的绘图调用以最小化开关。
我发现正确设置拓扑非常关键,因为无效的拓扑(例如,使用包含曲面细分的管道的三角形列表)不会绘制任何内容并给您一个运行时警告,但在 AMD 卡上它只会使您的驱动程序崩溃,因此最好避免这种情况,因为它变得相当难以调试。
通常永远不会真正解除绑定顶点/索引缓冲区(因为只是覆盖它们并且输入布局告诉无论如何如何获取)。只有在计算/流输出中生成这些缓冲区的情况下,此规则才有例外,以避免上述运行时警告。