库内部结构

本主题介绍 DirectXMath 库的内部设计。

调用约定

若要增强可移植性和优化数据布局,需要对 DirectXMath 库支持的每个平台使用适当的调用约定。 具体而言,将 XMVECTOR 对象作为参数传递(定义为在 16 字节边界上对齐)时,存在不同的调用要求集,具体取决于目标平台:

对于 32 位 Windows

对于 32 位 Windows,有两个调用约定可用于有效传递 __m128 值 (在该平台) 实现 XMVECTOR 。 标准为 __fastcall,可以将前三个 __m128 值 (XMVECTOR 实例) 作为参数传递给 SSE/SSE2 寄存器中的函数。 __fastcall 通过堆栈传递剩余参数。

较新的 Microsoft Visual Studio 编译器支持新的调用约定(__vectorcall),该约定最多可将六个 __m128 值 (XMVECTOR 实例) 作为参数传递给 SSE/SSE2 寄存器中的函数。 如果有足够的空间,它还可以通过 SSE/SSE2 寄存器传递异类矢量聚合 (也称为 XMMATRIX) 。

对于 64 位版本的 Windows

对于 64 位 Windows,有两个调用约定可用于高效传递 __m128 值。 标准为 __fastcall,它传递堆栈上的所有 __m128 值。

较新的 Visual Studio 编译器支持__vectorcall调用约定,该约定最多可将六 个__m128 值 (XMVECTOR 实例) 作为参数传递给 SSE/SSE2 寄存器中的函数。 如果有足够的空间,它还可以通过 SSE/SSE2 寄存器传递异类矢量聚合 (也称为 XMMATRIX) 。

对于 ARM 上的 Windows

ARM & ARM64 上的 Windows 支持将前四个__n128值 (XMVECTOR 实例) 注册。

DirectXMath 解决方案

FXMVECTORGXMVECTORHXMVECTORCXMVECTOR 别名支持以下约定:

  • 使用 FXMVECTOR 别名将作为参数传递给函数的前三个 XMVECTOR 实例。
  • 使用 GXMVECTOR 别名将用作参数的 XMVECTOR 的第 4 个实例传递给函数。
  • 使用 HXMVECTOR 别名将用作参数的 XMVECTOR 的第 5 个和第 6 个实例传递给函数。 有关其他注意事项的信息,请参阅__vectorcall文档。
  • 使用 CXMVECTOR 别名传递用作参数的 XMVECTOR 的任何进一步实例。

注意

对于输出参数,请始终使用 XMVECTOR* 或 XMVECTOR& ,并忽略上述输入参数规则。

 

由于__vectorcall的限制,建议不要对 C++ 构造函数使用 GXMVECTORHXMVECTOR 。 只需将 FXMVECTOR 用于前三个 XMVECTOR 值,其余值则使用 CXMVECTOR

FXMMATRIXCXMMATRIX 别名有助于支持使用 __vectorcall 传递 HVA 参数。

  • 使用 FXMMATRIX 别名将第一个 XMMATRIX 作为参数传递给函数。 这假定矩阵的“右侧”没有两个以上的 FXMVECTOR 参数或两个以上的 float、double 或 FXMVECTOR 参数。 有关其他注意事项的信息,请参阅__vectorcall文档。
  • 否则,请使用 CXMMATRIX 别名。

由于__vectorcall的限制,建议不要对 C++ 构造函数使用 FXMMATRIX 。 只需使用 CXMMATRIX

除了类型别名之外,还必须使用 XM_CALLCONV 注释来确保函数使用基于编译器和体系结构的适当调用约定 (__fastcall 与 __vectorcall) 。 由于__vectorcall的限制,建议不要对 C++ 构造函数使用 XM_CALLCONV。

下面是说明此约定的示例声明:

XMMATRIX XM_CALLCONV XMMatrixLookAtLH(FXMVECTOR EyePosition, FXMVECTOR FocusPosition, FXMVECTOR UpDirection);

XMMATRIX XM_CALLCONV XMMatrixTransformation2D(FXMVECTOR ScalingOrigin,  float ScalingOrientation, FXMVECTOR Scaling, FXMVECTOR RotationOrigin, float Rotation, GXMVECTOR Translation);

void XM_CALLCONV XMVectorSinCos(XMVECTOR* pSin, XMVECTOR* pCos, FXMVECTOR V);

XMVECTOR XM_CALLCONV XMVectorHermiteV(FXMVECTOR Position0, FXMVECTOR Tangent0, FXMVECTOR Position1, GXMVECTOR Tangent1, HXMVECTOR T);

