連結庫內部

本主題描述 DirectXMath 連結庫的內部設計。

呼叫慣例

若要增強可移植性並優化數據配置,您必須針對 DirectXMath Library 支援的每個平臺使用適當的呼叫慣例。 具體來說,當您將 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 解決方案

FXMVECTORGXMVECTORHXMVECTOR 和 CXMVECTOR 別名支援下列慣例:

  • 使用 FXMVECTOR 別名來傳遞至作為函式自變數的 XMVECTOR 前三個實例。
  • 使用 GXMVECTOR 別名,將做為函式自變數的 XMVECTOR 第 4 個實例傳遞。
  • 使用 HXMVECTOR 別名,將做為函式自變數的 XMVECTOR 第 5 個和第 6 個實例傳遞。 如需其他考慮的詳細資訊,請參閱__vectorcall檔。
  • 使用 CXMVECTOR 別名傳遞作為自變數之 XMVECTOR 的任何進一步實例。

注意

針對輸出參數,請一律使用 XMVECTOR* 或 XMVECTOR& ,並忽略它們與輸入參數的上述規則有關。

 

由於__vectorcall的限制,建議您不要針對 C++ 建構函式使用 GXMVECTORHXMVECTOR 。 只要針對前三個 XMVECTOR 值使用 FXMVECTOR,然後針對其餘值使用 CXMVECTOR

FXMMATRIX 和 CXMMATRIX 別名可協助支援利用使用 __vectorcall 傳遞的 HVA 自變數。

  • 使用 FXMMATRIX 別名,將第一個 XMMATRIX 當做自變數傳遞至函式。 這假設您沒有兩個 以上的 FXMVECTOR 自變數或兩個以上的浮點數、雙精度浮點數或 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_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。

注意

適用於 x86 或更新版本的 Windows 8 需要 SSE2 的支援。 所有版本的 Windows x64 都需要支援 SSE2。 ARM / ARM64 上的 Windows 需要ARM_NEON。

 

例程變體

DirectXMath 函式有數個變體,可讓您更輕鬆地執行您的工作:

  • 比較函式會根據較少的向量比較作業,建立複雜的條件式分支。 這些函式的名稱以 「R」 結尾,例如 XMVector3InBoundsR。 函式會以 UINT 傳回值或 UINT out 參數傳回比較記錄。 您可以使用 XMComparision* 宏來測試值。
  • 用於在較大的向量陣列上執行批次樣式作業的 Batch 函式。 這些函式的名稱會以 「Stream」 結尾,例如 XMVector3TransformStream。 函式會在輸入數位上運作,併產生輸出陣列。 一般而言,它們會採用輸入和輸出步幅。
  • 實作更快速估計的估計函式,而不是較慢且更精確的結果。 這些函式的名稱結尾為 「Est」 例如 XMVector3NormalizeEst 使用估計的品質和效能影響會因平臺而異,但我們建議您針對區分效能的程式代碼使用估計變異。

平臺不一致

DirectXMath 連結庫適用於效能敏感的圖形應用程式和遊戲。 因此,實作是針對在所有支援平臺上執行正常處理的最佳速度所設計。 界限條件的結果,特別是產生浮點特殊項目的結果,可能會因目標而異。 此行為也會取決於其他運行時間設定,例如 Windows 32 位無內部函數目標的 x87 控制字或 Windows 32 位和 64 位的 SSE 控件字。 此外,各種 CPU 廠商之間的界限條件會有差異。

請勿在數值精確度至關重要的科學或其他應用程式中使用 DirectXMath。 此外,這項限制反映在對雙精度計算或其他擴充精確度計算的支援不足。

注意

_XM_NO_INTRINSICS_純量程式代碼路徑通常是為了符合規範而撰寫,而不是效能。 其界限條件結果也會有所不同。

 

平臺特定擴充功能

DirectXMath 連結庫旨在使用廣泛支援的內建指令(SSE2 和 ARM-NEON),簡化 C++ SIMD 程式設計,為 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 程式設計指南