Megosztás a következőn keresztül:


Belső kódtárak

Ez a témakör a DirectXMath-kódtár belső kialakítását ismerteti.

Hívási konvenciók

A hordozhatóság javítása és az adatelrendezés optimalizálása érdekében a DirectXMath-kódtár által támogatott platformokhoz a megfelelő hívási konvenciókra van szükség. Pontosabban, ha XMVECTOR objektumokat paraméterekként ad át, amelyek egy 16 bájtos határvonalon vannak meghatározva, a célplatformtól függően különböző hívási követelmények állnak rendelkezésre:

32 bites Windows

A 32 bites Windows esetében két hívási konvenció érhető el a __m128 értékek hatékony átadásához (amely XMVECTOR implementál ezen a platformon). A szabvány az __fastcall, amely az első három __m128 értéket (XMVECTOR-példányokat) argumentumként továbbíthatja egy SSE/SSE2-regiszter egyik függvényéhez. __fastcall a fennmaradó argumentumokat a veremen keresztül adja át.

Az újabb Microsoft Visual Studio-fordítók támogatják az új hívási konvenciót, a __vectorcall, amely legfeljebb hat __m128 értéket (XMVECTOR-példányt) adhat át argumentumként egy SSE/SSE2-regisztráció egyik függvényéhez. Heterogén vektor aggregátumokat (más néven XMMATRIX) is átadhat SSE/SSE2 regisztereken keresztül, ha elegendő hely áll rendelkezésre.

Windows 64 bites kiadásaihoz

A 64 bites Windows esetében két hívási konvenció érhető el __m128 értékek hatékony átadásához. A szabvány __fastcall, amely a verem összes __m128 értékét átadja.

Az újabb Visual Studio-fordítók támogatják a __vectorcall hívási konvenciót, amely legfeljebb hat __m128 értéket (XMVECTOR példányt) adhat át argumentumként egy SSE/SSE2-regisztrációban lévő függvénynek. Heterogén vektor aggregátumokat (más néven XMMATRIX) is átadhat SSE/SSE2 regisztereken keresztül, ha elegendő hely áll rendelkezésre.

Windowshoz ARM-

Az ARM-alapú Windows & ARM64 támogatja az első négy __n128 érték (XMVECTOR példányok) regisztrálását.

DirectXMath-megoldás

A FXMVECTOR, GXMVECTOR, HXMVECTORés CXMVECTOR aliasok támogatják ezeket az egyezményeket:

  • Az FXMVECTOR alias használatával adja át a függvény argumentumaként használt XMVECTOR első három példányát.
  • A GXMVECTOR alias használatával adja át egy XMVECTOR 4. példányát egy függvény argumentumaként.
  • A HXMVECTOR alias használatával adja át egy XMVECTOR argumentumként használt 5. és 6. példányát egy függvénynek. További szempontokról a __vectorcall dokumentációjában talál további információt.
  • A CXMVECTOR alias használatával adja át az argumentumként használt XMVECTOR további példányait.

Jegyzet

Kimeneti paraméterek esetén mindig használja az XMVECTOR* vagy az XMVECTOR&, és hagyja figyelmen kívül azokat a bemeneti paraméterek előző szabályaival kapcsolatban.

 

A __vectorcall korlátozásai miatt javasoljuk, hogy ne használja GXMVECTOR vagy HXMVECTOR C++ konstruktorokhoz. Csak használja FXMVECTOR az első három XMVECTOR értékhez, majd a többihez használja CXMVECTOR.

Az FXMMATRIX és CXMMATRIX aliasok segítenek kihasználni a __vectorcall átadott HVA-argumentum előnyeit.

  • Az FXMMATRIX alias használatával adja át az első XMMATRIX argumentumként a függvénynek. Ez azt feltételezi, hogy nem rendelkezik két FXMVECTOR argumentumnál, vagy több mint két lebegőpontos, dupla vagy FXMVECTOR argumentumot a mátrix "jobb oldalán". További szempontokról a __vectorcall dokumentációjában talál további információt.
  • Máskülönben használja a CXMMATRIX aliast.

A __vectorcall korlátozásai miatt javasoljuk, hogy a C++ konstruktorokhoz soha ne használjon FXMMATRIX. Csak használja CXMMATRIX.

