在D3DXMath中,我们能够对向量类型进行乘法、加法或减法甚至除法运算,这些向量类型是D3DXVECTOR2、D3DXVECTOR3、D3DXVECTOR4结构。。。。。现在在DirectXMath化身中,我们有XMFLOAT2、XMFLOAT3、XMFLOAT 4和XMVECTOR。如果我想做任何数学运算,我必须从XMFLOAT转换为XMVECTOR,无论哪种方式,Visual Studio都会抛出错误"没有用户定义的转换"。为什么?事实上,在DirectX数学库的新版本(Windows 8.1,10)中,矢量操作略有变化。我是不是做错了什么…………?!
附言:矩阵还有一个问题,但现在我们只讨论向量。这些变化促使第三方开发人员创建自己的数学库,他们已经做到了……:)
这在MSDN上的DirectXMath程序员指南中有详细解释:
XMVECTOR和XMMIX类型是DirectXMath库的工作马。每个操作都会消耗或生成这些类型的数据。与他们合作是使用图书馆的关键。但是,由于DirectXMath使用SIMD指令集,这些数据类型受到许多限制。如果您想充分利用DirectXMath函数,了解这些限制至关重要。
您应该将XMVECTOR视为SIMD硬件寄存器的代理,将XMMIX视为四个SIMD硬件注册表的逻辑分组的代理。对这些类型进行注释,表示它们需要16字节对齐才能正常工作。当它们用作局部变量时,编译器会自动将它们正确地放置在堆栈上,或者当它们用作全局变量时,会将它们放置在数据段中。有了适当的约定,它们也可以作为参数安全地传递给函数(有关详细信息,请参阅调用约定)。
然而,堆中的分配更为复杂。因此,无论何时使用XMVECTOR或XMMIX作为要从堆中分配的类或结构的成员,都需要小心。在Windows x64上,所有堆分配都是16字节对齐的,但对于Windows x86,它们仅是8字节对齐的。有一些选项可以通过16字节对齐从堆中分配结构(请参阅正确对齐分配)。对于C++程序,如果需要,可以使用运算符new/delete/new[]/delete[]重载(全局或特定于类)来强制执行最佳对齐。
然而,避免在类或结构中直接使用XMVECTOR或XMMIX通常更容易、更紧凑。相反,请使用XMFLOAT3、XMFLOAT4、XMFLOAT 4X3、XMfloat 4X4等作为结构的成员。此外,您可以使用矢量加载和矢量存储功能将数据高效地移动到XMVECTOR或XMMIX局部变量中,执行计算并存储结果。还有一些流式传输函数(XMVector3TransformStream、XMVector4TransformStream等)可以有效地直接对这些数据类型的数组进行操作。
DirectXMath在设计上鼓励您编写高效、SIMD友好的代码。加载或存储向量的成本很高,因此您应该尝试在"流"模型中工作,在该模型中加载数据,在寄存器中大量使用它,然后编写结果。
也就是说,我完全明白,对于刚接触SIMD数学或DirectX的人来说,这种用法有点复杂,即使对于专业开发人员来说也有点冗长。这就是为什么我还为DirectXMath编写了SimpleMath包装器,使其更像您正在使用XNA Game Studio寻找的经典数学库,如Vector2
、Vector3
、Matrix
类,其中"C++魔术"掩盖了所有显式加载&商店。SimpleMath类型与DirectXMath巧妙地互操作,因此您可以根据需要进行混合和匹配。
请参阅此博客文章和GitHub。
DirectXMath是一个"内联"库,这意味着在优化的代码中,您不应该传递太多变量,而应该只计算更大函数中的值。不推荐使用的
D3DX9
、D3DX10
、D3DX11
库中的D3DXMath库更为老派,它依赖于函数指针表,并且受调用约定开销的严重性能限制。这些当然代表了不同的工程权衡。D3DXMath能够在运行时对专门的处理器代码路径进行更多的替换,但通过调用约定和间接开销来实现这种灵活性。另一方面,DirectXMath假设SIMD基线为SSE/SSE2(或Xbox One上的AVX),因此您无需运行时检测或间接检测,而是积极利用内联。