XMVECTOR学习笔记

来源:互联网 发布:发票二维码扫描软件 编辑:程序博客网 时间:2024/05/22 13:33

在DirectX Math中,向量的核心数据类型为 XMVECTOR,使用了SIMD指令集加速。
下面引用百度对SIMD的介绍:
Single Instruction Multiple Data,单指令多数据流,能够复制多个操作数,并把它们打包在大型寄存器的一组指令集。

SSE2指令集:
SSE2(Streaming SIMD Extensions 2,Intel官方称为SIMD 流技术扩展 2或数据流单指令多数据扩展指令集 2)指令集是Intel公司在SSE指令集的基础上发展起来的。相比于SSE,SSE2使用了144个新增指令,扩展了MMX技术和SSE技术,这些指令提高了广大应用程序的运行性能。

XMVECTOR的定义类似这样:typedef __m128 XMVECTOR;当我们使用它进行计算时,只利用了我们需要的维度数据,其他维度数据被置为0

XMVECTOR要求16字节对齐,对于全局变量和局部变量这是自动完成的。但在类成员变量中,我们需要利用其他定义的几个结构体XMFLOAT2(2D),XMFLOAT3 (3D),XMFLOAT4(4D)去代替。
定义如下:

struct XMFLOAT2{    float x;    float y;    XMFLOAT2() {}    XMFLOAT2(float _x, float _y) : x(_x), y(_y) {}    explicit XMFLOAT2(_In_reads_(2) const float *pArray):x(pArray[0]), y(pArray[1])     {}    XMFLOAT2& operator= (const XMFLOAT2& Float2)    { x = Float2.x; y = Float2.y; return *this; }};

但是,对于这种类型的运算是不会使用SIMD加速的,所以在调用类成员进行计算时,我们需要将其类型转化为XMVECTOR,幸运的是DirectXMath库提供了这样的转换函数。

//FLOAT TO VECTOR// Loads XMFLOAT2 into XMVECTORXMVECTOR XM_CALLCONV XMLoadFloat2(const XMFLOAT2 *pSource);// Loads XMFLOAT3 into XMVECTORXMVECTOR XM_CALLCONV XMLoadFloat3(const XMFLOAT3 *pSource);// Loads XMFLOAT4 into XMVECTORXMVECTOR XM_CALLCONV XMLoadFloat4(const XMFLOAT4 *pSource);//VECTOR TO FLOAT// Loads XMVECTOR into XMFLOAT2void XM_CALLCONV XMStoreFloat2(XMFLOAT2 *pDestination,FXMVECTOR V);// Loads XMVECTOR into XMFLOAT3void XM_CALLCONV XMStoreFloat3(XMFLOAT3 *pDestination,FXMVECTOR V);// Loads XMVECTOR into XMFLOAT4void XM_CALLCONV XMStoreFloat4(XMFLOAT4 *pDestination,FXMVECTOR V);

为了效率起见,在DX12中传参XMVECTOR 时,有一定规则的调用方式,以便利用SSE/SSE2硬件加速。
1、前三个XMVECTOR 参数调用时,应该使用FXMVECTOR
2、第四个XMVECTOR参数,应该使用GXMVECTOR
3、第五、六个XMVECTOR参数,应该使用HXMVECTOR
4、剩下的额外的参数使用CXMVECTOR
5、若其中穿插了其他类型参数,规则忽略其他参数照样生效

DirectXMath中对于不同的win平台XMVECTOR有不同的封装。(都是为了效率起见)

// 32-bit Windows __fastcall passes first 3 XMVECTOR arguments// via registers, the remaining on the stack.typedef const XMVECTOR FXMVECTOR;typedef const XMVECTOR& GXMVECTOR;typedef const XMVECTOR& HXMVECTOR;typedef const XMVECTOR& CXMVECTOR;// 32-bit Windows __vectorcall passes first 6 XMVECTOR arguments// via registers, the remaining on the stack.typedef const XMVECTOR FXMVECTOR;typedef const XMVECTOR GXMVECTOR;typedef const XMVECTOR HXMVECTOR;typedef const XMVECTOR& CXMVECTOR; 

