DX12 Do's And Don'ts
命令提交-命令列表(CommandList)
1.将命令交给CmdList不会开始任何工作,只有调用ExecuteCommandList,将CmdList提交给命令队列后才开始在GPU工作
2.记录命令是CPU密集型操作,CmdList不是线程自由对象,最好将工作均匀分配在各个线程中,一个CmdList只在一个线程中访问(CmdQueue好像是线程安全的)。
3.建立和重置命令列表会产生一定成本,Fence会强制拆分CmdList(用于查询FenceValue?)
4...后面是Bundle的,我暂时对这方面没什么了解
管线状态对象(PSO)
1.PSO最好异步创建【没什么感受】
2.避免运行时的PSO编译,这可能导致停顿
3.尽可能减少PSO之间的状态变化,PSO不一定会映射到GPU上的原子状态变化
我的理解:可能GPU执行的时候PSO还未完全切换完毕,如果状态变化越小(例如Blend、图元类型、光栅化设置)等越小,出问题的可能性越低。
//一堆我理解不了的
4.同一命令队列(CmdQueue)不要再计算(Compute)和图形(Graphics)之间切换,这是很消耗性能的行为
5.不要打开/关闭曲面细分,这也是消耗性能的行为
根签名(Root Signature)
1.CBV不需要用描述符表索引,这意味着不需要描述符堆(Descriptor Heap)来储存。
2.CBV、SRV、UAV值缓存在CPU中,当检测到更改时再更改根签名中的内容(龙书中是用Dirty来对三缓冲结构程序进行常量缓冲区更新)
3.限制CBV、SRV、UAV的着色器可见阶段
体现在slotRootParameter的InitAsDescriptorTable方法第三个参数设置可见性,本人是在编译着色器后,用反射检查着色器可见阶段。
4.最小化根签名更改次数
原文提到,问题不在于根签名(RS)的更改,而是在更改之后,通常要初始化根签名。
由于不确定每个着色器的根签名是否一致,每个物体在渲染之前都要设置一下根签名,而更改RS同时也意味着CommandList的PSO要切换
【不同Shader的RS可能一致吗?或许有可能,但我感觉机会很小,根签名与对各View的引用顺序、着色器可见性有关,或许对着色器约定俗称,前三个是PerPass、PerObject、PerMaterial的CBV,再设置其他数据,能提高不同Shader的RS相同的几率,但这依旧只是一个方案】
以后在渲染前,可能要对物体渲染次序做排序或归类,用同样Shader的物体同时渲染。
5.不要将更新频率不同的CBV放到一个描述符表中
我的理解是:这里提到,在Haswell这种平台下,更新描述符表中一个CBV会导致表中所有的CBV进行更新。
6.不要再同一着色器阶段同时设置访问和拒绝标志,否则,除非访问标志是D3D12_SHADER_VISIBILITY_ALL,拒绝(deny)标志是不会生效的。
这个……属于编程失误了吧?
7.不要将常量SRV、UAV直接设置在RS中,除非有很多使用它们的绘制(draw)、调度(dispatch)
看龙书以及别人的代码,SRV一般都是用描述符表存的
8.更改RS后,不要使资源绑定保持未定义状态,切换RS会删除、清除之前根签名的资源绑定。
分配器(Allocator)
1.重用命令分配器(CmdAlloc)和命令列表(CmdList),每次绘制调用数量最好相似,预热后分配速度会更快。
这样的话,命令列表和分配器最好不要池化了。
2.一帧调用之前,分配器先调用Reset方法,否则分配器会持续增长,直到耗尽内存
3.不要总是创建分配器,而是重用分配器
4.不要释放(Release)/重用(Reset)仍在使用的分配器,这是非法的(一般用围栏值确定当前帧渲染完毕再Reset)