是否有DirectX准则用于在绘制调用之间绑定和解除绑定资源? [英] Are there DirectX guidelines for binding and unbinding resources between draw calls?

查看:89
本文介绍了是否有DirectX准则用于在绘制调用之间绑定和解除绑定资源?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所有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.可以解决问题,但我始终不确定是否要使用这种懒惰"方法来挖掘下一个陷阱.

解决方案

您希望拥有更少的开销,同时又避免了无效的管道状态.这就是为什么某些人取消绑定所有内容(尝试尽可能避免绑定)的原因,这取决于用例,当然您可以对此进行一些平衡.

要达到此目的,您可以根据资源类型为插槽预先分配特定的资源,因为插槽的数量不同,因此可以应用不同的规则

1/样品和状态

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

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

尽可能重复使用此方法,并且仅绑定自定义采样器.

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

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

2/常量缓冲区

您有14个插槽,这是很多东西,至少在所有情况下(至少在我的用例中)很少使用它们,特别是现在您还可以使用缓冲区/结构化缓冲区.

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

将其绑定到(如果需要)着色器阶段插槽,唯一要做的就是每帧更新cbuffer,随时可以在任何地方使用.

3个着色器阶段

您几乎不需要取消绑定计算着色器,因为它已与管道完全分离.

另一方面,对于流水线阶段,不是解除绑定,一个合理的好习惯是将所有需要的设置为空,而将不需要的设置为空.

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

如果您忘记取消设置先前使用的Geometry Shader,则可能会以无效的布局组合结束,并且对象将无法渲染(错误只会在运行时调试模式下显示).

因此,设置完整的着色器阶段不会增加多少开销,但是在安全性方面的权衡绝非微不足道.

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

4/Uavs-RenderTargets-DepthStencil

对于写入资源,在完成工作单元后,请始终保持未设置状态.在同一例程中,您可以在内部进行优化,但是在渲染/计算着色器功能结束时,将输出设置为null,因为在输出时,管道将不允许任何东西作为ShaderResource反弹.

在功能结束时不取消写入资源是灾难的秘诀.

5/ShaderResourceView

这是非常情况,但是想法是在最小化的同时避免运行时警告(这可能是无害的,但随后会隐藏重要消息).

最后一件事是在帧开始时将所有着色器资源输入重置为空,例如,避免在VS中绑定仍被设置为UAV的VS中的缓冲区,这会使您每帧花费6个流水线调用,但是通常是值得的.

如果您有足够的备用寄存器和一些常量资源,那么您当然也可以在一些保留的插槽中设置它们,并一劳永逸地绑定它们.

6/IA相关资源

为此,您需要设置正确的数据以绘制几何图形,因此每次绑定时,设置InputLayout/Topology是非常合理的.您当然可以组织抽奖活动,以最大程度地减少切换次数.

我发现正确设置拓扑非常关键,因为无效的拓扑(例如,将Triangle List与包含tesselation的管道一起使用)将不会绘制任何内容并向您发出运行时警告,但是在AMD卡上这很常见它将使您的驱动程序崩溃,因此最好避免这种情况,因为它变得很难调试.

通常永远不会真正解除顶点/索引缓冲区的绑定(因为只是覆盖它们,并且输入布局会告诉我们如何获取). 如果在计算/流输出中生成了这些缓冲区,则该规则唯一例外,以避免上述运行时警告.

All DirectX books and tutorials strongly recommend reducing resource allocations between draw calls to a minimum – yet I can’t find any guidelines that get more into details. Reviewing a lot of sample code found in the web, I have concluded that programmers have completely different coding principles regarding this subject. Some even set and unset

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

before and after every draw call (although the setup remains unchanged), and others don’t.

I guess that's a bit overdone...

From my own experiments I have found that the only resources I have to bind on every draw call are the ShaderResourceViews (to VS and PS in my case). This requirement may be caused by the use of compute shaders since I bind/unbind UAVs to buffers that are bound to VS / PS later on.

I have lost many hours of work before I detected that this rebinding was necessary. And I guess that many coders aren’t sure either and prefer to unbind and rebind a "little too much" instead of running into a similar trap.