A típus-aliasok mellett a XM_CALLCONV széljegyzetet is használnia kell, hogy a függvény a fordító és az architektúra alapján a megfelelő hívási konvenciót (__fastcall és __vectorcall) használja-e. A __vectorcall korlátozásai miatt javasoljuk, hogy ne használja a C++ konstruktorokhoz készült XM_CALLCONV.

Az alábbi példadeklarációk szemléltetik ezt az egyezményt:

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);

Ezeknek a hívási konvencióknak a támogatásához ezek a típus-aliasok az alábbiak szerint vannak definiálva (a paramétereket érték szerint kell átadni a fordítónak, hogy figyelembe vehesse őket a regisztráción belüli továbbításhoz):

32 bites Windows-alkalmazásokhoz

A __fastcall használata esetén:

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 használata esetén:

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 bites natív Windows-alkalmazásokhoz

A __fastcall használata esetén:

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 használata esetén:

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

Windows ARM-

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

Jegyzet

Bár az összes függvényt beágyazottként deklarálják, és sok esetben a fordítónak nem kell hívási konvenciót használnia ezekhez a függvényekhez, vannak olyan esetek, amikor a fordító úgy dönthet, hogy hatékonyabb, ha nem beágyazotta a függvényt, és ezekben az esetekben a legjobb hívási konvenciót szeretnénk elérni minden platformhoz.

 

Grafikus tár típusának egyenértékűsége

A DirectXMath-kódtár használatának támogatásához számos DirectXMath-kódtártípus és -struktúra egyenértékű a D3DDECLTYPE és D3DFORMAT windowsos implementációival, valamint a DXGI_FORMAT típusokkal.

DirectXMath D3DDECLTYPE D3DFORMAT DXGI_FORMAT
XMBYTE2 DXGI_FORMAT_R8G8_SINT
XMBYTE4 D3DDECLTYPE_BYTE4 (csak Xbox konzolon) D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_SINT
XMBYTEN2 D3DFMT_V8U8 DXGI_FORMAT_R8G8_SNORM
XMBYTEN4 D3DDECLTYPE_BYTE4N (csak Xbox konzolon) D3DFMT_x8x8x8x8 DXGI_FORMAT_x8x8x8x8_SNORM
XMCOLOR D3DDECLTYPE_D3DCOLOR D3DFMT_A8R8G8B8 DXGI_FORMAT_B8G8R8A8_UNORM (DXGI 1.1+)
XMDEC4 D3DDECLTYPE_DEC4 (csak Xbox konzolon) D3DDECLTYPE_DEC3 (csak Xbox konzolon)
XMDECN4 D3DDECLTYPE_DEC4N (csak Xbox konzolon) D3DDECLTYPE_DEC3N (csak Xbox konzolon)
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 és XMStoreUDecN4_XRhasználata.)
XMUDEC4 D3DDECLTYPE_UDEC4 (csak Xbox konzolon)
D3DDECLTYPE_UDEC3 (csak Xbox konzolon)
D3DFMT_A2R10G10B10
D3DFMT_A2B10G10R10
DXGI_FORMAT_R10G10B10A2_UINT
XMUDECN4 D3DDECLTYPE_UDEC4N (csak Xbox konzolon)
D3DDECLTYPE_UDEC3N (csak Xbox konzolon)
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 (csak Xbox konzolon) D3DFMT_x16x16x16x16 DXGI_FORMAT_R16G16B16A16_UINT
XMUSHORTN4 D3DDECLTYPE_USHORT4N D3DFMT_x16x16x16x16 DXGI_FORMAT_R16G16B16A16_UNORM

 

Globális állandók a DirectXMath könyvtárban

Az adatszegmens méretének csökkentése érdekében a DirectXMath-kódtár a XMGLOBALCONST makrót használja számos globális belső állandó használatára a megvalósítás során. Konvenció szerint az ilyen belső globális állandókat g_XM. Ezek általában a következő típusok egyike: XMVECTORU32, XMVECTORF32vagy XMVECTORI32.

Ezek a belső globális állandók a DirectXMath-kódtár jövőbeli változataiban változhatnak. A g_XM globális értékek közvetlen használata helyett olyan nyilvános függvényeket használjon, amelyek lehetőség szerint belefoglalják az állandókat. Saját globális állandókat is deklarálhat XMGLOBALCONSThasználatával.

Windows SSE és SSE2

Az SSE-utasításkészlet csak egy pontosságú lebegőpontos vektorok számára nyújt támogatást. A DirectXMath-nak az SSE2 utasításkészletet kell használnia az egész számvektor támogatásához. Az SSE2-t minden Intel processzor támogatja a Pentium 4 bevezetése óta, az összes AMD K8 és újabb processzor, valamint az összes x64-kompatibilis processzor.

