1

我有一个内置在 D3D 中的正在运行的 3D 引擎(通过 SlimDX)。为了避免打断渲染管道,我将许多具有相同材质的对象组合成更大的网格(以减少状态切换)。这运作良好,并为我的需要提供了良好的性能水平。

我遇到的问题是,在运行期间,我需要更改那些较大批量网格的某些子集的材质属性。我为此使用了属性缓冲区,并且它运行得相当好。我之前一直在使用有限数量的活动属性(每个网格大约 5 个),但现在发现需要对材料有更多的变化(不同的不透明度/颜色混合),因此最终可能有数百个或更多的组合。而且由于这些更改发生在运行时,我无法在渲染开始之前将它们捆绑在一起。当然我可以重新构建网格,但我宁愿不这样做,因为它很慢,并且需要以交互速度在材质之间切换回来和第四次。

所以我的问题是,最好的路线是什么?

  • 我是否应该实现一个更强大的属性处理系统,根据需要使用可用的属性 ID 动态屏蔽面部,然后在完成后重置它们?我听说属性缓冲区中的碎片会产生额外的性能影响,我也不确定后续 DrawSubset() 调用之间的材料切换(即何时太多以及何时应该优化我的属性数组?)。有这方面经验的人吗?

  • 我的另一个想法是使用参数化像素着色器。我不需要任何花哨的效果,只需要最低限度(当前是内置的平面着色器,仅具有颜色和某些对象的透明度),因此着色器模型 1 足以满足我的需求。这里的想法是使用一个通用着色器,而不是在调用之间切换材质,只需更改一些着色器参数。但我不知道这是否比切换材质更快和/或可编程着色器是否比内置着色器慢(给出相同的结果)。

我也很好奇切换网格或在一个大网格中绘制不同子集之间的性能差异(两种情况下的材质切换数量相同)。

我知道这些问题在 GFX 卡及其各自的性能/年龄之间可能会有所不同,但我只是在这里寻找一般指导方针,了解最关注什么(即哪种类型的状态开关/CPU 干扰最大) GPU命中)。内存也是一个问题,因此任何复制整个(或大部分)网格的实现对我来说都是不可能的。

我的重点是较旧(5 年)/功能较差/集成 GFX 卡的性能,而不一定是顶级游戏玩家卡或工作站卡(如 Quadro)。我猜这可能会根据特定板上着色器性能的好坏来使用着色器来制造或破坏解决方案。

非常感谢任何和所有建议和反馈。

提前谢谢了!

4

1 回答 1

3

更改着色器参数将同样缓慢。理想情况下,您想编写一个基于 Shader 2 的着色器,将大部分属性缓冲区上传到显卡。然后,您有一个每个顶点属性字段,可以选择适当的属性缓冲区。

您的性能问题将来自您使用的绘图调用数量。您使用的绘图调用越多,性能受到的影响就越大。着色器常量或纹理的任何更改都需要新的 DIP 调用。您要做的是最小化着色器常量修改的数量和 DIP 调用的数量。

它变成了一个相当复杂的过程心智。

例如,如果您正在处理一个包含 64 个骨骼的骨骼模型,那么您有 2 个选项。1 您为每个骨骼的网格数据设置世界矩阵并调用 DIP. 或者您一次加载尽可能多的骨骼矩阵,并在顶点上使用一个值来选择它将使用哪个骨骼并调用 DIP 一次。第二个会更快。您可能会发现您也可以通过这种方式轻松地进行一些基于多骨骼的蒙皮。

这适用于导致着色器不断变化的任何事情。请注意,即使您可能使用固定功能管道,大多数现代图形硬件(即自 2002 年发布的 Radeon 9700 以来的任何硬件)都会将固定功能转换为基于着色器的功能,因此同样存在性能问题。

基本上要避免的事情是任何导致您进行另一个 DIP 呼叫的事情。显然,避免对所有事情都这样做是不切实际的,并且某些更改成本更低。作为一个粗略的经验法则,按照费用顺序,要避免的事情如下:(请注意,自从我测试这个以来已经有一段时间了,所以你可能想要对这个主题进行一些测试和替代阅读)

1) 更改着色器
2) 更改纹理
3) 更改着色器常数
4) 更改顶点缓冲区
5) 更改索引缓冲区

1是目前最贵的。

与其他版本相比,4 和 5 相当便宜,但不同的顶点格式可能会导致更大的问题,因为它可能会导致着色器发生变化。

编辑:我不完全确定为什么更改常量会造成如此大的伤害。我原以为这样的变化会很好地进行。也许在现代硬件上不是这样的问题。在一些早期的硬件上,常量被编译到着色器中,因此更改常量会导致完整的着色器更改。

与任何事情一样,您最好尝试看看会发生什么。

对所有调用进行排序的一个有趣的解决方案是使用排序键,其中前 8 位为您提供着色器 ID。接下来的 10 位用于纹理等。然后,您执行正常的数字排序,您可以轻松地使用不同的排序顺序来查看什么可以提供最佳性能:)

Edit2:值得注意的是,任何更改像素着色器状态的操作都比更改顶点缓冲区状态的成本更高,因为它在管道中更深。因此,它需要更长的时间才能通过...

于 2009-11-20T10:45:35.133 回答