XMMATRIX(FXMVECTOR R0, FXMVECTOR R1, FXMVECTOR R2, CXMVECTOR R3)

XMVECTOR XM_CALLCONV XMVector2Transform(FXMVECTOR V, FXMMATRIX M);

XMMATRIX XM_CALLCONV XMMatrixMultiplyTranspose(FXMMATRIX M1, CXMMATRIX M2);

为了支持这些调用约定,这些类型别名的定义如下 (参数必须按值传递,编译器才能将其视为用于寄存器内传递) :

对于 32 位 Windows 应用

使用__fastcall时:

typedef const XMVECTOR  FXMVECTOR;
typedef const XMVECTOR& GXMVECTOR;
typedef const XMVECTOR& HXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;
typedef const XMMATRIX& FXMMATRIX;
typedef const XMMATRIX& CXMMATRIX;

使用__vectorcall时:

typedef const XMVECTOR  FXMVECTOR;
typedef const XMVECTOR  GXMVECTOR;
typedef const XMVECTOR  HXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;
typedef const XMMATRIX  FXMMATRIX;
typedef const XMMATRIX& CXMMATRIX;

对于 64 位本机 Windows 应用

使用__fastcall时:

typedef const XMVECTOR& FXMVECTOR;
typedef const XMVECTOR& GXMVECTOR;
typedef const XMVECTOR& HXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;
typedef const XMMATRIX& FXMMATRIX;
typedef const XMMATRIX& CXMMATRIX;

使用__vectorcall时:

typedef const XMVECTOR  FXMVECTOR;
typedef const XMVECTOR  GXMVECTOR;
typedef const XMVECTOR  HXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;
typedef const XMMATRIX  FXMMATRIX;
typedef const XMMATRIX& CXMMATRIX;

基于 ARM 的 Windows

typedef const XMVECTOR  FXMVECTOR;
typedef const XMVECTOR  GXMVECTOR;
typedef const XMVECTOR& CXMVECTOR;
typedef const XMMATRIX& FXMMATRIX;
typedef const XMMATRIX& CXMMATRIX;

注意

虽然所有函数都是内联声明的,并且在许多情况下,编译器不需要对这些函数使用调用约定,但在某些情况下,编译器可能会决定不内联函数更高效,在这些情况下,我们希望每个平台都能获得最佳调用约定。

 

图形库类型等效

为了支持使用 DirectXMath 库,许多 DirectXMath 库类型和结构等效于 D3DDECLTYPED3DFORMAT 类型的 Windows 实现,以及 DXGI_FORMAT 类型。

