連結庫內部
本主題描述 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 解決方案
FXMVECTOR、GXMVECTOR、HXMVECTOR 和 CXMVECTOR 別名支援下列慣例:
- 使用 FXMVECTOR 別名來傳遞至作為函式自變數的 XMVECTOR 前三個實例。
- 使用 GXMVECTOR 別名,將做為函式自變數的 XMVECTOR 第 4 個實例傳遞。
- 使用 HXMVECTOR 別名,將做為函式自變數的 XMVECTOR 第 5 個和第 6 個實例傳遞。 如需其他考慮的詳細資訊,請參閱__vectorcall檔。
- 使用 CXMVECTOR 別名傳遞作為自變數之 XMVECTOR 的任何進一步實例。
注意
針對輸出參數,請一律使用 XMVECTOR* 或 XMVECTOR& ,並忽略它們與輸入參數的上述規則有關。
由於__vectorcall的限制,建議您不要針對C++建構函式使用 GXMVECTOR 或 HXMVECTOR。 只要針對前三個 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_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。
注意
適用於 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 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