Constant Vectors
当我们想使用常量XMVECTOR时,我们应该使用XMVECTORF32类型。
XMVECTORF32的定义如下:

// Conversion types for constants__declspec(align(16)) struct XMVECTORF32{    union    {        float f[4];        XMVECTOR v;//联合,共用一块内存    };    inline operator XMVECTOR() const { return v; } //重载了类型转换,直接可以当XMVECTOR使用    inline operator const float*() const { return f; }    #if !defined(_XM_NO_INTRINSICS_) && defined(_XM_SSE_INTRINSICS_)        inline operator __m128i() const { return_mm_castps_si128(v); }        inline operator __m128d() const { return_mm_castps_pd(v); }    #endif};//e.g.static const XMVECTORF32 g_vHalfVector = { 0.5f, 0.5f, 0.5f, 0.5f };

总结一下:
1、全局或局部变量 使用XMVECTOR e.g.XMVECTOR u = XMVectorSet(1.0f, 2.0f, 3.0f, 0.0f);
2、类成员变量使用 XMFLOAT2,XMFLOAT3,XMFLOAT4,但计算时需要转换(Store)为XMVECTOR以利用指令集加速
3、XMVECTORXMFLOATXMStoreFloat,XMFLOATXMVECTORXMLoadFloat
3、对于XMVECTOR的传参,需要遵从特殊的规则。

XMVECTOR的Setter函数
以下代码来源于DX12龙书

// Returns the zero vector 0XMVECTOR XM_CALLCONV XMVectorZero();// Returns the vector (1, 1, 1, 1)XMVECTOR XM_CALLCONV XMVectorSplatOne();// Returns the vector (x, y, z, w)XMVECTOR XM_CALLCONV XMVectorSet(float x, float y, float z, float w);// Returns the vector (s, s, s, s)XMVECTOR XM_CALLCONV XMVectorReplicate(float s);// Returns the vector (vx, vx, vx, vx)XMVECTOR XM_CALLCONV XMVectorSplatX(FXMVECTOR V);// Returns the vector (vy, vy, vy, vy)XMVECTOR XM_CALLCONV XMVectorSplatY(FXMVECTOR V);// Returns the vector (vz, vz, vz, vz)XMVECTOR XM_CALLCONV XMVectorSplatZ(FXMVECTOR V);

XMVECTOR相关常用函数
基本是所有3D向量的数学运算,包括点乘,叉乘等。不一一举例。
需要注意的是,大部分的运算都是返回XMVECTOR,但并不表示其为一个向量。例如点乘。

XMVECTOR k = XMVector3Dot(v1,v2);//k的所有分量均为点乘结果 k(v1·v2,v1·v2,v1·v2,v1·v2)

原因之一是SIMD向量操作更有效率,将使用到你计算完成。

Note:
DirectXMath库还提供了一些估算方法。当结果精度要求低但效率要求很高的时候可以使用它们

XMVECTOR XM_CALLCONV XMVector3LengthEst(FXMVECTOR V); // Input vXMVECTOR XM_CALLCONV XMVector3NormalizeEst(FXMVECTOR V); // Input v

浮点误差
由于我们在计算机上使用浮点数完成所有的向量运算,所以我们不能忽略浮点数带来的精度误差。特别是我们需要比较两个浮点数是否相等的时候。
所以在比较两个浮点数是否想等,我们应该使用这样的算法:

const float Epsilon = 0.001f;bool Equals(float lhs, float rhs){    // 判断两数之差绝对值是否小于我们定义的误差    return fabs(lhs - rhs) < Epsilon ? true : false;}

DirectXMath库给我们提供了这样的函数。

XMFINLINE bool XM_CALLCONV XMVector3NearEqual(FXMVECTOR U,FXMVECTOR V,FXMVECTOR Epsilon);
0 0