DirectXMath D3DDECLTYPE D3DFORMAT DXGI_FORMAT
XMBYTE2 DXGI_FORMAT_R8G8_SINT
XMBYTE4 D3DDECLTYPE_BYTE4 (仅 Xbox) D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_SINT
XMBYTEN2 D3DFMT_V8U8 DXGI_FORMAT_R8G8_SNORM
XMBYTEN4 D3DDECLTYPE_BYTE4N (仅 Xbox) D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_SNORM
XMCOLOR D3DDECLTYPE_D3DCOLOR D3DFMT_A8R8G8B8 DXGI_FORMAT_B8G8R8A8_UNORM (DXGI 1.1+)
XMDEC4 D3DDECLTYPE_DEC4 (仅 Xbox) D3DDECLTYPE_DEC3 (仅 Xbox)
XMDECN4 D3DDECLTYPE_DEC4N (仅 Xbox) D3DDECLTYPE_DEC3N (仅 Xbox)
XMFLOAT2 D3DDECLTYPE_FLOAT2 D3DFMT_G32R32F DXGI_FORMAT_R32G32_FLOAT
XMFLOAT2A D3DDECLTYPE_FLOAT2 D3DFMT_G32R32F DXGI_FORMAT_R32G32_FLOAT
XMFLOAT3 D3DDECLTYPE_FLOAT3 DXGI_FORMAT_R32G32B32_FLOAT
XMFLOAT3A D3DDECLTYPE_FLOAT3 DXGI_FORMAT_R32G32B32_FLOAT
XMFLOAT3PK DXGI_FORMAT_R11G11B10_FLOAT
XMFLOAT3SE DXGI_FORMAT_R9G9B9E5_SHAREDEXP
XMFLOAT4 D3DDECLTYPE_FLOAT4 D3DFMT_A32B32G32R32F DXGI_FORMAT_R32G32B32A32_FLOAT
XMFLOAT4A D3DDECLTYPE_FLOAT4 D3DFMT_A32B32G32R32F DXGI_FORMAT_R32G32B32A32_FLOAT
XMHALF2 D3DDECLTYPE_FLOAT16_2 D3DFMT_G16R16F DXGI_FORMAT_R16G16_FLOAT
XMHALF4 D3DDECLTYPE_FLOAT16_4 D3DFMT_A16B16G16R16F DXGI_FORMAT_R16G16B16A16_FLOAT
XMINT2 DXGI_FORMAT_R32G32_SINT
XMINT3 DXGI_FORMAT_R32G32B32_SINT
XMINT4 DXGI_FORMAT_R32G32B32A32_SINT
XMSHORT2 D3DDECLTYPE_SHORT2 D3DFMT_V16U16 DXGI_FORMAT_R16G16_SINT
XMSHORTN2 D3DDECLTYPE_SHORT2N D3DFMT_V16U16 DXGI_FORMAT_R16G16_SNORM
XMSHORT4 D3DDECLTYPE_SHORT4 D3DFMT_x16x16x16x16 DXGI_FORMAT_R16G16B16A16_SINT
XMSHORTN4 D3DDECLTYPE_SHORT4N D3DFMT_x16x16x16x16 DXGI_FORMAT_R16G16B16A16_SNORM
XMUBYTE2 DXGI_FORMAT_R8G8_UINT
XMUBYTEN2 D3DFMT_A8P8、D3DFMT_A8L8 DXGI_FORMAT_R8G8_UNORM
XMUINT2 DXGI_FORMAT_R32G32_UINT
XMUINT3 DXGI_FORMAT_R32G32B32_UINT
XMUINT4 DXGI_FORMAT_R32G32B32A32_UINT
XMU555 D3DFMT_X1R5G5B5、D3DFMT_A1R5G5B5 DXGI_FORMAT_B5G5R5A1_UNORM
XMU565 D3DFMT_R5G6B5 DXGI_FORMAT_B5G6R5_UNORM
XMUBYTE4 D3DDECLTYPE_UBYTE4 D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_UINT
XMUBYTEN4 D3DDECLTYPE_UBYTE4N D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_UNORM
DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM (使用 XMLoadUDecN4_XRXMStoreUDecN4_XR.)
XMUDEC4 D3DDECLTYPE_UDEC4 (仅 Xbox)
D3DDECLTYPE_UDEC3 (仅 Xbox)
D3DFMT_A2R10G10B10
D3DFMT_A2B10G10R10
DXGI_FORMAT_R10G10B10A2_UINT
XMUDECN4 D3DDECLTYPE_UDEC4N (仅 Xbox)
D3DDECLTYPE_UDEC3N (仅 Xbox)
D3DFMT_A2R10G10B10
D3DFMT_A2B10G10R10
DXGI_FORMAT_R10G10B10A2_UNORM
XMUNIBBLE4 D3DFMT_A4R4G4B4、D3DFMT_X4R4G4B4 DXGI_FORMAT_B4G4R4A4_UNORM (DXGI 1.2+)
XMUSHORT2 D3DDECLTYPE_USHORT2 D3DFMT_G16R16 DXGI_FORMAT_R16G16_UINT
XMUSHORTN2 D3DDECLTYPE_USHORT2N D3DFMT_G16R16 DXGI_FORMAT_R16G16_UNORM
XMUSHORT4 仅D3DDECLTYPE_USHORT4 (Xbox) D3DFMT_x16x16x16x16 DXGI_FORMAT_R16G16B16A16_UINT
XMUSHORTN4 D3DDECLTYPE_USHORT4N D3DFMT_x16x16x16x16 DXGI_FORMAT_R16G16B16A16_UNORM

 

DirectXMath 库中的全局常量

为了减小数据段的大小,DirectXMath 库使用 XMGLOBALCONST 宏在其实现中使用多个全局内部常量。 按照惯例,此类内部全局常量以 g_XM 为前缀。 通常,它们是以下类型之一: XMVECTORU32XMVECTORF32XMVECTORI32

这些内部全局常量在 DirectXMath 库的未来修订中可能会发生更改。 尽可能使用封装常量的公共函数,而不是直接使用 g_XM 全局值。 还可以使用 XMGLOBALCONST 声明自己的全局常量。

Windows SSE 与 SSE2

SSE 指令集仅支持单精度浮点向量。 DirectXMath 必须使用 SSE2 指令集来提供整数向量支持。 自推出Pentium 4、所有 AMD K8 及更高版本的处理器以及所有支持 x64 的处理器以来,所有 Intel 处理器都支持 SSE2。

注意

Windows 8 x86 或更高版本需要支持 SSE2。 所有版本的 Windows x64 都需要支持 SSE2。 ARM/ARM64 上的 Windows 需要ARM_NEON。

 

例程变体