Question 1: Are there at least some rules of thumb regarding this problem?

Question 2: Is it possible that my ShaderResourceViews bound to VS/PS are unbound by the driver/DirectX core because I bind UAVs to the same buffers before the CS dispatch call (I don’t unbind the SRVs myself)?

Question 3: I don't even set VS/PS to null before I use the compute shaders. Works w/o problems yet I feel constantly unsure whether or not I'm digging my next trap using such a "lazy" approach.

解决方案

You want to have as less overhead, but also while avoiding invalid pipeline state. That's why some people unbind everything (try to prevent as much), it depends on uses cases, and of course you can balance this a bit.

To balance this you can pre allocate a specific resource to a slot, depending on resource type, since you have a different number of slots, different rules can apply

1/Samplers and States

You have 16 slots, and generally 4-5 samplers you use 90% of the time (linear/point/anisotropic/shadow).

So on application startup create those states and bind them to each shader stage you need (try not to start at zero slot, since they would easily be overriden by mistake). Create a shader header file with mapping SamplerState -> slot, and use it in your shaders, so any slot update is reflected automatically.

Reuse this as much as possible, and only bind custom samplers.

For standard states (Blend/Depth/Rasterizer), building a small collection of common states on application startup and bind as needed is common practice.

An easy way to minimize Render State binding at low cost, you can build a stack, so you set a default state, and if a shader needs a more specific state, it can push new state to the stack, once it's done, pop last state and apply it again to the pipeline.

2/Constant Buffers

You have 14 slots, which is quite a lot, it's pretty rare (at least in my use cases) to use all of them, specially now you can also use buffers/structuredbuffers as well.

One simple common case is setting a reserved slots for camera (with all data that you need, view/projection/viewprojection, plus their inverses since you might need that too.

Bind it to (all if needed) shader stage slots, and only thing you have to do is update your cbuffer every frame, it's ready to use anywhere.

3/Shader stages

You pretty much never need to unbind Compute Shader, since it's fully separated from the pipeline.

On the other side, for pipeline stage, instead of unbinding, a reasonably good practice is to set all the ones you need and set to null the ones you don't.

If you don't follow this as example and render a shadow map (depth buffer only), a pixel shader might still be bound.

If you forget to unset a Geometry Shader that you previously used, you might end up with invalid layout combination and your object will not render (error will only show up in runtime debug mode).

So setting the full shader stage adds little overhead, but the safety trade off is very far from negligible.

In your use case (using only VS/PS and CS to build), you can safely ignore that.

4/Uavs-RenderTargets-DepthStencil

For write resources, always unset when you done with unit of work. Within the same routine you can optimize inside, but at the end of your render/compute shader function, set your output back to null, since pipeline will not allow anything to be rebound as ShaderResource while it's on output.

Not unsetting a write resource at the end of your function is recipe for disaster.

5/ShaderResourceView

This is very situational, but idea is to minimize while also avoiding runtime warnings (which can be harmless, but then hide important messages).

One eventual thing is to reset to null all shader resource inputs at the beginning of the frame, to avoid a buffer still bound in VS to be set as UAV in CS for example, this costs you 6 pipeline calls per frame, but it's generally worth it.

If you have enough spare registers and some constant resources you can also of course set those in some reserved slots and bind those once and for all.

6/IA related resources

For this one, you need to set the right data to draw your geometry, so everytime you bind it it's pretty reasonable to set InputLayout/Topology . You can of course organize your draw calls to minimize switches.

I find Topology to be rather critical to be set properly, since invalid topology (for example, using Triangle List with a pipeline including tesselation), will draw nothing and give you a runtime warning, but it's very common that on AMD card it will just crash your driver, so better to avoid that as it becomes rather hard to debug.

Generally never really unbinding vertex/index buffers (since just overwriting them and Input layout tells how to fetch anyway). Only exception to this rule if in the case those buffers are generated in compute/stream out, to avoid the above mentioned runtime warning.

这篇关于是否有DirectX准则用于在绘制调用之间绑定和解除绑定资源?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