Jegyzet

Az x86-os vagy újabb Windows 8-hoz az SSE2 támogatása szükséges. A Windows x64 minden verziója SSE2-t igényel. Az ARM/ARM64-alapú Windowshoz ARM_NEON szükséges.

 

Rutinvariánsok

A DirectXMath függvények számos változata megkönnyíti a munkát:

  • Összehasonlító függvények összetett feltételes elágaztatás létrehozásához kisebb számú vektor-összehasonlító művelet alapján. Ezeknek a függvényeknek a neve "R" végződésű, például XMVector3InBoundsR. A függvények egy összehasonlító rekordot ad vissza UINT visszatérési értékként vagy UINT out paraméterként. Az érték teszteléséhez használhatja az XMComparision* makrókat.
  • Batch-függvények a nagyobb vektortömbök kötegstílusú műveleteinek végrehajtásához. Ezeknek a függvényeknek a neve "Stream" végződik, például XMVector3TransformStream. A függvények bemenetek tömbjén működnek, és kimenetek tömbje jön létre. Általában bemeneti és kimeneti léptéket vesznek igénybe.
  • A lassabb, pontosabb eredmény helyett gyorsabb becslést megvalósító becslési függvények. Ezeknek a függvényeknek a neve "Est" végződik, például XMVector3NormalizeEst. A becslés minőségére és teljesítményére gyakorolt hatás platformonként eltérő, de javasoljuk, hogy a teljesítményérzékeny kódhoz használjon becslési változatokat.

Platform inkonzisztenciák

A DirectXMath kódtár teljesítményérzékeny grafikus alkalmazásokban és játékokban való használatra készült. Ezért az implementáció úgy lett kialakítva, hogy az optimális sebesség érdekében normál feldolgozást végez az összes támogatott platformon. A határfeltételek, különösen a lebegőpontos különlegességeket generáló eredmények valószínűleg céltól célig eltérőek. Ez a viselkedés más futásidejű beállításoktól is függ, például a Windows 32 bites no-intrinsics cél x87 vezérlőszavaitól vagy a Windows 32 bites és 64 bites SSE vezérlőszavaitól. Emellett a különböző processzorgyártók között különbségek lesznek a határfeltételek között.

Ne használja a DirectXMath-ot olyan tudományos vagy egyéb alkalmazásokban, amelyekben a numerikus pontosság a legfontosabb. Ez a korlátozás a dupla vagy más kiterjesztett pontosságú számítások támogatásának hiányában is tükröződik.

Jegyzet

A _XM_NO_INTRINSICS_ skaláris kód elérési útjai általában megfelelőségre, nem teljesítményre vannak megírva. A határfeltételek eredményei is eltérőek lesznek.

 

Platformspecifikus bővítmények

A DirectXMath kódtár célja, hogy leegyszerűsítse a C++ SIMD-programozást, amely kiváló támogatást nyújt az x86-os, x64-es és Windows RT-platformokhoz széles körben támogatott belső utasítások (SSE2 és ARM-NEON) használatával.

Vannak azonban olyan esetek, amikor a platformspecifikus utasítások előnyösnek bizonyulhatnak. A DirectXMath implementálásának módja miatt sok esetben triviális a DirectXMath-típusok közvetlen használata a standard fordító által támogatott belső utasításokban, valamint a DirectXMath tartalék elérési útja olyan platformok esetében, amelyek nem támogatják a kiterjesztett utasítást.

Íme például egy egyszerűsített példa az SSE 4.1 dot-product utasítás használatára. Vegye figyelembe, hogy a kód elérési útját explicit módon kell őriznie, hogy futásidőben ne generáljon érvénytelen utasításkivételeket. Győződjön meg arról, hogy a kód elérési útjai elég jelentős munkát végeznek az elágaztatás további költségeinek, a több kódútvonal fenntartásának összetettségének és így tovább.

#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;
}

A platformspecifikus bővítményekkel kapcsolatos további információkért lásd:

DirectXMath: SSE, SSE2 és ARM-NEON
DirectXMath: SSE3 és SSSE3
DirectXMath: SSE4.1 és SSE4.2
DirectXMath: AVX
DirectXMath: F16C és FMA
DirectXMath: AVX2
DirectXMath: ARM64

DirectXMath programozási útmutató