3

如果我的顶点位置是共享的,但我的法线和 UV 不是(以保留硬边缘等),是否可以在 DirectX11 中使用非交错缓冲区来解决此内存表示,以便我可以使用索引缓冲区与它? 或者我应该坚持交错缓冲区中重复的顶点位置吗?

交错和非交错顶点缓冲区之间是否存在任何性能问题?谢谢!

4

3 回答 3

14

如何

有几种方法。我将描述一个最简单的。

只需创建单独的顶点缓冲区:

ID3D11Buffer* positions;
ID3D11Buffer* texcoords;
ID3D11Buffer* normals;

创建输入布局元素,InputSlot为每个组件递增成员:

{ "POSITION",  0,  DXGI_FORMAT_R32G32B32_FLOAT,  0, 0,                            D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "TEXCOORD",  0,  DXGI_FORMAT_R32G32_FLOAT,     1, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "NORMAL",    0,  DXGI_FORMAT_R32G32B32_FLOAT,  2, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
                                             //  ^
                                             // InputSlot

将缓冲区绑定到它们的插槽(最好一次性完成):

ID3D11Buffer** vbs = {positions, texcoords, normals};
unsigned int strides[] = { /*strides go here*/ };
unsigned int offsets [] = { /*offsets go here*/ };
m_Context->IASetVertexBuffers(0, 3, vbs, strides, offsets );

像往常一样画。您不需要更改 HLSL 代码(HLSL 会认为它有单个缓冲区)。

请注意,代码片段是即时编写的,可能包含错误。

编辑:您可以改进这种方法,按更新率组合缓冲区:如果texcoords并且normals从未更改,则合并它们。

从表现来看

这完全与引用的局部性有关:数据越近,访问速度越快。

在大多数情况下,交错缓冲区(到目前为止)为 GPU 端(即渲染)提供了更多性能:对于每个顶点,每个属性彼此靠近。但是单独的缓冲区提供了更快的 CPU 访问:数组是连续的,每个下一个数据都接近前一个。

因此,总体而言,性能问题取决于您写入缓冲区的频率。如果您的限制因素是 CPU 写入,请坚持使用单独的缓冲区。如果没有,就去单身吧。

你怎么知道?只有一种方式——配置文件。CPU 端和 GPU 端(通过 GPU 供应商的图形调试器/分析器)。

另一个因素

最佳做法是限制 CPU 写入,因此,如果您发现您受到缓冲区更新的限制,您可能需要重新查看您的方法。如果我们有 500 fps,我们是否需要每帧更新缓冲区?如果您将缓冲区更新速率降低到每秒 30-60 次(从帧更新中取消绑定缓冲区更新),用户不会看到差异。所以,如果你的更新策略是合理的,你可能永远不会受到 CPU 的限制,最好的方法是经典的交错。

您还可以考虑重新设计数据管道,甚至以某种方式离线准备数据(我们称之为“烘焙”),因此您无需处理非交错缓冲区。这也将是相当合理的。

减少内存占用或提高性能?

内存与性能的权衡。这是永恒的问题。复制内存以利用交错?或不?

答案是......“这取决于”。您正在编写新的 CryEngine,针对具有千兆字节内存的顶级 GPU?或者您正在为内存资源缓慢且有限的移动平台嵌入式系统编程?1 兆字节的内存值得麻烦吗?或者你有巨大的模型,每个 100 MB?我们不知道。

这一切都由你决定。但请记住:没有免费的糖果。如果您发现内存经济值得性能损失,那就去做吧。配置文件并进行比较以确定。

希望它以某种方式有所帮助。快乐编码!=)

于 2013-11-07T04:59:23.340 回答
2

交错/分离将主要影响您的输入汇编器阶段(GPU 端)。

Interleaved 的完美场景是当您的缓冲区内存安排完全适合您的顶点着色器输入时。所以你的输入汇编器可以简单地获取数据。

在这种情况下,即使使用大型模型(相同数据的两个版本,一个交错,一个单独)进行测试,您也完全可以使用交错,TimeStamp 查询没有报告任何重大差异(一些非常小的顶点处理和基本像素着色器)。

现在拥有单独的缓冲区可以更容易地进行微调,以防您在不同的上下文中使用几何体。

假设你有位置/法线/紫外线(就像你的情况一样)。

现在,您的管道中还有一个只需要位置的着色器(阴影贴图就是一个很好的例子)。

使用单独的缓冲区,您可以简单地创建一个仅包含位置的新输入布局,然后绑定该缓冲区。您的 IA 阶段只需加载该缓冲区。最重要的是,您甚至可以使用着色器反射动态地做到这一点。

如果您绑定交错数据,您将有一些开销,因为它必须跨步加载。

当我测试那个时,我使用分离而不是交错获得了大约 20% 的增益,这可能相当不错,但由于这种类型的处理在很大程度上取决于架构,所以不要认为这是理所当然的(用于测试的 NVidia 740M)。

所以简单地说,分析(很多),并检查哪个可以让您在 GPU 和 CPU 负载之间取得最佳平衡。

另请注意,输入汇编器的开销会因着色器的复杂性而降低,如果您应用一些繁重的计算 + 添加一些细分 + 一些体面的着色,则交错/非交错之间的时间差将逐渐变得毫无意义。

于 2013-11-07T16:51:32.530 回答
0

您应该坚持使用交错缓冲区。任何其他技术都需要对非重复位置缓冲区进行某种形式的间接寻址,这会降低性能和缓存效率。

于 2013-11-06T20:39:33.247 回答