DirectXMath 函数有多个变体,可更轻松地完成工作:

  • 比较函数,用于基于较少数量的矢量比较操作创建复杂的条件分支。 这些函数的名称以“R”结尾,例如 XMVector3InBoundsR。 函数以 UINT 返回值或 UINT out 参数的形式返回比较记录。 可以使用 XMComparision* 宏来测试值。
  • 用于对较大的矢量数组执行批处理样式操作的批处理函数。 这些函数的名称以“Stream”结尾,例如 XMVector3TransformStream。 函数对输入数组进行操作,并生成输出数组。 通常,它们采用输入和输出步幅。
  • 实现更快估计而不是更慢、更准确的结果的估计函数。 这些函数的名称以“Est”结尾,例如 XMVector3NormalizeEst。 使用估算对质量和性能的影响因平台而异,但我们建议对性能敏感型代码使用估算变体。

平台不一致

DirectXMath 库适用于性能敏感的图形应用程序和游戏。 因此,实现旨在以最佳速度在所有受支持的平台上执行正常处理。 边界条件下的结果(尤其是生成浮点特殊值的结果)可能因目标而异。 此行为还取决于其他运行时设置,例如 Windows 32 位无内部函数目标的 x87 控制字或 Windows 32 位和 64 位的 SSE 控件字。 此外,各种 CPU 供应商之间的边界条件也存在差异。

请勿在数字准确性至关重要的科学或其他应用中使用 DirectXMath。 此外,这种限制反映在对双精度计算或其他扩展精度计算的支持不足上。

注意

_XM_NO_INTRINSICS_标量代码路径通常是为了符合性而不是性能而编写的。 它们的边界条件结果也会有所不同。

 

特定于平台的扩展

DirectXMath 库旨在简化 C++ SIMD 编程,使用广泛支持的内部函数指令 (SSE2 和 ARM-NEON) 为 x86、x64 和 Windows RT 平台提供出色的支持。

但是,有时,特定于平台的指令可能会证明是有益的。 由于 DirectXMath 的实现方式,在许多情况下,直接在标准编译器支持的内部函数语句中使用 DirectXMath 类型以及将 DirectXMath 用作不支持扩展指令的平台的回退路径是微不足道的。

例如,下面是利用 SSE 4.1 点产品指令的简化示例。 请注意,必须显式保护代码路径,以避免在运行时生成无效指令异常。 确保代码路径执行足够重要的工作,以证明分支的额外成本、维护多个代码路径的复杂性等。

#include <Windows.h>
#include <stdio.h>

#include <DirectXMath.h>

#include <intrin.h>
#include <smmintrin.h>

using namespace DirectX;

bool g_bSSE41 = false;

void DetectCPUFeatures()
{
#ifndef _M_ARM
   // See __cpuid documentation on MSDN for more information

   int CPUInfo[4] = {-1};
#if defined(__clang__) || defined(__GNUC__)
   __cpuid(0, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);
#else
   __cpuid(CPUInfo, 0);
#endif

   if ( CPUInfo[0] >= 1 )
   {
#if defined(__clang__) || defined(__GNUC__)
        __cpuid(1, CPUInfo[0], CPUInfo[1], CPUInfo[2], CPUInfo[3]);
#else
        __cpuid(CPUInfo, 1);
#endif

       if ( CPUInfo[2] & 0x80000 )
           g_bSSE41 = true;
   }
#endif
}

int main()
{
   if ( !XMVerifyCPUSupport() )
       return -1;

   DetectCPUFeatures();

   ...

   XMVECTORF32 v1 = { 1.f, 2.f, 3.f, 4.f };
   XMVECTORF32 v2 = { 5.f, 6.f, 7.f, 8.f };

   XMVECTOR r2, r3, r4;

   if ( g_bSSE41 )
   {
#ifndef _M_ARM
       r2 = _mm_dp_ps( v1, v2, 0x3f );
       r3 = _mm_dp_ps( v1, v2, 0x7f );
       r4 = _mm_dp_ps( v1, v2, 0xff );
#endif
   }
   else
   {
       r2 = XMVector2Dot( v1, v2 );
       r3 = XMVector3Dot( v1, v2 );
       r4 = XMVector4Dot( v1, v2 );
   }

   ...

   return 0;
}

有关特定于平台的扩展的详细信息,请参阅:

DirectXMath:SSE、SSE2 和 ARM-NEON
DirectXMath:SSE3 和 SSSE3
DirectXMath:SSE4.1 和 SSE4.2
DirectXMath:AVX
DirectXMath:F16C 和 FMA
DirectXMath:AVX2
DirectXMath:ARM64

DirectXMath 编程指南