库内部结构
本主题介绍 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 解决方案
FXMVECTOR、GXMVECTOR、HXMVECTOR 和 CXMVECTOR 别名支持以下约定:
- 使用 FXMVECTOR 别名将作为参数传递给函数的前三个 XMVECTOR 实例。
- 使用 GXMVECTOR 别名将用作参数的 XMVECTOR 的第 4 个实例传递给函数。
- 使用 HXMVECTOR 别名将用作参数的 XMVECTOR 的第 5 个和第 6 个实例传递给函数。 有关其他注意事项的信息,请参阅__vectorcall文档。
- 使用 CXMVECTOR 别名传递用作参数的 XMVECTOR 的任何进一步实例。
注意
对于输出参数,请始终使用 XMVECTOR* 或 XMVECTOR& ,并忽略上述输入参数规则。
由于__vectorcall的限制,建议不要对 C++ 构造函数使用 GXMVECTOR 或 HXMVECTOR 。 只需将 FXMVECTOR 用于前三个 XMVECTOR 值,其余值则使用 CXMVECTOR 。
FXMMATRIX 和 CXMMATRIX 别名有助于支持使用 __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 库类型和结构等效于 D3DDECLTYPE 和 D3DFORMAT 类型的 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_XR 和 XMStoreUDecN4_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 为前缀。 通常,它们是以下类型之一: XMVECTORU32、 XMVECTORF32 或 XMVECTORI32。
这些内部全局常量在 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
相关主题
反馈
https://aka.ms/ContentUserFeedback。
即将发布:在整个 2024 年,我们将逐步淘汰作为内容反馈机制的“GitHub 问题”,并将其取代为新的反馈系统。 有关详细信息,请参阅:提交和查看相关反馈