这实际上在 MSDN 上的DirectXMath Programmer's Guide中有详细说明:
XMVECTOR 和 XMMATRIX 类型是 DirectXMath 库的主要工作对象。每个操作都会消耗或产生这些类型的数据。与他们合作是使用图书馆的关键。但是,由于 DirectXMath 使用 SIMD 指令集,因此这些数据类型受到许多限制。如果您想充分利用 DirectXMath 函数,了解这些限制至关重要。
您应该将 XMVECTOR 视为 SIMD 硬件寄存器的代理,并将 XMMATRIX 视为四个 SIMD 硬件寄存器的逻辑分组的代理。这些类型被注释以表明它们需要 16 字节对齐才能正常工作。当它们用作局部变量时,编译器会自动将它们正确放置在堆栈中,或者当它们用作全局变量时将它们放置在数据段中。通过适当的约定,它们也可以作为参数安全地传递给函数(有关详细信息,请参阅调用约定)。
然而,堆中的分配更为复杂。因此,当您使用 XMVECTOR 或 XMMATRIX 作为要从堆分配的类或结构的成员时,您需要小心。在 Windows x64 上,所有堆分配都是 16 字节对齐的,但对于 Windows x86,它们只有 8 字节对齐。有一些选项可以从堆中分配具有 16 字节对齐的结构(请参阅正确对齐分配)。对于 C++ 程序,如果需要,您可以使用 operator new/delete/new[]/delete[] 重载(全局或特定于类)来强制执行最佳对齐。
但是,避免在类或结构中直接使用 XMVECTOR 或 XMMATRIX 通常更容易且更紧凑。相反,请使用 XMFLOAT3、XMFLOAT4、XMFLOAT4X3、XMFLOAT4X4 等作为结构的成员。此外,您可以使用向量加载和向量存储函数将数据有效地移动到 XMVECTOR 或 XMMATRIX 局部变量中,执行计算并存储结果。还有一些流函数(XMVector3TransformStream、XMVector4TransformStream 等)可以直接对这些数据类型的数组进行有效操作。
按照设计,DirectXMath 鼓励您编写高效、SIMD 友好的代码。加载或存储向量很昂贵,因此您应该尝试在加载数据的“流”模型中工作,在寄存器中大量使用它,然后写入结果。
也就是说,我完全明白,对于 SIMD 数学或 DirectX 的新手来说,这个用法有点复杂,即使对于专业开发人员来说也有点冗长。这就是为什么我还为 DirectXMath 编写了SimpleMath包装器,这使它更像是您使用 XNA Game Studio 寻找的经典数学库,例如Vector2
,具有“C++ 魔法”的类Vector3
,Matrix
涵盖所有显式加载和存储。SimpleMath 类型与 DirectXMath 巧妙地互操作,因此您可以随意混合和匹配。
另请参阅此博客文章和GitHub。
DirectXMath 故意是一个“内联”库,这意味着在优化的代码中,您不应该过多地传递变量,而只是计算较大函数中的值。已弃用 D3DX9
的 ,D3DX10
库中的 D3DXMath 库D3DX11
更老派,它依赖于函数指针表,并且性能受到调用约定开销的严重限制。
这些当然代表了不同的工程权衡。D3DXMath 能够在运行时对专门的处理器代码路径进行更多替换,但需要通过调用约定和间接开销来为这种灵活性付出代价。另一方面,DirectXMath 假定 SSE/SSE2(或 Xbox One 上的 AVX)的 SIMD 基线,因此您无需运行时检测或间接,而是积极利用内联。